3、MAVLink协议深度解析:消息结构、常用类型与mavlink-router配置

MAVLink 这东西,说白了就是无人机世界的“普通话”。不管你是 ArduPilot 还是 PX4,飞控和地面站、机载电脑之间怎么聊天?全靠它。我最早接触 MAVLink 是在一个多机协同项目里,两台无人机要互相传位置信息,结果发现消息格式没对齐,数据全乱套了。从那以后,我养成了一个习惯——先看协议栈,再碰硬件。

这一节,咱们就把 MAVLink 的底裤扒开看看。消息怎么组装的?常用的心跳、姿态、位置消息长什么样?还有那个 mavlink-router 到底怎么配?嗯,一个一个来。

3.1 MAVLink 消息结构:从字节流到结构化数据

MAVLink 目前有两个主流版本:v1.0 和 v2.0。v2.0 主要解决了 v1.0 的扩展性问题,比如消息 ID 范围更大、支持签名认证。我个人建议,新项目直接上 v2.0,兼容性更好。

先看 v2.0 的消息帧结构:

字节偏移 字段名 长度(字节) 说明
0 STX(起始标志) 1 0xFD 表示 v2.0
1 LEN(负载长度) 1 payload 部分的字节数
2 INC_FLAGS(不兼容标志) 1 比如是否启用签名
3 CMP_FLAGS(兼容标志) 1 扩展字段,接收方可忽略
4 SEQ(序列号) 1 每个组件独立计数
5 SYS_ID(系统 ID) 1 标识无人机或地面站
6 COMP_ID(组件 ID) 1 标识飞控、GPS、相机等
7-9 MSG_ID(消息 ID) 3 v2.0 扩展到 24 位
10-... PAYLOAD(负载) LEN 具体消息数据
... CHECKSUM(校验和) 2 CRC16-CCITT
... SIGNATURE(签名) 13 可选,INC_FLAGS 置位时存在

你想想看,一个完整的 MAVLink 消息,其实就是这么一串字节。飞控收到后,先找 STX,再解析长度、ID,最后校验 CRC。如果校验不过,直接丢掉。我在调试时经常遇到 CRC 错误,后来发现是波特率没对齐——115200 和 57600 混着用,数据全花了。

调试小技巧: 用 mavlink-inspector 或 mavproxy.py --map 可以实时查看消息流。如果看到大量 CRC 错误,先检查物理层——线缆、波特率、电平转换。

3.2 常用消息类型:HEARTBEAT、ATTITUDE、LOCAL_POSITION_NED

MAVLink 定义了上百种消息,但日常联调中,90% 的时间你只跟几个核心消息打交道。我挑三个最常用的,咱们逐个拆解。

3.2.1 HEARTBEAT(心跳)

心跳是 MAVLink 的“存在证明”。每个组件必须定期发送,告诉别人“我还活着”。地面站如果连续几秒收不到心跳,就会报连接断开。

HEARTBEAT 的消息 ID 是 0,负载结构如下:

字段 类型 说明
type uint8_t MAV_TYPE 枚举,如 2 表示固定翼
autopilot uint8_t MAV_AUTOPILOT 枚举,如 3 表示 ArduPilot
base_mode uint8_t MAV_MODE_FLAG 位掩码,指示是否已武装、是否在引导等
custom_mode uint32_t 自定义模式,ArduPilot 用此表示飞行模式
system_status uint8_t MAV_STATE 枚举,如 4 表示已激活

举个例子,ArduPilot 在 Loiter 模式下,custom_mode 的值是 5。PX4 则不同,它的模式编码方式不一样。所以联调时,如果你看到地面站显示的模式不对,别慌——先查一下 custom_mode 的映射表。

注意: ArduPilot 和 PX4 的 custom_mode 编码不兼容。跨平台联调时,建议用 mavlink-router 做协议转换,或者在地面站端手动映射。

3.2.2 ATTITUDE(姿态)

ATTITUDE 消息(ID=30)告诉你飞机当前转到了什么角度。负载包含滚转、俯仰、偏航角,以及对应的角速度。这是飞控最核心的状态之一。

负载字段:

  • roll(float):滚转角,单位弧度
  • pitch(float):俯仰角,单位弧度
  • yaw(float):偏航角,单位弧度
  • rollspeed(float):滚转角速度,rad/s
  • pitchspeed(float):俯仰角速度,rad/s
  • yawspeed(float):偏航角速度,rad/s

我记得有一次在仿真环境里,飞机姿态一直乱跳。查了半天,发现是仿真时间戳没对齐,导致姿态数据被重复消费。后来我在接收端加了一个序列号检查,问题就解决了。

3.2.3 LOCAL_POSITION_NED(本地位置)

这个消息(ID=32)给出飞机在 NED 坐标系下的位置和速度。NED 就是北-东-地,原点通常是起飞点。

负载字段:

  • x(float):北向位置,单位米
  • y(float):东向位置,单位米
  • z(float):向下位置,单位米(注意:向下为正)
  • vx(float):北向速度,m/s
  • vy(float):东向速度,m/s
  • vz(float):向下速度,m/s

这里有个坑:z 是向下为正。也就是说,飞机高度 10 米时,z 的值是 -10。我第一次用这个数据做高度控制时,PID 参数死活调不对,就是因为符号搞反了。你想想看,一个正反馈的控制回路,飞机不炸才怪。

核心要点: 在协同仿真中,LOCAL_POSITION_NED 是无人机之间共享位置的主要消息。如果你要做编队飞行,一定要确保所有飞机的 NED 原点一致,否则位置偏差会直接导致碰撞。

3.3 mavlink-router 配置:打通多设备通信

mavlink-router 是一个轻量级的 MAVLink 转发工具,专门用来连接飞控、地面站、机载电脑等多个端点。它的核心功能就是路由——把消息从一端送到另一端,同时支持 UDP、TCP、串口等多种传输方式。

为什么需要它?

  • 飞控通常只有一个串口输出,但你可能同时需要连地面站(QGC)和机载电脑(ROS)。
  • mavlink-router 可以同时转发到多个 UDP 端点,还能做消息过滤和带宽控制。
  • 在仿真场景中,它能把 SITL 的 MAVLink 数据桥接到真实硬件上。

安装很简单:

git clone https://github.com/mavlink-router/mavlink-router.git
cd mavlink-router
meson setup build
ninja -C build
sudo ninja -C build install

配置文件通常放在 /etc/mavlink-router/main.conf,格式如下:

[General]
MavlinkDialect = ardupilotmega
TcpServerPort = 5760
ReportStats = true

[UdpEndpoint qgc]
Mode = Normal
Address = 192.168.1.100
Port = 14550

[UdpEndpoint companion]
Mode = Normal
Address = 127.0.0.1
Port = 14540

[SerialEndpoint flight_controller]
Device = /dev/ttyACM0
Baud = 115200

解释一下:

  • General:全局设置,指定 MAVLink 方言(ardupilotmega 或 common),以及 TCP 监听端口。
  • UdpEndpoint:定义 UDP 目标。你可以配多个,比如一个给 QGC,一个给 ROS 节点。
  • SerialEndpoint:串口连接,指向飞控的物理端口。

启动命令:

mavlink-routerd -c /etc/mavlink-router/main.conf

我曾经在一个项目中,飞控通过串口连到树莓派,树莓派上跑 mavlink-router,同时转发到 QGC(WiFi)和 ROS(本地回环)。效果很稳定,延迟在 5ms 以内。唯一要注意的是,如果 UDP 目标断连,mavlink-router 会持续重试,不会阻塞其他通道。

避坑指南: 如果你用 SITL 仿真,记得把 --out 参数指向 mavlink-router 的 UDP 端口,而不是直接连地面站。这样你可以随时切换地面站,而不用重启仿真。

3.4 知识体系总览

下面这张图把 MAVLink 的核心逻辑串起来了。从消息结构到常用类型,再到路由转发,你可以看到数据是怎么从飞控一路流到地面站和机载电脑的。

MAVLink 消息流转与路由架构 飞控(FCU) 串口输出 MAVLink mavlink-router 消息路由与转发 地面站(QGC) UDP 14550 机载电脑(ROS) UDP 14540 核心消息类型 • HEARTBEAT (ID=0) 系统心跳与模式 • ATTITUDE (ID=30) 滚转/俯仰/偏航 • LOCAL_POSITION_NED (ID=32) 本地位置与速度 • 更多消息可扩展

从这张图可以看得很清楚:飞控通过串口把原始 MAVLink 数据发给 mavlink-router,router 再根据配置,把消息分发给地面站和机载电脑。地面站关心的是 HEARTBEAT 和 ATTITUDE,机载电脑可能更关注 LOCAL_POSITION_NED。router 本身不做协议转换,但它可以过滤消息,减少不必要的带宽占用。

嗯,MAVLink 这块内容其实挺多的,但只要你把消息结构、核心类型、路由转发这三块吃透了,后面的协同仿真联调就会顺畅很多。我当年踩过的那些坑,希望你能绕过去。