基于STM32开发的PID自整定和PID温控和PWM输出程序源码, 采用反馈法进行PID参数自动整定

张开发
2026/6/10 7:13:11 15 分钟阅读
基于STM32开发的PID自整定和PID温控和PWM输出程序源码, 采用反馈法进行PID参数自动整定
基于STM32开发的PID自整定和PID温控和PWM输出程序源码 采用反馈法进行PID参数自动整定得出系统临界值比例增益自动计算调节使系统进入正常状态 程序源码注释详细撸起袖子开干嵌入式温控系统PID自整定这玩意儿听着玄乎实操起来倒是有迹可循。咱们今天拿STM32F103开刀手把手整出个带参数自整定的智能温控系统重点聊聊怎么让单片机自己找合适的PID参数。先上硬菜——PWM输出这part。用TIM1的通道1输出20kHz高频PWM避免人耳听到的噪声// PWM初始化骚操作 void PWM_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); TIM_TimeBaseStructure.TIM_Period 399; //40MHz/400100kHz TIM_TimeBaseStructure.TIM_Prescaler 0; TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM1, TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 0; //初始占空比0% TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM1, TIM_OCInitStructure); TIM_CtrlPWMOutputs(TIM1, ENABLE); TIM_Cmd(TIM1, ENABLE); }注意TIM_CtrlPWMOutputs这个函数用高级定时器时必须开启主输出否则PWM没信号这个坑我当年踩过。基于STM32开发的PID自整定和PID温控和PWM输出程序源码 采用反馈法进行PID参数自动整定得出系统临界值比例增益自动计算调节使系统进入正常状态 程序源码注释详细温度采样得稳NTC热敏电阻接在ADC1通道5。上点软件滤波的私货#define SAMPLE_TIMES 8 //8次采样去极值平均 float Get_Temperature(void) { uint16_t buf[SAMPLE_TIMES]; uint32_t sum 0; for(uint8_t i0; iSAMPLE_TIMES; i){ buf[i] ADC_GetValue(ADC_Channel_5); Delay_us(200); //等采样保持电容充电 } //冒泡排序去极值 for(uint8_t i0; iSAMPLE_TIMES-1; i){ for(uint8_t ji1; jSAMPLE_TIMES; j){ if(buf[i] buf[j]){ uint16_t temp buf[i]; buf[i] buf[j]; buf[j] temp; } } } //取中间4个值平均 for(uint8_t i2; iSAMPLE_TIMES-2; i){ sum buf[i]; } return (sum*3.3/4096); //转电压值 }这个滤波算法比单纯平均靠谱实测能抗住电源毛刺干扰。注意ADC校准后才有精度上电时要执行ADC_Calibration()。重头戏来了——PID自整定算法。采用临界比例法自动寻找参数typedef struct { float Kp, Ki, Kd; float integral_max; //积分限幅 float output_max; //输出限幅 float last_error; float integral; } PID_TypeDef; void PID_AutoTune(PID_TypeDef* pid) { float Ku, Tu; float output 0; uint8_t oscillation_count 0; float last_temp Get_Temperature(); //进入临界振荡检测 pid-Kp 1.0; //初始比例系数 while(1){ float current_temp Get_Temperature(); float error target_temp - current_temp; //纯比例控制 output pid-Kp * error; output constrain(output, 0, 100); //限制在0-100% Set_PWM(output); //检测过零振荡 if((last_temp target_temp current_temp target_temp) || (last_temp target_temp current_temp target_temp)) { oscillation_count; if(oscillation_count 4) { //连续4次过零 Ku pid-Kp; Tu (HAL_GetTick() - start_time) / 1000.0 / 2; //计算振荡周期 break; } } last_temp current_temp; Delay_ms(100); pid-Kp * 1.2; //逐步增大比例系数 } //根据Ziegler-Nichols公式计算PID参数 pid-Kp 0.6 * Ku; pid-Ki 1.2 * Ku / Tu; pid-Kd 0.075 * Ku * Tu; }这个自整定过程像极了老电工调参——慢慢拧大比例系数直到系统开始震荡。检测到温度曲线在设定值附近连续震荡时记下此时的临界增益Ku和振荡周期Tu。最后套用经典公式算出PID三兄弟的数值。实际跑起来要注意几个坑加热器有热惯性采样周期别太短建议500ms-1s自整定前确保PWM输出与加热功率线性关系良好遇到超调别慌给积分项加上限幅就能稳住最后上个完整PID计算函数float PID_Calculate(PID_TypeDef* pid, float setpoint, float input) { float error setpoint - input; pid-integral error * dt; //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 d_error (error - pid-last_error) / dt; pid-last_error error; float output pid-Kp * error pid-Ki * pid-integral pid-Kd * d_error; return constrain(output, 0, pid-output_max); }这个实现加了微分先行和积分限幅实测控制烤箱能稳在±0.5℃内。完整工程里还埋了个彩蛋——用FFT分析温度波动曲线自动优化控制参数这就不展开说了源码注释里写得明明白白。

更多文章