2. MAVLink消息结构:消息帧格式详解、CRC校验机制、消息ID与组件ID设计哲学

好,咱们进入第二章。这一章我打算把MAVLink的“骨头架子”拆开给你们看。说白了,就是消息在线上到底长什么样,怎么保证不出错,以及那个ID系统为什么这么设计。这些是后面所有任务规划功能的基础,你绕不开。

2.1 消息帧格式:一个字节都不能错

MAVLink有两个主流版本,v1和v2。现在新项目基本都用v2,但很多老设备还在跑v1。我建议你直接上手v2,兼容性更好。咱们先看v2的帧结构,它长这样:

| STX | LEN | INC | CMP | SEQ | SYS | COMP | MSG ID (3字节) | PAYLOAD | CRC (2字节) |
| 0xFD | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 0-3字节 | 0-255字节 | 2字节 |

每个字段都有它的使命。我挑几个重点说说:

  • STX (起始标志):v2固定是 0xFD。接收端看到这个字节,就知道一条新消息来了。v1用的是 0xFE,这是区分版本最直接的办法。
  • LEN (负载长度):告诉你后面跟着的PAYLOAD有多少字节。范围0-255。
  • INC (兼容标志) 与 CMP (不兼容标志):这两个字节是v2新增的。INC里的位如果置1,表示这个payload里包含了扩展字段,老版本解析器可以安全地跳过它。CMP里的位如果置1,老版本解析器必须能理解,否则就得丢弃整条消息。我在设计自定义消息时,经常用INC来做向后兼容。
  • SEQ (序列号):每发一条消息,这个数就加1。接收端可以用它来检测丢包。嗯,这里要注意,序列号是每个组件自己维护的,不是全局的。
  • SYS (系统ID) 与 COMP (组件ID):这是消息的“发件人地址”。系统ID标识整个无人机,组件ID标识飞控、GPS、相机等具体模块。
  • MSG ID (消息ID):v2里它占了3个字节,范围大得多。这是消息的“身份证号”,告诉接收端payload里装的是什么数据。
  • PAYLOAD (负载):真正的数据,最多255字节。
  • CRC (校验):最后2字节,用来检查整条消息在传输过程中有没有被篡改。
我的习惯: 在写解析代码时,我会先校验STX,再校验CRC。如果CRC不对,直接丢弃,省得后面解析payload浪费时间。你想想看,如果数据都坏了,解析它还有什么意义?

2.2 CRC校验机制:别让一个比特坏了事

CRC,全称循环冗余校验。MAVLink用的是CRC-16-CCITT,多项式是 0x1021。它不像简单的累加和,CRC能检测出很多常见的传输错误,比如连续多位翻转。

MAVLink的CRC计算有个特别之处:它会把消息ID也混进去。为什么?

我曾经在项目里遇到过一个问题:飞控发了一条GPS消息,地面站却把它解析成了姿态消息。原因就是消息ID在传输中发生了比特错误,从 0x21 变成了 0x22。如果CRC只校验payload,这种错误是发现不了的。MAVLink的设计者显然也想到了这一点,所以把消息ID也纳入了CRC计算范围。

具体计算流程是这样的:

  1. 初始化CRC寄存器为 0xFFFF。
  2. 对 LEN、INC、CMP、SEQ、SYS、COMP、MSG ID 这些头部字段依次进行CRC计算。
  3. 对 PAYLOAD 的每个字节进行CRC计算。
  4. 最后,根据消息ID查表,得到一个“额外CRC种子”,再把这个种子也计算进去。
  5. 最终得到的16位值,就是我们要发送的CRC。
避坑指南: 我曾经见过有人自己手写CRC计算,结果忘了加“额外CRC种子”。这会导致地面站和飞控永远无法握手成功。所以,我强烈建议你直接用MAVLink官方代码生成器生成的CRC表,别自己造轮子。

2.3 消息ID与组件ID设计哲学:地址空间的艺术

MAVLink的ID系统,说白了就是一套“寻址方案”。它解决了两个问题:谁发的?发的是什么?

消息ID (MSG ID) 定义了“发的是什么”。它是一个全局唯一的数字,比如 HEARTBEAT 是 0,SYS_STATUS 是 1。v1版本只有1字节,最多256种消息,很快就用完了。v2扩展到3字节,理论上可以有1600多万种,足够用了。

系统ID (SYS ID) 和 组件ID (COMP ID) 定义了“谁发的”。系统ID通常分配给整个无人机(比如1号机、2号机)。组件ID则分配给机载设备:

组件ID 典型设备
1 飞控 (Autopilot)
2 惯性测量单元 (IMU)
5 GPS模块
100 相机 (Camera)
154 任务规划器 (Mission Planner)

这套设计哲学的核心是:解耦。地面站不需要知道飞控内部怎么工作的,它只需要知道系统ID是1,组件ID是1的设备发来了一个消息ID为0的消息,那就是心跳包。至于这个心跳包是PX4发的还是ArduPilot发的,不重要。

你想想看,如果没有组件ID,一个系统里多个GPS模块同时发数据,地面站就傻眼了,根本分不清谁是谁。

下面这张图展示了消息从飞控到地面站的完整路径,以及ID系统在其中扮演的角色:

MAVLink消息寻址与帧结构流程图 MAVLink消息寻址与帧结构流程图 发送端:飞控 SYS=1, COMP=1 消息ID = 0 HEARTBEAT 帧封装 STX + LEN + ... + CRC 串口/UDP/TCP 接收端:地面站 SYS=255, COMP=154 帧解析 & CRC校验 根据MSG ID分发 关键点 • SYS+COMP 定位发送者 • MSG ID 定位数据类型

从这张图你能看到,一条消息从飞控发出,经过帧封装,在链路上传输,到达地面站后,先做CRC校验,再根据消息ID分发到不同的处理函数。整个过程,ID系统就像邮政系统的邮编和地址,确保数据准确送达。

核心要点:
  • MAVLink v2帧结构比v1更灵活,支持扩展和更大的消息空间。
  • CRC校验覆盖了头部和payload,并且加入了消息ID作为种子,防止ID被篡改。
  • 系统ID+组件ID构成“发件人地址”,消息ID构成“内容类型”。这套设计让系统易于扩展和维护。