4、PID控制基础:比例(P)控制、积分(I)控制、微分(D)控制、PID参数对系统的影响
聊到力矩控制,PID是绕不开的核心。说白了,它就是一套“看偏差、算输出”的规则。我刚开始接触机器人时,觉得PID就是三个字母拼一起,调几个数就行。后来踩了不少坑才明白——每个字母背后,都藏着一段物理直觉。
4.1 比例控制(P)——最直接的“力反馈”
比例控制,就是根据当前误差的大小,成比例地输出控制量。公式很简单:u = Kp * e。其中e是目标值与实际值的差,Kp是比例增益。
举个例子。你让机器人关节转到90度,现在它停在60度。误差就是30度。Kp设成2,那输出力矩就是60。误差越大,推得越猛。这很符合直觉,对吧?
但比例控制有个天生的毛病——稳态误差。我在做六轴机械臂力控时遇到过:空载时位置挺准,一抓东西就偏了2度。为什么?因为系统需要持续输出力矩来对抗重力,而比例控制只有在有误差时才有输出。误差小了,输出也小了,最后卡在一个“刚好够用”的偏差上,就是稳态误差。
核心要点:
- Kp越大,响应越快,但容易超调、震荡
- Kp太小,系统反应迟钝,稳态误差大
- 纯P控制无法消除稳态误差
4.2 积分控制(I)——专治“老差一点”
积分控制,就是把过去的误差累积起来,再乘以Ki输出。公式:u = Ki * ∫e dt。
你想想看,比例控制搞不定的那点稳态误差,积分项能帮你“攒着”。误差一直存在,积分值就一直增长,输出力矩也跟着涨,直到把误差彻底吃掉。
嗯,这里要注意——积分是把双刃剑。我曾经调试一个移动平台的转向控制,Ki给大了,结果每次转向都 overshoot 一下再回来,像喝醉了酒。这就是“积分饱和”在作怪。误差大时积分快速累积,等误差变小了,积分值还没降下来,就会推过头。
避坑指南:
我曾经在调试一个高精度力控打磨项目时,积分项设得太大,导致系统在目标位置附近来回震荡了十几秒才稳定。后来加了积分限幅和分离逻辑,才解决。建议你:
- 给积分项设置上限(积分限幅)
- 误差较大时暂时关闭积分(积分分离)
- 只在误差较小时才启用积分
4.3 微分控制(D)——提前“踩刹车”
微分控制,看的是误差的变化率。公式:u = Kd * de/dt。误差变化越快,微分输出越大。
说白了,微分项就是个“预测器”。它看到误差在快速减小,就知道要超调了,提前输出反向力矩来刹车。我习惯把微分项叫做“阻尼”,它能让系统更稳。
但微分对噪声极其敏感。传感器信号稍微抖一下,微分值就会剧烈跳动。我记得有一次用便宜的电位计做位置反馈,微分项一加上去,电机就开始高频抖动,像得了帕金森。后来换了编码器,又加了低通滤波,才压住。
我的经验:
微分项通常用在位置控制或速度控制中。在纯力矩控制里,我一般很少用D,或者只用很小的值。因为力矩环本身响应快,加微分容易引入噪声。如果你非要用,记得先滤波。
4.4 PID参数对系统的影响——一张表说清楚
调参这件事,没有万能公式。但每个参数对系统的影响是有规律的。我整理了一张表,你调试时可以对照着看:
| 参数 | 增大后的效果 | 减小后的效果 | 常见问题 |
|---|---|---|---|
| Kp | 响应变快,超调增大,稳态误差减小 | 响应变慢,系统变稳,稳态误差增大 | Kp过大→震荡甚至发散 |
| Ki | 消除稳态误差,但可能引起低频震荡 | 稳态误差消除慢,甚至无法消除 | Ki过大→积分饱和、超调严重 |
| Kd | 抑制超调,提高稳定性,但放大噪声 | 系统阻尼不足,容易超调 | Kd过大→高频抖动、执行器饱和 |
4.5 调参的“土办法”——我常用的流程
调参没有标准答案,但我有个习惯性的流程,分享给你:
- 先调P:把I和D设成0,慢慢增大Kp,直到系统开始轻微震荡。然后退回一半左右的值。
- 再加D:如果系统超调明显,加一点Kd。加到震荡消失为止,别加太多。
- 最后加I:如果稳态误差不能忍,加一点Ki。从小往大加,看到系统开始“晃悠”就停下来。
- 微调:三个参数互相影响,需要来回调几轮。我一般会记录每次修改的值和系统响应,方便回溯。
一个重要的提醒:
调参时,一次只改一个参数。改完观察系统响应,记录数据。别同时动两个参数,否则你永远不知道是哪个起了作用。这是我早期犯过的错,改了两个参数,系统好了,但不知道是哪个起了作用——下次遇到类似问题,还是抓瞎。
4.6 知识体系总览
下面这张图,把PID控制的核心逻辑串起来了。你可以把它当作调试时的“地图”:
这张图展示了PID控制的基本回路:目标值减去反馈值得出误差,误差经过P、I、D三个通道的运算后叠加,输出控制量给被控对象,传感器再把实际值反馈回来,形成闭环。
调参这件事,说到底就是找到三个参数的最佳平衡点。没有捷径,多试、多记、多总结。我到现在调一个新系统,也得花上半天时间慢慢摸索。但只要你理解了每个参数的本质,心里就有底了。
一个小技巧:
如果你用代码实现PID,建议把P、I、D三个分量分别打印出来。调试时看哪个分量在主导,哪个在捣乱,一目了然。我所有的调试工具里,这个习惯帮我省了最多时间。
// 一个简单的位置式PID实现(伪代码)
float pid_update(float target, float actual) {
float error = target - actual;
// P
float p_out = Kp * error;
// I(带积分限幅)
integral += error * dt;
if (integral > integral_max) integral = integral_max;
if (integral < -integral_max) integral = -integral_max;
float i_out = Ki * integral;
// D(带一阶低通滤波)
float derivative = (error - last_error) / dt;
derivative = 0.9 * last_derivative + 0.1 * derivative; // 滤波
float d_out = Kd * derivative;
last_error = error;
last_derivative = derivative;
return p_out + i_out + d_out;
}
代码里我加了积分限幅和微分滤波,这两个小细节能帮你省掉很多调试时间。不信你试试不加,系统大概率会飘。
好了,PID的基础就聊到这儿。记住:P是现在,I是过去,D是未来。三个时间维度合在一起,才能把系统控稳。