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

推荐订阅源

Google DeepMind News
Google DeepMind News
博客园 - 司徒正美
WordPress大学
WordPress大学
爱范儿
爱范儿
小众软件
小众软件
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
罗磊的独立博客
博客园_首页
V
V2EX
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Tailwind CSS Blog
大猫的无限游戏
大猫的无限游戏
The Cloudflare Blog
MyScale Blog
MyScale Blog
IT之家
IT之家
H
Help Net Security
Blog — PlanetScale
Blog — PlanetScale
Microsoft Security Blog
Microsoft Security Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Recent Announcements
Recent Announcements
F
Fortinet All Blogs
The GitHub Blog
The GitHub Blog
Y
Y Combinator Blog
人人都是产品经理
人人都是产品经理

博客园 - longyue

[BF学院_卷1] -- 第30讲:mixTable() 总结 —— Mixer 完整控制模型 [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] -- 第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] -- 第18讲:PID Controller 的最后一环——为什么...
longyue · 2026-08-03 · via 博客园 - longyue

第18讲:PID Controller 的最后一环——为什么飞控还需要 Feedforward(前馈控制)?


本讲知识点

  • PID Controller(PID控制器)中的三个反馈控制项已经完成后,为什么仍然需要 Feedforward(前馈控制)
  • Feedback Control(反馈控制)与 Feedforward(前馈控制)的区别
  • Feedforward 为什么直接观察飞手输入变化
  • pidSetpointDelta(目标变化量)的作用
  • Kf(前馈系数)如何影响 Feedforward 输出
  • Betaflight 中 pidData[axis].F 的计算过程
  • PID Sum(最终控制输出)的形成

本讲对应 Betaflight 源码

文件:src/main/flight/pid.c

函数:pidController()

本讲涉及源码:

#ifdef USE_ABSOLUTE_CONTROL
    pidSetpointDelta += setpointCorrection - pidRuntime.oldSetpointCorrection[axis];
    pidRuntime.oldSetpointCorrection[axis] = setpointCorrection;
#endif

const float feedforwardGain = launchControlActive ? 0.0f : pidRuntime.pidCoefficient[axis].Kf;
pidData[axis].F = feedforwardGain * pidSetpointDelta;

以及:

const float pidSum = pidData[axis].P + pidData[axis].I + pidData[axis].D + pidData[axis].F;

回顾上一讲

上一讲中,我们已经完成 D Term(微分控制项)的建立。

至此,Rate Controller(角速度控制器)已经具备:

P Term(比例控制项)

I Term(积分控制项)

D Term(微分控制项)

三个基础反馈控制项。

控制链继续推进到:

P Term

↓

I Term

↓

D Term

↓

Feedforward

本讲学习目标

虽然 PID Controller(PID控制器)已经能够根据误差修正飞机状态,

但是, 反馈控制存在一个天然限制:它必须先等误差产生,然后才能开始修正。

当飞手快速打杆时,飞机需要快速产生响应,而不是等待:

目标变化

↓

产生误差

↓

PID修正

这个过程完成以后才开始动作。

因此,本讲进入:

Feedforward(前馈控制)

理解飞控如何直接观察目标变化,在误差出现之前,提前产生控制响应。


PID 三个反馈项已经足够了吗?

到目前为止,控制链已经进入:Rate Controller(角速度控制器)。

飞控拥有:Rate Error(角速度误差)。

然后通过:P Term -> I Term -> D Term 产生控制输出。

看起来,PID 已经完整了。

但是现实中仍然存在一个问题:飞手输入是主动产生的。

例如:

飞手快速向右打杆,控制目标瞬间变化:

当前目标角速度:0°/s -> 300°/s

这时候, 飞机实际角速度仍然可能接近 0°/s, 因此, Rate Error 会突然增大。

反馈控制需要经历:

目标变化

↓

产生误差

↓

P/I/D计算

↓

电机响应

这个过程需要时间。

而飞手希望看到的是:打杆以后,飞机立即开始响应。

因此,飞控需要一种不等待误差出现,提前响应目标变化的方法。

这就是:Feedforward(前馈控制)。


Feedback Control 与 Feedforward 的区别

理解 Feedforward 前,需要先区分两种控制思想。

Feedback Control(反馈控制)

反馈控制关注:飞机现在做得对不对。

它观察:

目标值

↓

当前状态

↓

计算误差。

例如:

目标角速度:300°/s

当前角速度:200°/s

产生:Rate Error 100°/s

然后 PID 根据误差修正。

所以, 反馈控制解决的是:飞机已经偏离目标以后,如何修回来。


Feedforward(前馈控制)

Feedforward 关注:目标正在如何变化。

例如:

飞手突然快速打杆:目标角速度 0°/s --> 300°/s

虽然飞机还没有产生误差,但是飞控已经知道:未来需要快速增加控制输出。

因此,Feedforward 不等待 Current Angular Rate(当前角速度)变化。

而是直接观察 Setpoint(目标值)的变化。


pidSetpointDelta:目标变化量

源码中:

pidSetpointDelta 表示:当前目标角速度,相比上一周期变化了多少。

简单理解:

如果上一周期目标:0°/s

当前目标:300°/s

那么,目标变化量 300°/s

这个变化量代表:飞手希望飞机快速改变运动状态。

因此,Feedforward 使用“目标变化速度”作为提前控制依据。


第一行核心源码

首先观察:

const float feedforwardGain = launchControlActive ? 0.0f : pidRuntime.pidCoefficient[axis].Kf;

这一行负责获取:Kf(Feedforward Gain,前馈增益)。

Kf 的作用:控制 Feedforward 对最终输出的影响大小。

如果Kf 较大, 目标变化时,提前产生更强响应。

如果Kf 较小, 提前响应较弱。

因此, Kf 决定飞控对飞手输入变化的敏感程度。


Feedforward 输出计算

核心代码:

pidData[axis].F = feedforwardGain * pidSetpointDelta;

翻译成控制思想:

目标变化量 × 前馈增益 --> Feedforward 输出

也就是说, 飞手动作变化越快,Feedforward 输出越大。

例如:

飞手轻微移动摇杆, 目标变化小。

↓

Feedforward 小。

飞手快速打杆, 目标变化大。

↓

Feedforward 大。

为什么 Feedforward 不替代 PID?

这里需要注意:Feedforward 不是替代 PID。

因为, 它只知道:飞手希望怎么变化, 但是它不知道:飞机实际有没有做到。

例如:

飞手快速打杆, Feedforward 提供提前推力。

但是, 如果电池电压下降;电机性能不足;受到风阻;飞机仍然可能达不到目标。

这时候, Rate Error 仍然存在。

于是, PID Feedback Control(反馈控制)继续负责修正实际误差。

所以完整控制逻辑是:

Feedforward 提前推动飞机。

PID:根据实际误差修正飞机。

两者共同作用:快速响应 + 准确控制。


PID Sum:最终控制输出

当:

P Term

I Term

D Term

Feedforward

全部计算完成以后, 源码进入:

const float pidSum = pidData[axis].P 
                   + pidData[axis].I 
                   + pidData[axis].D 
                   + pidData[axis].F;

这里表示所有控制作用最终合并。

也就是, 反馈控制 P + I + D + F 共同形成PID Controller(PID控制器)的最终输出。

控制链推进到:

Rate Error

↓

P Term

↓

I Term

↓

D Term

↓

Feedforward

↓

PID Sum

至此, Rate Controller 内部的主要控制组成已经建立完成。


本讲小结

本讲完成了 Feedforward(前馈控制)的建立。

Feedforward 与 P Term、I Term、D Term 不同,它不是根据已经产生的误差进行修正,而是直接观察目标变化趋势,提前产生控制响应。

Betaflight 通过 pidSetpointDelta 获得目标角速度变化量,

再结合 Kf 计算 pidData[axis].F。

至此,Rate Controller 内部主要控制组成已经全部建立:

P Term:解决当前误差。

I Term:解决长期累计误差。

D Term:观察变化趋势。

Feedforward:提前响应目标变化。

下一讲预告

下一讲,我们将离开 PID Controller 内部计算,

进入:

Motor Mixer(电机混控)。

理解飞控如何把单个轴的控制输出,转换为四个电机的具体控制量。