什么是 PID? 
我们有上篇文章内容的基础,现在从下面三个方面去进行扩展 
 
P(Proportional)比例项 
:对“当前误差”做出反应; 
 
I(Integral)积分项 
:对“历史误差”做累积,消除稳态误差; 
 
D(Derivative)微分项 
:预测“误差变化趋势”,防止系统超调、振荡。 
 
控制器的输出由这三部分叠加而成: 
 
e(t) 是当前误差(设定值 - 实际值) 
Kp, Ki, Kd 是比例、积分、微分系数 
比例项(P):反应越快,误差越小? 
比例项是最直观的一项,它的输出和误差成正比 
 
 优点: 
快速响应,提升系统速度; 
 
简单易调。 
 
 缺点: 
无法消除稳态误差 
Kp 过大易导致系统震荡甚至失稳。 
稳态误差的计算(阶跃输入): 
当系统稳定后,输出值 y∞ 和设定值 r 之间的差就是 稳态误差: 
 
说明:即使 Kp 很大,稳态误差仍然存在,想完全消除?那就需要积分项! 
 
积分项(I):消除稳态误差的关键 
积分项可以让误差“累加起来”,逼近目标值 
 
 作用: 
消除稳态误差; 
 
增加系统类型,提升精度。 
 
 风险: 
积分过快会导致过冲,甚至“积分风暴”(windup); 
 
响应变慢。 
 
如何防止积分风up? 
积分限幅 
:限制积分项累加的最大值; 
 
反积分(Anti-Windup) 
:控制器输出饱和时,停止或回退积分。 
 
 微分项(D):抑制超调、让系统更稳 
微分项相当于“预测未来”,对误差变化进行响应 
 
 作用: 
快速响应误差变化; 
 
抑制系统震荡和超调。 
 
 风险: 
对噪声非常敏感,必须加滤波。 
实验验证(你可能看到这样的曲线): 
 
 
 
 
这说明微分项对系统稳定性有显著提升。 
 
 PID 调参的推荐流程(手动调优) 
调 PID 就像调音一样,需要“一步一步来”: 
 
初始设定: 
 
Kp = 初始值;Ki = 0;Kd = 0; 
运行本项目 
先调 P: 
 
缓慢增加 Kp; 
 
直到系统反应不再迟钝,但振荡不明显; 
 
记录此时的稳态误差。 
 
加入 I: 
 
缓慢增加 Ki(或减小积分时间 Ti); 
 
直到系统可以消除稳态误差; 
 
注意观察是否“过冲”过大。 
 
最后加 D: 
 
缓慢增加 Kd,抑制震荡和超调; 
 
注意不要放大噪声。 
 
微调: 
 
在响应速度、稳态误差和系统稳定性之间找到平衡。 
 
 
 
 离散 PID 控制器 
/* pid.c - 简单且实用的 PID 控制器实现 
   浮点版本,适合 STM32/Arduino 等有浮点的 MCU。 
*/ 
#include <stdint.h> 
typedef struct { 
    float Kp; 
    float Ki;        // 注意:Ki 已包含采样时间缩放(或在 update 中乘 Ts) 
    float Kd; 
    float Ts;        // 采样周期(秒) 
    float out_min;   // 输出限幅下界 
    float out_max;   // 输出限幅上界 
    // 内部状态 
    float integrator; 
    float prev_error; 
    float prev_measurement; 
    float differentiator; // 用于滤波的微分项状态 
    float tau;            // 微分滤波时间常数(tau >= 0) 
} PID_t; 
/* 初始化 PID */ 
void pid_init(PID_t *pid, float Kp, float Ki, float Kd, float Ts, float out_min, float out_max, float tau){ 
    pid->Kp = Kp; 
    pid->Ki = Ki; 
    pid->Kd = Kd; 
    pid->Ts = Ts; 
    pid->out_min = out_min; 
    pid->out_max = out_max; 
    pid->integrator = 0.0f; 
    pid->prev_error = 0.0f; 
    pid->prev_measurement = 0.0f; 
    pid->differentiator = 0.0f; 
    pid->tau = tau; // 推荐 tau 在 0.01*Ts 到 10*Ts 之间尝试 
} 
/* 限幅辅助 */ 
static float clampf(float v, float lo, float hi){ 
    if(v < lo) return lo; 
    if(v > hi) return hi; 
    return v; 
} 
/* PID 核心更新:传入设定值和测量值,返回控制量 */ 
float pid_update(PID_t *pid, float setpoint, float measurement){ 
    float error = setpoint - measurement; 
    // 比例项 
    float P = pid->Kp * error; 
    // 积分项(矩形积分) 
    pid->integrator += 0.5f * pid->Ki * pid->Ts * (error + pid->prev_error); 
    // 积分防风(限制积分值,避免积分累积过大) 
    // 可将积分范围设为输出范围的一部分,或单独配置 
    float integ_min = pid->out_min; 
    float integ_max = pid->out_max; 
    pid->integrator = clampf(pid->integrator, integ_min, integ_max); 
    float I = pid->integrator; 
    // 微分项(使用测量值微分 + 一阶低通滤波,减少噪声放大) 
    // differentiator 状态使用滤波器: D = ( -Kd * (measurement - prev_measurement) * (1/Ts) ) filtered 
    // 使用标准形式: differentiator = (2*tau - Ts)/(2*tau + Ts) * differentiator_prev 
    //    - 2*Kd/(2*tau + Ts) * (measurement - prev_measurement) 
    if(pid->tau <= 0.0f){ 
        // 没有滤波,简单差分 
        pid->differentiator = pid->Kd * (error - pid->prev_error) / pid->Ts; 
    } else { 
        float alpha = (2.0f * pid->tau - pid->Ts) / (2.0f * pid->tau + pid->Ts); 
        float diff_measure = (measurement - pid->prev_measurement) / pid->Ts; 
        // 用 measurement 上的微分能减少 setpoint step 导致的 D 爆炸(常见做法) 
        pid->differentiator = alpha * pid->differentiator - (2.0f * pid->Kd / (2.0f * pid->tau + pid->Ts)) * diff_measure; 
    } 
    float D = pid->differentiator; 
    // 合并输出并限幅 
    float output = P + I + D; 
    float output_clamped = clampf(output, pid->out_min, pid->out_max); 
    // 抗积分风(simple back-calculation):如果输出被限幅,减小积分(选项) 
    // 若输出 被限幅 且 与未限幅输出有差异,则按差异调整积分(更稳定) 
    // 下面是一个简单的实现:当限幅时,撤销刚才的积分累加量 
    if(output != output_clamped){ 
        // 取消本次积分(另一种方法是使用反向补偿 gain) 
        pid->integrator -= 0.5f * pid->Ki * pid->Ts * (error + pid->prev_error); 
        I = pid->integrator; 
        // 可考虑更复杂的反向补偿法: integrator += (output_clamped - output) * K_aw 
    } 
    // 更新历史值 
    pid->prev_error = error; 
    pid->prev_measurement = measurement; 
    return output_clamped; 
} 
运行本项目 
 总结:记住这几点就能用好 PID 
P 控制当前误差 
,太大易振荡; 
 
I 消除长期误差 
,但可能带来超调; 
 
D 抑制振荡 
,但要防噪声; 
 
调参顺序:P → I → D; 
 
实验评估:看响应曲线和稳态误差; 
 
实现时注意 限幅、防风up 和滤波。 
———————————————— 
版权声明:本文为CSDN博主「平凡灵感码头」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 
原文链接:https://blog.csdn.net/Cha3043445754/article/details/151583357 
 
 |   
     
  
 |