3. MISSION_PROTOCOL详解:任务协议交互流程、MISSION_COUNT/MISSION_ITEM消息解析
讲QGC航点任务,绕不开MAVLink的MISSION_PROTOCOL。说白了,这就是地面站和飞控之间“商量”任务的那套规矩。我最早接触这个协议时,觉得不就是传几个点嘛,有啥复杂的?后来真踩了坑才发现——细节全在交互流程里。
今天咱们就把MISSION_PROTOCOL掰开揉碎。重点看两件事:交互流程和核心消息。你搞懂了这两个,后面做性能优化才有根基。
核心观点:MISSION_PROTOCOL不是简单的“上传-下载”,而是一个带状态机的握手协议。每个消息的收发顺序、超时处理、重传机制,都直接影响任务执行的可靠性。
3.1 协议交互流程:一次完整的任务上传
先看整体流程。我习惯把任务上传分成四个阶段:
- 写请求阶段:地面站告诉飞控“我要写任务”
- 计数协商阶段:飞控确认“好,你要写几个点”
- 逐点传输阶段:地面站一个一个发任务点
- 完成确认阶段:飞控说“收完了,没问题”
下面这张图把整个流程串起来了。你盯着看两分钟,后面讲消息就好理解了。
嗯,这里要注意:飞控收到MISSION_COUNT后,不是直接说“好”,而是先检查当前状态。如果飞控正在执行任务,或者存储空间不够,它会拒绝写请求。我遇到过一台老Pixhawk,EMMC快满了,写10个点的任务都报错——查了半天才发现是存储问题。
3.2 MISSION_COUNT消息解析
MISSION_COUNT是任务上传的“敲门砖”。地面站先发这个,告诉飞控“我要给你传N个点”。
| 字段名 | 类型 | 说明 |
|---|---|---|
| target_system | uint8_t | 目标系统ID(飞控的ID) |
| target_component | uint8_t | 目标组件ID(通常为MAV_COMP_ID_AUTOPILOT) |
| count | uint16_t | 任务点总数(包括航点、命令等) |
| mission_type | uint8_t | 任务类型:0=MISSION, 1=GEOCACHE, 2=FENCE, 3=RALLY |
个人经验:count字段是uint16_t,最大65535。但实际飞控的存储有限。我建议单次上传不超过500个点。超过这个数,飞控处理时间会明显变长,甚至触发看门狗复位。
飞控收到MISSION_COUNT后,会做两件事:
- 校验count值:如果count=0,飞控认为你要清空任务。如果count超过飞控容量,返回MISSION_ERROR。
- 发送MISSION_REQUEST:请求第0个任务点。注意,飞控不会一次性请求所有点,而是一个一个要。
为什么会这样?说白了,飞控的内存有限。一次处理一个点,处理完确认,再要下一个。这种“滑动窗口”机制虽然慢,但可靠。
3.3 MISSION_ITEM消息解析
MISSION_ITEM是任务的核心载体。每个任务点、每个命令(比如起飞、降落、拍照)都封装在这个消息里。
// MAVLink MISSION_ITEM 消息结构(简化版)
uint8_t target_system; // 目标系统
uint8_t target_component; // 目标组件
uint16_t seq; // 序号(从0开始)
uint8_t frame; // 坐标系:MAV_FRAME_GLOBAL_RELATIVE_ALT等
uint16_t command; // 命令:MAV_CMD_NAV_WAYPOINT等
uint8_t current; // 是否为当前任务点(0/1/2)
uint8_t autocontinue; // 是否自动继续到下一个点
float param1; // 参数1(如停留时间)
float param2; // 参数2(如接受半径)
float param3; // 参数3(如通过半径)
float param4; // 参数4(如偏航角)
float x; // 纬度(度)
float y; // 经度(度)
float z; // 高度(米,相对或绝对)
这里有个坑:frame字段决定了z的解释方式。MAV_FRAME_GLOBAL_RELATIVE_ALT表示z是相对起飞点的高度;MAV_FRAME_GLOBAL_TERRAIN_ALT表示z是相对地面的高度。我见过有人把相对高度当绝对高度传,结果飞机飞到100米高空——还好是在模拟器里。
避坑指南:我曾经在项目中遇到一个诡异问题:任务上传后,飞控总是跳过第3个点。查了两天,发现是seq字段写错了——我传了seq=0,1,2,2,4。飞控收到两个seq=2,直接覆盖了前一个。记住:seq必须连续且唯一。
3.4 任务下载流程
下载流程和上传是对称的,但方向相反。我简单列一下:
- 地面站发MISSION_REQUEST_LIST,请求任务列表
- 飞控回复MISSION_COUNT,告诉地面站有多少个点
- 地面站发MISSION_REQUEST(带seq),请求第N个点
- 飞控回复MISSION_ITEM,发送任务点数据
- 重复步骤3-4,直到所有点下载完成
- 地面站发MISSION_ACK,确认下载完成
你想想看,下载和上传的区别在哪?谁主动谁被动。上传是地面站主动推,飞控被动收;下载是地面站主动拉,飞控被动发。但核心消息是一样的。
3.5 协议中的超时与重传
实际通信中,丢包是常态。MISSION_PROTOCOL有简单的超时重传机制:
- 发送方:发送消息后启动定时器(通常500ms-1s)。超时未收到响应,重发上次消息。
- 接收方:收到重复消息(比如seq相同),直接丢弃,但重新发送ACK。
我遇到过最头疼的情况:WiFi链路下,丢包率10%左右,任务上传经常卡住。后来发现是QGC默认超时时间太短(300ms)。我改成800ms,配合重传3次,问题解决了。
优化建议:在QGC源码中,超时时间定义在MissionManager::_missionTransferTimeout。你可以根据链路质量动态调整。比如数传链路用1.5s,WiFi链路用800ms,4G链路用500ms。
3.6 协议状态机
飞控内部维护一个任务传输状态机。我画个简化的状态图:
这个状态机看着简单,但实际实现时有很多边界情况。比如:接收过程中飞控突然收到MISSION_REQUEST_LIST,应该怎么处理?按规范,飞控应该先完成当前传输,再响应新请求。但有些飞控固件实现得不好,直接中断当前传输——这就导致任务上传一半卡死了。
3.7 常见问题与排查方法
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 任务上传到一半卡住 | 超时时间过短、链路丢包 | 抓MAVLink日志,看是否收到MISSION_ACK |
| 飞控拒绝写任务 | 存储空间不足、飞控正在执行任务 | 检查飞控状态,清空当前任务 |
| 任务点顺序错乱 | seq字段不连续、重复 | 打印所有seq,检查是否递增 |
| 高度值异常 | frame字段错误、坐标系混淆 | 确认frame类型,检查z值单位 |
调试技巧:我习惯在QGC的MAVLink Inspector里实时看MISSION_PROTOCOL消息。重点关注MISSION_ACK的type字段。type=0表示ACCEPTED,type=1表示ERROR,type=2表示UNSUPPORTED。看到非0值,基本就是协议出问题了。
好了,MISSION_PROTOCOL的核心内容就这些。你记住三件事:交互流程是握手式的、MISSION_COUNT是总纲、MISSION_ITEM是血肉。后面讲性能优化时,我们会基于这些原理,讨论如何减少传输延迟、提高任务上传成功率。