PID控制算法详解:位置式与增量式的原理、实现与工程应用

发布时间:2026/8/2 16:03:55
PID控制算法详解:位置式与增量式的原理、实现与工程应用 1. 从“调温”到“控速”PID算法到底是个啥刚入行搞自动控制那会儿听到“PID”这三个字母总觉得它高深莫测是那些资深工程师手里的“黑魔法”。直到自己动手调一个电机的转速或者想让一个小车走直线被各种震荡、超调、响应慢折磨得焦头烂额时才真正明白PID的价值——它不是什么魔法而是一套极其朴素又无比强大的“纠错”方法论。简单来说PID就是一个聪明的“调节器”它的任务就是让一个物理量比如温度、速度、位置尽可能快、准、稳地达到我们设定的目标值。你可以把它想象成给浴缸放热水。目标水温是40度。你伸手一试发现水太凉了这就是“误差”。PID中的“P”比例部分会说“差得越多开热水阀门的劲儿就越大。”于是你猛开热水。随着水温接近40度P的作用会减小。但光有P水温很可能在40度附近来回晃或者永远差那么一点点到不了40度。这时候“I”积分部分出场了它会说“过去一段时间水温一直偏低这个‘欠账’累积起来了我得再加把劲把阀门开大点彻底消除这个稳态误差。”最后“D”微分部分像个预言家它发现水温正在快速上升误差变化率为负它会提前预警“喂别加太猛了上升势头很猛快关小点阀门防止冲过头”P、I、D三者协同最终让你舒舒服服地泡上澡。在嵌入式开发、机器人控制、无人机飞控这些领域PID算法几乎是工程师的“必修课”和“看家本领”。无论是让四轴飞行器稳稳悬停还是让智能车沿着黑线不跑偏亦或是让3D打印机的加热头精确控温背后都离不开PID的身影。今天我们就来彻底拆解PID中最核心的两种实现形式位置式PID和增量式PID。这不仅仅是两个公式的差别更关系到你代码的可靠性、抗干扰能力以及硬件资源的占用。理解了它们你才算真正摸到了PID控制的门道。2. 核心思想与公式拆解位置式与增量式的本质区别很多初学者会直接去背公式结果越看越糊涂。我们不如从“控制量”这个最根本的概念入手理解两者的分野。所谓控制量就是PID控制器计算出的最终输出它直接作用于执行机构比如PWM的占空比、阀门的开度、电机的电流等。2.1 位置式PID记住“绝对位置”的指挥官位置式PID也叫全量式PID。它的核心思想是控制器每一次计算出的输出量u(k)都是一个绝对的、完整的控制量值。这个值直接对应执行机构应该达到的“位置”。它的离散化公式是大家最熟悉的u(k) Kp * e(k) Ki * ∑e(j) Kd * [e(k) - e(k-1)]其中u(k)当前时刻第k次采样的控制量输出。e(k)当前时刻的误差e(k) 目标值 - 当前测量值。∑e(j)从开始到现在j0 to k所有误差的累加和这就是积分项。Kp,Ki,Kd比例、积分、微分系数。它像什么像一个严格的总指挥。它不关心上一次发出了什么指令它只根据当前战场的整体形势当前误差、历史累计战果、敌军变化趋势直接下达一个全新的、绝对的作战指令比如“全军前进至东经XXX度北纬XXX度”。带来的特点积分项累积 (∑e(j)): 这是位置式最显著的特征。积分项会不断累加历史所有误差只要误差不为零它就会一直增长。这是消除稳态误差的关键但也埋下了“积分饱和”的隐患——如果系统长时间达不到目标比如执行机构已到极限积分项会累积到一个巨大的值一旦误差反向系统需要很长时间才能“消化”这个巨大的积分导致控制响应迟钝甚至失控。输出与过去所有状态有关计算u(k)需要用到从开始到现在的所有误差历史理论上是“全局依赖”。直接输出绝对量非常适合控制阀门开度、伺服电机目标角度等本身就需要绝对位置指令的执行机构。2.2 增量式PID下达“调整指令”的参谋官增量式PID的核心思想则完全不同控制器每一次计算出的输出量Δu(k)只是控制量相对于上一次输出的“增量”或“变化量”。它的公式可以通过位置式公式推导出来用u(k)减去u(k-1)Δu(k) u(k) - u(k-1) Kp*[e(k)-e(k-1)] Ki*e(k) Kd*[e(k)-2e(k-1)e(k-2)]化简后我们得到增量式的标准形式Δu(k) A * e(k) B * e(k-1) C * e(k-2)其中A Kp Ki Kd,B -(Kp 2Kd),C Kd。它像什么像一个在前线观察的参谋官。他不需要知道部队的绝对坐标他每次只给部队下达基于当前位置的调整指令“比起上一秒现在请再向右转5度前进速度增加10%”。最终的“绝对位置”是由执行机构自己一次次累加这些增量指令得到的。带来的根本性优势无积分饱和这是增量式最突出的优点。公式中已经没有对历史误差的累加和 (∑e(j)) 了积分项变成了Ki * e(k)只与当前误差有关。因此即使系统长时间存在误差也不会产生巨大的积分累积从根本上避免了积分饱和问题。输出仅与最近几次误差有关计算Δu(k)只需要最近三次的误差值e(k),e(k-1),e(k-2)编程实现时只需要保存三个变量对内存更友好计算量也略小。手动/自动切换无扰动这是工程上的一个巨大优势。假设系统原来处于手动控制操作员给定一个输出值。当切换到自动PID控制时如果是位置式它可能会计算出一个与当前手动值截然不同的u(k)导致输出突变系统震荡。而增量式只输出增量Δu切换瞬间可以将Δu设为零那么实际输出就保持切换前的手动值实现无扰平滑切换。抗积分能力弱化注意增量式并非没有积分作用。它的积分作用体现在Ki * e(k)这一项上。只要存在误差e(k)它就会产生一个增量去修正。只是这种修正是“渐进式”的不如位置式的“累积式”那么有“强迫性”因此对于抑制大惯性系统的稳态误差有时需要更强的积分系数。关键理解很多人误以为增量式PID没有积分项这是不对的。Ki * e(k)就是它的积分作用体现。它放弃了“秋后算总账”累加历史改为“当下误差当下补”当前误差按比例补偿用持续的小步修正来替代可能的大步跳跃从而获得了更好的安全性和平滑性。3. 编程实现与代码细节全解析理论懂了不落到代码上都是空谈。下面我们分别用C语言展示两种PID的实现并深入每一个细节。3.1 位置式PID的代码实现与陷阱首先定义一个位置式PID的结构体用于存放所有状态和参数typedef struct { float Kp, Ki, Kd; // PID系数 float target; // 目标值 float measure; // 测量值 float error; // 当前误差 float last_error; // 上一次误差 (用于微分) float integral; // 积分累积值 - 这是关键 float output; // 输出值 float output_max; // 输出上限 float output_min; // 输出下限 float integral_max; // 积分限幅 (防止饱和) } Positional_PID;接下来是核心计算函数float Positional_PID_Calculate(Positional_PID *pid, float measure) { pid-measure measure; pid-error pid-target - pid-measure; // 计算当前误差 // 1. 比例项 float proportional pid-Kp * pid-error; // 2. 积分项核心与陷阱所在 pid-integral pid-error; // 累加误差 // *** 积分抗饱和处理必须做*** if (pid-integral pid-integral_max) { pid-integral pid-integral_max; } else if (pid-integral -pid-integral_max) { pid-integral -pid-integral_max; } float integral pid-Ki * pid-integral; // 3. 微分项不完全微分可选 float derivative pid-Kd * (pid-error - pid-last_error); pid-last_error pid-error; // 更新历史误差 // 4. 合成输出 pid-output proportional integral derivative; // 5. 输出限幅 if (pid-output pid-output_max) { pid-output pid-output_max; } else if (pid-output pid-output_min) { pid-output pid-output_min; } return pid-output; }位置式PID的编程核心与坑点积分饱和与积分限幅这是位置式PID最大的坑。注意代码中pid-integral这个变量它会一直累加。假设你的小车电机PWM最大是1000但计算出的输出早就达到1000了由于物理限制车速还是上不去误差e(k)持续为正。此时integral会疯狂增长到一个天文数字比如几万。当目标突然降低误差变负时这个巨大的正积分项需要很长时间的负误差才能被“抵消”掉在此期间输出会维持在最大值下不来系统完全失控。因此integral_max这个积分限幅参数至关重要它必须根据你的输出范围和系统特性合理设置通常设置为输出限幅值的若干倍如2-5倍是一个起点。积分分离另一个高级技巧。在误差很大的时候比如系统刚启动我们可能不希望积分项起作用因为强积分会导致超调。可以设置一个误差阈值当|error| threshold时将Ki临时设为0或者不累加积分。等误差缩小到合理范围再启用积分这样可以加快初始响应又减少超调。微分项的抗噪处理微分项对测量噪声极其敏感因为噪声会导致e(k)-e(k-1)产生剧烈变化。实践中常采用“不完全微分”或对测量值进行低通滤波。3.2 增量式PID的代码实现与优势增量式PID的结构体更简洁typedef struct { float Kp, Ki, Kd; // PID系数 float target; // 目标值 float measure; // 测量值 float error[3]; // 当前、上一次、上上次误差 (环形队列或移位) float output; // 当前实际输出值 (需要累加增量) float output_max; float output_min; // 注意没有 integral 变量 } Incremental_PID;核心计算函数体现了其“增量”本质float Incremental_PID_Calculate(Incremental_PID *pid, float measure) { pid-measure measure; // 更新误差队列 error[2]旧的上次error[1]旧的当前 error[0]新的当前 pid-error[2] pid-error[1]; pid-error[1] pid-error[0]; pid-error[0] pid-target - pid-measure; // 计算新的当前误差 // 计算增量 (使用推导后的A, B, C系数形式计算更高效) float delta_u (pid-Kp pid-Ki pid-Kd) * pid-error[0] (-pid-Kp - 2*pid-Kd) * pid-error[1] (pid-Kd) * pid-error[2]; // *** 核心在旧输出上增加增量 *** pid-output delta_u; // 输出限幅 if (pid-output pid-output_max) { pid-output pid-output_max; } else if (pid-output pid-output_min) { pid-output pid-output_min; } // 注意这里通常不需要对 delta_u 或 output 做积分限幅因为增量本身是有限的。 return pid-output; }增量式PID的编程精髓天然的“积分抗饱和”仔细看整个计算过程没有对任何变量进行持续累加。delta_u每次都是根据最近三次误差重新计算的一个有限值。即使系统卡住误差不变delta_u也会很快趋于零因为e(k)e(k-1)e(k-2)代入公式后delta_u0输出pid-output将保持恒定不会无限增长。这就是其天然抗饱和的特性。手动/自动切换的实现假设系统从手动切换到自动。在手动模式下pid-output被手动设定为某个值比如500。切换到自动模式的第一个周期调用Incremental_PID_Calculate计算出一个delta_u。如果我们希望切换瞬间无扰动可以在切换后第一个控制周期强制将delta_u设为0或者直接不调用计算函数这样pid-output就保持为500实现了平滑过渡。输出限幅的影响不同在位置式中积分饱和发生在限幅之前积分项自己疯涨。在增量式中限幅发生在pid-output delta_u之后。如果输出达到限幅值增量delta_u虽然被计算出来但加不上去。当下次误差变化时新的delta_u会继续尝试修正。由于没有积分累积系统一旦脱离饱和区能更快恢复响应。这也是工程上偏爱增量式的原因之一。4. 场景选择与参数整定实战经验明白了原理和实现到底该用哪个这完全取决于你的被控对象和执行机构。4.1 如何选择位置式 vs 增量式特性维度位置式PID增量式PID选择建议积分饱和非常严重必须处理天然免疫执行机构输出范围有限或易饱和的系统优先选增量式。输出类型直接输出绝对量输出变化量控制阀门开度、角度位置等需绝对指令的常用位置式。控制电机转速、PWM占空比等可接受增量指令的两者皆可增量式更安全。手动/自动切换需特殊处理否则易扰动可实现无扰切换需要频繁进行手自动切换的工业过程控制增量式是首选。执行机构特性对执行机构无特殊要求要求执行机构有记忆功能或位置反馈增量式输出的是“该增加多少”执行机构必须能“记住”自己当前在哪并加上这个增量。步进电机靠脉冲数记位置、带编码器的直流电机可反馈位置非常适合。纯开环的直流电机仅靠PWM调速不知自身速度也适用因为速度可视为对PWM占空比的“积分”。编程与内存需存储积分和可能需防饱和逻辑仅需存储最近2-3次误差逻辑简单对内存极其敏感的MCU增量式略有优势。抗干扰性积分项对持续偏差纠偏能力强对持续偏差的纠正相对温和依赖每次的Ki*e(k)对于需要强力消除静差的系统位置式积分效果更“硬”。可适当增大增量式的Ki来补偿。一个典型误区澄清有人说“位置式用于位置控制增量式用于速度控制”。这个说法不准确。关键在于“控制量的物理意义”。如果你用PID计算出的u(k)直接作为电机的位置指令比如发送给舵机的角度值那么这是一个“位置式”应用无论你用的是位置式还是增量式算法。实际上在机器人关节控制中虽然控制的是位置但底层驱动器如伺服驱动器通常接收的是“速度指令”或“扭矩指令”此时外环位置PID位置式计算出的结果是作为内环速度PID的目标值内环速度PID常用增量式。所以算法选择取决于你这一级PID输出的物理含义和执行机构的接受形式。4.2 参数整定从“瞎调”到“有章法”无论哪种形式PID参数 (Kp,Ki,Kd) 的整定都是核心技能。网上流传的“口诀法”先调P再调I最后调D有其道理但这里分享更系统化的工程步骤和心得第一步做好准备工作确保系统安全将输出限幅设置到安全范围特别是位置式务必设置积分限幅。初始化参数将所有参数 (Kp,Ki,Kd) 设为0。Ki和Kd在调试初期就是“捣乱分子”先关掉。确定采样周期T这至关重要采样太快计算浪费采样太慢信息丢失。一般取系统响应时间的1/5到1/10。对于电机控制1ms到10ms常见对于温度控制1s到10s也可能。第二步整定比例系数Kp将Ki和Kd设为0逐渐增大Kp。观察系统响应。目标系统能对设定值变化做出明显反应但会出现稳态误差始终差一点到不了目标和可能的一些振荡。继续增大Kp直到系统出现持续、等幅的振荡。记下此时的Kp值称为Ku临界增益并测量振荡周期Tu。工程安全做法实际中我们不一定非要调到等幅振荡。通常取Kp 0.5 * Ku作为一个稳健的起点。此时系统响应较快超调在可接受范围但有稳态误差。第三步整定积分系数Ki将上一步得到的Kp略微减小比如打8折以预留积分作用的空间。逐渐增大Ki。注意在离散公式中Ki实际对应的是Kp * T / Ti其中Ti是积分时间常数。所以Ki值本身大小和采样周期T强相关。观察稳态误差被消除的过程。Ki太小消除静差慢Ki太大会引起系统振荡加剧甚至出现“积分饱和振荡”输出在限幅值附近周期性跳动。一个技巧先设一个很小的Ki观察系统在到达目标值后是否缓慢而稳定地最终精确对准目标。如果出现超调后缓慢回落说明Ki可能大了。第四步整定微分系数KdKd是用来抑制超调、提高稳定性的。在Kp和Ki基本调好后如果系统超调明显或者响应有抖动再考虑加入Kd。逐渐增大Kd。微分项对噪声极其敏感Kd太大会放大噪声导致输出高频抖动。务必对测量值进行滤波如一阶低通滤波后再用于PID计算尤其是用了微分项时。根据齐格勒-尼科尔斯法则可以取Kd Kp * Td / T其中Td Tu / 8。这可以作为一个粗略的起点。增量式参数整定注意增量式PID的公式形式不同其Ki和Kd的物理意义与位置式略有差异。但整定思路相通先调Kp使系统有响应再调Ki消除静差最后调Kd抑制超调。由于增量式无积分饱和你可以更放心地尝试较大的Ki值来快速消除静差。5. 典型问题排查与进阶技巧在实际项目中调好了参数不等于一劳永逸。环境变化、负载变动都会影响性能。以下是几个常见问题及排查思路问题1系统始终在目标值附近低频、大幅度振荡。可能原因1位置式常见积分饱和。检查积分限幅是否设置过小或者输出限幅过早导致积分项不断累积-释放形成振荡。可能原因2Kp过大。虽然Kp大响应快但也会降低系统稳定性。尝试减小Kp。可能原因3Ki过大。过强的积分作用会“推过头”导致系统反复纠正。尝试减小Ki。排查工具记录下error、integral位置式、output的变化曲线。观察振荡时是不是integral在主导输出变化。问题2系统响应非常迟钝像“慢半拍”。可能原因1Kp过小。比例作用是响应的主力军Kp太小自然没劲。可能原因2位置式积分限幅integral_max设置过小限制了积分作用的威力。可能原因3增量式Ki过小。对于增量式消除静差主要靠Ki*e(k)Ki太小则修正力度弱。可能原因4采样周期T太长。控制器获取信息太慢无法做出及时反应。在MCU资源允许下适当提高控制频率。问题3电机或执行机构出现高频“尖叫”或抖动。几乎可以肯定微分系数Kd过大或者没有对测量信号进行滤波。微分项将测量噪声放大成了剧烈抖动的控制信号。立即减小Kd并务必在PID计算前对measure信号进行软件滤波如移动平均、一阶低通滤波。问题4设定值突变时系统冲击很大超调严重。进阶技巧微分先行。标准的PID是对误差e(k)进行微分。当设定值target突变时e(k)也会突变导致微分项Kd*[e(k)-e(k-1)]产生一个巨大的瞬时输出微分冲击。改进方法是“微分先行”或“设定值滤波”即只对测量值measure进行微分公式变为derivative -Kd * (measure(k) - measure(k-1))。这样设定值变化不会引起微分项的突变只有被控量实际变化时微分才起作用。另一个技巧对设定值的变化率进行限制斜坡函数避免阶跃突变。关于“陀螺仪走直线的PID算法”这通常指平衡车或直行机器人的“角度环”或“速度环”PID。以平衡车为例它使用MPU6050等陀螺仪获取车身倾角。一个典型的双环控制是外环是角度环位置式PID输入是期望角度通常为0度即直立与当前角度的误差输出是车体的目标角速度内环是角速度环增量式PID更常见输入是外环给出的目标角速度与陀螺仪直接测得的角速度的误差输出是电机的PWM占空比。内环使用增量式可以很好地处理电机PWM的限幅问题并且响应快。调试时务必先调内环角速度环让车能快速、稳定地响应角速度指令内环调好后再调外环角度环让车能保持直立。顺序反过来会非常难调。最后PID是一门实践的艺术。再多的理论也比不上在真实的系统上动手调一遍。最好的学习路径是先用一个简单的被控对象比如一个直流电机加编码器或者一个用电阻加热的小铜块搭建实验平台写一个能实时绘图和调整参数的上位机然后亲手去感受Kp增大时系统如何变得“敏感”Ki如何慢慢“磨平”误差D又如何像“阻尼器”一样让运动变得平滑。这个过程积累下来的手感才是你应对未来各种复杂控制问题的最大底气。