4、MAVLink模式切换命令:COMMAND_INT与COMMAND_LONG详解、MAV_CMD_DO_SET_MODE参数解析、模式切换的响应机制
好,咱们进入正题。模式切换,说白了就是告诉飞控:「我现在想换个玩法」。但怎么告诉它?通过什么指令?参数怎么填?飞控收到后又怎么回你?
这章我把这三个问题拆开揉碎了讲。我个人习惯是先把命令本身搞清楚,再看参数怎么配,最后盯一下响应流程——这样一套下来,你写模式切换代码基本不会翻车。
4.1 两条核心命令:COMMAND_INT 与 COMMAND_LONG
MAVLink 里用来发「指令」的消息主要有两条:COMMAND_INT(编号 75)和 COMMAND_LONG(编号 76)。
名字上就差一个「INT」,但实际用起来区别不小。我刚开始接触时也搞混过,后来踩了一次坑才彻底分清楚。
| 对比项 | COMMAND_INT (75) | COMMAND_LONG (76) |
|---|---|---|
| 坐标精度 | 使用 int32 纬度/经度,精度更高 | 使用 float 纬度/经度 |
| 参数数量 | 最多 7 个参数(param1-param7) | 最多 7 个参数(param1-param7) |
| 适用场景 | 需要高精度坐标的任务(如航点、区域) | 一般指令(模式切换、参数设置等) |
| 帧类型 | 包含 frame 字段(坐标系) | 无 frame 字段 |
| 推荐度 | 新协议更推荐使用 | 兼容性好,老设备常用 |
核心结论:模式切换不需要高精度坐标,所以两条命令都能用。但我个人建议——优先用 COMMAND_INT。为什么?因为它是 MAVLink 2 的推荐方式,未来兼容性更好。
4.2 MAV_CMD_DO_SET_MODE 参数解析
不管用哪条命令发,真正干活的都是命令 ID:MAV_CMD_DO_SET_MODE(数值 176)。
这个命令有 7 个参数位,但模式切换只用前两个。嗯,这里要注意——很多人把参数填错了,结果飞控没反应。
| 参数 | 名称 | 说明 | 常用值 |
|---|---|---|---|
| param1 | 模式基值 | 指定模式大类(MAV_MODE 枚举) | 1 = MANUAL, 2 = STABILIZE, 3 = AUTO… |
| param2 | 自定义模式 | 飞控厂商自定义的子模式 | 见下方表格 |
| param3-7 | 保留/未使用 | 填 0 或 NaN | 0 |
避坑指南:我曾经在 param1 里直接填了「4」(AUTO),结果飞控没切过去。后来才发现——param1 必须填 MAV_MODE 枚举值,不是自定义模式编号。自定义模式要放在 param2 里。
4.2.1 自定义模式(param2)详解
param2 才是真正决定「飞控飞成什么样」的关键。不同厂商定义不同,我列几个最常见的:
| 飞控类型 | 模式名称 | param2 值 | 说明 |
|---|---|---|---|
| ArduPilot | STABILIZE | 0 | 自稳模式 |
| ArduPilot | ALT_HOLD | 2 | 定高模式 |
| ArduPilot | AUTO | 3 | 自动任务模式 |
| ArduPilot | GUIDED | 4 | 引导模式 |
| ArduPilot | LOITER | 5 | 悬停模式 |
| ArduPilot | RTL | 6 | 返航模式 |
| PX4 | MANUAL | 0 | 手动模式 |
| PX4 | ALTCTL | 1 | 高度控制 |
| PX4 | POSCTL | 2 | 位置控制 |
| PX4 | AUTO_MISSION | 3 | 自动任务 |
| PX4 | AUTO_RTL | 4 | 自动返航 |
小技巧:如果你不确定 param2 的值,可以先用 QGroundControl 手动切一次模式,同时抓包看 MAVLink 日志。我经常这么干——省得翻文档翻到眼花。
4.3 模式切换的响应机制
命令发出去了,飞控怎么回你?这涉及到 MAVLink 的「命令确认」机制。
4.3.1 响应流程
- 地面站发送:
COMMAND_INT或COMMAND_LONG,命令 ID = 176 - 飞控接收:解析命令,检查参数合法性
- 飞控执行:切换模式(或拒绝切换)
- 飞控回复:发送
COMMAND_ACK(编号 77)
你想想看,如果没有这个 ACK,你根本不知道飞控到底切没切成功。我早期做地面站时,就遇到过发了命令但飞控没反应——后来才发现是 param2 填错了,飞控直接拒绝了。
4.3.2 COMMAND_ACK 结构
| 字段 | 说明 | 常见值 |
|---|---|---|
| command | 原始命令 ID | 176 (MAV_CMD_DO_SET_MODE) |
| result | 执行结果枚举 | 0 = 成功, 1 = 被拒绝, 2 = 错误… |
| progress | 进度(0-100) | 通常为 0 或 255 |
| result_param2 | 附加信息 | 失败原因代码 |
| target_system | 目标系统 ID | 与请求一致 |
| target_component | 目标组件 ID | 与请求一致 |
关键点:result 字段为 0 才表示模式切换成功。其他值都要查原因。我见过有人只看 ACK 收到了就以为成功了——结果飞控根本没切。
4.3.3 常见失败原因
- result = 1 (MAV_RESULT_DENIED):飞控拒绝了切换。常见原因:当前飞行状态不允许切换(比如正在执行关键任务)、参数值非法。
- result = 2 (MAV_RESULT_FAILED):执行失败。可能是飞控内部错误。
- result = 3 (MAV_RESULT_IN_PROGRESS):正在切换中,稍后重试。
- result = 4 (MAV_RESULT_UNSUPPORTED):飞控不支持该模式。
避坑指南:我曾经在无人机正在执行 AUTO 任务时,试图切到 STABILIZE——结果飞控返回 DENIED。后来查文档才知道,某些飞控在任务执行中会锁定模式切换。解决办法:先发 MAV_CMD_MISSION_START 暂停任务,再切模式。
4.4 完整代码示例
下面给一个实际可用的 Python 示例(使用 pymavlink 库)。我习惯用 COMMAND_INT,但也会注释 COMMAND_LONG 的写法,方便你对比。
from pymavlink import mavutil
# 连接飞控
master = mavutil.mavlink_connection('udp:127.0.0.1:14550')
# 等待心跳
master.wait_heartbeat()
print("已连接飞控")
# 准备参数
target_system = 1
target_component = 1
# 模式:ArduPilot 的 GUIDED 模式
param1 = 1 # MAV_MODE_FLAG_CUSTOM_MODE_ENABLED
param2 = 4 # GUIDED
# 方式一:使用 COMMAND_INT(推荐)
master.mav.command_int_send(
target_system,
target_component,
0, # frame: MAV_FRAME_GLOBAL
176, # command: MAV_CMD_DO_SET_MODE
0, # current: 0
0, # autocontinue: 0
param1, # param1
param2, # param2
0, 0, 0, 0, 0 # param3-7
)
# 方式二:使用 COMMAND_LONG(兼容)
# master.mav.command_long_send(
# target_system,
# target_component,
# 176, # command
# 0, # confirmation
# param1, # param1
# param2, # param2
# 0, 0, 0, 0, 0 # param3-7
# )
# 等待 ACK
ack = master.recv_match(type='COMMAND_ACK', blocking=True, timeout=5)
if ack:
if ack.result == 0:
print("模式切换成功!")
else:
print(f"模式切换失败,错误码:{ack.result}")
else:
print("未收到 ACK,请检查连接")
小提示:实际项目中,我建议加一个重试机制——如果 2 秒内没收到 ACK,重新发一次。网络丢包时这招很管用。
4.5 知识体系总览
下面这张图把本章的核心逻辑串起来了。你一看就明白:命令怎么发、参数怎么填、响应怎么收。
嗯,这张图基本把整个流程讲透了。你写代码时,就按这个顺序来:选命令 → 填参数 → 发出去 → 等 ACK → 检查 result。少一步都可能出问题。
本章核心要点:
- 模式切换用
MAV_CMD_DO_SET_MODE(176),通过COMMAND_INT或COMMAND_LONG发送 - param1 填模式基值(MAV_MODE 枚举),param2 填自定义模式(厂商定义)
- 飞控回复
COMMAND_ACK,必须检查 result 字段确认是否成功 - 常见失败原因:参数错误、任务锁定、不支持的模式