第2章:MAVlink协议深度解析
做无人机系统,绕不开MAVlink。这玩意儿说白了就是无人机界的“普通话”。不管你是Pixhawk、ArduPilot还是PX4,大家通过MAVlink聊天。今天我就带你把它扒个底朝天。
2.1 MAVlink协议栈结构
MAVlink不是一层协议,它是个协议栈。我习惯把它分成三层来看:
- 物理层:负责实际传输。串口、UDP、TCP都行。我项目里常用串口,115200波特率,稳定可靠。
- 链路层:MAVlink的帧结构就在这层。包括起始标志、长度、序列号、系统ID、组件ID、消息ID、负载和校验。
- 应用层:具体消息内容。心跳包、GPS数据、姿态信息都在这里定义。
你想想看,这三层各司其职。物理层只管“怎么传”,链路层管“传什么格式”,应用层管“传什么内容”。分层设计的好处是——换物理层不用改上层代码。
核心要点:MAVlink协议栈的每一层都可以独立替换。我在做RTK融合时,物理层从串口换成了4G数传,上层代码一行没改。
下面这张图是我自己画的MAVlink协议栈结构,你看一眼就明白了:
2.2 消息定义与编码
MAVlink的消息定义,说白了就是一张表。每个消息有个ID,比如心跳包是0号,GPS_RAW_INT是24号。消息体里塞什么字段,都在XML文件里定义好了。
我记得刚开始接触MAVlink时,最头疼的就是字节对齐。MAVlink 1.0的消息体默认是8字节对齐,但有些字段长度不是8的倍数。嗯,这里要注意——填坑的时候得手动补齐。
实战技巧:我习惯用MAVlink的代码生成器。你写好XML定义,它自动生成C语言结构体和编解码函数。省心省力,还不容易出错。
编码过程其实很简单:把结构体里的字段按顺序塞进字节流里。解码就是反过来。但有个坑——大小端问题。MAVlink规定用小端(Little-Endian),但有些ARM芯片默认是大端。我曾经在这上面栽过跟头,飞控和地面站对不上数据,排查了半天才发现是字节序搞反了。
2.3 心跳包与系统状态
心跳包是MAVlink里最重要的消息,没有之一。它的消息ID是0,每隔1秒发一次。说白了就是告诉对方:“我还活着,状态正常。”
心跳包里包含这些关键信息:
| 字段 | 类型 | 说明 |
|---|---|---|
| type | uint8_t | 飞行器类型(四旋翼、固定翼等) |
| autopilot | uint8_t | 飞控类型(PX4、ArduPilot等) |
| base_mode | uint8_t | 系统模式(手动、定高、自动等) |
| custom_mode | uint32_t | 自定义模式(各飞控自己定义) |
| system_status | uint8_t | 系统状态(未初始化、待机、激活等) |
| mavlink_version | uint8_t | MAVlink版本号(目前是3) |
你想想看,地面站收到心跳包,就能知道飞机当前是什么状态。如果连续3秒没收到心跳,地面站就该报警了——飞机可能失联了。
避坑指南:我曾经遇到过一个问题——心跳包发送频率太高,把串口带宽占满了。后来我把心跳间隔从100ms改到1s,问题解决。记住,心跳包不是越快越好,够用就行。
2.4 GPS_RAW_INT与GLOBAL_POSITION_INT消息
这两个消息是RTK定位的核心。很多新手分不清它们,我简单说一下:
- GPS_RAW_INT(ID:24):原始GPS数据。包括经纬度、海拔、速度、精度因子等。说白了就是GPS模块直接吐出来的数据,没经过任何滤波。
- GLOBAL_POSITION_INT(ID:33):融合后的位置数据。飞控把GPS、IMU、磁力计等数据融合后算出来的位置。精度更高,更平滑。
我做RTK融合时,两个消息都用。GPS_RAW_INT拿原始RTK修正数据,GLOBAL_POSITION_INT拿最终定位结果。两者对比,还能判断融合算法有没有问题。
关键区别:GPS_RAW_INT是“原始材料”,GLOBAL_POSITION_INT是“成品”。RTK修正主要作用于GPS_RAW_INT层面,然后飞控融合后输出GLOBAL_POSITION_INT。
下面是一个典型的数据流:
GPS模块 → RTK修正 → GPS_RAW_INT消息 → 飞控融合算法 → GLOBAL_POSITION_INT消息 → 地面站
我记得有一次,客户说飞机定位不准。我一看日志,GPS_RAW_INT的精度因子显示RTK Fix,但GLOBAL_POSITION_INT却飘得厉害。排查后发现是IMU标定参数丢了。嗯,这就是为什么两个消息都要看——能帮你快速定位问题。
2.5 自定义消息扩展
MAVlink最牛的地方在于——你可以定义自己的消息。标准消息不够用?自己加!
我做过一个项目,需要传输RTK基站的位置信息。标准MAVlink里没有专门的消息,我就自己定义了一个:
<message id="150" name="RTK_BASE_STATION">
<description>RTK基站位置信息</description>
<field type="int32_t" name="lat">基站纬度(度*1e7)</field>
<field type="int32_t" name="lon">基站经度(度*1e7)</field>
<field type="int32_t" name="alt">基站海拔(mm)</field>
<field type="uint8_t" name="fix_type">基站固定类型</field>
</message>
定义好XML后,用MAVlink代码生成器一跑,C语言结构体和编解码函数就出来了。然后在地面站和飞控两端都加上这个自定义消息的处理逻辑,搞定。
自定义消息的ID范围:MAVlink 1.0里,0-255是标准消息,256-511是用户自定义。我习惯用300-400之间的ID,避免和未来标准消息冲突。
你想想看,有了自定义消息,MAVlink就变成了一个可扩展的框架。不管你有什么特殊需求,都能往里塞。这就是为什么MAVlink能成为无人机领域的“通用语言”。
好了,MAVlink协议的核心内容就这些。下一节我们开始实战——怎么在飞控代码里收发这些消息。到时候我会带着你一行一行写代码。
公众号:蓝海资料掘金营,微信deep3321