2. MAVLink 消息结构:消息帧格式详解
好,咱们正式开始聊 MAVLink 的消息结构。说白了,就是地面站和飞控之间到底怎么“说话”的。你发一句“我在这”,对方得知道这是谁说的、说了什么、对不对。MAVLink 的帧格式,就是干这个的。
我记得刚接触 MAVLink 那会儿,看着一长串十六进制数,头都大了。后来拆开一看,其实就几个固定部分。今天咱们把它掰开揉碎了讲清楚。
2.1 消息帧格式
MAVLink 1.0 的帧结构,我习惯把它分成三块:头部、负载、尾部。每一块都有明确分工。来看这个标准格式:
| 起始标志 | 负载长度 | 序列号 | 系统ID | 组件ID | 消息ID | 负载数据 | 校验码 |
| 0xFE | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 0-255字节 | 2字节 |
嗯,这里要注意,MAVLink 2.0 的起始标志是 0xFD,消息 ID 也扩展到了 3 字节。但咱们先从 1.0 讲起,因为大部分入门场景都在用。
起始标志(STX)
就一个字节,固定值 0xFE。接收端看到这个字节,就知道“哦,一条新消息来了”。我曾经在调试时遇到过一个问题:串口线接触不良,飞控疯狂发乱码,地面站就是收不到有效数据。后来发现,就是因为起始标志被干扰了,接收端根本找不到消息头。
负载长度(LEN)
告诉你后面跟着的负载数据有多少字节。范围是 0 到 255。这个字段很关键,接收端靠它知道要读多少个字节才算完。
序列号(SEQ)
每发一条消息,这个数就加 1。从 0 开始,到 255 后归零。干嘛用的?检测丢包。你想想看,如果地面站收到序列号 5,下一个却是 7,那中间 6 就丢了。我在做数传链路测试时,就靠这个字段算丢包率。
系统 ID(SYS ID)
标识是哪个设备发的。比如飞控是 1,地面站是 255。多机场景下,这个字段特别重要。我见过有人把两架飞机的系统 ID 设成一样,结果地面站分不清谁是谁,控制指令全乱套了。
组件 ID(COMP ID)
同一个系统里可能有多个组件。比如飞控本身是一个组件,挂载的摄像头是另一个,GPS 模块又是一个。组件 ID 就是区分它们的。
消息 ID(MSG ID)
这个最核心。它告诉接收端“这条消息是什么类型”。比如 0 是心跳包,30 是姿态数据,24 是 GPS 数据。接收端根据消息 ID,就知道怎么解析后面的负载数据。
负载数据(PAYLOAD)
真正的数据内容。长度由 LEN 字段决定。不同消息 ID 对应不同的负载结构。比如心跳包只有 9 个字节,而 GPS 数据可能有 30 多个字节。
校验码(CRC)
两个字节,用来检查整条消息在传输过程中有没有被改过。发送端算一遍,接收端再算一遍,对不上就扔掉。嗯,这个机制很可靠。我遇到过数传模块受干扰,数据包被改得面目全非,全靠 CRC 把它们拦下来。
核心要点:MAVLink 帧结构就是“头 + 负载 + 尾”。头告诉你怎么读,负载告诉你读什么,尾告诉你读得对不对。
2.2 消息类型分类
MAVLink 定义了上百种消息类型。但咱们入门阶段,真正常用的也就那么几种。我按功能把它们分成了几类:
心跳消息(HEARTBEAT)
消息 ID 为 0。这是最基础的消息,飞控每隔 1 秒发一次。它告诉地面站:我还活着,我的状态是啥。负载里包含系统类型(四旋翼、固定翼、地面站等)、飞控类型(ArduPilot、PX4 等)、以及当前模式(悬停、返航、自动等)。
我个人的习惯是,地面站启动后第一件事就是等心跳。如果 5 秒内收不到,直接弹窗提示“飞控未连接”。这个逻辑简单但很实用。
姿态消息(ATTITUDE)
消息 ID 为 30。包含飞行器的横滚角、俯仰角、偏航角,以及三个轴的角速度。地面站拿到这些数据,就能在界面上画出飞行器的姿态。
我记得有一次做室内定位测试,飞控的姿态数据突然跳变,地面站上的模型直接翻了个跟头。后来查出来是 IMU 的安装方向错了。嗯,姿态数据对不上,其他都白搭。
GPS 消息(GLOBAL_POSITION_INT)
消息 ID 为 33。包含经纬度、海拔高度、地面速度、航向角等。这是地面站显示飞行器位置的核心数据。
这里有个坑:经纬度是以 度 * 1e7 的格式传输的。也就是说,收到 116397428 要除以 1e7 才是 116.397428 度。我曾经忘了做这个转换,结果地面站上显示飞行器在非洲……
其他常用消息
| 消息名称 | 消息 ID | 主要数据 | 用途 |
|---|---|---|---|
| BATTERY_STATUS | 147 | 电压、电流、剩余电量 | 电池监控 |
| RC_CHANNELS | 65 | 遥控器各通道值 | 遥控信号监测 |
| COMMAND_ACK | 77 | 命令执行结果 | 确认指令是否成功 |
| SYS_STATUS | 1 | 传感器状态、负载情况 | 系统健康检查 |
小技巧:刚开始学的时候,不用记所有消息 ID。记住心跳(0)、姿态(30)、GPS(33)这三个就够了。其他的用到再查。
2.3 消息帧结构图解
光说理论不够直观。我画了一张图,把整个消息帧的流转过程展示出来。从飞控发送,到地面站接收解析,每一步都清清楚楚。
这张图把整个流程串起来了。从飞控发出消息帧,到地面站按步骤解析,每一步都有明确的任务。你想想看,如果没有这个结构,地面站收到一堆乱码,根本不知道从哪开始读。
注意:MAVLink 1.0 和 2.0 的帧结构不完全一样。2.0 的消息 ID 扩展到了 3 字节,还增加了时间戳和兼容性标志。但 1.0 的结构更简单,适合入门学习。等 1.0 玩熟了,再切 2.0 也不迟。
2.4 实际抓包分析
光看理论不过瘾。咱们来拆一个真实的心跳包。假设串口上收到这么一串数据:
FE 09 01 01 01 00 00 00 00 00 00 00 00 00 00 05 3C
咱们按帧结构拆开看:
| 字节位置 | 值 | 字段 | 说明 |
|---|---|---|---|
| 0 | 0xFE | 起始标志 | MAVLink 1.0 固定值 |
| 1 | 0x09 | 负载长度 | 负载有 9 个字节 |
| 2 | 0x01 | 序列号 | 第 2 条消息(从 0 开始) |
| 3 | 0x01 | 系统 ID | 系统 1(飞控) |
| 4 | 0x01 | 组件 ID | 组件 1(飞控主控) |
| 5 | 0x00 | 消息 ID | 心跳包 |
| 6-14 | 9 字节 | 负载 | 心跳数据(系统类型、飞控类型、模式等) |
| 15-16 | 0x05 0x3C | 校验码 | CRC 校验值 |
看到了吧?整个消息才 17 个字节,但包含了所有必要信息。地面站收到后,先检查起始标志,再读长度,然后根据消息 ID 知道这是心跳包,最后用校验码确认数据没被篡改。
个人经验:我刚开始调试时,习惯用串口助手直接看十六进制数据。把收到的每一帧都按这个表格拆开,很快就能摸清规律。等熟悉了,再用 MAVLink 库自动解析。