4. DroneCAN消息与数据类型

在 DroneCAN 网络中,节点之间交换的不是“裸字节”,而是有明确语义的消息。每条消息都由一个唯一的消息 ID 标识,其内部字段按照固定的数据类型和顺序排列,最终被序列化成紧凑的二进制帧。理解消息结构、数据类型、数组、联合体、常量与类型签名,是编写自定义 DroneCAN 设备固件、生成 DSDL 定义以及排查通信问题的前提。

一、消息结构:从 DSDL 到二进制帧

DroneCAN 使用 DSDL(DroneCAN Specification Description Language)描述消息。一个 DSDL 文件通常包含消息名称、字段列表、常量定义以及可选的类型签名。编译工具链(如 dronecan_dsdlc)会把 DSDL 编译成 C/C++ 头文件,生成序列化与反序列化函数。

消息在传输时遵循“字段顺序即编码顺序”的原则:先声明的字段先编码,且每个字段按自身类型占用固定或可变字节数。接收方必须使用完全相同的 DSDL 定义才能正确解析,否则会出现字段错位、数值异常甚至节点掉线。

核心规则:消息 ID 决定“这是什么消息”,DSDL 定义决定“消息里有什么”,类型签名决定“这份定义是否与对端一致”。

二、数据类型:基础类型与取值范围

DroneCAN 的基础数据类型分为整型、浮点型和布尔型。整型又分为有符号与无符号,并支持 8/16/32/64 位宽度。浮点型支持 16 位半精度和 32 位单精度。布尔型在编码时通常占 1 位,但为了对齐方便,实际实现中常按 1 字节处理。

类型 位宽 取值范围/说明 典型用途
uint8 / int8 8 0~255 / -128~127 状态码、小范围计数
uint16 / int16 16 0~65535 / -32768~32767 PWM、温度、电压
uint32 / int32 32 0~4294967295 / -2147483648~2147483647 时间戳、计数器
uint64 / int64 64 大范围整数 高精度时间、唯一 ID
float16 16 半精度浮点 节省带宽的物理量
float32 32 单精度浮点 姿态、位置、速度
bool 1 true / false 使能标志、状态位

选择类型时,应优先考虑“够用且不溢出”。例如电池电压用 uint16 表示毫伏即可覆盖 0~65.535V,没必要用 float32;而姿态四元数必须用 float32,否则精度不足会导致控制抖动。

三、数组与联合体:表达重复与多选一

数组用于表达同一类型的一组数据。DroneCAN 支持固定长度数组和可变长度数组。固定长度数组在编码时直接连续排列;可变长度数组会先编码一个长度字段,再编码实际元素。数组元素可以是基础类型,也可以是嵌套消息类型。

# 固定长度数组示例
float32[3] gyro_xyz
uint8[16] node_id_list

# 可变长度数组示例
float32[<=8] temperatures
uint8[<=255] payload

联合体(union)用于表达“多个字段中同一时刻只有一个有效”的场景。在 DSDL 中,联合体通常通过标签字段加多个可选字段实现。接收方先读取标签,再根据标签决定后续解析哪个字段。联合体在 DroneCAN 中不如数组常见,但在某些配置消息或诊断消息中会出现。

使用可变长度数组时,务必在 DSDL 中写明最大长度(如 <=8),否则编译工具无法确定缓冲区大小,可能导致栈溢出或传输失败。

四、常量与类型签名:保证语义一致

常量用于在 DSDL 中定义固定值,例如枚举、模式选择、状态码。常量不参与序列化,但会出现在生成的头文件中,供应用程序直接引用。常见写法如下:

uint8 MODE_IDLE = 0
uint8 MODE_ARMED = 1
uint8 MODE_FAILSAFE = 2

uint8 mode

类型签名(type signature)是 DroneCAN 保证消息兼容性的关键机制。它由 DSDL 编译器根据消息的字段名、类型、顺序和常量计算出一个 64 位哈希值。当两个节点使用不同版本的 DSDL 通信时,类型签名不一致会导致消息被丢弃或节点拒绝通信。因此,修改消息定义后必须重新编译并同步更新所有相关节点。

DroneCAN 消息:从 DSDL 定义到二进制帧与类型签名 DSDL 消息定义 消息名称 + 消息 ID 字段:类型 + 名称 数组 / 联合体 常量定义 类型签名(64 位哈希) 编译工具:dronecan_dsdlc 编译 序列化后的二进制帧 消息 ID(如 3800) 字段 1:uint16 电压 字段 2:float32 电流 字段 3:uint8[4] 状态 字段 4:bool 使能 按字段声明顺序连续编码 校验 类型签名校验 发送方签名 0x8A3F...C21D 接收方签名 0x8A3F...C21D 一致 → 接收 不一致 → 丢弃 消息 ID 决定语义,DSDL 决定结构,类型签名决定兼容性

五、案例与排障:一次“字段错位”的复盘

某团队在调试电池管理节点时,飞控端收到的电压值始终为 0,而电流值却异常巨大。排查发现:电池节点固件使用的 DSDL 中,voltage 字段为 uint16,而飞控端使用的旧版 DSDL 中该字段为 float32。由于类型签名不一致,飞控端本应丢弃该消息,但测试时关闭了签名校验,导致按错误类型解析,出现字段错位。

不要为了“先跑通”而关闭类型签名校验。签名校验是 DroneCAN 防止协议不兼容的第一道防线,关闭后问题会以更隐蔽的方式出现。

正确的做法是:修改 DSDL 后重新编译,确保所有节点的类型签名一致;在 CI 流程中加入 DSDL 编译检查;对关键消息保留版本号或使用新的消息 ID,避免直接修改已发布消息的字段类型。

六、方法流程:定义一条自定义消息

  1. 确定消息用途与消息 ID,避免与现有标准消息冲突。
  2. 在 DSDL 文件中声明字段,选择合适的数据类型与数组长度。
  3. 如需枚举或模式选择,使用常量定义,不要硬编码魔法数字。
  4. 运行 dronecan_dsdlc 生成头文件,检查类型签名。
  5. 在节点固件中调用生成的编码/解码函数,完成收发。
  6. 使用 dronecan_gui_tool 或总线抓包工具验证字段值与签名。

掌握消息结构、数据类型、数组、联合体、常量与类型签名,就能在 DroneCAN 网络中定义出既紧凑又可靠的通信接口,为后续设备开发打下坚实基础。