2. MAVLink消息结构:消息帧格式详解、CRC校验机制、消息ID与组件ID、系统ID的概念

好,咱们今天来啃一块硬骨头——MAVLink的消息结构。说实话,我刚开始接触MAVLink的时候,看着那一串十六进制数据也是一头雾水。但后来我发现,只要你搞懂了它的帧格式,整个通信协议就像一张摊开的地图,清晰得很。

MAVLink的消息结构,说白了就是一套“信封”规则。你要寄一封信,总得有个信封吧?信封上写清楚从哪来、到哪去、内容是什么、怎么检查有没有被篡改。MAVLink的帧格式干的就是这个活儿。

2.1 消息帧格式详解

MAVLink有两个版本:v1.0和v2.0。现在主流都用v2.0,但很多老设备还在跑v1.0。我个人建议,新项目直接上v2.0,兼容性更好,扩展性也强。

咱们先看v2.0的帧格式,它长这样:

| STX | LEN | INC | CMP | SEQ | SYS | COMP | MSGID | PAYLOAD | CKA | CKB |
|  1  |  1  |  1  |  1  |  1  |  1  |   1   |   3   |  0-255 |  1  |  1  |

每个字段都是字节,我来一个个拆开讲:

字段 长度(字节) 说明
STX 1 起始标志,v2.0固定为0xFD,v1.0是0xFE
LEN 1 Payload长度,0-255字节
INC 1 需要参与CRC校验的“不兼容标志”字节数
CMP 1 需要参与CRC校验的“兼容标志”字节数
SEQ 1 消息序列号,用于检测丢包
SYS 1 系统ID,标识发送方
COMP 1 组件ID,标识发送方内部的子模块
MSGID 3 消息ID,v2.0扩展到24位
PAYLOAD 0-255 实际数据内容
CKA/CKB 2 CRC校验码,16位

嗯,这里要注意一个细节:INC和CMP字段。这两个是v2.0新增的,用来做协议扩展。INC字段里的位如果被置1,接收方如果不认识这个位,就必须丢弃整条消息。CMP字段里的位如果被置1,接收方如果不认识,可以忽略这个位,继续处理。说白了,INC是“你必须懂我”,CMP是“你不懂也没关系”。

我的小经验: 在实际项目中,我习惯把INC字段初始化为0x00,除非你明确知道自己在做什么扩展。否则,一个不小心设错了位,老版本地面站可能直接丢掉你的消息,排查起来很头疼。

2.2 CRC校验机制

CRC校验,全称是循环冗余校验。你不需要背它的数学公式,但得理解它的作用——保证消息在传输过程中没有被篡改或损坏。

MAVLink用的CRC算法是CRC-16-IBM,多项式是0x1021。计算范围包括:LEN、INC、CMP、SEQ、SYS、COMP、MSGID、PAYLOAD,以及一个额外的“密钥字节”——这个密钥字节是根据消息ID查表得到的。

为什么要加这个密钥字节?

我曾经在一个项目中遇到过这样的问题:地面站收到了一个看起来合法的消息,CRC校验也通过了,但解析出来的数据完全是乱的。后来排查发现,是消息ID被干扰了,变成了另一个消息ID,但恰好那个消息ID的CRC密钥和原始消息一样,导致校验通过。加了密钥字节后,这种概率就大大降低了。

CRC校验的流程大致如下:

  1. 初始化CRC寄存器为0xFFFF
  2. 依次处理LEN、INC、CMP、SEQ、SYS、COMP、MSGID每个字节
  3. 处理PAYLOAD的所有字节
  4. 处理消息ID对应的CRC密钥字节(查表获取)
  5. 得到最终的16位CRC值,填入CKA和CKB
避坑指南: 我曾经见过有人自己实现MAVLink解析器时,忘了把CRC密钥字节加进去。结果就是,自己发的消息自己解析能通过,但跟其他标准设备通信时,永远校验失败。排查了两天才找到原因。所以,CRC密钥字节一定不能漏。

2.3 消息ID与组件ID

这两个ID容易搞混,我分开讲。

消息ID(MSGID):标识这条消息里装的是什么数据。比如,消息ID=0表示心跳包,消息ID=30表示姿态数据。v2.0用了3个字节,所以最多支持16,777,216种消息类型。v1.0只有1个字节,最多256种,所以v2.0的扩展性更好。

组件ID(COMPID):标识同一个系统内部的不同组件。比如,飞控是1号组件,摄像头是2号组件,机械臂是3号组件。这样,地面站就能区分出“这条消息是飞控发的还是摄像头发的”。

我举个例子你就明白了:

系统ID=1, 组件ID=1 → 无人机A的飞控
系统ID=1, 组件ID=2 → 无人机A的摄像头
系统ID=2, 组件ID=1 → 无人机B的飞控

你看,通过系统ID+组件ID的组合,就能唯一确定一个消息来源。

2.4 系统ID的概念

系统ID(SYSID)是整个MAVLink网络中每个节点的唯一标识。一个系统可以是一架无人机、一个地面站、一个机械臂控制器,甚至是一个传感器节点。

系统ID的范围是1-255。0是保留值,255是广播地址。我建议你给每个设备分配一个固定的系统ID,不要动态变化,否则地面站会搞混。

在实际项目中,我一般这样分配:

  • 地面站:系统ID=255(广播)
  • 无人机1:系统ID=1
  • 无人机2:系统ID=2
  • 无人机3:系统ID=3
  • ……以此类推

为什么地面站用255?因为地面站需要跟所有无人机通信,用广播地址最方便。无人机之间一般不需要直接通信,所以各自用独立ID。

核心要点: 系统ID + 组件ID + 消息ID 这三个字段,共同构成了MAVLink消息的“三维坐标”。系统ID告诉你是谁发的,组件ID告诉你这个系统里的哪个模块发的,消息ID告诉你发的是什么内容。三个缺一不可。

2.5 消息结构总览图

下面这张图把整个消息帧的结构和校验流程串起来了,你一看就明白:

MAVLink v2.0 消息帧结构与CRC校验流程 MAVLink v2.0 消息帧结构与CRC校验流程 消息帧格式(共12-267字节) STX LEN INC CMP SEQ SYS COMP MSGID PAYLOAD CRC CRC校验范围 LEN + INC + CMP + SEQ + SYS + COMP + MSGID + PAYLOAD + 密钥字节 三维坐标:唯一标识一条消息的来源与内容 系统ID (SYS) → 谁发的? 无人机A / 地面站 / 机械臂 组件ID (COMP) → 哪个模块? 飞控 / 摄像头 / GPS 消息ID (MSGID) → 什么内容? 心跳 / 姿态 / 飞行模式 💡 接收端处理流程 检测STX → 解析LEN → 读取完整帧 → CRC校验 → 提取SYS/COMP/MSGID → 解析PAYLOAD

这张图把整个消息帧的骨架画出来了。你从上往下看:先看到完整的帧格式,然后CRC校验范围覆盖了哪些字段,最后是三维坐标的映射关系。接收端拿到一条消息后,就是按照底部那个流程一步步处理的。

好了,这一章的内容就到这儿。消息帧格式是MAVLink的基石,后面的所有内容都建立在这个基础上。你把这个搞透了,后面学飞行模式切换、参数配置什么的,就会轻松很多。