4、增量式PID算法:公式推导、C语言实现、与位置式的对比
好,咱们今天来聊聊增量式PID。说实话,很多初学者一听到“增量式”三个字,就觉得比位置式高级。其实不是的。它们各有各的脾气,各有各的适用场景。
我个人习惯把增量式PID叫做“只算变化量”的PID。什么意思呢?位置式PID每次都要算一个完整的控制量输出,而增量式只算这一次比上一次多了多少。你想想看,这有什么好处?
4.1 公式推导:从位置式到增量式
我们先回忆一下位置式PID的公式:
u(k) = Kp * e(k) + Ki * Σe(i) + Kd * [e(k) - e(k-1)]
这个公式里,Σe(i) 是从系统启动到现在所有误差的累加。嗯,这里要注意,这个累加项在单片机里是个隐患——万一积分饱和了,输出可能直接飞掉。
那增量式怎么来的?很简单。我们写出第 k-1 时刻的输出:
u(k-1) = Kp * e(k-1) + Ki * Σe(i-1) + Kd * [e(k-1) - e(k-2)]
然后用 u(k) 减去 u(k-1),得到增量 Δu(k):
Δu(k) = u(k) - u(k-1)
= Kp * [e(k) - e(k-1)] + Ki * e(k) + Kd * [e(k) - 2*e(k-1) + e(k-2)]
这就是增量式PID的核心公式。你看,它只跟最近三次的误差有关,跟历史累加没关系了。
核心要点:增量式PID的输出是控制量的增量,不是控制量本身。最终输出需要做累加:u(k) = u(k-1) + Δu(k)。
4.2 C语言实现:我常用的模板
我在项目中用过很多次增量式PID,下面这个结构体是我比较喜欢的一种写法。它把三个系数和三个历史误差都封装在一起,清爽又好维护。
typedef struct {
float Kp; // 比例系数
float Ki; // 积分系数
float Kd; // 微分系数
float e_k; // 当前误差
float e_k1; // 上一次误差
float e_k2; // 上上次误差
float u_k1; // 上一次输出
float delta_u; // 增量输出
} PID_Incremental;
void PID_Incremental_Init(PID_Incremental *pid, float Kp, float Ki, float Kd) {
pid->Kp = Kp;
pid->Ki = Ki;
pid->Kd = Kd;
pid->e_k = 0;
pid->e_k1 = 0;
pid->e_k2 = 0;
pid->u_k1 = 0;
pid->delta_u = 0;
}
float PID_Incremental_Calc(PID_Incremental *pid, float setpoint, float feedback) {
// 计算当前误差
pid->e_k = setpoint - feedback;
// 计算增量
pid->delta_u = pid->Kp * (pid->e_k - pid->e_k1)
+ pid->Ki * pid->e_k
+ pid->Kd * (pid->e_k - 2*pid->e_k1 + pid->e_k2);
// 更新历史误差
pid->e_k2 = pid->e_k1;
pid->e_k1 = pid->e_k;
// 累加得到当前输出
pid->u_k1 += pid->delta_u;
return pid->u_k1;
}
小技巧:我习惯把 u_k1 的初始值设为0。但如果你的执行器有中位值(比如舵机的中位是1500),记得在初始化时设置好。我曾经因为这个吃过亏,电机一启动就往一个方向猛冲。
4.3 与位置式的对比:谁更好?
这个问题没有标准答案。我根据自己的经验,整理了一个对比表:
| 对比项 | 位置式PID | 增量式PID |
|---|---|---|
| 输出形式 | 直接输出控制量 u(k) | 输出增量 Δu(k),需累加 |
| 积分项 | 误差累加,容易积分饱和 | 无累加项,无积分饱和问题 |
| 故障影响 | 输出突变,执行器可能飞车 | 增量小,冲击小 |
| 手动/自动切换 | 需要做无扰切换处理 | 天然无扰,切换平滑 |
| 计算量 | 需要存储所有历史误差 | 只需存储最近3个误差 |
| 适用场景 | 伺服电机、阀门等全量控制 | 步进电机、增量式执行器 |
避坑指南:我曾经在一个温控项目里用了增量式PID,结果发现温度稳定后还是有微小波动。后来排查发现,是因为增量式对积分项的截断误差比较敏感。解决办法是把 Ki 放大一点,或者改用位置式。所以你看,没有绝对的好坏,只有合不合适。
4.4 什么时候选增量式?
说白了,就两个原则:
- 执行器是增量型的——比如步进电机,你给它一个脉冲它就转一步。这时候用增量式PID,输出直接就是脉冲数,省事。
- 系统不允许输出突变——比如机械臂,位置式PID如果积分饱和了,输出可能从0直接跳到100,机械臂“咣”一下撞过去。增量式就不会,它每次只变一点点。
反过来,如果你的执行器需要绝对位置(比如伺服电机的位置环),或者你对稳态精度要求极高,那位置式PID可能更合适。
嗯,今天就聊到这儿。下一节我们讲积分分离和抗积分饱和,这两个在实际项目中太常用了。到时候我会拿一个我调过的温控系统做例子,保证你听完就能用。