通信协议基础:MAVLink 协议深度解析
做地面站开发,第一个绕不开的东西就是 MAVLink。说白了,它就是无人机和地面站之间的“共同语言”。没有它,你发指令无人机听不懂,无人机传回来的数据你也看不懂。
我个人习惯把 MAVLink 比作一套“航空专用电报码”。它不像 HTTP 那样动不动就传几百 KB 的 JSON,而是用极小的数据包,把关键信息塞进去。这在带宽有限、实时性要求高的无人机场景里,太重要了。
核心认知:MAVLink 不是协议栈,而是一种轻量级的消息序列化协议。目前主流是 v1 和 v2 两个版本,v2 增加了签名和更大的消息 ID 空间,地面站开发建议直接上 v2。
MAVLink 消息结构:拆开看看里面有什么
一个 MAVLink 消息包,就像一封信。有信封(帧头)、有正文(载荷)、还有签名(校验)。我刚开始看协议文档时,觉得这东西挺绕,后来拆开一个实际抓包的数据,一下就明白了。
下面这张图,是我自己画的消息结构拆解,你一看就懂:
嗯,这里要注意:消息 ID 是 3 字节的,v1 只有 1 字节。这意味着 v2 最多支持 16,777,215 种消息类型,而 v1 只有 255 种。做地面站时,我建议你直接解析 v2 帧头,兼容 v1 就好。
常用 MAVLink 消息类型:你一定会用到的几个
MAVLink 协议定义了上百种消息,但日常地面站开发,常用的就那么十几个。我把它们分成三类,你记牢了:
| 类别 | 消息名称 | 消息 ID | 主要用途 |
|---|---|---|---|
| 状态类 | HEARTBEAT | 0 | 系统心跳、飞行模式、系统状态 |
| 状态类 | SYS_STATUS | 1 | 电池电压、剩余电量、传感器健康度 |
| 导航类 | GPS_RAW_INT | 24 | GPS 经纬度、海拔、卫星数 |
| 导航类 | ATTITUDE | 30 | 横滚、俯仰、偏航角 |
| 导航类 | GLOBAL_POSITION_INT | 33 | 融合后的全球位置(GPS+IMU) |
| 控制类 | COMMAND_LONG | 76 | 发送指令(解锁、起飞、返航等) |
| 控制类 | MANUAL_CONTROL | 69 | 遥控器通道值覆盖 |
我的经验:刚开始做地面站时,我先把 HEARTBEAT、ATTITUDE、GPS_RAW_INT 这三个消息解析通了,剩下的就水到渠成。你想想看,心跳通了说明连接建立,姿态和位置有了,HUD 就能动了。
心跳包与系统状态:地面站的“生命线”
心跳包(HEARTBEAT)是 MAVLink 里最基础、最重要的消息。没有之一。
为什么这么说?因为地面站就是靠心跳包来判断“无人机还活着吗”。飞控会以固定频率(通常是 1Hz)持续发送心跳包。如果地面站连续 3-5 秒没收到,基本可以判定通信断了。
心跳包的结构很简单,但信息量很大:
// HEARTBEAT 消息载荷结构(伪代码)
struct heartbeat_t {
uint8_t type; // 系统类型(1=飞控, 2=地面站, 3=摄像头...)
uint8_t autopilot; // 飞控类型(3=ArduPilot, 12=PX4...)
uint8_t base_mode; // 基础模式标志位(是否已解锁、是否在自动模式)
uint32_t custom_mode; // 自定义模式(具体飞行模式:悬停、航线、返航...)
uint8_t system_status; // 系统状态(0=未连接, 3=已解锁, 4=飞行中...)
};
我记得有一次在项目现场,无人机飞出去后地面站突然显示“连接丢失”。排查了半天,发现是串口线被机架夹住了,心跳包发不出来。从那以后,我养成了一个习惯:地面站启动后第一件事,就是看心跳包接收频率。如果 1 秒内没收到,先别急着操作,检查物理连接。
避坑指南:我曾经遇到过一个问题——心跳包收到了,但 custom_mode 解析出来是乱码。后来发现是字节序搞反了。MAVLink 默认使用小端字节序(Little-Endian),如果你的地面站跑在大端架构的芯片上(比如某些 ARM 核),记得做字节序转换。
系统状态机:从“未连接”到“飞行中”
地面站需要根据心跳包里的 system_status 字段,来更新 UI 上的状态显示。我一般会在代码里维护一个状态机:
实际开发中,我建议你在地面站 UI 上做一个状态指示灯:
- 红色闪烁:未收到心跳包,通信中断
- 黄色常亮:已收到心跳,但系统未就绪(比如 GPS 未定位)
- 绿色常亮:一切正常,可以起飞
这个设计虽然简单,但非常实用。操作员扫一眼就知道当前能不能飞,不用去翻日志。
如何抓包验证 MAVLink 通信
理论说完了,来点实操。我推荐两个工具:
- MAVLink Inspector(QGroundControl 自带):连接飞控后,在工具菜单里打开,能看到实时消息流。
- mavlink-router 的日志功能:在命令行里跑
mavlink-routerd -l /tmp/mavlink.log,所有消息都会存成文件。
抓包后,你可以用 Python 的 pymavlink 库来解析:
from pymavlink import mavutil
# 连接串口(波特率 115200)
master = mavutil.mavlink_connection('/dev/ttyUSB0', baud=115200)
# 等待第一个心跳包
master.wait_heartbeat()
print(f"收到心跳!系统类型: {master.target_system}")
# 持续读取消息
while True:
msg = master.recv_match(blocking=True)
if msg.get_type() == 'HEARTBEAT':
print(f"模式: {msg.custom_mode}, 状态: {msg.system_status}")
elif msg.get_type() == 'ATTITUDE':
print(f"横滚: {msg.roll:.2f}, 俯仰: {msg.pitch:.2f}")
这段代码我用了好几年,基本没改过。你把它跑通,MAVLink 通信就算入门了。
总结一下本章核心:
- MAVLink 是轻量级消息协议,v2 是主流,支持更多消息类型和签名
- 消息帧由帧头、长度、ID 字段、载荷和 CRC 组成
- 心跳包是通信基础,1Hz 频率,丢了就说明连接断了
- 系统状态机帮助地面站 UI 准确反映无人机当前阶段
- 用 pymavlink 抓包验证,是排查问题最快的方法