












文件:
src/main/flight/pid.c
函数:
pidController()
本讲涉及源码:
const float delta = - (gyroRateDterm[axis] - previousGyroRateDterm[axis]) * pidRuntime.pidFrequency;
float preTpaD = pidRuntime.pidCoefficient[axis].Kd * delta;
pidData[axis].D = preTpaD * pidRuntime.tpaFactor;
本讲重点分析:
gyroRateDterm[axis] - previousGyroRateDterm[axis]
这一部分如何体现:
当前运动变化
↓
未来趋势预测
↓
提前抑制运动变化
上一讲中,我们建立了:
I Term(积分控制项)
它通过累计长期存在的Rate Error(角速度误差), 解决飞机总是差一点的问题。
控制链继续推进:
Rate Error
↓
P Term
↓
I Term
↓
D Term
即使加入 P Term 和 I Term,飞机仍可能因为运动惯性出现:已经接近目标,但是仍然继续增加旋转速度。
因此,PID Controller(PID控制器)还需要判断:变化正在发生得多快。
这正是 D Term(微分控制项)负责解决的问题。
本讲进入 D Term(微分控制项), 理解飞控如何通过观察角速度变化趋势,提前抑制飞机过冲和快速变化。
P Term 关注:现在差多少。
例如:
目标角速度:300°/s
当前角速度:200°/s
误差:100°/s
P Term 会增加控制输出。
但是,如果飞机正在快速加速:
200°/s -> 250°/s -> 290°/s
虽然当前误差仍然存在,但是飞机已经正在快速接近目标。
如果继续按照误差增加输出,飞机可能因为惯性,超过目标。
因此,控制器还需要知道:
误差正在扩大?还是正在快速减少?
这就是 D Term 引入的原因。
D Term 的本质不是观察:现在是多少。
而是观察:变化有多快。
数学上:D Term = 变化量 ÷ 时间
也就是:对当前状态变化趋势进行预测。
在飞控中,D Term 主要观察:
Angular Rate(角速度)随时间的变化。
也就是:
角速度增加很快 -> 提前降低控制输出
角速度变化稳定 -> 减少额外影响
首先观察:
const float delta = - (gyroRateDterm[axis] - previousGyroRateDterm[axis]) * pidRuntime.pidFrequency;
这一行可以拆开理解。
首先, gyroRateDterm[axis] 表示:当前用于 D Term 计算的角速度。
然后, previousGyroRateDterm[axis]表示:上一次 PID 循环时的角速度。
两者相减:gyroRateDterm[axis] - previousGyroRateDterm[axis] 得到:这一瞬间角速度变化了多少。
例如:
上一周期:200°/s
当前周期:220°/s
变化:+20°/s
说明:飞机正在加速旋转。
根据 PID 控制理论, D Term(微分控制项)关注的不是当前误差是多少, 而是误差正在变化得多快。
在连续控制系统中,微分项表示:
误差变化量 ÷ 时间变化量
也就是:
D Term ≈ Kd × (Δerror / Δt)
其中:
Δerror 表示误差发生了多少变化;
Δt 表示变化发生所经过的时间。
但是,Betaflight运行在数字控制系统中, 飞控不会连续观察飞机状态,而是在固定频率下:
读取传感器数据;
↓
执行PID计算;
↓
输出控制量。
因此,源码中需要使用离散方式近似连续微分。
也就是:
变化量 ÷ 采样时间 --> 变化速度
而 PID 运行频率:
pidFrequency 表示 1秒钟内PID计算执行多少次。
因此:
pidFrequency = 1 / Δt
所以:
Δerror / Δt 等价于 Δerror × pidFrequency
这也是源码中:
const float delta = - (gyroRateDterm[axis] - previousGyroRateDterm[axis]) * pidRuntime.pidFrequency;
的数学来源。
其中, gyroRateDterm[axis] 表示 当前采样周期内,陀螺仪角速度数据。
而 previousGyroRateDterm[axis] 表示 上一次PID计算周期中的角速度数据。
两者相减:
当前角速度 - 上一次角速度 -> 角速度变化量
也就是 Δerror
再乘以 pidRuntime.pidFrequency 以后,就与刚才的公式对上了。
也就是,飞控知道:
飞机旋转速度正在增加还是减少;
以及增加或减少得有多快。
这正是 D Term(微分控制项)能够预测飞机运动趋势的基础。
同时,源码采用:
Δerror × pidFrequency
而不是:
Δerror / Δt
还有一个工程原因:
嵌入式控制系统需要高频实时运行。
乘法运算通常比除法运算更加高效,因此提前计算:
pidFrequency = 1 / Δt
然后在PID循环中直接使用:
变化量 × pidFrequency
可以减少实时计算负担。
因此:
pidRuntime.pidFrequency
并不是额外增加的控制参数。
它的作用是:
把离散采样得到的角速度变化量,转换成为实际的变化速率。
这就是 Betaflight 中 D Term 计算中乘以 pidFrequency 的真正原因。
源码:
const float delta = -(...)
原因是:D Term 的作用是抵抗变化趋势。
如果飞机角速度正在快速增加,那么 D Term 希望产生:
反方向控制力
降低继续增加的趋势。
因此:
变化趋势 -> 反向修正
这就是负号存在的原因。
源码:
float preTpaD = pidRuntime.pidCoefficient[axis].Kd * delta;
其中, Kd 表示 D Term 增益。
它决定:
对于变化趋势,控制器反应多强。
如果 Kd 较大, 更敏感地抑制快速变化。
如果 Kd 较小, D Term 作用较弱。
因此:
D输出 = 变化趋势 × Kd
需要注意, D Term 使用:
gyroRateDterm[axis]
而不是简单直接使用:
gyroRate
原因是:D Term 对高频变化非常敏感。
陀螺仪中的噪声:
小幅快速变化
经过微分以后会被放大。
因此 Betaflight 会对 D Term 使用的数据进行专门处理。
例如:
目的:
保留真实运动变化,减少噪声变化。
两者都具有:
提前响应
的特点。
但是解决的问题不同。
Feedforward(前馈控制):
飞手刚打杆
↓
提前知道期望变化
↓
提前输出控制
关注目标变化。
D Term:
飞机已经开始运动
↓
检测变化趋势
↓
提前抑制变化
关注:
实际运动变化。
简单理解:
Feedforward:提前推动。
D Term:提前刹车。
两者共同作用:
更快响应 + 更稳定停止。
本讲完成了 PID Controller(PID控制器)中 D Term(微分控制项)的建立。
D Term 与 P Term、I Term 不同,它并不关注当前误差大小,而关注飞机旋转速度变化趋势。。
Betaflight 通过比较:
当前 gyroRateDterm 与 上一周期 gyroRateDterm
得到角速度变化量,再结合:Kd
生成 D Term 输出。
至此,PID 三个基础控制项已经全部建立:
P Term:解决现在差多少。
I Term:解决过去累计差多少。
D Term:解决变化趋势有多快。
下一讲,我们将进入:
Feedforward(前馈控制)
理解飞控如何直接利用飞手输入变化,提前产生控制响应。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。