2. MAVLink核心概念:消息、命令、微服务、系统ID与组件ID
好,咱们进入正题。MAVLink 协议看起来复杂,其实核心就四个东西:消息、命令、微服务,还有系统ID与组件ID。把这四个搞明白,后面读代码、调参、写地面站都会顺很多。
我个人习惯把 MAVLink 比作一个「无人机聊天群」。群里每个人都有自己的身份,聊天的内容分几种类型,有些是日常闲聊,有些是直接下指令,还有些是自动化的后台服务。嗯,咱们一个一个拆开看。
2.1 消息(Message)—— 群里的「日常聊天」
消息是 MAVLink 最基础的数据单元。说白了,就是飞控和地面站之间互相发的「数据包」。飞控每隔一段时间往外广播自己的状态,比如当前高度、GPS坐标、电池电压,这些都是通过消息来传递的。
每条消息都有一个唯一的 ID,比如 HEARTBEAT 的 ID 是 0,GPS_RAW_INT 的 ID 是 24。地面站收到消息后,根据 ID 就知道该怎么解析后面的数据。
消息的结构长这样:
- 起始字节(0xFE 或 0xFD,取决于协议版本)
- 载荷长度
- 消息ID(1~3字节)
- 载荷数据(具体内容)
- 校验和
举个例子,飞控发一条 HEARTBEAT 消息,里面就包含了飞控的类型、状态、MAVLink 协议版本等信息。地面站收到后,就知道「哦,飞控还活着」。我在项目中遇到过一个问题:飞控的 HEARTBEAT 频率设置得太低,地面站老是报「连接超时」。后来我把心跳间隔从 5 秒改成 1 秒,问题就解决了。你想想看,心跳消息虽然小,但它是整个通信链路的基础。
2.2 命令(Command)—— 群里的「指令」
命令和消息不一样。消息是「告诉你我现在怎么样」,命令是「你给我去做某件事」。比如地面站发送 MAV_CMD_NAV_TAKEOFF,飞控收到后就会执行起飞动作。
命令通过 COMMAND_LONG 或 COMMAND_INT 消息来传输。每个命令也有一个唯一的 ID,比如起飞命令的 ID 是 22。命令可以带参数,最多 7 个浮点数,用来指定高度、速度、经纬度等。
| 命令名称 | ID | 用途 | 关键参数 |
|---|---|---|---|
| MAV_CMD_NAV_TAKEOFF | 22 | 起飞 | 目标高度 |
| MAV_CMD_NAV_LAND | 21 | 降落 | 降落位置(可选) |
| MAV_CMD_DO_SET_MODE | 176 | 切换飞行模式 | 模式编号 |
| MAV_CMD_COMPONENT_ARM_DISARM | 400 | 解锁/上锁 | 1=解锁,0=上锁 |
这里有个坑,我曾经踩过:发送命令后,飞控会回复一个 COMMAND_ACK 消息,告诉你命令是成功还是失败。很多新手只发命令,不检查 ACK,结果命令没执行成功都不知道。嗯,一定要养成检查 ACK 的习惯。
注意:命令的执行结果不一定是即时的。比如起飞命令,飞控收到后需要执行一系列安全检查,可能几秒后才真正开始起飞。所以 ACK 只代表「命令已接收并开始处理」,不代表「动作已完成」。
2.3 微服务(Microservice)—— 群里的「自动化机器人」
微服务是 MAVLink 2 引入的概念。它是一组预定义的消息交互流程,用来完成特定的功能。比如参数读写、任务上传、航点规划,这些都是微服务。
我个人觉得,微服务是 MAVLink 最优雅的设计之一。它把复杂的交互逻辑封装成标准化的「协议子集」,开发者不需要自己设计消息交互流程,直接用现成的就行。
常见的微服务有:
- 参数协议(PARAM):读写飞控参数,比如 PID 值、传感器校准参数
- 任务协议(MISSION):上传/下载航点任务
- 相机协议(CAMERA):控制相机拍照、录像
- 日志协议(LOG):下载飞控日志文件
每个微服务都有固定的消息序列。比如参数协议,地面站先发 PARAM_REQUEST_LIST,飞控回复 PARAM_VALUE,一条一条把参数发回来。如果中间丢包了,地面站可以重新请求。这种设计保证了可靠性。
小技巧:如果你需要自定义一些功能,尽量复用现有的微服务,而不是自己造轮子。我在项目中见过有人自己写了一套参数读写流程,结果和标准地面站不兼容,调试起来非常痛苦。
2.4 系统ID与组件ID—— 群里的「身份标识」
现在问题来了:一个地面站可能同时连接多架无人机,一架无人机上可能有飞控、GPS、相机等多个设备。怎么区分谁是谁?答案就是系统ID和组件ID。
- 系统ID(System ID):标识一个独立的系统,比如一架无人机、一个地面站。范围 1~255。
- 组件ID(Component ID):标识系统内的一个组件,比如飞控、GPS、相机。范围 1~255。
举个例子:系统ID=1 的无人机,它的飞控组件ID=1,GPS组件ID=5,相机组件ID=100。地面站系统ID=255,组件ID=1。这样,地面站发一条消息给「系统1的组件1」,飞控就知道是发给自己的。
| 组件 | 组件ID | 说明 |
|---|---|---|
| MAV_COMP_ID_AUTOPILOT1 | 1 | 主飞控 |
| MAV_COMP_ID_GPS | 5 | GPS模块 |
| MAV_COMP_ID_CAMERA | 100 | 相机 |
| MAV_COMP_ID_SERVO1 | 140 | 舵机1 |
我建议你在设计系统时,给每个设备分配固定的系统ID和组件ID。比如地面站固定用 255,无人机从 1 开始编号。这样多机协同的时候,消息路由就不会乱。
2.5 四者关系——一张图看懂
说了这么多,咱们用一张图把关系理清楚。下面这张 SVG 图展示了消息、命令、微服务、系统ID/组件ID 在 MAVLink 通信中的位置和作用。
从这张图可以看得很清楚:地面站通过消息和命令与飞控通信,微服务封装了更复杂的交互逻辑,而系统ID和组件ID则确保了消息能准确送达目标设备。四者缺一不可。
2.6 避坑指南
最后,分享几个我实际项目中踩过的坑,希望能帮你少走弯路。
坑1:系统ID冲突
我曾经在测试多机协同的时候,两台无人机都用了系统ID=1,结果地面站发出去的指令两台飞控都收到了,场面一度非常混乱。后来我规定每台无人机必须分配唯一的系统ID,从1开始递增。
坑2:组件ID不匹配
有个朋友自己写了个相机控制程序,一直收不到相机的状态消息。查了半天,发现他用的组件ID是1(飞控),但相机的组件ID是100。消息发错了对象,当然没反应。
建议:开发初期就把系统ID和组件ID的分配方案写进设计文档里。别等到联调的时候再临时分配,那时候容易乱。
好了,这一章的核心概念就讲到这里。消息、命令、微服务、系统ID与组件ID,这四个东西是 MAVLink 的基石。后面的章节我们会深入每个细节,包括怎么编码、怎么调试、怎么在实际项目中用好它们。