2. MAVLink消息帧结构解析
当飞控通过串口向地面站发送数据时,我们在串口助手里看到的并不是"心跳包"这样的文字,而是一串十六进制字节。要真正读懂数据链,必须能从这串字节中逐字段还原出一条 MAVLink 消息。本节以 MAVLink v2 为主线(v1 差异单独说明),从 STX 开始逐字段拆解,并用一个真实可复现的心跳包解析过程把字段含义落到字节上。
一、帧结构总览:9 个字段的固定顺序
MAVLink v2 的一帧由帧头、负载、校验和、可选签名四部分组成,共 9 个逻辑字段,顺序固定、不可调换:
- STX(1 字节):起始标志。v2 为
0xFD,v1 为0xFE。接收方靠它判断"一帧从哪里开始"。 - LEN(1 字节):PAYLOAD 的有效字节数,不含帧头、CRC 和签名。v2 中它是负载真实长度,v1 中为负载长度减 1。
- SEQ(1 字节):发送序号,0~255 循环。用于检测丢包,接收方按序号连续性判断是否漏帧。
- SYSID(1 字节):系统 ID,标识消息来自哪个系统(如飞控=1,地面站=255)。
- COMPID(1 字节):组件 ID,标识系统内的具体组件(如自动驾驶仪=1,云台=154)。
- MSGID(3 字节,小端):消息 ID,决定 PAYLOAD 的字段布局。v1 中为 1 字节。
- PAYLOAD(LEN 字节):消息体,字段按 MAVLink 定义的顺序和类型排列,多字节字段采用小端序。
- CRC(2 字节,小端):对帧头(不含 STX)到 PAYLOAD 的 CRC-16/MCRF4XX 校验,并叠加消息 ID 对应的 CRC 种子。
- 签名(13 字节,可选):仅在启用签名时存在,含链路 ID、时间戳和 6 字节签名,用于防篡改。
理解帧结构的关键在于:LEN 决定 PAYLOAD 边界,MSGID 决定 PAYLOAD 如何解释,CRC 决定这帧是否可信。三者缺一不可。
二、逐字段拆解:每个字节在说什么
STX 是解析的起点。串口数据是连续流,接收方不断扫描字节,遇到 0xFD 才认为可能是一帧的开头。如果负载中恰好出现 0xFD,也不会误判,因为解析器会按 LEN 跳过整个负载,不会在负载内部重新找 STX。
LEN 告诉解析器"往后读多少字节属于负载"。以心跳包为例,其负载固定为 9 字节,所以 LEN=0x09。如果 LEN 与实际负载不符,CRC 几乎必然失败,这也是检测帧错位的第一道防线。
SEQ 是发送方维护的计数器,每发一帧加一,溢出后回绕。接收方记录上一帧 SEQ,若新帧 SEQ 不是"上一帧+1",说明中间有丢包。注意 SEQ 是每链路独立维护的,不同 SYSID/COMPID 的 SEQ 互不干扰。
SYSID 与 COMPID 共同构成消息来源地址。常见约定:飞控 SYSID=1、COMPID=1;地面站 SYSID=255、COMPID=190;云台 COMPID=154。解析时先看这两个字段,才能知道"这条消息是谁发的"。
MSGID 是 3 字节小端整数,决定负载的字段布局。心跳包的 MSGID=0,所以负载按 HEARTBEAT 定义解析。若把 MSGID 读成大端,会得到完全错误的消息类型,这是初学者最常见的错误之一。
PAYLOAD 的字段顺序和类型由消息定义决定,且多字节字段一律小端。心跳包负载 9 字节依次为:custom_mode(4 字节)、type(1 字节)、autopilot(1 字节)、base_mode(1 字节)、system_status(1 字节)、mavlink_version(1 字节)。
CRC 的计算范围是"从 LEN 到 PAYLOAD 末尾",不含 STX,再叠加该 MSGID 对应的 CRC 种子。心跳包的 CRC 种子为 50。校验失败说明帧损坏或 MSGID 解析错误。
签名 仅在链路启用签名时出现,位于 CRC 之后,共 13 字节。它不参与 CRC 计算,但接收方会用共享密钥验证签名,防止伪造消息注入。
三、实操:用十六进制工具手工解析一帧心跳包
下面是一段从串口抓取的心跳包原始字节(示例数据,用于演示解析方法):
FD 09 00 01 01 00 00 00 00 02 03 51 04 03 7A 3F
按字段切分:
| 字段 | 字节 | 值 | 含义 |
|---|---|---|---|
| STX | FD | 0xFD | MAVLink v2 帧起始 |
| LEN | 09 | 9 | 负载 9 字节 |
| SEQ | 00 | 0 | 本链路第 0 帧 |
| SYSID | 01 | 1 | 来自飞控系统 |
| COMPID | 01 | 1 | 来自自动驾驶仪组件 |
| MSGID | 00 00 00 | 0 | HEARTBEAT 心跳包 |
| PAYLOAD | 00 00 00 00 02 03 51 04 03 | — | 见下方逐字段解析 |
| CRC | 7A 3F | 0x3F7A | 小端读取的校验值 |
负载逐字段解析(小端):
00 00 00 00→ custom_mode = 0,自定义飞行模式。02→ type = 2,MAV_TYPE_QUADROTOR,四旋翼。03→ autopilot = 3,MAV_AUTOPILOT_ARDUPILOTMEGA,ArduPilot。51→ base_mode = 0x51 = 0b01010001,包含 SAFETY_ARMED(0x80 未置位)、MANUAL_INPUT_ENABLED(0x40)、CUSTOM_MODE_ENABLED(0x01) 等标志位。04→ system_status = 4,MAV_STATE_ACTIVE,系统活动。03→ mavlink_version = 3,表示 MAVLink v2。
校验验证:把 LEN 到 PAYLOAD 的字节按 CRC-16/MCRF4XX 计算,再叠加心跳包种子 50,得到 0x3F7A,与帧尾 7A 3F 小端读取一致,说明这帧完整可信。
四、v1 与 v2 的关键差异
如果抓到的帧以 0xFE 开头,说明是 MAVLink v1。v1 的 MSGID 只有 1 字节,LEN 表示"负载长度减 1",且没有签名段。解析 v1 时,帧头长度为 6 字节(STX、LEN、SEQ、SYSID、COMPID、MSGID),负载长度需用 LEN+1 计算。混用 v1/v2 解析规则会导致负载边界错位,进而 CRC 失败。
掌握帧结构后,后续无论是编写 MAVLink 解析器、排查通信异常,还是分析日志中的丢包与校验错误,都可以回到这 9 个字段上定位问题:STX 找不到说明帧同步丢失,LEN 异常说明帧错位,SEQ 不连续说明丢包,CRC 失败说明数据损坏或 MSGID 解析错误,签名验证失败说明来源不可信。