3. 飞行模式切换指令:MAV_CMD_DO_SET_MODE 深度拆解
说实话,在飞控系统里,切换飞行模式这件事看起来简单——不就是发个指令吗?但实际做起来,坑不少。我见过不少新手,上来就照着文档发 MAV_CMD_DO_SET_MODE,结果飞机没反应,或者直接切到了意想不到的模式。
这一节,我们就把它彻底讲透。
3.1 指令结构:COMMAND_LONG 与 MAV_CMD_DO_SET_MODE
在 MAVLink 协议里,MAV_CMD_DO_SET_MODE 是 COMMAND_LONG 消息的一个子命令。说白了,COMMAND_LONG 是一个“大信封”,里面装了很多种指令,而 MAV_CMD_DO_SET_MODE 就是其中专门用来切换飞行模式的。
它的参数定义如下:
| 参数编号 | 字段名 | 说明 | 典型值 |
|---|---|---|---|
| Param1 | base_mode | 基础模式掩码,定义系统状态 | 1 (MAV_MODE_FLAG_CUSTOM_MODE_ENABLED) |
| Param2 | custom_mode | 自定义模式编号,即具体的飞行模式 | 4 (AUTO), 5 (GUIDED), 3 (LOITER) 等 |
| Param3~7 | 保留/未使用 | 通常填 0 或 NaN | 0 |
核心要点:Param1 必须设置为 MAV_MODE_FLAG_CUSTOM_MODE_ENABLED(值为 1),否则飞控会忽略 Param2 的内容。这是很多人踩过的坑。
3.2 参数详解:base_mode 与 custom_mode
这两个参数,我分开讲。
3.2.1 base_mode:系统基础状态
base_mode 是一个 8 位的掩码,每一位代表一个系统状态标志。常用的标志位如下:
- MAV_MODE_FLAG_CUSTOM_MODE_ENABLED (0x01):启用自定义模式。这是切换飞行模式的必须标志。
- MAV_MODE_FLAG_TEST_ENABLED (0x02):测试模式,一般不用。
- MAV_MODE_FLAG_AUTO_ENABLED (0x04):自动模式标志,通常与自定义模式配合使用。
- MAV_MODE_FLAG_GUIDED_ENABLED (0x08):引导模式标志。
- MAV_MODE_FLAG_STABILIZE_ENABLED (0x10):增稳模式标志。
- MAV_MODE_FLAG_HIL_ENABLED (0x20):硬件在环仿真标志。
- MAV_MODE_FLAG_MANUAL_INPUT_ENABLED (0x40):手动输入标志。
- MAV_MODE_FLAG_SAFETY_ARMED (0x80):解锁标志。
对于飞行模式切换,我们通常只设置 MAV_MODE_FLAG_CUSTOM_MODE_ENABLED,即 base_mode = 1。
我的经验:我曾经在项目里尝试过同时设置多个标志位,比如 base_mode = 1 | 4 | 8,结果飞控的行为变得很奇怪。后来查了源码才发现,ArduPilot 和 PX4 对 base_mode 的解析方式不同。所以,老老实实只设 1 就对了。
3.2.2 custom_mode:具体的飞行模式
custom_mode 是一个 32 位的整数,具体值由飞控固件定义。不同固件,同一个数字可能代表不同的模式。
以 ArduPilot 为例,常用的模式编号如下:
| 模式名称 | custom_mode 值 | 说明 |
|---|---|---|
| STABILIZE | 0 | 增稳模式 |
| ACRO | 1 | 特技模式 |
| ALT_HOLD | 2 | 定高模式 |
| AUTO | 3 | 自动模式 |
| GUIDED | 4 | 引导模式 |
| LOITER | 5 | 悬停模式 |
| RTL | 6 | 返航模式 |
| CIRCLE | 7 | 环绕模式 |
| LAND | 9 | 降落模式 |
注意:PX4 的 custom_mode 定义与 ArduPilot 完全不同。例如,PX4 的 AUTO 模式编号是 4,而 ArduPilot 是 3。跨固件开发时,一定要查对应固件的源码或文档。
3.3 发送流程:从地面站到飞控
整个发送流程,我习惯把它分成三步:
- 构建 COMMAND_LONG 消息:填充 target_system、target_component、command、param1~7 等字段。
- 序列化并发送:通过串口、UDP 或 TCP 发送 MAVLink 帧。
- 等待飞控确认:飞控收到后会回复
COMMAND_ACK消息,告知指令执行结果。
下面是一个完整的 Python 示例,使用 pymavlink 库发送切换指令:
from pymavlink import mavutil
# 创建连接(假设飞控在 /dev/ttyUSB0,波特率 115200)
master = mavutil.mavlink_connection('/dev/ttyUSB0', baud=115200)
# 等待心跳包,确保连接建立
master.wait_heartbeat()
print("心跳已接收,系统ID:", master.target_system)
# 构建并发送 MAV_CMD_DO_SET_MODE 指令
# 目标:切换到 AUTO 模式(custom_mode = 3)
master.mav.command_long_send(
master.target_system, # target_system
master.target_component, # target_component
mavutil.mavlink.MAV_CMD_DO_SET_MODE, # command
0, # confirmation (0 表示首次发送)
1, # param1: base_mode = MAV_MODE_FLAG_CUSTOM_MODE_ENABLED
3, # param2: custom_mode = AUTO (ArduPilot)
0, 0, 0, 0, 0 # param3~7: 保留,填 0
)
print("指令已发送,等待飞控确认...")
# 等待 COMMAND_ACK 回复
ack = master.recv_match(type='COMMAND_ACK', blocking=True, timeout=5)
if ack:
if ack.result == mavutil.mavlink.MAV_RESULT_ACCEPTED:
print("指令被飞控接受,模式切换成功!")
else:
print(f"指令被拒绝,结果码: {ack.result}")
else:
print("超时:未收到飞控确认")
避坑指南:我曾经在发送指令后没有等待 COMMAND_ACK,结果连续发了 10 次,飞控直接进入了保护模式。记住,每次发送后一定要等确认,不要无脑重发。
3.4 飞控端的处理逻辑
飞控收到 MAV_CMD_DO_SET_MODE 后,内部处理流程大致如下:
- 校验 base_mode:检查是否包含
MAV_MODE_FLAG_CUSTOM_MODE_ENABLED。如果没有,直接返回MAV_RESULT_DENIED。 - 校验 custom_mode:检查该模式在当前系统状态下是否合法。例如,在未解锁状态下,某些模式可能被禁止。
- 执行模式切换:调用内部的状态机切换函数。
- 发送 COMMAND_ACK:返回执行结果。
下面我用一张 SVG 流程图来展示这个过程:
3.5 常见问题与避坑指南
最后,我总结几个实际项目中容易遇到的问题:
- 模式编号搞混:ArduPilot 和 PX4 的 custom_mode 值不同。我建议在代码里用枚举或常量定义,不要硬编码数字。
- base_mode 忘记设 1:这是最常见的错误。飞控收到 base_mode=0 的指令,会直接忽略。
- 未等待 ACK 就发下一条:飞控处理指令需要时间。连续发送会导致指令队列溢出。
- 在非安全状态下切换:比如飞机正在执行紧急降落,此时切换模式可能被飞控拒绝。
重要提醒:在实际飞行中测试模式切换前,务必先在仿真环境(如 SITL)中验证。我曾经在真机上测试新写的切换逻辑,结果飞机直接失控——还好当时高度只有 2 米,没有造成事故。
好了,关于 MAV_CMD_DO_SET_MODE 的内容就这些。参数定义、发送流程、飞控处理逻辑,这三块你吃透了,飞行模式切换这块就算入门了。