4、PID控制原理:比例(P)、积分(I)、微分(D)的作用,PID参数对系统响应的影响(超调、稳态误差、响应时间),数字PID实现。
聊到电机伺服,绕不开的就是PID。说实话,我入行头三年,天天跟这哥仨打交道。很多人觉得PID就是调三个数,没什么技术含量。但我要说,能把PID调明白的,才是真正懂控制的人。
今天咱们就把PID拆开揉碎了讲清楚。比例、积分、微分,它们各自扮演什么角色?参数调不好会出什么问题?数字域里又该怎么实现?
4.1 比例控制(P)—— 最直接的“纠偏”
比例控制,说白了就是“有多大错,使多大劲”。
你想想看,电机目标转速1000转,现在实际只有800转。偏差是200转。比例控制器会怎么做?它把偏差乘以一个系数Kp,然后输出一个控制量去拉高转速。
公式很简单:u(t) = Kp * e(t)
这里e(t)就是偏差,Kp是比例增益。
Kp大了会怎样?
- 响应变快,系统更“灵敏”
- 但容易超调,甚至震荡
- 稳态误差依然存在
Kp小了会怎样?
- 系统反应迟钝,像个“慢性子”
- 稳态误差更大
- 但系统稳定,不容易震荡
我个人习惯:先给一个较小的Kp,让系统动起来。然后逐步增大,直到出现轻微震荡,再回调20%-30%。这个点通常是比较理想的。
但比例控制有个天生的缺陷——稳态误差。什么意思?就是系统稳定后,实际值跟目标值之间总差那么一点。比如你调温,设定50度,最后稳定在49.5度。那0.5度的偏差,比例控制就是消不掉。
为什么会这样?因为要维持输出,就必须有偏差存在。没有偏差,比例项输出就是0。这是个死循环。
4.2 积分控制(I)—— 专治“稳态误差”
积分项的出现,就是为了解决比例控制那个“差一口气”的问题。
积分干的事很简单:把过去的偏差累加起来。只要有偏差存在,积分项就会不断累积,输出持续增大,直到把偏差彻底消除。
公式:u(t) = Ki * ∫e(t)dt
我在项目中遇到过这样一个案例:一个位置伺服系统,用纯比例控制,定位精度始终差0.1mm。怎么调Kp都没用。加上积分项后,那0.1mm的误差慢慢就消失了。嗯,这就是积分的威力。
但积分也不是万能的。
我曾经踩过一个坑:积分时间常数设得太小(Ki太大),结果系统出现了低频震荡。电机在那来回晃,就是稳不住。后来查资料才知道,这叫“积分饱和”或“积分windup”。
积分参数的影响:
| 参数 | Ki过大 | Ki过小 |
|---|---|---|
| 稳态误差 | 消除快 | 消除慢或无法消除 |
| 响应时间 | 可能变慢(积分饱和) | 影响不大 |
| 稳定性 | 容易震荡 | 稳定 |
4.3 微分控制(D)—— 提前“踩刹车”
微分项,我更喜欢叫它“预测器”。它不看偏差有多大,而是看偏差变化得有多快。
公式:u(t) = Kd * de(t)/dt
你想想看,当电机快要到达目标位置时,偏差在快速减小。微分项检测到这个“减小趋势”,就会输出一个反向的控制量,相当于提前踩刹车。这样就能有效抑制超调。
微分的作用:
- 抑制超调
- 提高系统阻尼
- 改善动态响应
但微分很敏感。
我的建议:微分项对噪声特别敏感。如果编码器信号有毛刺,微分输出会剧烈跳动。我一般会在微分前加一个低通滤波器,或者干脆在噪声大的场合不用微分。
Kd过大:系统震荡加剧,高频噪声被放大。
Kd过小:几乎没效果,超调依然存在。
4.4 PID参数对系统响应的影响
咱们用一张表总结一下三个参数对系统性能的影响:
| 性能指标 | 增大Kp | 增大Ki | 增大Kd |
|---|---|---|---|
| 响应时间 | 加快 | 影响较小 | 影响较小 |
| 超调量 | 增大 | 增大 | 减小 |
| 稳态误差 | 减小 | 消除 | 无影响 |
| 稳定性 | 变差 | 变差 | 改善(适度) |
说白了,调参就是个平衡的艺术。你想让响应快,就得接受一定的超调。你想消除稳态误差,就得冒积分震荡的风险。没有完美的参数,只有最适合当前工况的参数。
4.5 数字PID实现
模拟PID是连续的,但咱们现在都用数字控制器。数字PID就是把连续的公式离散化。
位置式PID:
直接计算当前时刻的控制量:
u(k) = Kp * e(k) + Ki * Σe(i) * T + Kd * (e(k) - e(k-1)) / T
其中T是采样周期,Σe(i)是误差累积和。
增量式PID:
只计算控制量的增量:
Δu(k) = Kp * (e(k) - e(k-1)) + Ki * e(k) * T + Kd * (e(k) - 2*e(k-1) + e(k-2)) / T
最终输出:u(k) = u(k-1) + Δu(k)
我个人更偏爱增量式PID。为什么?因为它没有积分累积项,不会出现积分饱和的问题。而且就算计算出错,输出也只是增量变化,不会导致电机突然飞车。安全第一嘛。
下面是一个简单的数字PID实现代码(C语言风格):
typedef struct {
float Kp, Ki, Kd;
float integral;
float prev_error;
float dt;
} PIDController;
float PID_Update(PIDController *pid, float setpoint, float measurement) {
float error = setpoint - measurement;
// 比例项
float P = pid->Kp * error;
// 积分项(带限幅)
pid->integral += error * pid->dt;
if (pid->integral > INTEGRAL_MAX) pid->integral = INTEGRAL_MAX;
if (pid->integral < -INTEGRAL_MAX) pid->integral = -INTEGRAL_MAX;
float I = pid->Ki * pid->integral;
// 微分项
float D = pid->Kd * (error - pid->prev_error) / pid->dt;
// 更新上一次误差
pid->prev_error = error;
return P + I + D;
}
注意:积分限幅一定要做。我曾经在调试一个伺服系统时,忘记加积分限幅。结果电机长时间堵转,积分项累积到天文数字。一松开,电机直接飞了出去,差点出事故。从那以后,积分限幅成了我的“铁律”。
4.6 知识体系总览
下面这张图,是我自己梳理的PID知识框架。你看一眼,就能把今天讲的内容串起来。
这张图把PID的三个核心要素、各自的作用、对系统性能的影响,以及数字实现方式都串起来了。你保存下来,以后调参时拿出来看看,思路会清晰很多。
好了,PID的原理和实现就聊到这儿。记住一句话:P是现在,I是过去,D是未来。把这三个时间维度控制好,你的电机就能指哪打哪。