一个实用的单片机PID方式控温实例§1:基本情况本例中控制对象是一款小型专用工业烘箱要求恒温范围:室温--300°C恒温精度:±1C(其它指标略)§2:控制器硬件(参考原理图)① .单片机 采用(C51系列)TI公司的MCS1210Y4(内部有8通道24位AD转换器) 串行口编程② .前向通道 温度信号(来自烘箱的Ptl00电阻信号)经INA118放大送入AD 通道 CH0③ .后向通道I/O 口驱动光耦MOC-3061,再驱动大功率双向可控硅输出④ .键盘up递增按钮和down递减按钮,设置目标温度⑤ .LED(3位)显示温度值(软件切换显示目标温度或采样温度)⑥ .电源+5V单电源§3:软件介绍(由C语言编写)① .流程采样当前温度-->PID运算-->PWM (占空比式)输出② .温度采样采样周期是一个很重要的参数其确定取决于烘箱的固有响应特性参数(比如纯滞后时间0以及响应时间常数T)一般值在4--20秒之间(例中取16秒)③.PID运算每采样一次之后进行一次PID运算,得到一个输出量,供输出函数调用. 为了下面叙述方便先定义几个变量定义:T_target 表示目标温度T_real 表示当前温度T_diff 表示当前温差 并且 T_diff=T_target-T_realPID 运算表达式如下PWM_OUT=P_OUT+I_OUT+D_OUT+P_H;(求代数和)其中 P_OUT=KP*(T_diff) 称为比例项, KP 是比例系数,比例项的作用是纠正偏 差.比例项输出等于比例系数乘当前温差(原理图)I_OUT=KI*Y(diff)称为积分项,KI是积分系数,积分项用于消除系统稳态误差 Y(diff)含义是由当前算起前面N次采样温差的和(例中N取20)D-_OUT=KD*Adiff 称为微分项,KD是微分系数, 微分项用于减小系统超调量,增加系统稳定性.(Adiff=当前温差-上次温差)P_H=KC*( T_target)称为维持功率项,达温后(其它项均趋于 0)此项起抵消散热维持温度的作用,可 增加系统稳定性.KC是维持功率系数如果约定满功率值为100,停止输出功率值为0那末PWM_OUT的取值范围就确 定为0--100主要是为了后面编制输出函数时方便简明,直接调用PWM_OUT作为输出占空比的百分数)后面整定系数时就要兼顾 PWM_OUT 的取值范围§4.源程序(部分)#define KP 3.0〃比例系数#define KI 0.3〃积分系数#define KD 200.0〃微分系数#define KC 0.1〃维持功率系数#define T_c 16//采样周期(单位:秒)sbit pid_port=P3A5; 〃控制输出端口float T_target=0; //目标温度float T_real=0; //当前温度float PWM=0;//输出控制量 1-100bit read_AD_enable=0; //PID 运算允许标志位//T0 定时器初始化void Timer0_Init(){ TMOD|=0x01; TF0 =0; TR0 =1;IE |=0x02;}//读取 AD 转换值并刻度void read_AD(void){int delta_ad;unsigned char ad[3]; ad[0]=ADRESH; ad[1]=ADRESM; ad[2]=ADRESL;delta_ad=ad[0]*0xl00+ad[l]-0x23cb;//???if(delta_ad<=0)delta_ad=0;//* PID 运算函数void pid(void){static float diff[20]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //静态变量 第 n 次温static float sum_diff=0; //》(diff 温差求和 积分static int curr_=0; //20 次采样记数float p_out,i_out,d_out,temp;float pwm_0; //维持功率temp=diff[curr_]; //记录上一次温差 供微分用if(curr_+1>=20)curr_=0; //20 次采样else curr_+=1;sum_diff-=diff[curr_]; //减去最老的一次温差值diff[curr_]=T_target-T_real; //当前温差sum_diff+=diff[curr_]; //加上最新的一次温差值,即当前p_out=KP*diff[curr_]; //比例项输出 比例 x 当前温差i_out=KI*sum_diff; //积分项输出 积分系数 x 积分值d_out=KD*(diff[curr_]-temp); //微分项输出 微分系数 x 微分值pwm_0=KC*T_target; //维持功率项 维持系数 x 设定温度if(i_out>100)i_out=100; //积分分离 防止积分饱和if(i_out<-100)i_out=-100;PWM=p_out+i_out+d_out+pwm_0; //总输出量if(PWM<0)PWM=0; //小于 0 时为 0else if(PWM>=100)PWM=100; //大于 100 时为 100}//输出函数void PWM_OUT(float PWM){static unsigned char t=l; 〃t=(l一100)周期为 4 秒 静态变量 40ms 调用一次 即 4 秒分成100份unsigned char limit; //pid_value 输出百分比 limit=(unsigned char)PWM; //强制转换 if(t<=limit)pid_port=0; //加热else pid_port=1; //停止加热t++;if(t>100)t=1;}//T0 中断服务程序void Timer0_ISR() interrupt 1 using 1{static unsigned int x=0;TH0=(28672)>>8; // 11.0592MHz,interval 40mSTL0=(28672+20)&0xff; // +20 compensate 补尝 TF0=0;if((x++)>(T_c*25)){x=0;read_AD_enable=1; //PID 运算允许标志位}PWM_OUT(PWM); //可控硅输出}主程序 */void main (void){// 程序初始化(略)while (1){if(read_AD_enable==1) //PID 运算允许标志位 每 40ms 运算一次 PID 读一次 AD{read_AD_enable=0;read_AD(); //读 ADpid(); //运算 PID}}}§5.参数的整定(这大概是最困难而且费时的事)① T_c (采样周期)单从控制精度上考虑,T_c当然是越小越好,但过小占用处理器的运算时间会加长,从而导致系统成本增加,例中主要考虑被控对象的时间常数t和纯滞后时间e,下面用扩充响应曲线法求取t及e.a. 使系统处于开环,手动直接在被控对象输入端加一个阶跃信号 描绘出响应曲线(如图)b. 在响应曲线的最大斜率处作切线即可得到e和tc. 选择t_c,当e占主导地位时,应尽量使e为T_c的整倍数如e=2T_c或e=T_c,当e比T_c小得多时,可取T_c= t/10② KP(比例系数)先定为 1,做一个简单的只有比例调节的函数,使系统工作于闭环,T_target定在150°C(恒温范围的中间值)纪录响应曲线,如果曲线没有出现振铃则可能是KP偏小,调整KP,使曲线出现轻微振铃.反之振铃幅度过大,则可能是KP偏大.③KC(维持功率系数)借用§5.②中的曲线(如下图) 根据图中所示 由 KC*150=PWM_H 导出 KC④ KI(积分系数)KI 的选取与 T_c 和积分时长有关,回过头看源程序 PID 运算函数中有一个静态数 组,记录过去 t 时刻到当前的各个温度采样值.每一次采样之后都要更新(淘汰掉 最早的采样值,加入当前采样值) .积分时长=T_c*采样个数(即数组大小[SIZE])积分时长的选择一般使它大于纯滞后时间0即可,反过来通过T_c和0可以确定数组的大小[SIZE],接着套用下式就可以大致确定KI 了.KI*SIZE=10%*满功率值⑤ KD(微分系数)Kd 取值大致为纯滞后时间 0(比如 0=200 秒, KD 先取 200,后根据实验在调整)以上的步骤可能要反复进行多次,各个系数也要作相应的调整.这里我介绍一个站长编写的并已成功应用的调试软件HUANENG.EXE专用于串行口数据分析绘图,可同时描绘8条曲线,(VC编写)使用非常简单, 只需在上面源程序中pid()函数末尾加上几行数据打印输出的语句比如:printf("CH0=%x\nCH1=%x",T_real,PWM);通过串行口接到计算机,打开该软件即可.界面如下图统调中记住:增大KP,会加快系统响应,减小静差,但超调量增大,稳定性变差. 减小KI,削弱积分作用,系统超调量变小,但消除静差的时间长.增大KD,可减小超调量,稳定性好,但系统响应速度变慢.。