[BF学院_卷1] -- 第26讲:calculateThrottleAndCurrentMotorEndpoints() —— Mixer 如何确定油门基础范围
第26讲:calculateThrottleAndCurrentMotorEndpoints() —— Mixer 如何确定油门基础范围
本讲知识点
- 为什么 Mixer 不能直接使用 RC Throttle;
- calculateThrottleAndCurrentMotorEndpoints() 的控制职责;
- 普通模式下 Throttle 的转换过程;
- 3D 模式下 Throttle 的特殊处理;
- motorRangeMin 与 motorRangeMax 的作用;
- motorOutputMin、motorOutputRange、motorOutputMixSign 的意义;
- Mixer 为什么需要先建立电机输出空间。
本讲对应 Betaflight 源码
文件:
src/main/flight/mixer.c
核心函数:
static void calculateThrottleAndCurrentMotorEndpoints(timeUs_t currentTimeUs)
调用位置:
FAST_CODE_NOINLINE void mixTable(timeUs_t currentTimeUs)
{
calculateThrottleAndCurrentMotorEndpoints(currentTimeUs);
...
}
本讲覆盖范围:
- calculateThrottleAndCurrentMotorEndpoints() 主流程;
- 普通模式 Throttle 处理;
- 3D 模式 Throttle 处理;
- 电机输出范围变量建立。
回顾上一讲
上一讲中,我们进入了 Mixer 运行阶段。
已经知道:
PID Output -> mixTable() -> Motor Output
其中,mixTable() 的任务不是产生控制量,而是把:
Roll,Pitch,Yaw,Throttle
转换成每一个电机应该执行的输出。
但是,在真正计算之前,Mixer 首先需要解决一个基础问题:
当前油门应该映射到什么输出范围?
这就是本讲分析的:calculateThrottleAndCurrentMotorEndpoints()。
本讲学习目标
理解Mixer 为什么需要先建立 Throttle 对应的电机输出空间。
学习完成后,需要知道:Throttle 在 Betaflight 中并不是简单的:
RC输入 -> 电机转速
而是:
RC输入
↓
内部 throttle
↓
当前电机输出范围
↓
Motor Output
为什么 Mixer 不能直接使用 RC Throttle?
在飞手操作遥控器时, 油门输入来自:"RC Channel"。
例如:
1000us -> 1500us -> 2000us
这个信号只是飞手摇杆的位置, 但是电机输出并不直接使用这个数值。
原因是: 电机控制过程中存在很多限制:
例如:
- 最低启动输出;
- 最大输出限制;
- 电池补偿;
- 动态怠速;
- 3D模式正反转区域;
- DShot 特殊输出范围。
因此, RC Throttle 只是:"飞手希望多少油门"
而 Mixer 需要的是:"当前条件下,允许使用的电机输出范围"
所以在进入 Mixer 核心计算之前, 必须先完成:
RC Throttle -> 内部 throttle -> 电机输出空间
calculateThrottleAndCurrentMotorEndpoints() 的职责
观察函数:
static void calculateThrottleAndCurrentMotorEndpoints(timeUs_t currentTimeUs)
它并不是计算最终电机输出,它负责准备后续 Mixer 计算需要的基础参数。
主要建立:
throttle 内部油门, 归一化百分比
motorRangeMin 当前允许使用的电机输出下限(动态约束)
motorRangeMax 当前允许使用的电机输出上限(动态约束)
motorOutputMin 电机输出指令下限 (原始基准最小值)
motorOutputRange 电机输出指令的跨度
motorOutputMixSign 电机转动方向 (非3D模式固定为 1)
这些变量会被后续:
applyMixerAdjustment() -> applyMixToMotors()
使用。
因此, 这个函数处于:
RC Throttle -> Mixer输入准备 -> Motor Output计算
之间。
补充信息:
- [RangeMin, RangeMax] 是 [OutputMin, +OutputRange] 的子集
- 普通模式电机只能正转, 而3D模式下, 电机可以反转。
普通模式下 Throttle 如何转换?
先看普通模式分支代码:
else {
throttle = rcCommand[THROTTLE] - PWM_RANGE_MIN + throttleAngleCorrection;
currentThrottleInputRange = PWM_RANGE;
...
}
这里处理的是普通飞行模式。
RC Throttle
遥控器输入通常类似:
1000us ~ 2000us
但是 Mixer 内部不直接使用这个范围, 需要转换。
转换过程
代码:
throttle = rcCommand[THROTTLE] - PWM_RANGE_MIN + throttleAngleCorrection;
假设:PWM_RANGE_MIN = 1000, 飞手油门:1500,
那么:
1500 - 1000 = 500
此时, 它表示: 油门在整个范围中的相对位置。
另外,当飞机倾斜时,电机的升力并不与重力在一条线上,所以需要进行补偿,而 throttleAngleCorrection 就是飞机倾斜时,需要补偿的油门。
为什么后面还需要归一化?
函数最后:
throttle = constrainf(throttle / currentThrottleInputRange, 0.0f, 1.0f);
其中, currentThrottleInputRange 是当前 PWM 输入的范围。
而刚才我们计算到:throttle = 500,代入这里的公式 :
throttle = 500 / 1000 = 0.5,这就是 Mixer 使用的内部油门。
因此:
RC Throttle 1000~2000 -> 内部 throttle 0.0~1.0
throttle 为什么不是电机输出?
这里是初学者最容易混淆的地方。
内部 throttle = 0.5 并不代表:电机输出 50%。
它只表示飞手希望使用50%的油门控制范围。
真正输出还需要经过:
motorOutputMin + motorOutputRange
映射。
也就是说,最终:
Motor Output = 输出起点 + 油门比例 × 输出范围
motorRangeMin 与 motorRangeMax
普通模式代码:
motorRangeMax = mixerRuntime.motorOutputHigh;
motorRangeMin = mixerRuntime.motorOutputLow;
它们描述:当前允许的电机输出边界,这是一个动态调节的范围。
例如,假设:
motorOutputLow = 1000
motorOutputHigh = 2000
那么:
motorRangeMin = 1000
motorRangeMax = 2000
表示:电机输出不能低于 1000,不能高于:2000。
motorOutputMin 与 motorOutputRange
代码:
motorOutputMin = motorRangeMin;
motorOutputRange = motorRangeMax - motorRangeMin;
这两个变量用于后续输出转换。
假设:
motorOutputMin = 1000
motorOutputRange = 1000
那么,后续:
motorOutput = motorOutputMin + motorOutputRange * motorOutput
等价于:
1000 + 0~1范围 × 1000
得到:
1000~2000
所以,Mixer 内部计算并不是直接操作PWM数值。
而是在 0 ~ 1 控制空间中计算。
最后,再映射到真实电机输出范围。
为什么需要建立这个输出空间?
因为 Mixer 后续需要同时处理:
Throttle + Roll + Pitch + Yaw
例如,油门:0.5,Roll修正:+0.1,Pitch修正:-0.05,这些都是相对控制量。
如果直接使用 1000 ~ 2000 PWM值,不同功能之间很难组合。
所以 Betaflight 设计先转换到 “内部归一化控制空间”,然后Mixer 完成控制叠加。
最后再转换成ESC需要的输出。
3D 模式为什么不同?
普通模式只有:
最低油门 -> 最高油门
但是 3D 模式, 电机需要支持正转和反转。
因此油门空间变成:
反转区域 -> 死区 -> 正转区域
对应代码:
if (rcCommand[THROTTLE] <= rcCommand3dDeadBandLow)
{
// INVERTED
}
else if (rcCommand[THROTTLE] >= rcCommand3dDeadBandHigh)
{
// NORMAL
}
其中 rcCommand3dDeadBandLow 和 rcCommand3dDeadBandHigh 是死区区域的上限和下限。
3D 模式中的 motorOutputMixSign
3D 模式增加了:
motorOutputMixSign
普通模式:
motorOutputMixSign = 1;
表示正常方向。
反转模式:
motorOutputMixSign = -1;
表示输出方向反转。
因为,Mixer 后续计算需要知道当前电机输出方向。
例如,正常:
Throttle + Mixer修正
反转:
-(Throttle + Mixer修正)
因此需要这个符号变量。
本函数完成后的状态
执行完成后,Mixer 获得:
throttle 内部油门, 归一化百分比
motorRangeMin 当前允许使用的电机输出下限(动态约束)
motorRangeMax 当前允许使用的电机输出上限(动态约束)
motorOutputMin 电机输出指令下限 (原始基准最小值)
motorOutputRange 电机输出指令的跨度
motorOutputMixSign 电机转动方向 (非3D模式固定为 1)
这些变量共同建立Mixer 的工作空间,之后, mixTable() 才能够继续:
PID Output -> Mixer计算 -> Motor Output
本讲小结
本讲分析了 mixTable() 开始阶段的 calculateThrottleAndCurrentMotorEndpoints()。
这个函数的核心职责不是计算电机输出,而是在 Mixer 工作之前,建立统一的油门与电机输出空间。
它完成了:
RC Throttle
↓
内部 throttle
↓
电机输出范围
的转换。
同时,它处理了普通模式与3D模式的差异,使后续 Mixer 可以在统一的数学空间中完成 Roll、Pitch、Yaw 和 Throttle 的组合。
下一讲预告
下一讲继续分析 mixTable()。
重点进入PID Output 如何与 motorMixer_t 结合。
学习:
Roll, Pitch, Yaw
↓
motorMix[]
↓
每个电机的姿态修正量
这一部分将正式进入 Mixer 核心数学计算。
浙公网安备 33010602011771号