惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
The Cloudflare Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Y
Y Combinator Blog
G
Google Developers Blog
Vercel News
Vercel News
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Jina AI
Jina AI
Blog — PlanetScale
Blog — PlanetScale
H
Help Net Security
博客园 - 三生石上(FineUI控件)
MongoDB | Blog
MongoDB | Blog
S
SegmentFault 最新的问题
阮一峰的网络日志
阮一峰的网络日志
H
Hackread – Cybersecurity News, Data Breaches, AI and More
aimingoo的专栏
aimingoo的专栏
T
Tailwind CSS Blog
博客园 - 叶小钗
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
月光博客
月光博客
Microsoft Security Blog
Microsoft Security Blog
P
Proofpoint News Feed
The GitHub Blog
The GitHub Blog
云风的 BLOG
云风的 BLOG

博客园 - longyue

[BF学院_卷1] -- 第29讲:applyMixToMotors() —— 最终 Motor Output 生成 [BF学院_卷1] -- 第28讲:applyMixerAdjustment() —— 为什么 Mixer 需要重新分配控制权限 [BF学院_卷1] -- 第27讲:PID Output 如何进入 Mixer —— 从控制需求到 motorMix [BF学院_卷1] -- 第26讲:calculateThrottleAndCurrentMotorEndpoints() —— Mixer 如何确定油门基础范围 [BF学院_卷1] -- 第25讲:mixTable() 总览 —— Mixer 在控制循环中的位置 [BF学院_卷1] -- 第24讲:mixer_init.c —— 飞控启动时如何准备 Mixer [BF学院_卷1] -- 第23讲:Mixer 数学模型 —— motorMixer_t 如何形成 Motor Output [BF学院_卷1] -- 第22讲:Betaflight 如何描述 Mixer —— mixer.h 数据结构 [BF学院_卷1] -- 第21讲:四旋翼为什么可以通过电机控制姿态 —— Mixer 背后的物理基础 [BF学院_卷1] -- 第20讲:从 PID Output 到 Motor Command(电机指令)——为什么飞控需要 Mixer [BF学院_卷1] -- 第19讲:PID Controller(PID控制器)——飞控到底如何理解误差并控制飞机? [BF学院_卷1] -- 第18讲:PID Controller 的最后一环——为什么飞控还需要 Feedforward(前馈控制)? [BF学院_卷1] -- 第17讲:D Term(微分控制项)——飞控如何提前感知运动变化? [BF学院_卷1] -- 第16讲:I Term(积分控制项)——飞机为什么总是差一点? [BF学院_卷1] -- 第14讲:Rate Error(角速度误差)——PID 真正控制的到底是什么? [BF学院_卷1] -- 第15讲:P Term(比例控制项)——飞控是如何利用误差开始控制飞机的? [BF学院_卷1] -- 第13讲:Outer Loop 的终点——为什么还要读取 Current Angular Rate(当前角速度)? [BF学院_卷1] -- 第十二讲:Yaw Compensation(偏航补偿) [BF学院_卷1] -- 第12讲脚本 [BF学院_卷1] -- 第十一讲:Current Angular Rate(当前角速度)——飞控怎样知道飞机现在转得有多快? [BF学院_卷1] -- 第十讲:Angle Error(姿态角误差)如何变成 Desired Angular Rate(目标角速度) [BF学院_卷1] -- 第九讲:Current Angle(当前姿态角)遇见 Target Angle(目标姿态角) [BF学院_卷1] -- 第八讲:飞手打杆以后,飞控首先得到什么? [BF学院_卷1] -- 第七讲:为什么 Pitch Flip 只在 180° 看起来正确 [BF学院_卷1] -- 第7讲脚本 [BF学院_卷1] -- 第6讲:为什么 Pitch Flip 会骗人 [BF学院_卷1] -- 第6讲脚本 [BF学院_卷1] -- 第五讲:第一次修正——Pitch Flip 为什么看起来有效 [BF学院_卷1] -- 第5讲脚本 - longyue [BF学院_卷1] -- 第4讲:为什么"绕飞机自己转"不等于"朝世界转" - longyue
[BF学院_卷1] -- 第30讲:mixTable() 总结 —— Mixer 完整控制...
longyue · 2026-08-03 · via 博客园 - longyue

第30讲:mixTable() 总结 —— Mixer 完整控制模型


本讲知识点

  • Mixer 在 Betaflight 控制链中的作用;
  • 为什么 PID Output 不能直接控制电机;
  • motorMixer_t 的数学意义;
  • mixTable() 从输入到输出的完整流程;
  • Throttle、Roll、Pitch、Yaw 如何共同生成 Motor Output;
  • Mixer 如何处理电机输出限制;
  • Mixer 在整个飞控架构中的位置。

本讲对应 Betaflight 源码

主要函数:

mixTable(timeUs_t currentTimeUs)

相关函数:

calculateThrottleAndCurrentMotorEndpoints()

applyMixerAdjustment()

applyMixerAdjustmentLinear()

applyMixToMotors()

本讲学习目标

通过前面第25讲~第29讲的学习,建立完整认识:

Mixer 并不是简单计算电机转速,

而是将飞机控制需求,转换为每个电机应该输出多少,

最终理解:

Throttle,Roll,Pitch,Yaw

↓

Mixer Matrix

↓

Motor Output

↓

ESC

↓

电机

回顾前面几讲

在进入 Mixer 之前,飞控已经完成:

RC输入 -> 期望姿态 -> PID Controller -> Roll / Pitch / Yaw 控制需求

PID Controller 输出的是飞机需要什么运动。

例如:

需要向右 Roll

需要向前 Pitch

需要逆时针 Yaw

但是,PID Controller 并不知道:

哪个电机增加,哪个电机减少,增加多少,减少多少

因此需要 Mixer。


为什么需要 Mixer?

四旋翼飞机的控制,本质上不是 “控制某一个电机”,

而是通过不同电机之间的 “推力差”,产生 “力矩”。

例如:

Roll 控制

希望飞机向右滚转:

左侧电机增加推力,右侧电机减少推力 -> 产生 Roll 力矩

Pitch 控制

希望飞机向前俯冲:

后侧电机增加推力, 前侧电机减少推力 -> 产生 Pitch 力矩

Yaw 控制

利用:

电机旋向不同 -> 反作用力矩变化 -> 产生 Yaw

所以,

PID 输出:飞机需要多少控制力;

Mixer:如何把控制力分配给电机。


motorMixer_t —— Mixer 的数学模型

Betaflight 使用:

motorMixer_t

描述每个电机对应的控制权重。

例如,一个电机可能:

roll  = 0.5
pitch = -0.5
yaw   = 1.0

表示这个电机参与:

50%的 Roll 控制

-50%的 Pitch 控制

100%的 Yaw 控制

因此,对于第 i 个电机:

Motor Mix = Roll需求 × Roll权重
          + Pitch需求 × Pitch权重
          + Yaw需求 × Yaw权重

对应代码:

float mix =
    scaledAxisPidRoll  * activeMixer[i].roll +
    scaledAxisPidPitch * activeMixer[i].pitch +
    scaledAxisPidYaw   * activeMixer[i].yaw;

这个结果:motorMix[i] 代表:

该电机因为姿态控制需要增加或者减少多少输出

注意,它不是最终电机输出。


mixTable() 完整流程

现在回顾:

mixTable()

内部完成的整个过程。


第一步:建立 Throttle 输出空间

对应:

calculateThrottleAndCurrentMotorEndpoints()

作用,确定:

Throttle -> 允许的 Motor Output 区间

因为油门输入不是直接对应电机转速,

而是需要映射:

RC Throttle -> 内部 throttle -> Motor Output Range

得到基础升力需求。


第二步:计算 PID 控制需求

代码:

pidData[FD_ROLL].Sum

pidData[FD_PITCH].Sum

pidData[FD_YAW].Sum

经过:

限制 -> 缩放

得到:

scaledAxisPidRoll

scaledAxisPidPitch

scaledAxisPidYaw

这些变量表示:飞机需要多少 Roll / Pitch / Yaw 修正


第三步:Mixer Matrix 分配

核心:motorMix[i]。

计算:

飞机级控制需求 -> 每个电机修正量

例如, 输入:

Roll需求

Pitch需求

Yaw需求

经过motorMixer_t 权重矩阵, 输出:

M1 motorMix

M2 motorMix

M3 motorMix

M4 motorMix

此时, 控制链:

PID Output -> motorMix

完成。


第四步:处理 Mixer 饱和问题

问题,最终电机输出:

Motor Output = Throttle + motorMix

但是,电机存在限制:0.0 ~ 1.0。

例如,某个电机:

Throttle = 0.2

motorMix = -0.4

结果:

Motor Output = 0.2 - 0.4 = -0.2

已经超过电机能力。

这会导致输出被限制到0,于是,原本需要的姿态差异消失。


因此,Mixer 需要进行“混控调整”,

对应:

applyMixerAdjustment()

applyMixerAdjustmentLinear()

作用:在不破坏整体控制效果的情况下, 重新分配 Throttle 与 motorMix 之间的空间。


第五步:生成最终 Motor Output

对应:

applyMixToMotors()

核心:

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

也就是:

最终电机输出 = 姿态修正量 + 基础油门

展开:

Throttle -> 基础升力

motorMix -> 姿态修正

二者相加 -> Motor Output

最后, 经过:

输出范围限制 -> PWM / DShot映射 -> ESC

Mixer 在整个飞控控制链中的位置

完整控制链:

飞手输入RC遥控器

↓

RC Command

↓

姿态目标

↓

PID Controller

↓

Roll / Pitch / Yaw 控制需求

↓

Mixer

↓

Motor Output

↓

ESC

↓

电机

↓

飞机姿态变化

其中,

PID Controller 负责:我需要什么动作;

Mixer 负责:每个电机应该如何配合完成这个动作;

ESC负责:把控制信号转换成电机驱动力


Mixer 的核心思想总结

如果只记住一句话:

Mixer 不是控制电机。

Mixer 是把飞机级控制需求,转换成电机级控制输出。

或者:

PID Controller 产生需求

Mixer 负责分配

Motor 执行动作

至此:

Betaflight 中:

控制需求 -> Mixer -> 电机输出

这一条核心链路已经完整建立。

下一阶段可以继续进入:

Motor Output之后

↓

ESC通信协议

↓

DShot输出

↓

电机执行

也就是:飞控如何把内部计算结果,最终变成真实的电机驱动信号。