2. 固定翼状态机:深入剖析飞行模式切换逻辑,从手动到自动,状态机如何保证安全与平滑过渡
飞行模式切换,说白了就是飞机的大脑在不同工作状态之间跳转。你想想看,一个正在手动飞行的固定翼,突然要切到自动返航,如果没有任何过渡逻辑,飞控直接接管舵面——那画面太美我不敢看。飞机大概率会直接翻个跟头。
我在ArduPilot的源码里泡了几年,最让我佩服的就是这套状态机设计。它不是什么高深的理论,而是一套非常务实的工程方案。今天我就带你拆开看看,里面到底藏着哪些门道。
2.1 状态机的核心骨架
ArduPilot固定翼的状态机,本质上是一个有限状态机(FSM)。它定义了飞机在任意时刻只能处于一种模式,并且模式之间的切换必须遵循严格的规则。
核心原则:任何模式切换,都必须经过一个“安全校验点”。这个校验点会检查当前飞行状态是否允许切换,如果不允许,要么拒绝切换,要么强制执行一个预设的恢复流程。
我个人习惯把状态机分成三层:
- 顶层:模式枚举——定义了所有可用的飞行模式,比如MANUAL、STABILIZE、AUTO、RTL等。
- 中层:切换逻辑——处理模式切换时的条件判断、参数校验、以及必要的预处理动作。
- 底层:模式执行——每个模式独立运行的控制循环,输出舵面指令。
这三层之间通过一个全局的状态变量来通信。嗯,这里要注意,这个变量是受保护的,不能随便改。
2.2 模式切换的完整流程
我们来看一个具体的例子:从手动模式(MANUAL)切换到自动模式(AUTO)。
在ArduPilot的代码里,切换入口是set_mode()函数。我截取了一段核心逻辑,你感受一下:
bool AP_FixedWing::set_mode(enum FlightMode mode, ModeReason reason)
{
// 1. 检查当前模式是否已经是要切换的模式
if (mode == control_mode) {
return true;
}
// 2. 检查新模式是否有效
if (!mode_is_valid(mode)) {
return false;
}
// 3. 检查是否允许切换(比如是否在地面、是否在降落中)
if (!mode_allows_transition(mode)) {
return false;
}
// 4. 退出当前模式,执行清理工作
exit_mode(control_mode);
// 5. 进入新模式,执行初始化
enter_mode(mode);
// 6. 更新全局状态
control_mode = mode;
return true;
}
这段代码看起来简单,但每一步都有讲究。我曾经在项目中遇到过一个问题:飞机在低空手动飞行时,遥控器误触了自动模式开关。如果直接切过去,飞控会立刻接管,但当时飞机的姿态很糟糕,结果就是飞机直接俯冲。后来我加了一个条件——如果飞机当前俯仰角超过30度,切换会被延迟,直到姿态恢复。
避坑指南:我曾经在调试时发现,如果模式切换时飞机正在执行一个急转弯,直接切到AUTO模式会导致飞控的导航控制器瞬间饱和。解决办法是在enter_mode()里重置所有积分项,让控制器从零开始。
2.3 安全与平滑过渡的工程实现
状态机要保证安全,说白了就是两件事:拒绝危险切换和平滑过渡。
2.3.1 拒绝危险切换
ArduPilot里有一个专门的函数叫mode_allows_transition(),它会检查一系列条件:
- 飞机是否在地面?如果是,某些模式(如AUTO)不允许切换。
- 飞机是否在执行降落流程?如果是,不能切到手动模式。
- 当前模式是否正在执行紧急操作(如地形跟随)?如果是,切换会被阻塞。
你想想看,如果飞机正在自动降落,你突然切到手动模式,而遥控器摇杆不在中立位置——飞机可能会瞬间做出剧烈反应。所以ArduPilot的做法是:在降落过程中,手动模式切换会被延迟,直到降落完成或者你主动触发一个取消操作。
2.3.2 平滑过渡
平滑过渡的核心是渐进式接管。ArduPilot使用了一个叫做“模式混合”的技术:
// 伪代码:模式混合
float mix_ratio = get_mix_ratio(); // 0.0 ~ 1.0
output = (1 - mix_ratio) * old_mode_output + mix_ratio * new_mode_output;
这个mix_ratio会在几百毫秒内从0线性增加到1。这样,舵面指令就不会突变,而是平滑过渡。我在项目中测试过,这个时间常数设为300ms比较合适。太短了飞机还是会抖,太长了又感觉反应迟钝。
注意:模式混合只适用于同类型的控制输出(比如都是角度控制)。如果从纯手动(直接控制舵面)切换到增稳模式(角度控制),混合逻辑会失效。这时候ArduPilot的做法是:先让飞控的控制器跟踪当前舵面位置,然后再逐步切换到目标模式。
2.4 状态机的可视化
下面这张图是我自己画的,展示了固定翼状态机的主要模式切换路径。你可以看到,所有路径都必须经过一个“安全校验”节点。
从图上你可以看到,所有模式切换都必须经过安全校验节点。这个节点不是摆设,它背后是一整套条件判断逻辑。我建议你在调试时,重点关注这个节点的输出日志,它能帮你快速定位切换失败的原因。
2.5 实际调试中的经验
最后分享一个我踩过的坑。有一次我在测试自动航线切换,发现飞机从AUTO模式切到RTL模式时,会先做一个剧烈的横滚动作。查了半天,发现是exit_mode()里没有正确保存当前航点索引,导致RTL模式启动时以为飞机在很远的地方,直接做了一个大半径转弯。
解决办法很简单:在退出AUTO模式时,把当前航点索引保存到一个全局变量里,RTL模式启动时直接读取这个变量,而不是重新计算。
调试小技巧:在set_mode()函数里加一个日志输出,打印当前模式、目标模式、以及切换原因。这样你回看日志时,就能清楚知道每次切换发生了什么。我个人习惯用gcs().send_text(MAV_SEVERITY_INFO, ...)来输出,既不影响飞行性能,又能实时查看。
好了,关于固定翼状态机的模式切换逻辑,今天就聊到这里。记住一句话:状态机不是用来限制你的,而是用来保护你的飞机的。理解它,你就能更好地驾驭ArduPilot。
公众号:蓝海资料掘金营,微信deep3321