BD网盘链接:
链接:https://pan.baidu.com/s/1L22HHHCdJ5PvelaUS_tlxQ?pwd=vtik
提取码:vtik
1.PID温控系统是一种常用的控制系统,用于实现对温度变量的精确控制。PID算法根据当前的温度误差以及误差的变化率,计算一个控制信号,用于调节加热器的输出。以下是PID算法的三个主要组成部分:
①比例(Proportional)控制:比例控制是根据当前的温度误差来计算控制信号。具体而言,通过将设定温度与实际温度之间的差异称为误差,然后将误差乘以一个比例增益参数,得到一个修正值。这个修正值与控制设备的输出信号相加,以调整温度控制。
②积分(Integral)控制:积分控制用于处理长期的温度误差。它通过对温度误差进行积分来计算一个积分误差。积分误差乘以积分增益参数,并且在一段时间内进行积累,得到一个修正值。积分控制可以帮助消除持续的稳态误差,使系统更快地达到设定温度。
③微分(Derivative)控制:微分控制用于处理温度变化的速率。它通过计算温度误差的变化率,即误差的导数,得到一个微分值。微分值乘以一个微分增益参数,用于调整修正值。微分控制可以帮助系统更快地响应温度变化,以防止过冲。
通过结合比例、积分和微分部分的修正值,PID控制算法可以计算出最终的控制信号。这个控制信号会被传递给加热器,以控制温度的变化。
2.本实验用到了CW32-48大学计划开发板OK、温控实验模块及Keil5开发环境。
CW32-48大学计划开发板OK
温控实验模块
温控模块电路原理图
3.PID控制算法的具体原理可参考以下链接中的文章
1)https://zhuanlan.zhihu.com/p/39573490
2)https://zhuanlan.zhihu.com/p/347372624
3)https://zhuanlan.zhihu.com/p/41962512
利用热敏电阻采集温度的原理及方法可参考往期文章及视频。
4.核心代码
mian.c: #include "config.h" unsigned char face = 0; //界面变量 unsigned char face_brush = 0; //界面刷新频率控制 void InitSystem(void) { RCC_Configuration(); //时钟配置 ADC_Configuration(); //ADC采集通道配置,采集NTC热敏电阻电压 PID_Configuration(); //PID参数配置 GPIO_KEYS_Configuration(); //按键GPIO配置 PWM_Init(); //两路PWM输出初始化 Lcd_Init(); //TFT屏幕初始化 BTIM_Init(); //定时器初始化 } void Interface(void) //人机交互界面 { if ( face_brush > 200 ) //200ms刷新一次 { face_brush = 0; switch(face) { case 0://显示PV和SV,该界面下,可以设定SV TFTSHOW_STRING_HEADLINE(0,0," PID Control "); TFTSHOW_STRING(2,0,"REAL_Temper(℃):"); TFTSHOW_STRING(4,0," P V: "); TFTSHOW_FLOAT_NUMBER(4,8,pid.Pv); TFTSHOW_STRING(6,0,"SET_Temper(℃):"); TFTSHOW_STRING(8,0," S V: "); TFTSHOW_FLOAT_NUMBER(8,8,pid.set_Sv); break; case 1://该界面下,可以设定P参数 TFTSHOW_STRING_HEADLINE(0,0," PID Control "); TFTSHOW_STRING(2,0,"SET PID Control:"); TFTSHOW_STRING(4,0," P : "); TFTSHOW_INT_NUMBER(4,8,pid.set_Kp); break; case 2://该界面下,可以设定I参数 TFTSHOW_STRING_HEADLINE(0,0," PID Control "); TFTSHOW_STRING(2,0,"SET PID Control:"); TFTSHOW_STRING(4,0," I : "); TFTSHOW_FLOAT_NUMBER(4,8,pid.set_Ki); break; case 3://该界面下,可以设定D参数 TFTSHOW_STRING_HEADLINE(0,0," PID Control "); TFTSHOW_STRING(2,0,"SET PID Control:"); TFTSHOW_STRING(4,0," D : "); TFTSHOW_INT_NUMBER(4,8,pid.set_Kd); break; case 4://该界面下,可以设定Out0,即修正值 TFTSHOW_STRING_HEADLINE(0,0," PID Control "); TFTSHOW_STRING(2,0,"SET PID Control:"); TFTSHOW_STRING(4,0," OUT0 : "); TFTSHOW_INT_NUMBER(4,10,pid.set_Out0); break; } } } int main() //主函数 { InitSystem(); //系统初始化 while(1) { PID_Calc(); //PID运算 Interface(); //人机交互界面 Keys_Function(); //按键控制 } } pwm.c: #include "pwm.h" void PWM_Init(void) { RCC_APBPeriphClk_Enable1(RCC_APB1_PERIPH_GTIM2,ENABLE); //使能GTIM2时钟 __RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟 PA01_AFx_GTIM2CH2(); //打开PWM输出通道 PA02_AFx_GTIM2CH3(); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.IT = GPIO_IT_NONE; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出模式 GPIO_InitStruct.Pins = GPIO_PIN_1|GPIO_PIN_2; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_Init(CW_GPIOA, &GPIO_InitStruct); GTIM_InitTypeDef GTIM_Initstructure; //通用定时器 GTIM_Initstructure.Mode=GTIM_MODE_TIME; //计数模式 GTIM_Initstructure.OneShotMode=GTIM_COUNT_CONTINUE; //连续计数 GTIM_Initstructure.Prescaler=GTIM_PRESCALER_DIV64; //预分频 GTIM_Initstructure.ReloadValue=2000-1; //ARR,计数重载周期2000 GTIM_Initstructure.ToggleOutState=DISABLE; GTIM_TimeBaseInit(CW_GTIM2,>IM_Initstructure); GTIM_OCInit(CW_GTIM2,GTIM_CHANNEL3,GTIM_OC_OUTPUT_PWM_LOW); //GTIM2输出比较,CH3、CH2 GTIM_OCInit(CW_GTIM2,GTIM_CHANNEL2,GTIM_OC_OUTPUT_PWM_LOW); //有效占空比为低电平 GTIM_Cmd(CW_GTIM2,ENABLE); //使能GTIM2 } void PWM1_Output(uint32_t value) { GTIM_SetCompare3(CW_GTIM2,value); //设置GTIM2通道3的CCR } void PWM2_Output(uint32_t value) { GTIM_SetCompare2(CW_GTIM2,value); //设置GTIM2通道2的CCR } void PWM_ALL_Output(uint32_t value) //PWM1、2同步输出 { PWM1_Output(value); PWM2_Output(value); } pid.c: #include "pid.h" PID pid; //定义PID结构体变量pid void PID_Configuration(void) //PID参数初始化配置 { pid.Sv = 55; pid.Kp = 350; //比例系数 pid.Ki = 10; //积分系数 pid.Kd = 38; //微分系数 pid.Ek_1 = 0; //上一次偏差 pid.T = 400; //PID计算周期 pid.cnt = 0; pid.cycle = 2000; //PWM周期 pid.Out0 = 500; //PID修正值 pid.set_Sv = pid.Sv; pid.set_Kp = pid.Kp; pid.set_Ki = pid.Ki; pid.set_Kd = pid.Kd; pid.set_Out0 = pid.Out0; } float Get_Pv(void) //Pv意为当前测量值,即当前温度 { return Get_Temperture(); } void PID_Calc(void) //PID算法 { float Pout,Iout,Dout; float out; if ( pid.cnt > pid.T ) //控制计算周期 { pid.cnt = 0; pid.Pv = Get_Pv(); pid.Ek = pid.Sv - pid.Pv; //计算偏差 pid.SumEk += pid.Ek; //偏差累积 Pout = pid.Kp * pid.Ek; //比例控制 Dout = pid.Kd * (pid.Ek - pid.Ek_1); //微分控制 if(pid.Pv>(pid.Sv-1)) //当测量值非常接近目标值的时候加入积分控制 { Iout = pid.Ki * pid.SumEk; //积分控制 out = Pout + Iout + Dout + pid.Out0; } else out = Pout + Dout + pid.Out0; //测量值距离目标值较远时只使用PD控制 if ( out > pid.cycle ) pid.Out = pid.cycle; //限幅 else if ( out < 0 ) pid.Out = 0; else pid.Out = out; PWM_ALL_Output(pid.Out); //控制PWM输出 pid.Ek_1 = pid.Ek; //进行下一次PID运算之前,将本次偏差变为上次偏差 } }
5.实验最终现象
来源:CW32生态社区
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。