搞嵌入式开发的小伙伴应该都遇到过PID调参这个头疼的问题吧?今天咱们直接上干货,聊聊怎么在STM32上玩转PID自整定和温度控制。先扔个核心代码片段镇楼
基于STM32开发的PID自整定和PID温控和PWM输出程序源码,采用反馈法进行PID参数自动整定,得出系统临界值比例增益,自动计算调节,使系统进入正常状态。 程序源码注释详细
typedef struct { float Kp; float Ki; float Kd; float integral_max; // 积分限幅 float output_max; // 输出限幅 float last_error; float integral; } PID_TypeDef;这个结构体把PID的核心参数都打包了,重点看integral_max这个参数——很多新手调PID时积分项爆表的问题,就是靠它解决的。接下来看中断服务里的处理逻辑:
void TIM2_IRQHandler(void) { static uint32_t tick = 0; if(__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE)) { __HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE); float current_temp = DS18B20_Read(); // 读取当前温度 float error = target_temp - current_temp; pid.integral += error * dt; if(pid.integral > pid.integral_max) pid.integral = pid.integral_max; // 抗积分饱和 else if(pid.integral < -pid.integral_max) pid.integral = -pid.integral_max; float derivative = (error - pid.last_error) / dt; float output = pid.Kp * error + pid.Ki * pid.integral + pid.Kd * derivative; pid.last_error = error; __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, (uint32_t)(output)); // 更新PWM占空比 } }这里有个细节处理得很妙:在计算积分项时做了限幅处理,避免系统启动时积分项过大导致的超调。dt是采样周期,建议根据系统响应速度在50ms-200ms之间调整。
自整定算法才是重头戏,咱们用阶跃响应法找临界增益。核心逻辑是逐步增大比例增益直到系统出现等幅振荡:
void PID_AutoTune(void) { float Ku, Tu; float output = 0; uint8_t oscillation_count = 0; while(1) { output += 0.5f; // 每次增加0.5%的PWM输出 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, (uint32_t)output); HAL_Delay(100); float peak1 = 0, peak2 = 0; // 检测振荡波形 if(DetectOscillation(&peak1, &peak2)) { Ku = output / (peak1 - peak2); // 计算临界增益 Tu = CalculateOscillationPeriod(); // 获取振荡周期 break; } } // Ziegler-Nichols整定公式 pid.Kp = 0.6 * Ku; pid.Ki = 1.2 * Ku / Tu; pid.Kd = 0.075 * Ku * Tu; }这段自整定代码需要配合信号采集,实际操作中发现,对于温控系统,振荡周期通常在几十秒量级,所以采样间隔不能太短。有个坑要注意:自整定过程中需要断开积分项,否则会影响振荡检测。
基于STM32开发的PID自整定和PID温控和PWM输出程序源码,采用反馈法进行PID参数自动整定,得出系统临界值比例增益,自动计算调节,使系统进入正常状态。 程序源码注释详细
最后看PWM配置,这里用TIM3的通道1输出,关键配置:
htim3.Instance = TIM3; htim3.Init.Prescaler = 84-1; // 84MHz/84 = 1MHz htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = 1000-1; // 1MHz/1000 = 1kHz PWM htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);这里把PWM频率设置在1kHz,既避免高频噪声,又能满足大多数加热元件的响应需求。实际调试时发现,对于继电器控制,需要把频率降到10Hz以下,否则会缩短触点寿命。
整套系统跑起来后,实测从25℃升温到100℃的曲线,超调控制在2℃以内,稳定时间约3分钟。有个小技巧:在温差较大时适当提高PWM上限,温差小时自动降低,能显著加快响应速度。代码里output_max这个参数就是干这个的,可以根据当前误差动态调整。
