万事开头难。 刚接触PID的时候调得焦头烂额,当把程序调出来后,发现其实挺简单。 其实也都是因为网上的发出来的例程说得都太复杂,或者是发的资料没全还藏了部分(个人感觉)。 闲话少说直接上资料: 先前发的一些网上的资料,程序原理与实现上主要参考了“PID控制算法的C语言实现.(绝对的好东西)”。 https://www.amobbs.com/thread-5685071-1-1.html 本次PID主要是通过固态继电器控制加热片进行加热,温度探测使用的DS18B20,稳定后在0.5度范围内(然并卵)。 程序如下: 定义结构体 typedef struct { float SetSpeed; //定义设定值 float ActualSpeed; //定义实际值 float err; //定义偏差值 float err_next; //定义上一个偏差值 float err_last; //定义上上一个的偏差值 float Kp; //定义比例系数 float Ki; //定义积分系数 float Kd; //定义微分系数 float voltage; //定义电压值(控制执行器的变量) float integral; //定义积分值 float umax; //正极限位置 float umin; //负极限位置 }_PID_T; _PID_T pid; /* PID初始化 */ void PID_init(void) { pid.SetSpeed = 0.0; pid.ActualSpeed = 0.0; pid.err = 0.0; pid.err_last = 0.0; pid.err_next = 0.0; pid.Kp = 1.0; //比例调节 pid.Ki = 0.01; //积分调节 pid.Kd = 0.0; //微分调节 } /* 取绝对值 */ float Abs(float val) { if(val<0) { val = -val; } return val; } /* PID计算 */ float PIDCalc(float speed) { UINT8 index=0; index = index; //------------------------------------------------------------------------------------------------------------// //积分分离式 pid.SetSpeed = speed; pid.ActualSpeed = (float)DS18B20Temp()/10; pid.err = pid.SetSpeed - pid.ActualSpeed; if(Abs(pid.err)>3) //温度差值超过3度,不加入积分环节 { index = 0; } else { index = 1; pid.integral += pid.err; } pid.voltage = pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last); //算法具体实现过程 pid.err_last = pid.err; // pid.ActualSpeed = pid.voltage*1.0; if(pid.voltage>50) {//设置一个PWM量程上限值 pid.voltage = 50; } return pid.ActualSpeed; } 定时器5中进行固态继电器的控制 void TIM5_IRQHandler(void) { static UINT8 heatTime=0; if(TIM5->SR&TIM_IT_Update)//溢出中断 { TIM5->SR &= ~TIM_IT_Update ;//清除中断标志位 if(pid.ActualSpeed<40) {//调试时超过40度关闭加热,避免烧坏加热片 if(pid.voltage>0) { if(++heatTime>50) { heatTime = 0; } ((heatTime*10)<(UINT8)(pid.voltage*10))?(HEAT_CTL = ON_OP):(HEAT_CTL = OFF_OP); } else { heatTime = 0; HEAT_CTL = OFF_OP; } } else { HEAT_CTL = OFF_OP; } } } 下面这个函数是在主函数里面调用的,我这里是250ms进行一次PID。 void DebugOut(void) { UINT16 cnt=0; cnt = cnt; if(timerPara.timeDebug > 250) { timerPara.timeDebug = 0; if(PidEn) { PIDCalc(35.0); } else { pid.ActualSpeed = (float)DS18B20Temp()/10; pid.voltage=0; } printf("\r\nTEMPER=%f, U(t)=%f", pid.ActualSpeed,pid.voltage); } } 这次调试主要觉得有两个难点: 1、程序算法怎么实现。参考上面的“PID控制算法的C语言实现.(绝对的好东西)”,基本就明白怎么写了,或者抄一下,网上的源码很多。 2、PID输出怎么用,U(t)是什么?算法程序有了,但是怎么拿来控制加热片进行加热?我反正是在网上找了好多圈,有写PID的基本都没说到这点,都只是说程序怎么实现,或者就是一句话,输出就是PWM。 说的其实也没错,只是不够明白。这里说说我的做法: 给输出设置一个上限,或者把输出当做PWM量程,比如50等分,实测中,实际温度与设置温度越来越靠近,PID的输出值会越来越小。那这个值就可以拿来做PWM方波的高电平,用于启动加热(参考定时器里的做法)。 3、参数的整定。网上的做法也很简单,一个参数一个参数调整,单个参数按一个方向进行调整。 a、把微积分的值设置为0,只调节比例参数,我这里调节到加热的时候不过冲,但是加热会比较慢。 b、调节积分参数,让稳定值会比较趋近到设置值。 |
2020-09-24
最新推荐文章于 2024-07-12 10:31:40 发布