3、MAVLink代码生成工具:从环境搭建到自定义消息
这一节,我们来搞定 MAVLink 的代码生成工具链。说白了,就是让机器帮我们把 XML 定义文件转成能用的 C 或 Python 代码。我刚开始接触 MAVLink 时,觉得这步挺简单,结果在环境配置上栽了好几次跟头。嗯,咱们一步步来,把坑都填平。
3.1 安装 Python 环境
mavgen 是个 Python 工具,所以 Python 环境是第一步。我个人习惯用 Python 3.8 以上版本,太老的版本有些依赖包会报错。
安装步骤很简单,分三步走:
- 下载 Python:去
python.org下载 3.8 或 3.10 版本。Windows 用户记得勾选「Add Python to PATH」。 - 验证安装:打开终端,输入
python --version,看到版本号就对了。 - 安装 pip:Python 3.4 以上自带 pip,你可以用
pip --version确认一下。
python3 -m venv mavlink_env 创建一个虚拟环境。这样项目之间不会互相污染依赖。激活命令:Windows 用 mavlink_env\Scripts\activate,Linux/macOS 用 source mavlink_env/bin/activate。
3.2 安装 mavgen 工具
mavgen 是 MAVLink 官方提供的代码生成器。它其实就藏在 MAVLink 的 Python 库里。安装命令很简单:
pip install mavlink
等等,别急着敲回车。这里有个坑——直接装 mavlink 库可能会装到旧版本。我建议从 GitHub 克隆最新源码来安装:
git clone https://github.com/mavlink/mavlink.git
cd mavlink
python setup.py install
为什么会推荐源码安装?因为 MAVLink 协议还在不断更新,有些新消息类型只有最新版才支持。我在项目中遇到过用 pip 装的老版本,生成的代码里缺少 COMPONENT_INFORMATION 消息,折腾了半天才发现是版本问题。
安装完成后,验证一下:
mavgen --help
如果看到帮助信息,说明装好了。没看到?检查一下 Python 脚本目录有没有加到 PATH 里。
3.3 使用 mavgen 生成 C/Python 代码
mavgen 的核心用法就一条命令:
mavgen --lang=C --output=./generated ./message_definitions/v1.0/common.xml
参数说明:
| 参数 | 说明 | 常用值 |
|---|---|---|
--lang |
目标语言 | C, Python, C++11, Java, JavaScript |
--output |
输出目录 | ./generated 或自定义路径 |
--wire-protocol |
协议版本 | 1.0(默认), 2.0 |
--validate |
是否校验 XML | 加上这个参数会做严格检查 |
生成 C 代码的例子:
mavgen --lang=C --wire-protocol=2.0 --output=./mavlink_c ./message_definitions/v1.0/common.xml
生成 Python 代码:
mavgen --lang=Python --output=./mavlink_python ./message_definitions/v1.0/common.xml
--validate 参数。虽然会慢一点,但能提前发现 XML 里的语法错误。我曾经因为少写了一个闭合标签,生成的代码在运行时疯狂报段错误,排查了整整一下午。
生成后的目录结构大概是这样:
./mavlink_c/
├── mavlink.h
├── mavlink_helpers.h
├── common/
│ ├── mavlink_msg_heartbeat.h
│ ├── mavlink_msg_attitude.h
│ └── ...
└── ...
Python 版本会生成一个 mavlink.py 文件,里面包含所有消息类和编解码函数。
3.4 自定义消息定义文件
标准消息不够用怎么办?比如你要给自己的无人机加一个「电池健康状态」消息,就得自己写 XML 定义文件。
先看一个标准消息的定义长什么样:
<?xml version="1.0"?>
<mavlink>
<version>3</version>
<messages>
<message id="0" name="HEARTBEAT">
<field type="uint8_t" name="type">MAV_TYPE</field>
<field type="uint8_t" name="autopilot">MAV_AUTOPILOT</field>
<field type="uint8_t" name="base_mode">MAV_MODE_FLAG</field>
<field type="uint32_t" name="custom_mode"></field>
<field type="uint8_t" name="system_status">MAV_STATE</field>
</message>
</messages>
</mavlink>
现在我们来写一个自定义消息。假设我要定义一个 BATTERY_HEALTH 消息,ID 用 300(标准消息 ID 范围是 0-255,自定义建议用 256 以上):
<?xml version="1.0"?>
<mavlink>
<version>3</version>
<messages>
<message id="300" name="BATTERY_HEALTH">
<description>自定义电池健康状态消息</description>
<field type="uint8_t" name="battery_id">电池编号</field>
<field type="uint16_t" name="voltage">电压,单位 mV</field>
<field type="int16_t" name="current">电流,单位 mA</field>
<field type="uint8_t" name="remaining_pct">剩余电量百分比</field>
<field type="uint16_t" name="temperature">温度,单位 0.1°C</field>
<field type="uint8_t" name="health_flags">健康状态标志位</field>
</message>
</messages>
</mavlink>
SET_MODE 消息冲突了。飞控收到数据后直接乱套。记住:自定义消息 ID 一定要避开标准消息范围(0-255),最好从 300 或 1000 开始。
写好 XML 文件后,用 mavgen 生成代码:
mavgen --lang=C --output=./custom_mavlink ./my_battery_health.xml
生成的代码里就会包含 mavlink_msg_battery_health.h,你可以像使用标准消息一样用它。
3.5 本章知识体系总览
下面这张图把整个流程串起来了,你一看就明白: