PID控制算法

参考教学视频:
通俗易懂的 PID 控制算法讲解

PID 控制算法(比例-积分-微分)

PID 控制器的输出 u(t) 是比例(P)、积分(I)、微分(D)三项作用的线性叠加,其连续形式的标准公式为:

u(t)=Kpe(t)+Kie(t)dt+Kdde(t)dtu(t) = K_p \cdot e(t) + K_i \cdot \int e(t) \, dt + K_d \cdot \frac{de(t)}{dt}

其中:

  • e(t) 为误差信号,定义为:目标值 (Setpoint) - 当前测量值 (Process Variable)
  • u(t) 为控制器输出的控制量(如电机转速、阀门开度)。

以无人机悬停为例解读公式

假设我们想让无人机从地面垂直上升到 100 米高度并悬停。

开环控制(非 PID)的局限性

在没有干扰的完美模型中,我们可以根据物理公式算出所需速度和上升时间,让无人机开足马力飞行固定时间后停止。这就是开环控制——输出不影响输入。

【修正】 现实存在空气阻力、气流扰动、电池电压波动等干扰,开环控制无法修正这些偏差,误差会不断累积。PID 算法的作用恰恰相反:利用输出(当前高度)与目标(100米)的偏差来反向调整输入(电机升力),形成闭环反馈,逐步消除误差。

比例控制(P)- 快速响应

假设电机升力与误差(剩余距离)成正比,则比例控制项为:

FP=Kpe(t)F_P = K_p \cdot e(t)
  • 作用:误差越大,升力(或反向推力)越大,响应迅速。
  • 局限
    • Kp 过大,会在目标值附近剧烈震荡(如同刹车过猛导致车身晃动)。
    • 仅靠 P 控制无法消除稳态误差(例如当无人机升力恰好等于重力时,加速度为 0,但可能停在 98 米处,无法达到 100 米)。

微分控制(D)- 预测未来,抑制震荡

微分项相当于“预见性刹车”,它根据误差变化的速率来提前调整输出:

FD=Kdde(t)dtF_D = K_d \cdot \frac{de(t)}{dt}
  • 作用:当误差迅速减小时(即快速接近目标),D 项产生反向力,防止冲过头(超调)。
  • 效果:合适的 Kd 能大幅削弱震荡,让无人机“缓升缓降”。
  • 注意Kd 过小不起作用,过大则会导致过度制动(无人机在目标值前很远就开始减速,导致响应迟钝)。且 D 项对测量噪声极度敏感。

积分控制(I)- 消除遗留误差

当无人机悬停在 98 米或者 102 米(误差为 ±2 米)且升力等于重力时(没有加速度了),P 项输出较小,无法突破静摩擦或风阻,此时需要积分项“算旧账”:

FI=Kie(t)dtF_I = K_i \cdot \int e(t) \, dt
  • 作用:累积过去的微小误差,持续增加(或减少)升力,直到彻底消除静差,将无人机推到 100 米。
  • 风险Ki 过大或积分饱和(Windup)会导致系统大幅震荡。

离散化与 C 语言实现(位置式 PID)

由于计算机无法处理连续积分和微分,需将其离散化(令采样周期为 TT):

u(k)=Kpe(k)+Kii=0ke(i)T+Kde(k)e(k1)Tu(k) = K_p \cdot e(k) + K_i \cdot \sum_{i=0}^{k} e(i)T + K_d \cdot \frac{e(k) - e(k-1)}{T}

【修正】 实际编程中,常将 KiK_iKdK_d 合并采样周期,简化为:

u(k)=Kpe(k)+Kie(k)+Kd(e(k)e(k1))u(k) = K_p \cdot e(k) + K_i \cdot \sum e(k) + K_d \cdot (e(k) - e(k-1))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
typedef struct {
float Kp, Ki, Kd; // PID 参数
float setpoint; // 目标值(如 100.0 米)
float integral; // 积分累积和
float prev_error; // 上次误差(用于微分)
float out_max, out_min; // 输出限幅(防止积分饱和)
} PID_TypeDef;

float PID_Update(PID_TypeDef *pid, float measurement) {
float error = pid->setpoint - measurement; // 计算偏差

// 1. 比例项
float P_out = pid->Kp * error;

// 2. 积分项(带积分限幅防止饱和)
pid->integral += error;
// 限幅处理(防止积分饱和)
if (pid->integral > pid->out_max) pid->integral = pid->out_max;
if (pid->integral < pid->out_min) pid->integral = pid->out_min;
float I_out = pid->Ki * pid->integral;

// 3. 微分项
float D_out = pid->Kd * (error - pid->prev_error);

// 4. 计算总输出
float output = P_out + I_out + D_out;

// 5. 输出限幅
if (output > pid->out_max) output = pid->out_max;
if (output < pid->out_min) output = pid->out_min;

// 6. 保存本次误差供下次微分使用
pid->prev_error = error;

return output;
}

除位置PID之外还有很多种PID算法,模糊PID,智能PID等等,如果有应用场景再看看相关知识吧。