3. 位置式PID与增量式PID:两种算法原理对比、代码实现、应用场景选择、优缺点分析

PID控制算法,说白了就是一套“纠偏”的数学规则。你给系统一个目标值,它测量当前值,算出偏差,然后通过比例、积分、微分三个环节去修正。但在实际工程落地时,这个算法有两种最常见的实现形式:位置式PID和增量式PID。

很多刚入行的工程师会纠结选哪个。我刚开始做电机控制那会儿,也在这上面栽过跟头。今天咱们就把这两种算法掰开揉碎了讲清楚。

3.1 位置式PID:最直观的“全量输出”

位置式PID,顾名思义,它的输出直接就是执行机构应该到达的绝对位置。比如控制一个阀门开度,它直接输出“开45%”;控制电机转速,它直接输出“占空比60%”。

3.1.1 算法原理

它的数学表达式长这样:

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

其中:

  • u(k):第k次采样时的控制输出(绝对量)
  • e(k):第k次采样时的偏差(目标值 - 当前值)
  • Σe(i):从开始到现在的偏差累加和
  • Kp, Ki, Kd:比例、积分、微分系数

这里有个关键点——积分项Σe(i)。它记录了系统从启动到现在的所有历史偏差。这意味着,只要系统运行着,这个累加和就会一直增长。

⚠ 我曾经踩过的坑: 位置式PID的积分饱和问题。有一次我做温控系统,启动时偏差很大,积分项疯狂累加。等温度到达目标值时,积分项已经积累了一个巨大的值,导致系统输出迟迟降不下来,温度直接冲过了头。这就是典型的“积分饱和”。

3.1.2 代码实现(C语言)

typedef struct {
    float Kp;
    float Ki;
    float Kd;
    float integral;   // 积分累加和
    float last_error; // 上一次偏差
    float output;     // 控制输出
    float out_max;    // 输出上限
    float out_min;    // 输出下限
} PositionalPID;

float PositionalPID_Calc(PositionalPID *pid, float setpoint, float feedback) {
    float error = setpoint - feedback;
    
    // 比例项
    float p_out = pid->Kp * error;
    
    // 积分项(累加)
    pid->integral += error;
    float i_out = pid->Ki * pid->integral;
    
    // 微分项
    float d_out = pid->Kd * (error - pid->last_error);
    
    // 计算输出
    pid->output = p_out + i_out + d_out;
    
    // 输出限幅(防积分饱和的简单处理)
    if (pid->output > pid->out_max) {
        pid->output = pid->out_max;
        // 这里应该做积分退饱和,后面会讲
    } else if (pid->output < pid->out_min) {
        pid->output = pid->out_min;
    }
    
    // 更新状态
    pid->last_error = error;
    
    return pid->output;
}

3.2 增量式PID:只关心“变化量”

增量式PID的思路完全不同。它不输出绝对位置,而是输出当前控制量相对于上一次控制量的增量。说白了,它告诉执行机构:“你再往前走这么多”。

3.2.1 算法原理

增量式PID的公式推导其实是从位置式来的。我们写出第k次和第k-1次的输出,然后相减:

Δ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)]

你看,这个公式里没有积分累加项Σe(i)。它只依赖最近三次的偏差值。这意味着:

  • 计算量小,只做加减法
  • 没有积分饱和问题
  • 输出是增量,误动作影响小

3.2.2 代码实现(C语言)

typedef struct {
    float Kp;
    float Ki;
    float Kd;
    float error[3];   // 环形缓冲区,存最近三次偏差
    float delta_out;  // 输出增量
    float output;     // 实际输出(需要外部累加)
} IncrementalPID;

float IncrementalPID_Calc(IncrementalPID *pid, float setpoint, float feedback) {
    // 更新偏差队列
    pid->error[2] = pid->error[1];
    pid->error[1] = pid->error[0];
    pid->error[0] = setpoint - feedback;
    
    // 计算增量
    pid->delta_out = pid->Kp * (pid->error[0] - pid->error[1])
                   + pid->Ki * pid->error[0]
                   + pid->Kd * (pid->error[0] - 2*pid->error[1] + pid->error[2]);
    
    return pid->delta_out;
}

// 外部调用时,需要自己累加:
// motor_output += IncrementalPID_Calc(&pid, target_speed, current_speed);
💡 我个人习惯: 增量式PID的代码里,我通常会把error数组做成环形缓冲区,这样每次更新只需要移动指针,不用真的搬数据。在STM32上跑,一个周期也就几个微秒。

3.3 核心差异对比

这两种算法到底差在哪?我整理了一张表,你一看就明白:

对比维度 位置式PID 增量式PID
输出形式 绝对位置(如占空比60%) 增量(如+5%)
积分实现 偏差累加和Σe(i) 无累加,仅用当前偏差
积分饱和 容易发生,需额外处理 天生免疫
计算依赖 所有历史偏差 最近3次偏差
误动作影响 输出突变,可能造成冲击 增量小,影响有限
手动/自动切换 输出会跳变,需做bumpless 天然无冲击
存储开销 需保存积分累加值 只需3个偏差值

3.4 应用场景选择:什么时候用哪个?

这个问题没有标准答案,但我可以给你一些实战经验:

3.4.1 优先选增量式PID的场景

  • 步进电机/伺服电机位置控制:增量式输出的是“再走多少步”,天然匹配
  • 需要频繁切换手动/自动:比如有些设备调试阶段,操作员要切手动调位置,再切回自动。增量式不会导致输出跳变
  • 执行机构有记忆功能:比如舵机,你给它一个增量,它自己会记住当前位置
  • 系统容易受干扰:增量式误动作小,不会因为一次采样错误导致系统剧烈抖动

3.4.2 优先选位置式PID的场景

  • 执行机构需要绝对位置指令:比如比例阀,你告诉它“开50%”,它就去50%
  • 积分项需要独立控制:有些高级算法需要单独处理积分项,位置式更方便
  • 系统对稳态精度要求极高:位置式的积分项是全局累加,理论上可以消除所有静差
  • 输出本身就是绝对量:比如PWM占空比,你直接给一个0-100%的值
🔑 我的选择原则: 如果执行机构是“积分型”(比如步进电机,脉冲数累加),我无脑选增量式。如果执行机构是“比例型”(比如阀门,直接对应开度),我选位置式。但记得一定要处理积分饱和。

3.5 优缺点深度分析

3.5.1 位置式PID的优缺点

优点:

  • 物理意义直观,调试时容易理解每个参数的作用
  • 积分项可以完全消除静差
  • 适合需要绝对位置输出的场景

缺点:

  • 积分饱和是老大难问题,需要做抗积分饱和处理(如积分分离、变速积分)
  • 手动/自动切换时输出会跳变,需要做无扰切换
  • 计算量相对大一些(虽然现在MCU性能都够)

3.5.2 增量式PID的优缺点

优点:

  • 没有积分饱和问题,代码实现简单
  • 误动作影响小,系统鲁棒性好
  • 手动/自动切换天然无冲击
  • 计算量小,适合低性能MCU

缺点:

  • 输出是增量,需要外部累加,如果累加器溢出或初始化不当会出问题
  • 积分作用是通过Ki*e(k)实现的,本质上是一个比例+积分的效果,参数整定思路和位置式略有不同
  • 对于需要绝对位置输出的场景,需要额外维护一个累加器

3.6 知识结构图

下面这张图帮你理清两种算法的核心脉络:

位置式PID vs 增量式PID 核心对比 位置式PID 增量式PID 输出:绝对位置(全量) 积分:偏差累加 Σe(i) ⚠ 易积分饱和 输出:增量 Δu(k) 积分:仅用当前偏差 e(k) ✅ 无积分饱和 应用场景选择 位置式:比例型执行机构 (阀门、PWM占空比) 增量式:积分型执行机构 (步进电机、舵机)

3.7 避坑指南

最后,分享几个我实际项目中遇到的坑:

⚠ 我曾经犯过的错: 有一次做四轴飞行器的姿态控制,我用了位置式PID。飞行器起飞时,由于姿态偏差大,积分项迅速饱和。等飞行器姿态回正后,积分项还保留着巨大的值,导致电机输出异常,飞行器直接翻了个跟头。后来我改用了增量式PID,配合限幅,问题就解决了。
💡 如果你非要用位置式: 一定要做积分分离或抗积分饱和处理。我的做法是:当输出达到限幅值时,停止积分累加,或者把积分项清零。这样至少不会让积分项无限增长。

好了,两种PID算法的对比就讲到这里。记住一个核心原则:看你的执行机构是“记位置”还是“不记位置”。记位置的用增量式,不记位置的用位置式。这个选择对了,后面调试会省很多事。