[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 核心数学计算。

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

导航