2.从洗澡调水温到工业控制:PID算法之积分控制(I)深度解析与代码实现
一、积分控制的核心思想误差的历史问责积分控制的核心公式是$$u K_i \times \int e \, dt$$其中$K_i$ 是积分系数$\int e \, dt$ 是误差随时间的累积面积1. 积分到底是什么积分就是误差随时间的累积。比如洗澡时水温一直偏冷1度1分钟后误差累积为 $1^\circ\text{C} \times 60\text{s} 60^\circ\text{C·s}$10分钟后误差累积为 $1^\circ\text{C} \times 600\text{s} 600^\circ\text{C·s}$积分项等于这个累积量乘以系数$K_i$误差存在越久积分值越大输出的额外加热就越多。2. P和I的区别即时反应 vs 历史问责P比例即时反应只看当前误差误差越大调整越多但永远有余差I积分历史问责看误差累积误差存在越久调整越多专门消除余差两者配合的PI控制器既能快速响应又能持续纠偏直到误差为零。二、积分控制的致命陷阱积分饱和Windup积分控制虽然强大但有个致命的坑叫积分饱和。如果系统长时间达不到目标比如加热器坏了积分项会一直累积到天文数字。等加热器修好后巨大的积分值会让输出瞬间拉满造成剧烈超调。1. 积分饱和的危害系统响应变慢出现剧烈超调甚至导致系统震荡2. 解决方案积分限幅Anti-Windup在实际代码中一定要给积分值设天花板防止它无限累积。具体做法是设定积分上下限每次积分后检查超过上限就设为上限低于下限就设为下限三、P vs PI控制效果对比我们用洗澡调水温的例子来对比纯P控制和PI控制的效果控制方式最终温度余差特点纯P控制36.5℃0.5℃快速响应但永远有余差PI控制37℃0℃快速响应消除余差从曲线可以看到纯P控制最终稳定在36.5℃离目标37℃差半度PI控制开始和P控制一样上升但因为I持续累积最终把实际温度推到了37℃整误差归零I功成身退四、嵌入式C语言PI控制器实现可直接运行的代码下面是一套基于STM32 HAL库的完整PI控制器实现包含积分限幅功能可直接用于温度、电机、液位等控制场景。1. 头文件定义pi.h#ifndef __PI_H #define __PI_H #include stm32f1xx_hal.h // PI控制器结构体 typedef struct { // 核心参数 float Kp; // 比例系数 float Ki; // 积分系数 float Ts; // 采样周期单位s // 限幅参数 float max_integral; // 积分上限 float min_integral; // 积分下限 float max_output; // 输出上限 float min_output; // 输出下限 // 状态变量 float integral; // 积分累加值 float output; // 当前输出 } PI_HandleTypeDef; // 函数声明 void PI_Init(PI_HandleTypeDef *pi, float Kp, float Ki, float Ts, float max_integral, float min_integral, float max_output, float min_output); float PI_Calc(PI_HandleTypeDef *pi, float setpoint, float feedback); void PI_Reset(PI_HandleTypeDef *pi); #endif2. 实现文件pi.c#include pi.h #include math.h /** brief PI控制器初始化 param pi: PI控制器指针 param Kp: 比例系数 param Ki: 积分系数 param Ts: 采样周期s param max_integral: 积分上限 param min_integral: 积分下限 param max_output: 输出上限 param min_output: 输出下限 retval 无 */ void PI_Init(PI_HandleTypeDef *pi, float Kp, float Ki, float Ts, float max_integral, float min_integral, float max_output, float min_output) { pi-Kp Kp; pi-Ki Ki; pi-Ts Ts; pi-max_integral max_integral; pi-min_integral min_integral; pi-max_output max_output; pi-min_output min_output; pi-integral 0.0f; pi-output 0.0f; } /** brief PI计算函数 param pi: PI控制器指针 param setpoint: 目标设定值 param feedback: 实际反馈值 retval PI输出值 */ float PI_Calc(PI_HandleTypeDef *pi, float setpoint, float feedback) { // 1. 计算当前误差 float error setpoint - feedback; // 2. 比例项 float P_out pi-Kp * error; // 3. 积分项带抗饱和限幅 pi-integral error * pi-Ts; // 积分限幅 if (pi-integral pi-max_integral) { pi-integral pi-max_integral; } else if (pi-integral pi-min_integral) { pi-integral pi-min_integral; } float I_out pi-Ki * pi-integral; // 4. 总输出 pi-output P_out I_out; // 5. 输出限幅 if (pi-output pi-max_output) { pi-output pi-max_output; } else if (pi-output pi-min_output) { pi-output pi-min_output; } return pi-output; } /** brief 重置PI控制器状态 param pi: PI控制器指针 retval 无 */ void PI_Reset(PI_HandleTypeDef *pi) { pi-integral 0.0f; pi-output 0.0f; }3. 使用示例温度控制#include pi.h // 定义PI控制器实例 PI_HandleTypeDef temp_pi; // 温度控制初始化 void TempControl_Init(void) { // 初始化PI参数Kp2.0, Ki0.1, 采样周期100ms PI_Init(temp_pi, 2.0f, 0.1f, 0.1f, 100.0f, -100.0f, // 积分限幅 1000.0f, 0.0f); // 输出限幅PWM占空比0~1000 } // 温度控制循环在定时器中断中调用100ms一次 void TempControl_Loop(void) { float target_temp 37.0f; // 目标温度37℃ float actual_temp Get_Temperature(); // 读取实际温度 // 计算PI输出 float pwm_output PI_Calc(amp;temp_pi, target_temp, actual_temp); // 设置PWM占空比 Set_PWM_Duty(pwm_output); }五、总结PI控制器是工程上最常用的组合PI控制器已经是工程上最常用的组合了P负责快速跟踪I负责消除余差温度控制、电机调速、液位控制——大部分场景PI就够了。那D微分加进来有什么用下期我们讲D是给PI踩刹车的。代码说明该实现采用位置式PI适合大多数嵌入式控制场景包含积分抗饱和和输出限幅功能防止系统失控采样周期Ts需与实际控制周期一致参数Kp、Ki需根据实际系统进行整定可先用Ziegler-Nichols方法粗调再根据响应微调