3. MAVLink 1与MAVLink 2协议差异
MAVLink 1 与 MAVLink 2 在应用层消息语义上高度兼容,但帧层差异显著。理解这些差异的关键不是背字段表,而是能在一段真实字节流中定位帧头、判断版本、解释载荷长度与 CRC 覆盖范围。本节先对比帧结构,再通过同一消息在两种版本下的字节差异分析,最后完成一次版本协商实验。
一、帧结构对比:从 8 字节帧头到 12 字节帧头
MAVLink 1 的帧头固定 6 字节,加上 1 字节起始标志 0xFE,共 7 字节引导部分;MAVLink 2 起始标志为 0xFD,帧头扩展为 9 字节,加起始标志共 10 字节。两者尾部结构相同:2 字节 CRC,MAVLink 2 在启用签名时额外追加 13 字节签名块。
| 字段 | MAVLink 1 | MAVLink 2 | 说明 |
|---|---|---|---|
| 起始标志 STX | 0xFE | 0xFD | 抓包时第一判据 |
| 载荷长度 len | 1 字节 | 1 字节 | v2 可截断尾部零 |
| 序列号 seq | 1 字节 | 1 字节 | 每发一帧递增 |
| 系统/组件 ID | sysid+compid | sysid+compid | 标识发送方 |
| 消息 ID | 1 字节 | 3 字节 | v2 支持 24 位 ID |
| 兼容性标志 | 无 | 1 字节 | incompat_flags |
| 兼容性标志 | 无 | 1 字节 | compat_flags |
| CRC | 2 字节 | 2 字节 | 覆盖范围不同 |
| 签名 | 无 | 13 字节 | 可选 |
MAVLink 2 的 incompat_flags 表示“不兼容标志”:若接收方不认识其中任何一位,必须丢弃该帧。目前仅定义了 0x01 表示签名有效。而 compat_flags 表示“兼容标志”:接收方即使不认识也可以忽略并继续解析,为未来扩展留出空间。这是 v2 在向前兼容设计上的核心机制。
二、载荷扩展与 CRC 计算范围
MAVLink 2 允许消息在末尾追加扩展字段(extension fields),这些字段不参与 CRC 计算,因此旧版接收方即使不认识它们,也能正确校验并解析原有字段。CRC 计算范围在两种版本中都以“消息 ID + 载荷 + CRC_EXTRA”为基础,但 v2 的 CRC_EXTRA 种子表按 24 位消息 ID 索引,且载荷在计算前会先去除尾部零截断。
crc = 0xFFFF
crc = crc_accumulate(msg_id_low, crc)
crc = crc_accumulate(msg_id_mid, crc) // v1 无此步
crc = crc_accumulate(msg_id_high, crc) // v1 无此步
for b in payload_trimmed:
crc = crc_accumulate(b, crc)
crc = crc_accumulate(crc_extra, crc)
注意:v1 的 CRC_EXTRA 种子表按 8 位消息 ID 索引,v2 按 24 位索引。同一消息(如 HEARTBEAT,ID=0)在两版中的 CRC_EXTRA 值相同,因此 CRC 差异主要来自载荷截断与 ID 字节数。
三、同一消息在 v1 与 v2 下的字节差异
下面用 HEARTBEAT(ID=0)为例,假设载荷为 9 字节:00 00 00 00 02 03 51 04 03(type=2 四旋翼、autopilot=3、base_mode=0x51、custom_mode=0x0403、system_status=3)。v1 帧共 8+9+2=19 字节,v2 帧共 12+9+2=23 字节(未签名)。
从图中可见,同一 HEARTBEAT 消息在 v2 下多出 4 字节:消息 ID 从 1 字节扩为 3 字节(+2),新增两个标志字节(+2)。若启用签名,再追加 13 字节,总长变为 36 字节。抓包时若看到 FD 开头且第 6 字节为 00 00 00,即可判定为 v2 的 HEARTBEAT。
四、消息签名机制
MAVLink 2 的签名块 13 字节由三部分组成:1 字节 link ID、6 字节时间戳(自 2015-01-01 起的 10 微秒计数)、6 字节签名值。签名值是对“密钥 + 帧头 + 载荷 + CRC + link ID + 时间戳”做 SHA-256 后取前 6 字节。接收方用相同密钥重算并比对,同时检查时间戳是否在允许窗口内,防止重放攻击。
SETUP_SIGNING 消息分发,双方需预先共享同一 32 字节密钥。
五、版本协商实验
MAVLink 2 定义了 MAV_PROTOCOL_CAPABILITY_MAVLINK2 能力位,通过 AUTOPILOT_VERSION 消息的 capabilities 字段广播。协商流程如下:
- 地面站先以 v2 发送
HEARTBEAT,并在compat_flags中置位MAV_PROTOCOL_CAPABILITY_MAVLINK2对应标志。 - 飞控若支持 v2,回发 v2 帧并在
AUTOPILOT_VERSION.capabilities中置位该能力位。 - 地面站收到后确认双方均支持 v2,后续通信保持 v2。
- 若飞控仅支持 v1,会以 v1 帧回复或丢弃 v2 帧,地面站检测到超时后降级为 v1 重试。
实验可用 mavlink-router 或 pymavlink 搭建。以下为 pymavlink 发送 v2 HEARTBEAT 并读取响应的示例:
from pymavlink import mavutil
master = mavutil.mavlink_connection('udp:127.0.0.1:14550',
source_system=255,
source_component=0)
master.mav.heartbeat_send(
mavutil.mavlink.MAV_TYPE_QUADROTOR,
mavutil.mavlink.MAV_AUTOPILOT_GENERIC,
0, 0, 0)
msg = master.recv_match(type='HEARTBEAT', blocking=True, timeout=3)
print(msg.get_srcSystem(), msg.get_srcComponent())
print('协议版本:', master.mavlink20())
判断依据:若 master.mavlink20() 返回 True 且收到的帧以 0xFD 开头,说明协商成功;若持续收到 0xFE 帧或超时,则对端仅支持 v1,需降级。抓包时可用 Wireshark 的 MAVLink 解析器,或直接十六进制查看首字节。
六、实践检查清单
- 抓包首字节:
0xFE为 v1,0xFD为 v2。 - v2 帧第 6~8 字节为 24 位消息 ID,注意小端序。
- 检查
incompat_flags的0x01位判断是否签名。 - CRC 校验失败时,先确认 CRC_EXTRA 种子是否按正确版本索引。
- 协商实验至少验证三种情况:双方 v2、一方 v1、签名启用。