[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 中:"飞手打杆之后,飞控如何计算最终电机输出"
浙公网安备 33010602011771号