1. 课程导入与开发环境搭建
MAVLink(Micro Air Vehicle Link)是一套轻量级的二进制消息协议,最初由 Lorenz Meier 于 2009 年提出,如今已成为无人机飞控与地面站、机载计算机、云平台之间通信的事实标准。理解它的定位,是后续所有开发工作的起点:它不是传输层协议,而是建立在串口、UDP、TCP 等链路之上的应用层消息协议。也就是说,MAVLink 只规定"消息长什么样、怎么打包、怎么校验",而"消息怎么送出去"由底层链路负责。
这种分层设计带来两个直接好处。第一,同一套消息定义可以跑在 915MHz 数传、2.4GHz WiFi、4G/5G 图传甚至 USB 串口上,上层代码几乎不用改。第二,协议本身极简:一帧 MAVLink v2 消息由起始标志 0xFD、长度、兼容标志、不兼容标志、序列号、系统 ID、组件 ID、消息 ID、载荷和 2 字节 CRC 组成,最小开销仅 12 字节,非常适合带宽受限的无线链路。
典型应用场景与学习路线
在真实项目中,MAVLink 主要承担四类任务:遥测下行(姿态、位置、电池、GPS 等状态回传)、指令上行(解锁、起飞、模式切换、航点上传)、任务交互(Mission、GeoFence、Rally Point 协议)、参数与日志(参数读写、日志下载、固件升级)。地面站 QGroundControl、Mission Planner,机载端 MAVSDK、pymavlink、DroneKit,都是围绕这些消息构建的。
本课程的学习路线遵循"先通链路、再懂消息、后做应用"的顺序:搭建仿真环境 → 用工具观察原始消息 → 用代码收发消息 → 实现自定义任务逻辑 → 优化链路可靠性。第 1 章的目标很明确:让 PX4 或 ArduPilot 仿真器跑起来,让地面站连上,让一条 MAVLink 心跳包能被你亲手抓到并解析。
环境搭建:三条并行的链路
推荐使用 Ubuntu 22.04(或 WSL2)作为主开发环境,Windows 原生也可行但串口与网络调试略麻烦。整体需要准备三部分:仿真飞控、MAVLink 开发库、地面站与调试工具。
仿真飞控方面,PX4 使用 PX4-Autopilot 仓库,编译后通过 make px4_sitl gazebo 启动带 Gazebo 的 SITL 仿真;ArduPilot 使用 ardupilot 仓库,通过 sim_vehicle.py -v ArduCopter --console --map 启动。两者默认都会在 UDP 14550 端口输出 MAVLink 数据流,这正是地面站默认监听的端口。
# PX4 SITL 启动示例
git clone https://github.com/PX4/PX4-Autopilot.git --recursive
cd PX4-Autopilot
make px4_sitl gazebo
# ArduPilot SITL 启动示例
git clone https://github.com/ArduPilot/ardupilot.git
cd ardupilot && ./Tools/environment_install/install-prereqs-ubuntu.sh -y
./Tools/autotest/sim_vehicle.py -v ArduCopter --console --map
MAVLink 库方面,Python 生态用 pip install pymavlink 即可,它自带消息定义生成器和常用工具;C/C++ 项目则用 mavlink 仓库的 mavgen 从 XML 消息定义生成头文件。地面站推荐 QGroundControl(跨平台、对 PX4 友好)或 Mission Planner(Windows、对 ArduPilot 友好)。调试工具必备三个:mavproxy(命令行路由与转发)、mavlink-router(多路复用)、以及 Wireshark 配合 MAVLink 解析插件。
连通性验证:从心跳包开始
环境搭好后,第一步验证不是看界面,而是确认能收到 HEARTBEAT(消息 ID 0)。心跳包默认 1Hz 发送,包含 type(载具类型)、autopilot(飞控类型)、base_mode、custom_mode、system_status 五个关键字段。只要能看到它,说明链路、端口、协议版本三者都对了。
# 用 pymavlink 监听 UDP 14550 上的心跳
from pymavlink import mavutil
master = mavutil.mavlink_connection('udp:127.0.0.1:14550')
master.wait_heartbeat()
print("Heartbeat from system %u component %u" %
(master.target_system, master.target_component))
如果 wait_heartbeat() 长时间阻塞,按以下顺序排查:仿真器是否真的在运行(看终端有无 Simulator started);端口是否被占用(ss -lunp | grep 14550);防火墙是否拦截 UDP;地面站是否已占用该端口导致冲突。常见做法是让 mavproxy 作为中间路由,把 14550 转发到 14551 给地面站、14552 给脚本,避免抢占。
常见坑与验证清单
初学者最容易卡在三个地方。其一是协议版本混用:PX4 默认发 v2,但某些老地面站只认 v1,此时需要设置 MAV_PROTO_VER 参数强制降级。其二是端口冲突:QGC 启动时会独占 14550,导致脚本收不到数据,解决办法是让 QGC 监听 14551、脚本监听 14552。其三是系统 ID 冲突:多机仿真时若所有实例都用 SYSID=1,地面站会混淆,需在启动参数中显式指定 -i 1、-i 2。
| 验证项 | 命令 / 操作 | 通过判据 |
|---|---|---|
| 仿真器运行 | 终端出现 Simulator started | Gazebo 窗口弹出,无报错退出 |
| UDP 端口监听 | ss -lunp | grep 14550 | 显示对应进程与端口 |
| 心跳可达 | wait_heartbeat() 返回 | 打印 system/component ID |
| 地面站连接 | QGC 自动识别载具 | 顶部显示"已连接",姿态仪有数据 |
| 消息解析 | Wireshark 加载 MAVLink 插件 | 能展开 HEARTBEAT 字段树 |
完成本章后,你应当拥有一个可重复启动的仿真环境、一个能抓到原始 MAVLink 帧的调试链路,以及一段能打印心跳的最小 Python 脚本。这三样东西是后续所有章节的实验底座——无论是解析姿态消息、上传航点,还是实现自定义控制指令,都建立在"链路已通、消息可见"的前提之上。