飞行模式基础架构:ArduPilot飞行模式枚举、模式切换的顶层逻辑、飞行模式与状态机的关系
各位好,我是老张。今天咱们聊聊飞行模式的基础架构。说实话,很多新手一上来就盯着PID调参,却忽略了模式切换这个"交通枢纽"。我见过太多SITL仿真跑着跑着突然炸机,最后发现是模式切换逻辑没搞明白。嗯,咱们今天就把这块地基打牢。
一、ArduPilot飞行模式枚举:不只是数字编号
ArduPilot里每种飞行模式都有一个唯一的枚举值。你打开代码会看到类似这样的定义:
// 在 AP_Mode.h 或类似头文件中
enum class Mode : uint8_t {
STABILIZE = 0, // 自稳模式
ACRO = 1, // 特技模式
ALT_HOLD = 2, // 定高模式
AUTO = 3, // 自动模式
GUIDED = 4, // 引导模式
LOITER = 5, // 悬停模式
RTL = 6, // 返航模式
CIRCLE = 7, // 绕圈模式
LAND = 9, // 降落模式
// ... 还有更多
};
我个人习惯把模式枚举分成三类:
| 分类 | 典型模式 | 核心特征 |
|---|---|---|
| 手动模式 | STABILIZE, ACRO | 飞手直接控制姿态,飞控只做辅助稳定 |
| 辅助模式 | ALT_HOLD, LOITER | 飞控接管部分自由度(高度/位置) |
| 自动模式 | AUTO, GUIDED, RTL | 飞控完全自主执行任务 |
小提示:枚举值不是连续的。比如LAND是9,中间跳过了8。这是因为历史版本中有些模式被废弃了,但枚举值保留了下来。你在SITL里写切换逻辑时,千万别用for循环遍历所有数字——会踩坑。
二、模式切换的顶层逻辑:谁在发号施令?
模式切换不是随便一个函数就能搞定的。ArduPilot里有一套完整的"切换仲裁"机制。说白了,就是问三个问题:
- 谁请求切换?——遥控器通道?地面站指令?还是任务脚本?
- 当前状态允许切换吗?——比如正在降落中,就不允许切到特技模式。
- 目标模式能正常初始化吗?——比如GPS没锁星,就别想切到AUTO模式。
核心代码在 GCS_Mavlink.cpp 和 AP_Mission.cpp 里。我截取一段关键逻辑:
// 简化后的模式切换入口
bool Copter::set_mode(Mode mode, ModeReason reason)
{
// 1. 检查当前模式是否允许退出
if (!mode_allows_exit(control_mode)) {
gcs().send_text(MAV_SEVERITY_WARNING, "当前模式不允许退出");
return false;
}
// 2. 检查目标模式是否有效
if (!mode.is_valid()) {
return false;
}
// 3. 尝试初始化目标模式
if (!mode.enter()) {
gcs().send_text(MAV_SEVERITY_CRITICAL, "模式初始化失败");
return false;
}
// 4. 执行切换
control_mode = mode;
return true;
}
注意:我曾经在SITL仿真里遇到一个诡异问题——模式切换偶尔会失败,但没有任何报错。后来发现是
mode.enter()里有个传感器健康检查,而仿真环境里某个虚拟传感器没按时响应。所以你在写SITL测试用例时,一定要模拟传感器延迟场景。
三、飞行模式与状态机的关系:模式是壳,状态机是核
很多人把"飞行模式"和"状态机"混为一谈。其实它们的关系是这样的:
- 飞行模式是用户可见的"外壳"——比如你拨动遥控器开关,飞机从Loiter切到RTL。
- 状态机是每个模式内部的"引擎"——比如RTL模式内部,要经历"爬升到安全高度→调转机头→飞向Home→下降降落"这几个状态。
说白了,每个模式内部都跑着一个独立的状态机。我画了张图帮你理解:
你看,RTL模式内部有三个子状态。每个子状态都有自己的进入条件、执行逻辑和退出条件。这就是状态机的典型用法。
四、状态机在SITL仿真中的调试技巧
在SITL里调试模式切换,我建议你关注这几个点:
- 用
--console参数启动SITL,可以实时看到模式切换的日志输出。 - 在
set_mode()函数里加断点,观察ModeReason参数——它能告诉你切换请求来自哪里。 - 模拟遥控器通道变化:用
rc 3 1500这样的命令模拟摇杆输入,看模式是否按预期切换。
核心要点:模式切换不是瞬间完成的。从请求到生效,中间要经过"检查→初始化→切换→后处理"四个阶段。任何一个阶段失败,切换都会被拒绝。你在SITL里测试时,一定要覆盖这些失败场景。
五、避坑指南:我踩过的三个坑
做SITL仿真这些年,我在模式切换上栽过不少跟头。挑三个典型的说说:
- 模式切换太快导致死锁——我曾经在测试脚本里每100ms切换一次模式,结果飞控直接卡死。后来发现是状态机还没退出完,新的切换请求就来了。解决方案:加一个
mode_switch_in_progress标志位。 - 传感器未就绪就切模式——有一次SITL启动后立刻切到AUTO模式,结果飞控报"GPS not ready"。其实仿真环境里GPS需要几秒才能完成搜星。我后来在测试脚本里加了
sleep(5)。 - 模式枚举值混淆——ArduPilot不同版本之间,模式枚举值可能不一样。我吃过亏,把Copter-3.6的枚举值直接用在4.0上,结果切到了完全不同的模式。所以一定要检查你用的固件版本对应的枚举定义。
好了,模式切换的基础架构就聊到这儿。记住:模式是用户看到的壳,状态机是内部跑的核。搞懂这两层关系,后面学高级切换逻辑就轻松多了。