[BF学院_卷1] -- 第29讲:applyMixToMotors() —— 最终 Motor Output 生成

第29讲:applyMixToMotors() —— 最终 Motor Output 生成


本讲知识点

  • motorMix 为什么还不是最终电机输出;
  • Throttle 与 motorMix 如何合成为 Motor Output;
  • motorMixer_t 在运行阶段的作用;
  • throttle 权重 activeMixer[i].throttle;
  • motorOutputMixSign 的作用;
  • motorOutputMin;
  • motorOutputRange;
  • 电机输出空间映射;
  • constrainf() 限幅;
  • failsafe 状态下的特殊处理;
  • 未解锁状态下的电机输出处理。

本讲对应 Betaflight 源码

主要函数:

applyMixToMotors(float motorMix[MAX_SUPPORTED_MOTORS], motorMixer_t *activeMixer)

核心代码:

float motorOutput = motorOutputMixSign * motorMix[i]
                  + throttle * activeMixer[i].throttle;

以及:

motorOutput = motorOutputMin + motorOutputRange * motorOutput;

最终:

motor[i] = motorOutput;

本讲学习目标

理解 Mixer 输出链路的最后一步。

前面的 Mixer 已经完成:

PID控制需求 -> motorMix[]

但是 motorMix[] 仍然只是姿态修正量, 它并不能直接发送给 ESC。

最终电机需要的是实际输出值,

因此, applyMixToMotors() 负责完成:

Throttle基础推力 + 姿态修正量 -> 单个电机最终输出

回顾上一讲

上一讲分析了:

applyMixerAdjustment()

applyMixerAdjustmentLinear()

它们解决的问题是:Mixer 产生的 motorMix[] 如何避免进入:

Motor Output = Throttle + motorMix

之后发生饱和。


从 motorMix 到 Motor Output

回顾 Mixer 控制链:

PID Controller

↓

Roll / Pitch / Yaw 控制需求

↓

mixTable()

↓

motorMix[]

↓

applyMixToMotors()

↓

motor[]

↓

ESC

↓

电机

其中,motorMix[] 表示:

为了改变飞机姿态,每个电机需要增加或者减少多少输出

例如,某个四旋翼:

M1 = +0.1

M2 = -0.1

M3 = +0.1

M4 = -0.1

表示:某些电机提高,某些电机降低。目的:产生Roll / Pitch / Yaw 力矩。

但是,它没有包含飞机需要多少升力,升力来自 "Throttle"。

所以最终需要:

基础推力 + 姿态修正

Motor Output 计算公式

核心代码:

float motorOutput = motorOutputMixSign * motorMix[i]
                  + throttle * activeMixer[i].throttle;

对应数学模型:

Motor Output = 姿态修正量 + 油门基础输出

展开:

Motor Output = motorMix × 方向系数 + Throttle × 油门权重

activeMixer[i].throttle —— 油门权重

代码:

throttle * activeMixer[i].throttle

这里的 activeMixer[i] 来自 motorMixer_t,它保存当前飞行器的混控表。

一个电机对应:

Roll 权重,Pitch 权重,Yaw 权重,Throttle 权重

例如,四旋翼:

motorMixer_t

M1: roll,pitch,yaw,throttle

M2: roll,pitch,yaw,throttle

...

对于普通四旋翼,Throttle 权重通常:

throttle = 1.0

表示所有电机获得相同油门输入。

所以:

Throttle -> 平均增加所有电机推力

但是某些特殊机型, 例如:

  • 非标准布局;
  • 多旋翼特殊结构;
  • 其他 Mixer 类型;

不同电机可能具有不同Throttle 权重, 因此源码不会直接:

motorOutput = throttle + motorMix;

而是:

throttle * activeMixer[i].throttle

让 Mixer 表决定每个电机如何接收基础推力。


motorOutputMixSign —— 为什么需要方向修正?

代码:

motorOutputMixSign * motorMix[i]

其中 "motorOutputMixSign" 用于调整motorMix 的符号方向。

因为不同飞行模式或者不同 Mixer 配置下,可能存在 "正负方向定义不同",

因此, 源码通过 "motorOutputMixSign" 统一调整,

最终保证进入电机输出计算时:

正值 = 增加输出

负值 = 减少输出

符合当前 Mixer 定义。


motorOutput 仍然不是最终电机协议值

经过:

float motorOutput = motorOutputMixSign * motorMix[i]
                  + throttle * activeMixer[i].throttle;

得到的 "motorOutput", 仍然处于Mixer 内部归一化空间。

通常:

0.0 ~ 1.0

但是 ESC 需要实际输出范围。

例如, PWM:1000us ~ 2000us,或者 DShot:数字油门值。

因此还需要下一步映射。


motorOutputMin 和 motorOutputRange —— 输出空间映射

代码:

motorOutput = motorOutputMin + motorOutputRange * motorOutput;

公式:

实际输出 = 最小输出 + 归一化输出 × 输出范围

例如,假设:

motorOutputMin = 1000

motorOutputRange = 1000

输入:motorOutput = 0.5,那么,

实际输出 = 1000 + 1000 × 0.5 = 1500

对应 50% 油门。


因此,Mixer 内部 "0.0 ~ 1.0" 经过转换:

内部控制空间 -> ESC 输出空间

constrainf() —— 最终保护

代码:

motorOutput = constrainf( motorOutput,
    mixerRuntime.disarmMotorOutput,
    motorRangeMax
);

作用:限制最终输出范围。

因为即使经过前面的:

  • PID 限制;
  • Mixer 调整;
  • Throttle 限制;

仍然可能由于:

  • 数值误差;
  • 特殊模式;
  • 外部控制;

导致输出超过范围。

因此,最后必须保护:不能低于最小值,不能超过最大值。


Failsafe 状态下的 Motor Output

在正常飞行状态下:

RC输入 -> PID控制 -> Mixer计算 -> Motor Output -> ESC

但是,飞控运行过程中,可能出现:

RC信号丢失 -> Failsafe触发

这时,飞控不能继续按照飞手输入控制飞机。

因此 Betaflight 会进入:

Failsafe 状态

什么是 Failsafe?

Failsafe 的核心目的:不是继续控制飞机飞行,而是在控制链异常时:

避免飞机失控

例如:

  • 遥控器关闭;
  • 接收机信号丢失;
  • 遥控链路中断。

正常情况下:

RC Command -> throttle / roll / pitch / yaw

来自飞手。

而 Failsafe 状态下:

RC Command

×

飞控无法继续获得有效控制输入,因此系统会按照预设策略接管控制权。


Failsafe 对 Motor Output 的影响

applyMixToMotors() 之前,

Mixer 已经完成:

Throttle + motorMix -> Motor Output

但是如果 Failsafe 激活,飞控可能不会继续使用正常飞行逻辑产生输出,而是根据 Failsafe 设置。

例如:

降低油门 -> 保持一段时间 -> 执行降落或者停止电机

具体行为取决于 failsafe 配置,以及当前飞行状态。


为什么需要在 Motor Output 阶段处理?

因为前面的控制链:

PID Controller -> Mixer -> motorMix

描述的是飞机需要什么控制动作。

例如:

Roll修正

Pitch修正

Yaw修正

但是 Failsafe 关注的是:飞机是否仍然处于可控状态,这属于更高优先级的安全逻辑。

因此,Failsafe 不应该改变 PID 算法,而是在最终输出阶段决定是否继续输出电机控制量。


控制链中的位置

完整理解:

正常情况:

RC输入 -> PID控制 -> mixTable() -> Throttle + motorMix -> applyMixToMotors() -> Motor Output -> ESC

Failsafe:

RC信号丢失 -> Failsafe触发 -> 覆盖或者限制控制输出 -> Motor Output

所以, Failsafe 的作用不是参与 Mixer 混控计算,而是在最终输出阶段:防止错误控制继续传递到电机。

它是 Betaflight 控制链中的:

安全保护层

failsafe 状态处理

源码:

if (failsafeIsActive()) {

表示,当前飞控进入 "Failsafe" 状态。

Failsafe 下, 电机输出策略不同。

例如:

DShot:

motorOutput = (motorOutput < motorRangeMin)
            ? mixerRuntime.disarmMotorOutput
            : motorOutput;

在普通状态时, 输出的范围最小值是motorRangeMin,

而低于该值,则是 DShot的特殊保留区,也就是特殊命令。

当Failsafe状态被激活时,如果motorOutput的值低于motorRangeMin,那么就直接设置成特殊命令disarmMotorOutput。


未解锁状态处理

最后:

if (!ARMING_FLAG(ARMED)) {
    motor[i] = motor_disarmed[i];
}

表示: 如果飞机没有解锁, 直接使用 motor_disarmed[]。

例如:

1000us

或者配置的停转值。


完整控制链总结

到这里,Mixer 整个流程闭环:

RC输入

↓

PID Controller

↓

Roll / Pitch / Yaw 控制需求

↓

mixTable()

↓

motorMix[]

↓

applyMixerAdjustment()

↓

控制权限调整

↓

applyMixToMotors()

↓

Throttle + motorMix

↓

motor[]

↓

ESC

↓

电机

其中,Throttle 负责整体升力,而 motorMix 负责姿态控制差动,

二者结合才能同时满足:

保持高度 + 控制姿态

本讲小结

applyMixToMotors() 是 Mixer 输出链路的最后一步。

它首先将 "motorMix[]" 的姿态修正量,与 "Throttle" 提供的基础推力结合,

形成单个电机输出需求, 随后, 通过:

motorOutput = motorOutputMin + motorOutputRange * motorOutput;

将内部归一化控制量映射到实际 ESC 输出范围。


下一讲预告

下一讲将总结 mixTable() 整个函数。

重点:

RC输入 -> PID控制 -> Mixer -> motorMix -> Motor Output

完整还原 Betaflight 4.5.0 中:"飞手打杆之后,飞控如何计算最终电机输出"

posted on 2026-08-03 15:44  longyue  阅读(11)  评论(0)    收藏  举报

导航