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 状态机的可视化

下面这张图是我自己画的,展示了固定翼状态机的主要模式切换路径。你可以看到,所有路径都必须经过一个“安全校验”节点。

固定翼状态机模式切换路径 MANUAL STABILIZE AUTO RTL 安全校验节点 检查姿态、高度、速度、地面状态 目标模式 保持当前模式 允许切换 拒绝切换

从图上你可以看到,所有模式切换都必须经过安全校验节点。这个节点不是摆设,它背后是一整套条件判断逻辑。我建议你在调试时,重点关注这个节点的输出日志,它能帮你快速定位切换失败的原因。

2.5 实际调试中的经验

最后分享一个我踩过的坑。有一次我在测试自动航线切换,发现飞机从AUTO模式切到RTL模式时,会先做一个剧烈的横滚动作。查了半天,发现是exit_mode()里没有正确保存当前航点索引,导致RTL模式启动时以为飞机在很远的地方,直接做了一个大半径转弯。

解决办法很简单:在退出AUTO模式时,把当前航点索引保存到一个全局变量里,RTL模式启动时直接读取这个变量,而不是重新计算。

调试小技巧:set_mode()函数里加一个日志输出,打印当前模式、目标模式、以及切换原因。这样你回看日志时,就能清楚知道每次切换发生了什么。我个人习惯用gcs().send_text(MAV_SEVERITY_INFO, ...)来输出,既不影响飞行性能,又能实时查看。

好了,关于固定翼状态机的模式切换逻辑,今天就聊到这里。记住一句话:状态机不是用来限制你的,而是用来保护你的飞机的。理解它,你就能更好地驾驭ArduPilot。


公众号:蓝海资料掘金营,微信deep3321