3. UAVCAN数据类型与DSDL
在UAVCAN节点开发中,数据能否被正确收发,首先取决于“数据长什么样”是否被双方一致理解。UAVCAN用DSDL(Data Structure Description Language,数据结构描述语言)来定义总线上传输的数据结构。DSDL不是编程语言,而是一种接口契约:它规定字段名称、数据类型、排列顺序、数组长度以及消息与服务的边界。节点固件、上位机工具、仿真模型都依据同一份DSDL生成对应的序列化与反序列化代码,从而保证跨平台、跨编译器的一致性。
一、DSDL语法核心:从文件到类型
一个DSDL文件通常以固定头部开始,声明该数据类型的完整名称、默认传输优先级和数据类型签名。随后是字段定义区,每行定义一个字段,格式为“数据类型 字段名”。DSDL支持的基本类型包括布尔、有符号/无符号整数、浮点数以及它们的数组形式。数组分为定长数组和变长数组,定长数组用方括号内写长度,变长数组用方括号内留空表示。字段名必须唯一,且不能与保留字冲突。
# 示例:一个简单的传感器消息DSDL
uavcan.sensors.RawImu.0.1
default_dtid: 0
signature: 0x1234567890ABCDEF
float16[3] acceleration
float16[3] gyro
float16 temperature
uint8 status
上述定义中,uavcan.sensors.RawImu.0.1是完整类型名,0.1表示主版本与次版本。字段按声明顺序在内存中排列,序列化时也按此顺序写入字节流。DSDL还支持常量定义,常量不占用传输字节,只在编译期参与校验。理解DSDL语法是后续所有开发工作的起点,因为任何字段顺序或类型错误都会导致总线数据解析失败。
二、消息与服务的区别:单向广播与双向请求
UAVCAN中只有两类可传输对象:消息(Message)和服务(Service)。消息是单向广播,一个节点发布,多个节点可订阅,没有确认机制,适合周期性传感器数据、状态心跳等场景。服务是双向请求/响应,客户端发送请求,服务端返回响应,具有明确的配对关系,适合参数读写、固件升级、校准触发等需要确认的操作。
从DSDL文件结构看,消息文件只包含字段定义;服务文件则分为请求段和响应段,中间用---分隔。服务必须成对出现,请求和响应各自拥有独立的字段列表。消息的传输标识符(DTID)由发布者决定,服务的DTID由请求方指定,响应方使用相同DTID但方向相反。开发中常见的误区是把需要确认的操作写成消息,导致发送方无法判断对方是否执行成功;或者把高频数据写成服务,造成总线请求风暴。
三、常用标准数据类型解析
UAVCAN标准库中定义了大量通用数据类型,开发传感器节点时最常接触的有以下几类。第一类是uavcan.protocol.NodeStatus,用于节点心跳,包含健康状态、运行模式、运行时间等字段,所有节点都应周期性发布。第二类是uavcan.equipment.传感器系列,如uavcan.equipment.ahrs.Solution、uavcan.equipment.gnss.Fix等,它们定义了姿态、位置、速度等标准字段,便于不同厂商设备互操作。第三类是uavcan.protocol.param服务,用于参数读写,是配置传感器量程、滤波系数、校准偏移的常用通道。
| 标准数据类型 | 类别 | 典型用途 | 关键字段 |
|---|---|---|---|
| uavcan.protocol.NodeStatus | 消息 | 节点心跳与健康上报 | health, mode, uptime_sec |
| uavcan.equipment.ahrs.Solution | 消息 | 姿态解算结果广播 | orientation_q, angular_velocity |
| uavcan.equipment.gnss.Fix | 消息 | GNSS定位数据 | latitude, longitude, altitude |
| uavcan.protocol.param.GetSet | 服务 | 参数读取与写入 | name, value, default_value |
| uavcan.protocol.RestartNode | 服务 | 远程重启节点 | magic_number |
四、案例:为自定义传感器定义DSDL并接入总线
假设要开发一个三轴磁力计节点,需要自定义消息类型。首先创建DSDL文件mycompany.sensors.Magnetometer.0.1,定义三个浮点字段和一个状态字节。然后使用UAVCAN提供的代码生成工具,将DSDL编译为C++头文件。节点固件中包含该头文件,创建发布者对象,按周期填充字段并调用广播接口。地面站使用相同的DSDL文件生成解析代码,即可正确显示磁场强度。
# 编译DSDL生成C++代码
uavcan_dsdlc mycompany.sensors.Magnetometer.0.1.uavcan
# 生成文件
# include/uavcan/mycompany/sensors/Magnetometer.hpp
# 在节点代码中:
# uavcan::Publisher<mycompany::sensors::Magnetometer> mag_pub;
# mag_pub.broadcast(msg);
五、常见误区与复盘
- 误区一:消息和服务混用。把需要确认的校准操作写成消息,导致无法获知执行结果。应使用服务,并检查响应中的错误码。
- 误区二:忽略DSDL签名。签名用于校验数据类型一致性,若两端DSDL文件不一致但签名未更新,总线可能静默丢弃数据。每次修改DSDL后应重新生成签名。
- 误区三:变长数组滥用。变长数组会增加序列化开销和内存占用,传感器周期数据应优先使用定长数组,避免动态分配。
- 误区四:字段顺序随意调整。DSDL字段顺序即传输顺序,调整顺序等同于改变协议,必须作为主版本升级处理。
uavcan.protocol前缀,v1使用uavcan.node等新命名空间。开发前务必确认目标总线使用的协议版本,避免类型名不匹配导致编译或运行失败。
掌握DSDL语法、消息与服务的区别以及常用标准类型,是传感器节点开发中“数据能通”的基础。实际工程中,建议先查阅UAVCAN标准类型库,优先复用已有定义;只有在标准类型无法覆盖时,才自定义DSDL,并严格遵循版本管理和签名更新流程。