3.PID控制中的微分环节:给PI踩刹车的关键
·
在嵌入式控制领域,PID算法是当之无愧的"万能钥匙"。其中,P负责快速响应,I负责消除余差,但这两个环节配合时,很容易出现"超调"问题——就像水温快速升到38度,等I反应过来往下拉时已经晚了。这时候,第三个环节D(微分控制)就登场了,它的核心作用是给PI踩刹车,预判误差变化趋势,提前抑制超调。
一、微分控制的核心原理
微分环节的本质是误差的变化率,公式为:
$$u_D = K_d \times \frac{de}{dt}$$
其中:
- $K_d$:微分系数
- $\frac{de}{dt}$:误差的变化速率
简单来说,微分环节不看当前误差有多大,而是看误差变化有多快。比如水温以每秒2度的速度飙升,这是危险信号,微分项会立刻输出反向力,提前减速,避免超调。
二、微分控制的三个核心特性
1. 看趋势,不看现状
微分环节关注的是误差的变化趋势,而非当前误差值。误差变化越快,D的反向输出越强;误差匀速变化,D保持恒定反向力;误差不变,D完全不出力。
2. 提前刹车,避免超调
就像开车时遇到红灯,不会等车到停止线才踩刹车,而是提前收油减速。D就是这个提前动作,通过预判误差变化趋势,提前抑制超调。
3. 对噪声敏感,需合理设置
微分环节的弱点是怕噪声。传感器采集的温度数据中常有小毛刺,这些毛刺意味着瞬间的剧烈变化,微分环节会把这些噪声放大,误以为是误差在快速变化,输出不该有的反向力。因此,$K_d$不能设置过大。
三、PID三力合体:P+I+D协同工作
当三个环节一起工作时,系统会呈现完美的控制效果:
- P(比例):系统启动误差大时,P出主力快速拉升
- I(积分):接近目标误差小时,I慢慢推最后那一点,消除余差
- D(微分):上升太快要冲过时,D踩刹车往回拉,抑制超调
三力平衡后,系统既快又稳还没有余差,这就是完整的PID控制。
四、STM32 LL库实现PID微分控制
以下是基于STM32 LL库的完整PID控制代码,包含微分环节的实现:
1. PID结构体定义
typedef struct {
float Kp; // 比例系数
float Ki; // 积分系数
float Kd; // 微分系数
float target; // 目标值
float actual; // 实际值
float error; // 当前误差
float last_error; // 上一次误差
float integral; // 积分累积
float output; // 输出值
float output_max; // 输出最大值
float output_min; // 输出最小值
} PID_HandleTypeDef;
2. PID初始化函数
void PID_Init(PID_HandleTypeDef *pid, float kp, float ki, float kd,
float target, float output_max, float output_min) {
pid->Kp = kp;
pid->Ki = ki;
pid->Kd = kd;
pid->target = target;
pid->actual = 0.0f;
pid->error = 0.0f;
pid->last_error = 0.0f;
pid->integral = 0.0f;
pid->output = 0.0f;
pid->output_max = output_max;
pid->output_min = output_min;
}
3. PID计算函数(含微分环节)
float PID_Calculate(PID_HandleTypeDef *pid, float actual) {
// 更新实际值
pid->actual = actual;
// 计算当前误差
pid->error = pid->target - pid->actual;
// 比例项
float p_out = pid->Kp * pid->error;
// 积分项(带限幅)
pid->integral += pid->error;
if (pid->integral > pid->output_max / pid->Ki) {
pid->integral = pid->output_max / pid->Ki;
} else if (pid->integral < pid->output_min / pid->Ki) {
pid->integral = pid->output_min / pid->Ki;
}
float i_out = pid->Ki * pid->integral;
// 微分项(核心:误差变化率)
float derivative = pid->error - pid->last_error;
float d_out = pid->Kd * derivative;
// 总输出
pid->output = p_out + i_out + d_out;
// 输出限幅
if (pid->output > pid->output_max) {
pid->output = pid->output_max;
} else if (pid->output < pid->output_min) {
pid->output = pid->output_min;
}
// 更新上一次误差
pid->last_error = pid->error;
return pid->output;
}
4. 主函数调用示例
#include "stm32f1xx_ll_adc.h"
#include "stm32f1xx_ll_tim.h"
// 定义PID控制器
PID_HandleTypeDef pid;
// 初始化ADC和定时器
void Hardware_Init(void) {
// ADC初始化(读取温度传感器数据)
LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_PATH_INTERNAL_NONE);
LL_ADC_SetResolution(ADC1, LL_ADC_RESOLUTION_12B);
LL_ADC_SetDataAlignment(ADC1, LL_ADC_DATA_ALIGN_RIGHT);
LL_ADC_Enable(ADC1);
// 定时器初始化(生成PWM控制加热片)
LL_TIM_SetPrescaler(TIM2, __LL_TIM_CALC_PSC(SystemCoreClock, 1000000));
LL_TIM_SetAutoReload(TIM2, 1000);
LL_TIM_EnableCounter(TIM2);
LL_TIM_OC_SetMode(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM1);
LL_TIM_OC_EnablePreload(TIM2, LL_TIM_CHANNEL_CH1);
LL_TIM_EnableAllOutputs(TIM2);
}
// 读取温度值(模拟)
float Read_Temperature(void) {
uint32_t adc_value = LL_ADC_REG_ReadConversionData12(ADC1);
// 将ADC值转换为温度值(根据实际传感器校准)
float temperature = (float)adc_value * 50.0f / 4095.0f;
return temperature;
}
// 主函数
int main(void) {
// 硬件初始化
Hardware_Init();
// PID初始化:Kp=2.0, Ki=0.1, Kd=0.5, 目标温度=50℃, 输出范围0-100%
PID_Init(&pid, 2.0f, 0.1f, 0.5f, 50.0f, 100.0f, 0.0f);
while (1) {
// 读取实际温度
float actual_temp = Read_Temperature();
// 计算PID输出
float pwm_duty = PID_Calculate(&pid, actual_temp);
// 设置PWM占空比
LL_TIM_OC_SetCompareCH1(TIM2, (uint32_t)pwm_duty);
}
}
五、微分环节的调参建议
- 先调P,再调I,最后调D:按照"先P后I再D"的顺序调参,每次只改一个参数,观察系统响应。
- Kd不宜过大:微分环节对噪声敏感,Kd过大容易导致系统震荡,建议从0开始逐步增大。
- 配合滤波使用:如果传感器噪声较大,建议在微分环节前加入低通滤波,减少噪声干扰。
六、总结
微分环节是PID控制中最容易被误解的环节,但也是实现"既快又稳"控制效果的关键。通过预判误差变化趋势,提前抑制超调,微分环节让PID控制真正成为工业控制领域的"万能算法"。
在实际应用中,需要根据具体场景合理设置Kd值,配合滤波处理噪声,才能发挥微分环节的最大作用。。
更多推荐


所有评论(0)