3. 数据类型与信号:Simulink数据类型、总线信号、数据字典基础

做飞控代码生成,数据类型这块儿,我见过太多人栽跟头了。

你想想看,Simulink里默认都是double,仿真跑得飞起。一生成代码,好家伙,嵌入式芯片哪受得了这个?浮点运算又慢又占内存。所以这一章,咱们就把数据类型、总线信号、数据字典这几个硬骨头啃下来。

3.1 Simulink数据类型:别让double拖垮你的飞控

Simulink支持的数据类型挺多的。但做飞控,常用的就那几个:

数据类型 位宽 取值范围 飞控中典型用途
double 64位 ±1.7e308 仿真调试、参数标定
single 32位 ±3.4e38 姿态解算、控制律计算
int16 16位 -32768 ~ 32767 传感器原始数据(加速度计、陀螺仪)
uint16 16位 0 ~ 65535 PWM捕获值、ADC采样值
uint8 8位 0 ~ 255 状态机、标志位、遥控器通道
boolean 1位 true/false 解锁状态、故障标志

我个人习惯:控制律计算用single就够了。飞控的传感器精度也就16位,你用double算半天,最后输出给电机还是16位的PWM,纯属浪费。

核心原则:能用定点不用浮点,能用单精度不用双精度。代码生成后,RAM和Flash能省下一大半。

3.2 总线信号:把散乱的信号装进一个篮子里

飞控系统里信号太多了。加速度计三个轴、陀螺仪三个轴、磁力计三个轴、GPS经纬高……你如果一根一根线连,模型图能画到天上去。

总线信号就是干这个的。它把相关的信号打包成一个整体。

3.2.1 虚拟总线 vs 非虚拟总线

这里有个坑,我刚开始做的时候踩过。

  • 虚拟总线:只在Simulink里存在,生成代码时会被展开。说白了就是个视觉上的分组工具。
  • 非虚拟总线:生成代码时会保留为结构体。这个才是我们真正需要的。

我建议:只要涉及到代码生成,一律用非虚拟总线。怎么设置?在Bus Creator模块上右键,勾选「Output as nonvirtual bus」就行。

注意:非虚拟总线要求所有输入信号的数据类型必须一致。如果你把int16和single混在一起,Simulink会报错。解决办法是用Data Type Conversion模块统一类型。

3.2.2 总线对象的定义

用Bus Editor定义总线对象,这是标准做法。

// 生成代码后,总线会变成这样的结构体
typedef struct {
  int16_T x;
  int16_T y;
  int16_T z;
} IMU_Accel;

typedef struct {
  IMU_Accel accel;
  IMU_Gyro  gyro;
  IMU_Mag   mag;
} IMU_Data;

你看,代码里清清楚楚。以后维护起来,看结构体就知道数据怎么组织的。

3.3 数据字典:把参数和模型解耦

做飞控最怕什么?参数写死在模型里。

你今天调个PID参数,明天改个滤波系数,每次都要打开模型改常数模块。改完还得重新生成代码,烦不烦?

数据字典就是解决这个问题的。

3.3.1 什么是数据字典

说白了,就是一个中央仓库。所有参数、数据类型、总线定义都放在里面。模型通过名字去引用,而不是直接写数值。

我的经验:把数据字典和模型文件分开管理。模型里只放逻辑,字典里只放参数。这样团队协作时,一个人改参数,另一个人改逻辑,互不干扰。

3.3.2 创建数据字典的步骤

  1. 在MATLAB里输入 Simulink.data.dictionary.create('myDict.sldd')
  2. 在Model Explorer里添加参数条目
  3. 在模型中使用 myParam 这样的变量名
  4. 将模型关联到数据字典

举个例子。你有一个PID比例系数 Kp:

// 在数据字典中定义
Name: Kp_Roll
Value: 2.5
DataType: single
StorageClass: ExportedGlobal

// 在模型中使用
增益模块直接填 Kp_Roll

// 生成代码后
extern real32_T Kp_Roll;  // 可以在外部修改

这样,你只需要修改数据字典里的数值,重新生成代码,参数就更新了。模型本身不用动一根线。

3.4 知识体系总览

这一章的内容,我用一张图给你捋清楚:

数据类型与信号知识体系 数据类型 • double:仿真调试 • single:控制律计算 • int16:传感器数据 • uint16:PWM/ADC • uint8:状态/标志位 • boolean:解锁/故障 原则:够用就好,别浪费 总线信号 • 虚拟总线:仅视觉分组 • 非虚拟总线:生成结构体 • Bus Editor定义对象 • 类型必须一致 坑:混用类型会报错 用Data Type Conversion统一 数据字典 • 中央参数仓库 • 模型与参数解耦 • 支持多种存储类型 • 团队协作利器 优点:改参数不改模型 重新生成代码即可 三者配合:数据类型定精度 → 总线信号定结构 → 数据字典定参数

3.5 避坑指南

最后,分享几个我实际项目中踩过的坑:

  • 我曾经把加速度计数据用double传给总线,结果生成的结构体里每个字段都是64位。一个IMU数据结构体就占了48字节。改成int16后,直接缩到6字节。
  • 还有一次,数据字典里定义了一个参数叫Kp,模型里也用了Kp。但同事在另一个分支里把Kp改成了Kp_Roll。合并代码时,模型找不到参数,编译报错。所以命名规范一定要统一。
  • 嗯,这里要注意:数据字典的StorageClass选ExportedGlobal,生成的变量才是全局可访问的。如果选Auto,编译器可能把它优化掉,你外部程序就改不了参数了。

小技巧:调试阶段,可以在数据字典里把参数都设成ExportedGlobal。等代码稳定了,再根据需要调整存储类型。这样前期调试方便,后期优化也灵活。

数据类型、总线信号、数据字典,这三样东西搞明白了,你的Simulink模型才算真正具备了工程落地的能力。别嫌麻烦,前期多花点时间定义清楚,后面代码生成和调试能省下几倍的时间。


公众号:蓝海资料掘金营,微信deep3321