3. MAVLink XML消息定义:理解XML消息定义文件、常用消息集与自定义规则

说到MAVLink的消息定义,很多人第一反应是去翻那个庞大的common.xml。说实话,我第一次打开这个文件时也懵了——好几千行,密密麻麻的。但别怕,这东西的套路其实很固定。

XML消息定义文件,说白了就是MAVLink的“字典”。它告诉地面站和飞控:这条消息叫什么、里面装了什么数据、每个数据占几个字节。没有这个字典,两边就是鸡同鸭讲。

核心认知:MAVLink的XML定义文件不是给飞控运行时读的,而是给代码生成器用的。你写好XML,生成器自动产出C/Python/Java等语言的解析代码。所以,写XML就是在定义通信协议本身。

3.1 XML消息定义文件的结构

一个标准的MAVLink XML文件,长这样:

<?xml version="1.0"?>
<mavlink>
    <version>2</version>
    <dialect>1</dialect>
    
    <enums>
        <enum name="MAV_TYPE">
            <description>飞行器类型</description>
            <entry value="0" name="MAV_TYPE_GENERIC">
                <description>通用类型</description>
            </entry>
            <entry value="1" name="MAV_TYPE_FIXED_WING">
                <description>固定翼</description>
            </entry>
        </enum>
    </enums>
    
    <messages>
        <message id="0" name="HEARTBEAT">
            <description>心跳包,维持连接</description>
            <field type="uint8_t" name="type" enum="MAV_TYPE">飞行器类型</field>
            <field type="uint8_t" name="autopilot" enum="MAV_AUTOPILOT">飞控类型</field>
            <field type="uint8_t" name="base_mode" enum="MAV_MODE_FLAG">基础模式</field>
            <field type="uint32_t" name="custom_mode">自定义模式</field>
            <field type="uint8_t" name="system_status" enum="MAV_STATE">系统状态</field>
        </message>
    </messages>
</mavlink>

结构其实就三大块:

  • <version>:协议版本。现在主流是v2,v1基本淘汰了。
  • <enums>:枚举定义。比如飞行器类型、飞控类型、状态码等。这些枚举会被消息字段引用。
  • <messages>:消息定义。每条消息有一个唯一ID,一组字段。

我的习惯:写XML时,我会先把枚举定义好,再写消息。因为消息字段经常引用枚举,枚举先写好,后面写字段时直接填enum属性,不容易出错。

3.2 常用消息集:common.xml

common.xml是MAVLink官方维护的核心消息集。几乎所有地面站和飞控都支持它。里面定义了大约200多条消息,从心跳、姿态、GPS到遥控器信号,应有尽有。

我挑几个最常用的说说:

消息ID 消息名 用途 频率建议
0 HEARTBEAT 心跳包,检测连接是否存活 1Hz
24 GPS_RAW_INT GPS原始数据(经纬度、高度、速度) 5-10Hz
30 ATTITUDE 姿态数据(滚转、俯仰、偏航) 10-50Hz
33 GLOBAL_POSITION_INT 全局位置(经纬度+相对高度) 5-10Hz
65 RC_CHANNELS 遥控器通道值 5-10Hz

你想想看,地面站上显示的那些数据——飞机在哪、飞多高、姿态怎么样——都是从这些消息里解析出来的。所以二次开发时,熟悉common.xml就等于拿到了地面站的“数据地图”。

避坑指南:我曾经在项目里直接拿common.xml去解析一个定制飞控的数据,结果发现飞控发来的消息ID和common.xml对不上。后来才知道,那个飞控团队自己改了消息ID映射表。所以,一定要确认飞控端用的是哪个版本的common.xml,最好让飞控团队把他们的XML定义文件发给你。

3.3 自定义消息的XML编写规则

有时候common.xml不够用。比如你要传一个自定义的传感器数据,或者飞控内部的一个特殊状态。这时候就得自己写消息。

自定义消息的规则其实很简单,记住这几点:

  1. 消息ID范围:0-255是MAVLink官方保留的,自定义消息建议用256-511。我一般从300开始,留点余量。
  2. 字段类型:支持uint8_t、int8_t、uint16_t、int16_t、uint32_t、int32_t、uint64_t、int64_t、float、double、char数组。注意字节对齐,MAVLink v2会自动处理。
  3. 字段顺序:按字节对齐要求排列。通常把8字节的放前面,4字节的其次,1字节的放最后。这样能减少填充字节。
  4. 消息长度:不要超过255字节。虽然v2支持更长,但很多地面站和飞控的缓冲区就256字节。

举个例子,假设我要定义一个自定义传感器消息:

<message id="300" name="CUSTOM_SENSOR">
    <description>自定义传感器数据</description>
    <field type="uint64_t" name="timestamp_us">时间戳,微秒</field>
    <field type="float" name="temperature">温度,摄氏度</field>
    <field type="float" name="humidity">湿度,百分比</field>
    <field type="float" name="pressure">气压,hPa</field>
    <field type="uint8_t" name="sensor_id">传感器ID</field>
    <field type="uint8_t" name="status">状态标志位</field>
</message>

你看,我把uint64_t放最前面,float次之,uint8_t放最后。这样整个消息没有填充字节,长度正好是8+4+4+4+1+1=22字节,干净利落。

我的经验:写自定义消息时,一定要加时间戳字段。很多地面站需要知道数据是什么时候产生的,尤其是做日志回放时。没有时间戳,后期分析数据会非常痛苦。

3.4 消息定义的整体流程

为了让你更直观地理解,我画了一张图,展示从XML定义到地面站解析的完整链路:

MAVLink消息定义到解析的完整流程 XML消息定义 common.xml / 自定义 代码生成器 mavgen / pymavlink 生成代码 C / Python / Java 飞控端 发送消息 地面站端 接收并解析 数据展示 仪表盘 / 日志 💡 关键点: XML定义 → 代码生成器 → 生成解析代码 → 飞控和地面站使用同一套代码 只要XML定义一致,两端就能正确通信。这就是MAVLink“定义即协议”的精髓。

从这张图你能看到,XML定义是整个通信链路的起点。飞控和地面站都基于同一份XML生成代码,这样两边才能“说同一种语言”。

3.5 自定义消息的避坑指南

做自定义消息时,有几个坑我踩过,分享给你:

  • 消息ID冲突:我曾经在项目里用了ID=255,结果和官方消息冲突了,地面站解析全乱套。后来我统一用300+,再也没出过问题。
  • 字段类型不匹配:飞控端用float发温度,地面站用uint32_t解析,结果温度显示成几十亿度。嗯,这锅得背。
  • 字节对齐问题:虽然MAVLink v2会自动处理对齐,但如果你手动计算消息长度,一定要考虑填充字节。我习惯用pymavlink的mavparse工具验证一下。
  • 枚举值不一致:自定义枚举时,飞控和地面站的XML里枚举值必须完全一致。我见过一个项目,飞控端枚举从0开始,地面站从1开始,结果状态判断全错。

重要提醒:自定义消息的XML文件,一定要和飞控团队同步。我见过最惨的情况是:飞控改了消息定义,但没更新XML文件,地面站拿着旧版本解析,数据全是错的。后来我们定了个规矩:每次改XML,必须提交到版本库,两边都拉最新的。

3.6 小结

XML消息定义是MAVLink的基石。你掌握了common.xml,就能理解地面站上大部分数据的来源;你学会了自定义消息,就能扩展通信能力,满足项目特殊需求。

记住:写XML就是在定义协议。一份清晰、规范的XML定义,能让飞控和地面站的开发效率翻倍。反之,一份混乱的XML定义,会让你在调试时怀疑人生。