[BF学院_卷1] -- 第25讲:mixTable() 总览 —— Mixer 在控制循环中的位置
第25讲:mixTable() 总览 —— Mixer 在控制循环中的位置
本讲知识点
- mixTable() 在 Betaflight 控制循环中的位置;
- Mixer 在控制链中的职责;
- mixTable() 的输入与输出;
- Throttle、Roll、Pitch、Yaw 如何进入 Mixer;
- mixTable() 内部执行流程;
- 从 PID Output 到 Motor Output 的整体路径。
本讲对应 Betaflight 源码
文件:
src/main/flight/mixer.c
核心函数:
mixTable(timeUs_t currentTimeUs)
本讲覆盖范围:
- mixTable() 整体结构;
- 输入输出关系;
- 内部几个主要阶段。
不展开:
- calculateThrottleAndCurrentMotorEndpoints()
- applyMixerAdjustment()
- applyMixToMotors()
这些函数将在后续章节单独分析。
本讲学习目标
理解 mixTable() 为什么存在。
建立:
PID Controller -> Mixer -> Motor Output
这一控制链关系。
学习完成后,需要知道:Mixer 不是控制飞机姿态的模块。
它负责把已经计算好的控制需求,转换为每个电机应该执行的输出。
回顾上一阶段控制链
在前面的课程中,我们已经建立了:
RC输入
↓
飞行模式
↓
姿态控制
↓
PID Controller
↓
PID Output
到了这里,飞控已经知道: 飞机需要:
- 增大 Roll 控制;
- 增大 Pitch 控制;
- 修正 Yaw;
- 提供当前油门。
但是, PID Output 仍然不是电机信号。
例如, PID Controller 输出:
Roll = +100
Pitch = -50
Yaw = +30
这个结果表达的是:飞机需要产生这些方向的控制力。
但是飞控仍然不知道:哪个电机增加?哪个电机减少?增加多少?减少多少?
这个问题,就是 Mixer 需要解决的问题。
为什么需要 Mixer?
四旋翼飞机有一个基本特点:单个电机无法直接产生 Roll, Pitch, Yaw 控制。
飞机姿态变化来自不同电机之间的推力差。
例如, 想让飞机向右 Roll, 需要左侧电机增加推力, 右侧电机减少推力。
形成:
左侧推力 ↑, 右侧推力 ↓
↓
产生 Roll 力矩
↓
飞机向右倾斜
因此, PID 输出描述:"飞机需要向右滚转多少"
而 Mixer 需要回答:"具体哪些电机应该变化多少"
这就是:
控制需求 -> 电机分配
mixTable() 在控制链中的位置
mixTable() 位于PID 计算之后,电机输出之前。
完整控制链:
飞手输入
↓
RC Command
↓
姿态 / 角速度控制
↓
PID Controller
↓
PID Output
↓
mixTable()
↓
Motor Output
↓
ESC
↓
电机
因此,mixTable() 是控制链中的最后一个"控制分配层"。
它不负责:产生 PID。
它也不负责:读取陀螺仪。
它负责:把已有的控制需求转换为电机输出。
mixTable() 的输入是什么?
观察函数定义:
FAST_CODE_NOINLINE void mixTable(timeUs_t currentTimeUs)
函数输入 "currentTimeUs", 表示: 当前系统时间。
它主要用于时间相关功能, 例如动态功能。
但是 Mixer 真正使用的控制输入,并不是函数参数,而是函数内部读取的控制状态。
主要包括:
Throttle : 油门需求。
来源:RC 输入经过前处理后的 throttle。
物理意义:飞手希望飞机产生多少基础升力。
Roll / Pitch / Yaw PID Output
来源:
pidData[FD_ROLL].Sum
pidData[FD_PITCH].Sum
pidData[FD_YAW].Sum
它们表示:PID Controller 为了修正飞机状态,需要施加多少控制量。
例如:
Roll PID Output = +50
表示:飞机需要产生一个正方向 Roll 控制力。
注意:这里不是某个电机输出 +50。而是整个飞机需要产生 +50 的 Roll 力矩。
mixTable() 的输出是什么?
最终输出:
motor[]
这个数组保存每个电机最终输出值。
例如, 四旋翼:
motor[0] ~ motor[3]
分别对应四个电机的控制输出。
最终:
motor[] -> ESC -> 电机转速
mixTable() 内部整体流程
虽然 mixTable() 有大量代码,但是从控制思想来看,可以分成几个阶段:
第一阶段:准备油门输出空间
代码:
calculateThrottleAndCurrentMotorEndpoints(currentTimeUs);
作用:确定当前电机输出范围。
例如,
普通模式:最低输出 -> 最高输出。
3D模式:反转区域 -> 死区 -> 正转区域
这个阶段解决的问题:"当前油门应该映射到哪个电机输出范围?"
下一讲详细分析。
第二阶段:选择当前 Mixer 数据
代码:
motorMixer_t * activeMixer = &mixerRuntime.currentMixer[0];
这里选择:当前使用的 Mixer 参数。
这些参数来自前面第22讲、第23讲学习的:
motorMixer_t
它描述每个电机如何响应:Throttle, Roll, Pitch, Yaw。
第三阶段:处理 PID 输出
代码:
const float scaledAxisPidRoll = constrainf(pidData[FD_ROLL].Sum,
-currentPidProfile->pidSumLimit,
currentPidProfile->pidSumLimit)
/ PID_MIXER_SCALING;
Pitch 和 Yaw 同理, 这里完成两个动作:
限制 PID 输出
如果 PID 输出过大, Mixer 会要求电机产生超过能力范围的变化。
例如:电机最大输出 100%, 但是 Mixer 计算 120%, 最终只能限制。
因此需要提前限制 PID 权限。
缩放 PID 输出
因为 PID 输出单位 和 Mixer 内部计算单位 不同。
所以需要转换到 Mixer 使用的范围。
第四阶段:计算每个电机的控制需求
核心代码:
float mix =
scaledAxisPidRoll * activeMixer[i].roll +
scaledAxisPidPitch * activeMixer[i].pitch +
scaledAxisPidYaw * activeMixer[i].yaw;
这一部分是 Mixer 最核心的数学关系。
它表示:某个电机的控制修正量等于:
Roll控制需求 × 该电机Roll权重
+ Pitch控制需求 × 该电机Pitch权重
+ Yaw控制需求 × 该电机Yaw权重
注意, 这里还没有加入 Throttle。
因为这里计算的是 "姿态修正量", 而不是最终电机输出。
为什么 Throttle 没有立即加入?
这是初学者容易疑惑的问题。
直觉上, 电机输出应该:
Throttle + Roll + Pitch + Yaw
但是代码不是立即这样做。
因为 Roll/Pitch/Yaw 是控制修正, Throttle 是基础推力。
如果直接相加, 可能出现:
某个电机超过最大输出 -> 控制量被截断 -> 飞机失去控制能力
所以 Betaflight 会先计算姿态控制需求, 再根据整体范围调整, 最后才加入 Throttle。
这个过程就是后续章节重点分析的:Mixer Adjustment。
7. 本讲源码流程总结
本讲只建立 mixTable() 的整体框架:
mixTable()
↓
准备油门范围
↓
读取 Mixer 参数
↓
读取 PID Output
↓
计算每个电机的姿态修正量
↓
调整混控范围
↓
加入 Throttle
↓
生成 motor[]
当前阶段需要记住 mixTable() 的核心任务:不是产生控制, 而是把控制需求翻译成电机动作。
本讲小结
本讲建立了 mixTable() 在整个控制链中的位置。
从控制系统角度看,PID Controller 输出的是飞机需要的控制力,而 Mixer 负责把这些控制力转换成多个电机之间的推力变化。
源码层面,本讲分析了 mixTable() 的整体结构,明确了它接收 Throttle、Roll、Pitch、Yaw 控制需求,并最终生成 motor[] 输出。
其中,calculateThrottleAndCurrentMotorEndpoints()、混控调整函数以及最终电机输出映射过程,只进行了功能定位,没有展开细节。
下一讲预告
下一讲继续进入 mixTable() 内部。
重点分析:calculateThrottleAndCurrentMotorEndpoints()
学习:飞控如何把 RC Throttle 转换成 Mixer 可以使用的电机输出空间。
浙公网安备 33010602011771号