第4章 PID控制器入门:比例、积分、微分的作用

说实话,我刚入行那会儿,觉得PID这玩意儿挺玄乎的。三个字母,三个参数,调来调去就是调不好。后来带我的老工程师跟我说了一句话,我到现在都记得——「PID没那么复杂,你把它想成三个人在干活就行了。」

嗯,今天我就用这个思路,带你彻底搞懂比例、积分、微分到底在干嘛。

4.1 先问一个问题:为什么要用PID?

你想想看,一个机器人要沿着一条线走。你给它一个目标位置,它得动起来。但怎么动?

  • 动得太慢,跟不上轨迹
  • 动得太快,冲过头了
  • 动得刚刚好?那得有个控制器

PID就是干这个的。它根据「当前误差」来决定「下一步怎么动」。误差大就猛动,误差小就微调。

核心思想: 误差 = 目标值 - 当前值。PID就是对这个误差做三种不同的处理,然后加起来输出。

4.2 比例控制(P)—— 最直接的反应

比例控制,说白了就是「误差越大,输出越大」。你离目标远,我就使劲推你;你离目标近了,我就轻轻推。

数学表达式:

u(t) = Kp * e(t)

其中:

  • u(t) 是控制输出
  • Kp 是比例增益
  • e(t) 是当前误差

我在项目中遇到过一个问题:只用比例控制,机器人永远到不了目标点。为什么?因为当误差很小时,输出也很小,小到不足以克服摩擦力。这就叫「稳态误差」。

我的习惯: 调PID时,先把P调到一个能让系统「动起来但不振荡」的值。别急着加I和D。

4.3 积分控制(I)—— 消除累积误差

比例控制搞不定的稳态误差,积分来搞定。积分看的是「过去所有误差的总和」。只要误差一直存在,积分项就会一直增长,直到把误差消除。

数学表达式:

u(t) = Ki * ∫e(t) dt

这里有个坑,我踩过。积分项太强,系统会「过冲」—— 冲过头了,然后来回振荡。这叫「积分饱和」。

我曾经... 在一个电机控制项目里,积分项设得太大,结果电机一启动就疯狂振荡,差点把减速箱搞坏。后来加了积分限幅才解决。

积分的作用,说白了就是「纠偏」。你一直偏左,我就一直往右推,直到你回到中间。

4.4 微分控制(D)—— 预测未来

比例和积分都是看「过去和现在」。微分不一样,它看的是「误差的变化趋势」。误差在快速增大?微分项会提前输出一个反向力,阻止它继续增大。

数学表达式:

u(t) = Kd * de(t)/dt

微分就像个「刹车」。你想想看,开车时看到前面红灯,你不会等到跟前才刹车吧?你会提前减速。微分干的就是这个事。

但微分有个毛病:它对噪声特别敏感。传感器抖一下,微分项就跳一下。我在做四足机器人时,腿部的加速度计噪声很大,加了微分后反而更抖了。后来我加了低通滤波才搞定。

建议: 如果系统噪声大,微分增益Kd别设太大,或者干脆不用D。很多场合P+I就够了。

4.5 三个合在一起:完整的PID

把三个加起来,就是完整的PID控制器:

u(t) = Kp * e(t) + Ki * ∫e(t) dt + Kd * de(t)/dt

用一张图来理解它们的关系:

目标值 r(t) ∑ - e(t) Kp Ki∫ Kd d/dt ∑ u(t) 反馈回路(当前值 y(t)) 图例 比例路径 积分路径 微分路径

4.6 三个参数怎么调?

调参这事儿,没有标准答案。但我可以给你一个我常用的「三板斧」:

  1. 先调P:从小到大慢慢加,直到系统开始振荡。然后回调一点,让振荡消失。这时候P就差不多了。
  2. 再加I:如果存在稳态误差(到不了目标点),加一点I。从很小的值开始,慢慢加,直到误差消除。
  3. 最后加D:如果系统过冲严重或者振荡,加一点D来抑制。注意D别太大,否则噪声会放大。

记住: 不是所有系统都需要三个参数。很多场合P+I就够了。D是锦上添花,不是雪中送炭。

4.7 一个简单的代码示例

下面是一个位置式PID的C语言实现,我在STM32上跑过:

typedef struct {
    float Kp;
    float Ki;
    float Kd;
    float integral;
    float prev_error;
} PID_Controller;

float PID_Update(PID_Controller *pid, float target, float current) {
    float error = target - current;
    
    // 比例项
    float p_out = pid->Kp * error;
    
    // 积分项(带限幅)
    pid->integral += error;
    if (pid->integral > 100.0f) pid->integral = 100.0f;
    if (pid->integral < -100.0f) pid->integral = -100.0f;
    float i_out = pid->Ki * pid->integral;
    
    // 微分项
    float derivative = error - pid->prev_error;
    float d_out = pid->Kd * derivative;
    
    // 保存当前误差供下次使用
    pid->prev_error = error;
    
    return p_out + i_out + d_out;
}

注意: 积分限幅很重要。不加限幅的话,积分项会一直累积,导致「积分饱和」。我吃过这个亏,现在每次写PID都会加上限幅。

4.8 常见问题与避坑

问题 原因 解决方法
系统振荡 P太大,或者D太小 减小Kp,或者增加Kd
到不了目标点 P太小,或者没有I 增加Kp,或者加一点Ki
过冲严重 I太大,或者没有D 减小Ki,或者加一点Kd
噪声放大 D太大 减小Kd,或者加低通滤波

嗯,PID入门其实就这么点东西。别被那些复杂的公式吓到,说白了就是三个参数的组合游戏。你多调几次,手感就出来了。

我刚开始做轨迹跟踪时,调一个PID花了两天。现在嘛,十分钟搞定。这东西就是个经验活。

可可精品资料库