4、增量式PID算法:公式推导、C语言实现、与位置式的对比

好,咱们今天来聊聊增量式PID。说实话,很多初学者一听到“增量式”三个字,就觉得比位置式高级。其实不是的。它们各有各的脾气,各有各的适用场景。

我个人习惯把增量式PID叫做“只算变化量”的PID。什么意思呢?位置式PID每次都要算一个完整的控制量输出,而增量式只算这一次比上一次多了多少。你想想看,这有什么好处?

4.1 公式推导:从位置式到增量式

我们先回忆一下位置式PID的公式:

u(k) = Kp * e(k) + Ki * Σe(i) + Kd * [e(k) - e(k-1)]

这个公式里,Σe(i) 是从系统启动到现在所有误差的累加。嗯,这里要注意,这个累加项在单片机里是个隐患——万一积分饱和了,输出可能直接飞掉。

那增量式怎么来的?很简单。我们写出第 k-1 时刻的输出:

u(k-1) = Kp * e(k-1) + Ki * Σe(i-1) + Kd * [e(k-1) - e(k-2)]

然后用 u(k) 减去 u(k-1),得到增量 Δu(k):

Δu(k) = u(k) - u(k-1)
       = Kp * [e(k) - e(k-1)] + Ki * e(k) + Kd * [e(k) - 2*e(k-1) + e(k-2)]

这就是增量式PID的核心公式。你看,它只跟最近三次的误差有关,跟历史累加没关系了。

核心要点:增量式PID的输出是控制量的增量,不是控制量本身。最终输出需要做累加:u(k) = u(k-1) + Δu(k)。

4.2 C语言实现:我常用的模板

我在项目中用过很多次增量式PID,下面这个结构体是我比较喜欢的一种写法。它把三个系数和三个历史误差都封装在一起,清爽又好维护。

typedef struct {
    float Kp;      // 比例系数
    float Ki;      // 积分系数
    float Kd;      // 微分系数
    float e_k;     // 当前误差
    float e_k1;    // 上一次误差
    float e_k2;    // 上上次误差
    float u_k1;    // 上一次输出
    float delta_u; // 增量输出
} PID_Incremental;

void PID_Incremental_Init(PID_Incremental *pid, float Kp, float Ki, float Kd) {
    pid->Kp = Kp;
    pid->Ki = Ki;
    pid->Kd = Kd;
    pid->e_k = 0;
    pid->e_k1 = 0;
    pid->e_k2 = 0;
    pid->u_k1 = 0;
    pid->delta_u = 0;
}

float PID_Incremental_Calc(PID_Incremental *pid, float setpoint, float feedback) {
    // 计算当前误差
    pid->e_k = setpoint - feedback;

    // 计算增量
    pid->delta_u = pid->Kp * (pid->e_k - pid->e_k1)
                 + pid->Ki * pid->e_k
                 + pid->Kd * (pid->e_k - 2*pid->e_k1 + pid->e_k2);

    // 更新历史误差
    pid->e_k2 = pid->e_k1;
    pid->e_k1 = pid->e_k;

    // 累加得到当前输出
    pid->u_k1 += pid->delta_u;

    return pid->u_k1;
}

小技巧:我习惯把 u_k1 的初始值设为0。但如果你的执行器有中位值(比如舵机的中位是1500),记得在初始化时设置好。我曾经因为这个吃过亏,电机一启动就往一个方向猛冲。

4.3 与位置式的对比:谁更好?

这个问题没有标准答案。我根据自己的经验,整理了一个对比表:

对比项 位置式PID 增量式PID
输出形式 直接输出控制量 u(k) 输出增量 Δu(k),需累加
积分项 误差累加,容易积分饱和 无累加项,无积分饱和问题
故障影响 输出突变,执行器可能飞车 增量小,冲击小
手动/自动切换 需要做无扰切换处理 天然无扰,切换平滑
计算量 需要存储所有历史误差 只需存储最近3个误差
适用场景 伺服电机、阀门等全量控制 步进电机、增量式执行器

避坑指南:我曾经在一个温控项目里用了增量式PID,结果发现温度稳定后还是有微小波动。后来排查发现,是因为增量式对积分项的截断误差比较敏感。解决办法是把 Ki 放大一点,或者改用位置式。所以你看,没有绝对的好坏,只有合不合适。

4.4 什么时候选增量式?

说白了,就两个原则:

  • 执行器是增量型的——比如步进电机,你给它一个脉冲它就转一步。这时候用增量式PID,输出直接就是脉冲数,省事。
  • 系统不允许输出突变——比如机械臂,位置式PID如果积分饱和了,输出可能从0直接跳到100,机械臂“咣”一下撞过去。增量式就不会,它每次只变一点点。

反过来,如果你的执行器需要绝对位置(比如伺服电机的位置环),或者你对稳态精度要求极高,那位置式PID可能更合适。

嗯,今天就聊到这儿。下一节我们讲积分分离和抗积分饱和,这两个在实际项目中太常用了。到时候我会拿一个我调过的温控系统做例子,保证你听完就能用。