2、MAVLink核心协议解析:消息格式、心跳包、系统状态与GPS定位数据

各位同学,欢迎来到第二章。上一章我们聊了MAVLink的诞生背景和它在无人机生态里的位置。这一章,咱们直接动手拆协议——把MAVLink的“肉”和“骨头”都翻出来看看。

说实话,我最早接触MAVLink时,觉得它不就是一堆二进制数据嘛,有啥好研究的?直到有一次在野外调试,飞机突然报“GPS丢失”,我盯着地面站上那一串乱码似的十六进制数,才意识到:不懂协议细节,你连故障都定位不了。从那以后,我养成了一个习惯——看原始MAVLink报文,就像看自己的掌纹一样熟悉。

2.1 MAVLink消息帧结构——数据到底怎么装的?

MAVLink 1.0的消息帧,说白了就是一个固定格式的“信封”。每个消息都按这个模板打包,接收方按模板拆包。咱们先看最常用的v1版本,v2版本后面会单独讲。

字节偏移 字段名 长度(字节) 说明
0 STX(起始标志) 1 固定值 0xFE(v1版本)
1 LEN(负载长度) 1 后面payload部分的字节数,0~255
2 SEQ(序列号) 1 每个组件独立计数,用于检测丢包
3 SYS ID(系统ID) 1 标识整个无人机系统(飞控、地面站等)
4 COMP ID(组件ID) 1 标识系统内的具体模块(飞控、GPS、相机等)
5 MSG ID(消息ID) 1 标识消息类型,如 0 代表HEARTBEAT
6 ~ 6+LEN-1 PAYLOAD(负载) LEN 具体消息数据,按消息类型定义排列
6+LEN CKA(校验和低字节) 1 CRC校验,包含整个消息(不含STX)
6+LEN+1 CKB(校验和高字节) 1 CRC校验高字节
我的小习惯: 调试时,我经常用 mavlink_msg_xxx_decode() 函数直接解析,但遇到奇怪问题时,我会手动对照这个表格,逐字节检查原始数据。有一次发现SEQ号跳变,顺藤摸瓜找到了串口缓冲区溢出的bug。

嗯,这里要注意:MAVLink消息是可变长度的。LEN字段告诉接收方payload有多长,接收方读完payload后,再读2字节校验和,一帧消息就完整了。你想想看,如果LEN写错了,整个解析链就全乱了——这也是很多通信故障的根源。

2.2 心跳包(HEARTBEAT)——系统还活着吗?

心跳包是MAVLink里最基础、最重要的消息。没有它,地面站根本不知道天上飞的是什么玩意儿。它的消息ID是 0,payload固定为9字节。

我个人的经验是:调试任何MAVLink系统,第一步就是看心跳包。如果心跳包都不正常,后面的数据全是白搭。

2.2.1 心跳包结构

字段名 类型 说明
type uint8_t 系统类型,如 MAV_TYPE_QUADROTOR(2)
autopilot uint8_t 飞控类型,如 MAV_AUTOPILOT_ARDUPILOTMEGA(3)
base_mode uint8_t 位掩码,表示系统状态(已上锁、已解锁、已连接等)
custom_mode uint32_t 自定义模式,ArduPilot用它表示飞行模式(Stabilize、Loiter等)
system_status uint8_t 系统状态,如 MAV_STATE_STANDBY(3)
mavlink_version uint8_t MAVLink协议版本,固定为3(v1版本)
关键点: base_mode 和 custom_mode 是判断飞机是否解锁、当前飞行模式的核心字段。地面站就是靠这两个字段来显示“已解锁/已上锁”和“Stabilize/Loiter/Auto”等状态的。

2.2.2 实战:解析心跳包

假设你从串口收到一串原始数据,开头是 FE 09 1A 01 01 00 ...。咱们来拆一下:

  • FE → STX,v1版本
  • 09 → LEN=9,心跳包payload正好9字节
  • 1A → SEQ=26,这是第27个消息(从0开始)
  • 01 → SYS ID=1,通常是飞控
  • 01 → COMP ID=1,飞控主组件
  • 00 → MSG ID=0,确认是心跳包
  • 后面9字节就是payload,按上面表格顺序解析

我曾经在项目里遇到过一个问题:地面站一直显示“未连接”,但串口明明有数据。后来一查,发现是SYS ID和COMP ID不匹配——地面站只监听SYS ID=1的组件,但飞控配置成了SYS ID=2。你看,一个字节的差异,就能让整个系统“失联”。

2.3 系统状态(SYS_STATUS)——传感器都正常吗?

心跳包告诉你系统“活着”,但SYS_STATUS告诉你系统“健康不健康”。它的消息ID是 1,payload固定为31字节。

说白了,SYS_STATUS就是一张传感器和硬件的“体检报告”。我每次起飞前,都会盯着这个数据看几秒——尤其是 onboard_control_sensors_present 和 onboard_control_sensors_enabled 这两个字段。

2.3.1 关键字段解读

字段名 类型 说明
onboard_control_sensors_present uint32_t 位掩码,表示系统有哪些传感器(GPS、陀螺仪、磁力计等)
onboard_control_sensors_enabled uint32_t 位掩码,表示哪些传感器已启用
onboard_control_sensors_health uint32_t 位掩码,表示哪些传感器健康(1=健康,0=故障)
load uint16_t 飞控CPU负载,单位:千分之一(0~1000)
voltage_battery uint16_t 电池电压,单位:毫伏(mV)
current_battery int16_t 电池电流,单位:10毫安(10mA)
battery_remaining int8_t 剩余电量百分比(-1表示未知)
drop_rate_comm uint16_t 通信丢包率,单位:千分之一(0~1000)
errors_comm uint16_t 通信错误计数
errors_count1~3 uint16_t 其他错误计数
避坑指南: 我曾经遇到过 onboard_control_sensors_health 显示GPS健康,但实际定位数据是错的。后来发现,飞控的“健康”判断只是检查GPS模块是否在发送数据,并不验证数据质量。所以,别只看健康位,还要看GPS_RAW_INT里的精度字段。

2.4 GPS定位数据(GPS_RAW_INT)——飞机到底在哪?

GPS_RAW_INT是MAVLink里最常用的定位消息,消息ID是 24,payload固定为30字节。它提供的是未经滤波的原始GPS数据(相对于飞控内部融合后的位置估计)。

2.4.1 字段详解

字段名 类型 说明
time_usec uint64_t 时间戳,单位:微秒(通常用不到)
lat int32_t 纬度,单位:度×10^7(例如 40.1234567° → 401234567)
lon int32_t 经度,单位:度×10^7
alt int32_t 海拔高度,单位:毫米(mm)
eph uint16_t 水平定位精度(HDOP),单位:厘米(cm)
epv uint16_t 垂直定位精度(VDOP),单位:厘米(cm)
vel uint16_t 地面速度,单位:厘米/秒(cm/s)
cog uint16_t 航向角(地面航迹),单位:百分之一度(cdeg)
fix_type uint8_t 定位类型:0=无定位,1=无GPS,2=2D定位,3=3D定位,4=差分GPS,5=RTK浮点解,6=RTK固定解
satellites_visible uint8_t 可见卫星数
实用技巧: 我习惯在日志里同时记录 fix_type 和 satellites_visible。如果 fix_type 是3(3D定位)但卫星数少于8颗,我会怀疑GPS信号质量有问题。有一次在峡谷里飞行,卫星数掉到6颗,定位精度(eph)飙到5米以上——幸好提前发现了,没敢飞远。

2.4.2 数据转换示例

假设你从GPS_RAW_INT里读到:

  • lat = 401234567 → 纬度 = 401234567 / 10^7 = 40.1234567°
  • lon = 1161234567 → 经度 = 1161234567 / 10^7 = 116.1234567°
  • alt = 50000 → 海拔 = 50000 / 1000 = 50米
  • eph = 150 → 水平精度 = 150 / 100 = 1.5米
  • vel = 500 → 速度 = 500 / 100 = 5米/秒
  • cog = 9000 → 航向 = 9000 / 100 = 90.00°(正东方向)

你想想看,如果 eph 是1500(15米),你敢用这个数据做自动降落吗?反正我是不敢。

2.5 本章知识体系图

下面这张图,把本章三个核心消息的关系和关键字段串起来了。建议你保存下来,调试时对照着看。

MAVLink核心协议知识体系 MAVLink核心协议解析——消息结构图 MAVLink v1 消息帧结构 STX(0xFE) | LEN | SEQ | SYS ID | COMP ID | MSG ID | PAYLOAD | CKA | CKB HEARTBEAT (MSG ID=0) type | autopilot | base_mode custom_mode | system_status mavlink_version → 判断系统类型、飞行模式、是否解锁 SYS_STATUS (MSG ID=1) sensors_present/enabled/health load | voltage_battery current_battery | battery_remaining → 检查传感器健康、电池状态、CPU负载 GPS_RAW_INT (MSG ID=24) lat | lon | alt | eph | epv vel | cog | fix_type satellites_visible → 获取定位数据、精度、速度、航向 💡 调试顺序:先看HEARTBEAT确认连接 → 再看SYS_STATUS检查健康 → 最后看GPS_RAW_INT获取定位

2.6 本章小结

这一章我们干了三件事:

  1. 拆了MAVLink消息帧——知道了每个字节的含义,尤其是STX、LEN、MSG ID这三个关键字段。
  2. 解析了心跳包——它是系统的“身份证”,告诉你谁在说话、什么状态、什么模式。
  3. 啃下了SYS_STATUS和GPS_RAW_INT——前者是健康报告,后者是定位数据,两者结合才能判断飞机是否真的“安全”。