消息结构深度解析:MAVLink v1与v2协议帧结构对比、CRC校验机制、消息ID分配规则
做嵌入式飞控开发这些年,我解析过不下几十种通信协议。但MAVLink给我的印象最深——它简单,却藏着不少细节。今天咱们就把它拆开看看,从帧结构到校验,再到ID分配,把v1和v2的区别彻底讲明白。
一、MAVLink v1 帧结构:经典但有限
先看v1。说实话,这个版本设计得很克制,每个字节都有明确用途。我最早接触PX4时用的就是v1,那时候觉得这协议真清爽。
| 偏移 | 字段 | 长度(字节) | 说明 |
|---|---|---|---|
| 0 | STX | 1 | 起始标志,固定0xFE |
| 1 | LEN | 1 | 有效载荷长度(0-255) |
| 2 | SEQ | 1 | 序列号,用于丢包检测 |
| 3 | SYS | 1 | 系统ID |
| 4 | COMP | 1 | 组件ID |
| 5 | MSG | 1 | 消息ID(0-255) |
| 6..N+5 | Payload | LEN | 有效载荷,最长255字节 |
| N+6..N+7 | CKA/CKB | 2 | 16位CRC校验 |
v1的帧头只有6个字节,加上2字节CRC,总开销才8字节。这在低速串口上很友好。但有个硬伤——消息ID只有1个字节,最多256种消息。早期够用,后来MAVLink生态膨胀,256个ID根本不够分。
二、MAVLink v2 帧结构:扩展与兼容
v2的出现就是为了解决v1的瓶颈。我参与过一个大型无人机集群项目,v1的256个ID根本不够用,后来全切到了v2。v2的帧结构多了不少东西:
| 偏移 | 字段 | 长度(字节) | 说明 |
|---|---|---|---|
| 0 | STX | 1 | 起始标志,固定0xFD |
| 1 | LEN | 1 | 有效载荷长度(0-255) |
| 2 | INC | 1 | 不兼容标志位 |
| 3 | CMP | 1 | 兼容标志位 |
| 4 | SEQ | 1 | 序列号 |
| 5 | SYS | 1 | 系统ID |
| 6 | COMP | 1 | 组件ID |
| 7..9 | MSG | 3 | 消息ID(0-16777215) |
| 10..N+9 | Payload | LEN | 有效载荷 |
| N+10..N+11 | CKA/CKB | 2 | 16位CRC校验 |
| N+12 | Signature | 0/13 | 可选签名(链路加密) |
v2最核心的变化有三点:
- 消息ID扩展到3字节:从256种直接跳到1600万种,再也不用担心ID不够用了。
- 增加了INC和CMP标志位:INC用于标记不兼容的特性(比如签名),CMP用于标记兼容的特性。接收方看到INC中有自己不懂的位,可以直接丢弃该消息。
- 可选的签名段:13字节的签名用于防止伪造消息。这在安全敏感的场景下很有用。
三、v1与v2的对比:一张表说清楚
很多初学者会问:v2到底比v1强在哪?我整理了一张对比表,你一看就明白:
| 特性 | MAVLink v1 | MAVLink v2 |
|---|---|---|
| 起始字节 | 0xFE | 0xFD |
| 帧头长度 | 6字节 | 10字节 |
| 消息ID宽度 | 1字节(0-255) | 3字节(0-16777215) |
| 标志位 | 无 | INC + CMP (2字节) |
| 签名 | 不支持 | 可选(13字节) |
| 最大有效载荷 | 255字节 | 255字节 |
| CRC覆盖范围 | Payload + 除STX外的所有帧头 | Payload + 除STX外的所有帧头 + 签名(如有) |
| 向后兼容 | — | 可解析v1消息 |
v2的帧头多了4个字节,但换来了巨大的ID空间和扩展能力。我个人觉得,新项目直接上v2就好,没必要再用v1。
四、CRC校验机制:别小看这2个字节
CRC校验是MAVLink的保命符。v1和v2都使用16位CRC,但算法略有不同。我来说说核心逻辑。
MAVLink的CRC算法基于CRC-16-IBM(多项式0x8005),但做了一点改动——它使用了一个额外的密钥字节来防止误判。每个消息类型都有自己的密钥,这个密钥由消息名称的CRC计算得到。
具体计算步骤:
- 初始化CRC寄存器为0xFFFF
- 对帧头(除STX外)和有效载荷逐字节进行CRC计算
- 对消息对应的密钥字节进行CRC计算
- 最终CRC值取反后放入CKA/CKB
为什么要加密钥?说白了,就是为了防止不同消息类型之间产生相同的CRC。我曾经遇到过一个问题:两个不同消息的载荷恰好相同,如果没有密钥,它们的CRC就会一样,接收方可能误判消息类型。加了密钥后,这种概率几乎为零。
v2的CRC计算范围比v1多了一个东西——签名段。如果消息带有签名,CRC必须覆盖签名段。这意味着签名后的消息不能直接修改签名,否则CRC会失效。
五、消息ID分配规则:从0x00到0xFFFFFF
消息ID是MAVLink的核心索引。v1只有0-255,v2扩展到了0-16777215。但ID不是随便分配的,官方有一套规则:
- 0-255:保留给v1兼容消息。所有v1消息在v2中ID不变。
- 256-299:ArduPilot自定义消息。我常用的很多调试消息都在这个区间。
- 300-10000:官方标准消息。新加入MAVLink协议的消息都在这里。
- 10001-16777215:留给厂商自定义。你可以在这里定义自己的私有消息。
举个例子,HEARTBEAT消息的ID是0(0x00),这是所有MAVLink系统必须支持的消息。而ArduPilot的AHRS消息ID是163(0xA3),属于v1兼容区间。
六、知识体系总览:一张SVG图看懂
下面这张图把v1和v2的帧结构、CRC校验、ID分配串在了一起。你可以把它当作本章的思维导图:
七、实战中的避坑指南
最后分享几个我踩过的坑,希望能帮你少走弯路:
- 版本检测别偷懒:我曾经写过一个解析器,默认按v2解析。结果接了一个老设备发v1消息,解析全乱套了。后来我改成先读STX判断版本,再走不同分支。
- CRC计算别漏密钥:有个同事移植MAVLink库时,直接用了标准CRC-16-IBM,没加消息密钥。结果地面站一直报CRC错误,排查了两天才找到原因。
- 消息ID别乱用:自定义消息ID一定要避开官方保留区间。我建议用10000以上的ID,或者用ArduPilot的256-299区间(如果你在用ArduPilot)。
- v2的INC标志位要重视:如果INC中设置了签名位,但你的解析器不支持签名,直接丢弃这条消息。别尝试解析,否则数据可能是伪造的。