3、MAVLink消息定义与XML规范

聊到MAVLink的消息定义,我脑子里第一个蹦出来的就是那堆XML文件。说实话,刚接触的时候我也觉得有点懵——怎么一个通信协议还要用XML来定义?后来做项目多了才明白,这恰恰是MAVLink最聪明的地方。

3.1 消息定义文件的结构

MAVLink的消息定义,核心就靠几个XML文件。最常用的是common.xml和ardupilotmega.xml。前者是所有飞控厂商都遵守的公共定义,后者是ArduPilot生态的扩展。

我习惯把common.xml看作「通用字典」,里面定义了所有飞控都应该理解的消息。比如心跳包、姿态数据、GPS信息这些。而ardupilotmega.xml则是「方言」,里面是ArduPilot特有的消息,比如一些高级调试参数。

来看一个典型的消息定义长什么样:

<mavlink>
    <version>2</version>
    <messages>
        <message id="0" name="HEARTBEAT">
            <description>心跳包,用于检测连接状态</description>
            <field type="uint8_t" name="type">飞行器类型</field>
            <field type="uint8_t" name="autopilot">飞控类型</field>
            <field type="uint8_t" name="base_mode">基础模式标志位</field>
            <field type="uint32_t" name="custom_mode">自定义模式</field>
            <field type="uint8_t" name="system_status">系统状态</field>
        </message>
    </messages>
</mavlink>

嗯,这里要注意:每个消息都必须有唯一的ID。ID的范围是0到255,其中0到199是公共消息,200到255留给厂商自定义。

3.2 消息ID分配规则

消息ID的分配,说白了就是「谁先占坑谁先用」。我刚开始做无人机项目时,就踩过这个坑——自己定义了一个消息ID=200,结果跟别人的冲突了,调试了整整两天才发现。

官方推荐的分配规则是这样的:

ID范围 用途 示例
0 - 49 核心协议消息 HEARTBEAT(0), SYS_STATUS(1)
50 - 99 传感器数据 GPS_RAW(24), ATTITUDE(30)
100 - 149 执行器控制 SET_ATTITUDE_TARGET(82)
150 - 199 任务与规划 MISSION_ITEM(39)
200 - 255 厂商自定义 ArduPilot专用消息

我个人建议:如果你在开发自己的飞控,尽量先用公共ID范围内的消息。实在不够用了,再去申请厂商自定义ID。别问我为什么知道——我曾经图省事直接用了ID=200,结果后来要跟PX4通信,两边消息定义对不上,改代码改到怀疑人生。

3.3 消息负载字段的数据类型

MAVLink支持的数据类型不多,但够用。常用的就这几种:

  • uint8_t / int8_t:1字节,适合标志位、小范围数值
  • uint16_t / int16_t:2字节,适合角度、小范围传感器值
  • uint32_t / int32_t:4字节,适合时间戳、计数器
  • float:4字节,适合GPS坐标、姿态角
  • char数组:变长,适合文本信息

这里有个坑:MAVLink的float是IEEE 754标准的单精度浮点数。我遇到过有人用double,结果编译出来消息长度对不上,通信直接崩了。记住,MAVLink不支持double,只能用float。

看一个实际的消息定义:

<message id="24" name="GPS_RAW_INT">
    <field type="uint64_t" name="time_usec">时间戳(微秒)</field>
    <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="uint16_t" name="eph">水平精度(cm)</field>
    <field type="uint16_t" name="epv">垂直精度(cm)</field>
    <field type="uint16_t" name="vel">速度(cm/s)</field>
    <field type="uint16_t" name="cog">航向角(度×100)</field>
    <field type="uint8_t" name="satellites_visible">可见卫星数</field>
</message>

你想想看,为什么GPS坐标要用int32_t而不是float?因为int32_t的精度更高,而且传输过程中不会丢失精度。float只有23位尾数,而int32_t有31位有效位。这就是MAVLink设计者的用心之处。

3.4 枚举与位掩码定义

枚举和位掩码,是MAVLink里用来表示状态和选项的利器。枚举适合互斥的状态,位掩码适合可以同时存在的标志。

先看枚举的例子:

<enum name="MAV_TYPE">
    <entry value="0" name="MAV_TYPE_GENERIC">通用飞行器</entry>
    <entry value="1" name="MAV_TYPE_FIXED_WING">固定翼</entry>
    <entry value="2" name="MAV_TYPE_QUADROTOR">四旋翼</entry>
    <entry value="3" name="MAV_TYPE_HELICOPTER">直升机</entry>
    <entry value="4" name="MAV_TYPE_GROUND_ROVER">地面车辆</entry>
</enum>

再看位掩码:

<enum name="MAV_MODE_FLAG" bitmask="true">
    <entry value="1" name="MAV_MODE_FLAG_CUSTOM_MODE_ENABLED">自定义模式</entry>
    <entry value="2" name="MAV_MODE_FLAG_TEST_ENABLED">测试模式</entry>
    <entry value="4" name="MAV_MODE_FLAG_AUTO_ENABLED">自动模式</entry>
    <entry value="8" name="MAV_MODE_FLAG_GUIDED_ENABLED">引导模式</entry>
    <entry value="16" name="MAV_MODE_FLAG_STABILIZE_ENABLED">稳定模式</entry>
    <entry value="32" name="MAV_MODE_FLAG_HIL_ENABLED">硬件在环</entry>
    <entry value="64" name="MAV_MODE_FLAG_MANUAL_INPUT_ENABLED">手动输入</entry>
    <entry value="128" name="MAV_MODE_FLAG_SAFETY_ARMED">已解锁</entry>
</enum>

注意看,位掩码的value都是2的幂次方。这样设计的好处是,你可以用按位或运算组合多个状态。比如MAV_MODE_FLAG_AUTO_ENABLED | MAV_MODE_FLAG_SAFETY_ARMED就表示「自动模式已解锁」。

核心要点:枚举用value表示互斥状态,位掩码用2的幂次方表示可组合标志。两者不要混用,否则解析的时候会出大问题。

我的小技巧:定义枚举时,尽量给每个entry都加上description。虽然不强制,但后来维护代码的人会感谢你。我曾经接手过一个项目,枚举值没有注释,我花了整整一天才搞清楚每个值的含义。

避坑指南:我曾经在定义位掩码时,不小心把两个entry的value写成了相同的值。结果在解析消息时,这两个标志位永远同时为真。调试了三天才发现是XML定义写错了。所以,写完XML后一定要用官方工具验证一下。

3.5 知识体系总览

下面这张图,是我自己总结的MAVLink消息定义的知识体系。你可以把它当作一个快速索引:

MAVLink消息定义 XML定义文件 common.xml ardupilotmega.xml 厂商自定义XML 消息ID分配 0-49:核心协议 50-149:传感器/执行器 150-199:任务规划 200-255:厂商自定义 字段数据类型 uint8/16/32/64 int8/16/32/64 float (单精度) char数组 枚举与位掩码 枚举:互斥状态 位掩码:可组合标志 value为2的幂次方 验证与生成 官方验证工具 代码生成器 协议分析器 核心原则:标准化、可扩展、向后兼容

这张图把消息定义的几个核心要素串起来了。你从中心节点出发,沿着分支往下看,就能理清整个知识脉络。我个人做项目时,经常把这张图打印出来贴在工位上,写XML定义的时候对照着看,基本不会出错。


好了,关于消息定义与XML规范,核心内容就这些。记住:XML文件是MAVLink的「宪法」,所有通信行为都得按它来。定义消息时,ID别冲突,类型别用错,枚举和位掩码别混用。做到这几点,你的MAVLink通信基本就稳了。