2.从洗澡调水温到工业控制:PID算法之积分控制(I)深度解析与代码实现
2026/7/26 8:44:30 网站建设 项目流程

一、积分控制的核心思想:误差的历史问责

积分控制的核心公式是:

$$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); #endif

2. 实现文件(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参数:Kp=2.0, Ki=0.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方法粗调,再根据响应微调

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询