2. MAVLink协议基础:消息格式、消息ID、CRC校验、心跳包机制
好,咱们进入正题。MAVLink 这东西,说白了就是无人机世界里的一种“通用语言”。你想想看,飞控、地面站、摄像头、机械臂……这么多设备要互相说话,总得有个统一的规矩。这个规矩,就是 MAVLink 协议。
我最早接触 MAVLink 是在一个农业植保项目里。当时飞机在天上撒农药,地面站突然收不到高度数据了。排查了半天,发现是消息格式里一个字节对齐的问题。从那以后,我对协议细节就再也不敢马虎了。
2.1 消息格式:每一帧都长什么样?
MAVLink 的消息帧,就像一封信。信封上写着地址、邮戳,里面才是正文。咱们先看 MAVLink 1.0 的经典格式,这是最常用的版本。
| 字节偏移 | 字段名 | 长度(字节) | 说明 |
|---|---|---|---|
| 0 | STX(起始标志) | 1 | 固定为 0xFE,表示 MAVLink 1.0 |
| 1 | LEN(负载长度) | 1 | Payload 部分的字节数,0-255 |
| 2 | SEQ(序列号) | 1 | 每个组件独立计数,用于丢包检测 |
| 3 | SYS ID(系统ID) | 1 | 标识整个无人机系统,比如 1 号飞机 |
| 4 | COMP ID(组件ID) | 1 | 标识系统内的具体模块,比如飞控、GPS |
| 5 | MSG ID(消息ID) | 1 | 标识消息类型,0-255 |
| 6 到 6+LEN-1 | Payload(负载) | LEN | 真正的数据内容 |
| 6+LEN | CKA / CKB(校验和) | 2 | CRC-16-CCITT 校验结果 |
嗯,这里要注意。MAVLink 2.0 的起始标志是 0xFD,而且消息 ID 扩展到了 3 个字节(0-16777215)。不过咱们做 SITL 仿真,大部分场景还是用 1.0 版本,兼容性更好。
2.2 消息ID:给每条消息一个身份证
消息 ID 是 MAVLink 的灵魂。它告诉接收方:“我这条消息是干嘛的”。比如 ID=0 是心跳包,ID=30 是姿态信息,ID=74 是 GPS 原始数据。
我举个例子。你在 SITL 里跑仿真,地面站想看看飞机当前的速度。地面站不会傻等着,而是监听所有消息。当它收到一条 MSG ID=32 的消息(LOCAL_POSITION_NED),就知道里面装的是位置和速度数据,直接解析对应字段就行。
常用的消息 ID 有哪些?我列几个你一定会遇到的:
- 0 (HEARTBEAT):心跳包,每秒 1-10 Hz,告诉你设备还活着
- 1 (SYS_STATUS):系统状态,电池电压、通信质量等
- 24 (GPS_RAW_INT):GPS 原始数据,经纬度、海拔、卫星数
- 30 (ATTITUDE):姿态四元数、角速度
- 32 (LOCAL_POSITION_NED):局部坐标系下的位置和速度
- 74 (VFR_HUD):空速、地速、航向,地面站最常用的显示数据
2.3 CRC校验:别让噪声毁了你的数据
无人机在天上飞,电磁环境复杂得很。数据在无线链路里传着传着,可能一个比特就翻了个跟头。CRC 校验就是用来揪出这种错误的。
MAVLink 用的是 CRC-16-CCITT,多项式是 0x1021。计算范围包括:负载(Payload)加上一个“额外秘钥”。这个秘钥是什么?是消息定义里每个字段的 CRC 值,说白了就是消息的“指纹”。
我直接给你看代码,这是 MAVLink 官方库里的核心 CRC 函数:
// 经典的 CRC-16-CCITT 实现
uint16_t crc_calculate(const uint8_t* data, uint32_t len) {
uint16_t crc = 0xFFFF;
for (uint32_t i = 0; i < len; i++) {
crc ^= data[i];
for (uint8_t j = 0; j < 8; j++) {
if (crc & 0x0001) {
crc = (crc >> 1) ^ 0x8408; // 0x8408 是 0x1021 的反转
} else {
crc = crc >> 1;
}
}
}
return crc;
}
为什么校验和要算两遍?第一遍算 payload,第二遍把消息定义的 CRC 额外值加进去再算。这样做的好处是:接收方不仅能知道数据有没有传错,还能确认消息格式是不是自己认识的那个版本。
关键点: 在 SITL 仿真里,如果你自己写 MAVLink 解析器,一定要把 CRC 校验做完整。很多初学者只校验 payload,忽略了那个“额外秘钥”。结果就是:明明数据是对的,但地面站就是不认。我当年调试 Pixhawk 时,就因为这个 bug 浪费了整整一个下午。
2.4 心跳包机制:设备还活着吗?
心跳包,英文叫 HEARTBEAT,消息 ID=0。它是 MAVLink 通信里最基础、最重要的消息。没有心跳,其他消息都是白搭。
心跳包的结构很简单,只有 9 个字节的 payload:
| 字段 | 类型 | 说明 |
|---|---|---|
| type | uint8_t | 系统类型,比如 2 是四旋翼,12 是地面站 |
| autopilot | uint8_t | 飞控类型,比如 3 是 PX4,4 是 ArduPilot |
| base_mode | uint8_t | 位掩码,表示是否已上锁、是否在自动模式等 |
| custom_mode | uint32_t | 自定义模式,比如 PX4 的 manual、mission、offboard |
| system_status | uint8_t | 系统状态,4 表示已解锁,3 表示已就绪 |
| mavlink_version | uint8_t | MAVLink 协议版本,固定为 3 |
心跳包的作用,说白了就是三个字:报平安。地面站每隔一段时间(通常是 1 秒)收不到心跳,就会认为飞机失联了。在 SITL 仿真里,如果你看到地面站提示“通信超时”,十有八九是心跳包没发出来。
我个人的习惯是:在 SITL 启动后,先用 Wireshark 抓一下 UDP 包,确认心跳包是否以 1 Hz 的频率正常发出。如果心跳正常,其他消息基本不会出大问题。
你看这张图,左边是飞控(SITL 仿真),右边是地面站。飞控每秒发一条心跳包,地面站收到后解析出系统状态。如果超过 3 秒没收到,地面站就会弹出“通信丢失”的警告。