课程设计(综合实验)报告( 2011-- 2012 年度第二学期)名称:过程计算机控制系统题目:DDC单回路PID闭环控制系统的设计及实时仿真院系:控制与计算机工程学院班级:学号:学生:指导教师:朱耀春设计周数:一周成绩:日期:2012 年 6 月20 日一、 课程设计的目的与要求1.设计目的在计算机控制系统课程学习的基础上,加强学生的实际动手能力,通过对DDC 直接数字闭环控制的仿真加深对课程容的理解。
2.设计要求本次课程设计通过多人合作完成DDC 直接数字闭环控制的仿真设计,学会A/D 、D/A 转换模块的使用。
通过手动编写PID 运算式掌握数字PID 控制器的设计与整定的方法,并做出模拟计算机对象飞升特性曲线,熟练掌握DDC 单回路控制程序编制及调试方法。
二、 设计正文1.设计思想本课程设计利用Turboc2.1开发环境,通过手动编写C 语言程序完成PID 控制器的设计,A/D 、D/A 转换,绘出PID 阶跃响应曲线与被控对象动态特性曲线。
整个设计程序模块包含了PID 配置模块,PLCD-780定时采样、定时输出模块,PID 手/自动切换模块(按键控制)及绘图显示模块。
设计中,通过设定合理的PID 参数,控制PLCD-780完成模拟计算机所搭接二阶惯性环节数据的采集,并通过绘图程序获得对象阶跃响应曲线。
2. 设计步骤(1)前期准备工作(1.1)配备微型计算机一台,系统软件Windows 98或DOS (不使用无直接I/O 能力的NT 或XP 系统), 装Turbo C 2.0/3.0集成开发环境软件;(1.2)配备模拟计算机一台(XMN-1型), 通用数据采集控制板一块(PLCD-780型);(1.3)复习Turboc2.0并参照说明书学习PLCD-780的使用(2) PID 的设计(2.1)PID 的离散化理想微分PID 算法的传递函数形式为:⎪⎪⎭⎫⎝⎛++=s T s T K s G d i p 11)(采用向后差分法对上式进行离散,得出其差分方程形式为: u[k]=u[k-1]+q0*e[2]+q1*e[1]+q2*e[0];其中各项系数为:q0=kp*(1+T/Ti+Td/T);q1=-kp*(1+2*Td/T);q2=kp*Td/T;实际微分PID 算法的传递函数形式为:⎪⎪⎭⎫ ⎝⎛+++=s T s T s T K s G d i f p 111)( 采用向后差分法对上式进行离散化,写成差分方程的形式为:u[k]=c0*(Δu[k-1])+c1*e[k]+c2*e[k-1]+c3*e[k-2]+u[k-1];其中各项系数为:c0=Tf/(T+Tf);c1=kp*T/(T+Tf)*(1+T/Ti+Td/T);c2=-kp*T/(T+Tf)*(1+2*Td/T);c3=kp*Td/(T+Tf);(2.2)数字PID 算法的改进○1积分分离算法 积分分离算法通过控制PID 输入偏差e 达到优化目的,当偏差较大时停止积分作用,只有当偏差较小时才投入积分,算法如下表示:当|e(k)|>β时,采用PD 控制;当|e(k)|<β时, 采用PID 控制;β的值根据具体对象及要求确定。
○2抗积分饱和算法 抗积分饱和算法依据控制系统最终的控制输出量u 达到优化目的,当控制量u 较大且超出执行机构与A/D 转换围时,控制器停止积分作用,保证输出超限时不积分;○3带死区的数字PID 算法 在实际控制系统中,计算机控制为了避免控制动作过于频繁,以消除系统振荡,就会采用带死区的PID 算法。
该算法是在原PID 算法前加一个不灵敏区来实现,即当|e(k)|>C 时,|e(k)|=|e(k)|;当|e(k)|<C 时,|e(k)|=0其中C 代表不灵敏区值;(2.3) 手动/自动双向无扰切换自动切手动:系统处于自动时,手操器实时跟踪自动PID 调节器的输出,切换瞬间由于手操器部电路起保持作用,使得切换没有扰动产生,此时对象处于手操器的开环控制,调节器跟踪手操器的输出。
手动切自动:手动到自动的切换过程主要由计算机软件实现,一方面PID 调节器获得手操器输出,同时软件使得算法中的Δu[k-1])、e[k]、e[k-1]、e[k-2]等历史状态清零。
程序过设置键盘,使的按下手动键H 时,系统处于手动状态,按下自动键A 时,系统处于自动状态。
(3)硬件二阶惯性环节搭建利用模拟计算机中的电容电阻及运算放大器,搭接二阶惯性环节,仿真一个被控对象。
其传递函数为2)1()(+=Ts K s G ,硬件电路如下:图中各元件参数如下:R3=R2=510K ;R1=R4=R5=R6=R7=1M ;C1=C2=C=4.7uF ;则可得:K=(R5/R1)*(R6/R4)=1T1=T2=R5*C1=R6*C2=1000000*0.0000047=4.7s所以G (s )=1/(4.7s+1)*(4.7s+1)搭建好硬件电路后,将PLCD-780插入IPC 机箱插槽,用导线将PLCD-780中的A/D 、D/A 、电源的接线端子与所搭二阶惯性环节的输出、输入端口及机箱上的电源连接,组成一个完整的PID 闭环控制系统,为通信做好准备。
(4)PID 参数的整定8.75,6.6*1.2,6.6*04运用过程控制中PID 参数的工程整定方法,运用衰减曲线法对PID 参数进行整定。
在matlab 中,设置PID 参数为Td=0,Ti=∞,设置合适的比例带使得对象闭环阶跃响应曲线衰减率为0.9,从而确定PID 的整定参数为:P=0.8δ,Ti=1.2t r , Td=0.4t r ;matlab 中对象响应曲线为:0.70.60.50.40.30.20.1由曲线可得PID参数为:P=0.8 =0.8×5=4,Ti=1.2t r=1.2×10=12,Td=0.4t r=0.4×10=4(5)实验结果输出通过在程序中编写相应的绘图模块子程序,在需要画图时调用相应的子程序实现曲线的绘制。
同时在程序中,本小组采用按键实现了PID手自动切换,理想PID与实际PID的切换,以及在手自动状态下由按键改变PID参数,使得调节方式更加的灵活。
3.设计结果(1)PID阶跃响应曲线调用程序,向PID模块输入一个阶跃信号,绘出PID阶跃响应曲线如下:(1.1)理想PID阶跃响应图:(1.2)实际PID阶跃响应图:(2)被控对象(惯性环节)阶跃响应曲线上图通过D/A输出一个1伏左右的信号输入模拟的被控对象(惯性环节),A/D采集对象的输入信号及其响应,再使D/A输出一个幅度为2伏左右的阶跃信号,同时采集输入输出信号。
然后,D/A再反向在输出一个幅度为2伏左右负的阶跃信号,同时采集输入输出信号,得出仿真对象飞升特性曲线。
程序中,通过按键实现模拟对象输入信号的加减。
当按下H按键时,且按下U键时,D/A输出一个1伏阶跃信号,再次按下按键时阶跃信号累加。
每次按下D键时,D/A输出的阶跃信号递减1。
(3)设定值r、控制量u和被控对象输出y的响应曲线:4.程序清单/*---------------头文件定义---------------*/#include<conio.h>#include<graphics.h>#include <stdio.h>#include <dos.h>/*---------------定义绘图坐标---------------*/#define ox 8 /*-----原点横坐标-------*/ #define oy 440 /*------原点纵坐标------*/#define xx 620 /*------x轴顶点横坐标--*/#define xy 440 /*-----x轴顶点纵坐标---*/#define lenx 580#define leny 400#define yx 8 /*-----y轴顶点横坐标----*/#define yy 15 /*------y轴顶点纵坐标----*//*-----------------定义绘图区域----------------*/#define left 20#define top 20#define right 620#define bottom 460/*----------------坐标轴注释---------------------*/#define xtext1x 450#define xtext1y 450#define ytext1x 10#define ytext1y 60#define xtext2x 610#define xtext2y 450#define ytext2x 10#define ytext2y 20/*-------------------------理想PID运算式--------------------------*/ float lxpid(float kp,float td,float ti,float e[3],float u1){int t=1;float u;float q0=kp*(1+t/ti+td/t);float q1=-kp*(1+2*td/t);float q2=kp*td/t;u=q0*e[0]+q1*e[1]+q2*e[2]+u1;return u;}/*-------------------------实际PID运算式--------------------------*/ float sjpid(float kp,float tf,float td,float ti,float e[3],float du1,float u1){int t=1,k=1000;float u2;float c1=tf/(t+tf);float c2=kp*t*(1+t/ti+td/t)/(t+tf);float c3=-kp*t*(1+2*td/t)/(t+tf);float c4=kp*td/(t+tf);u2=c1*du1+c2*e[0]+c3*e[1]+c4*e[2]+u1;return u2;}/*-------------------------绘图初始化--------------------------*/ void Initial_Sys(void){int GraphDriver;int GraphMode;detectgraph(&GraphDriver,&GraphMode);initgraph(&GraphDriver,&GraphMode,"C:\\TC201E\\BGI");cleardevice();}/*-------------------------绘制坐标系------------------*/void DrawAxis(void){int i;setbkcolor(15);setcolor(5);line(ox,oy,xx,xy); /*x_axis*/line(xx-5,xy-5,xx,xy);line(xx,xy,xx-5,xy+5);line(ox,oy,yx,yy); /*y_axis*/line(yx-5,yy+10,yx,yy);line(yx+5,yy+10,yx,yy);for(i=0;i<51;i++){line(ox+10*i,oy,ox+10*i,oy-10);line(ox+10*i+5,oy,ox+10*i+5,oy-5); }for(i=1;i<=8;i++)line(ox,oy-50*i,ox+10,oy-50*i);outtextxy(ox+50*0-7,oy+20,"0");outtextxy(ox+50*1-7,oy+20,"5");outtextxy(ox+50*2-7,oy+20,"10");outtextxy(ox+50*3-7,oy+20,"15");outtextxy(ox+50*4-7,oy+20,"20");outtextxy(ox+50*5-7,oy+20,"25");outtextxy(ox+50*6-7,oy+20,"30");outtextxy(ox+50*7-7,oy+20,"35");outtextxy(ox+50*8-7,oy+20,"40");outtextxy(ox+50*9-7,oy+20,"45");outtextxy(ox+50*10-7,oy+20,"50");outtextxy(ox-10,oy-50*1,"1");outtextxy(ox-10,oy-50*2,"2");outtextxy(ox-10,oy-50*3,"3");outtextxy(ox-10,oy-50*4,"4");outtextxy(ox-10,oy-50*5,"5");outtextxy(ox-10,oy-50*6,"6");outtextxy(ox-10,oy-50*7,"7");outtextxy(ox-10,oy-50*8,"8");settextstyle(SMALL_FONT,HORIZ_DIR,5); outtextxy(xtext1x,xtext1y,"Time");settextstyle(SMALL_FONT,VERT_DIR,5);outtextxy(ytext1x,ytext1y,"The output (Response)");outtextxy(ytext2x,ytext2y,"U(t)\/V");}main(){float kp,ti,td,tf,e[3]={0},ee[3]={0},u[6]={0},au1=0;int r=1,k=1;Initial_Sys();DrawAxis();while(k<100){u[0]=lxpid(1,3.0,10,e,u[1]);e[0]=r;/*printf("%f\n",u[0]);*/u[3]=sjpid(1,5,3.0,10,ee,au1,u[4]);setcolor(5);line((k-1)*10,130-u[1]*100,k*10,130-u[1]*100);line(k*10,130-u[1]*100,k*10,130-u[0]*100);delay(10000);u[2]=u[1];u[1]=u[0];e[2]=e[1];e[1]=e[0];ee[0]=r;setcolor(3);line((k-1)*10,150-u[4]*100,k*10,150-u[4]*100);line(k*10,150-u[4]*100,k*10,150-u[3]*100);u[5]=u[4];u[4]=u[3];ee[2]=ee[1];ee[1]=ee[0];au1=u[4]-u[5];k++;}}/*---------------头文件定义---------------*/#include "stdio.h"#include "math.h"#include "graphics.h" /*for graph driver installing,only can be called in Turbo C*/#include "string.h"#include "dos.h"#include "bios.h"#include "conio.h" /*for interrupt program 头文件定义* /#include "stdlib.h"#include "io.h"/*--------------按键地址区定义--------------------*//*statements*/double key_ESC=0x011b; /*define can not suit the length of bioskey 键盘存定义*/ double key_E=0x1265;double key_A=0x1e61;double key_H=0x2368;double key_U=0x1675;double key_D=0x2064;double key_I=0x1769;double key_P=0x1970;double key_down=0x5000;double key_left=0x4b00;double key_right=0x4d00;double key_pgup=0x4900;double key_pgdown=0x5100;/*--------------PLCD780基址定义--------------------*/#define BASE 0x220 /*------------PCL812G need 16 addresses in a row,from 220H to 3F0H*/ #define REG 0/*---------------定义绘图坐标---------------*/#define ox 40 /*------------原点横坐标-------------*/#define oy 440 /*------------原点纵坐标------------*/#define xx 600 /*------------x轴顶点横坐标--------*/#define xy 440 /*------------x轴顶点纵坐标--------*/#define yx 40 /*------------y轴顶点横坐标--------*/#define yy 40 /*------------y轴顶点纵坐标---------*//*---------------PID参数定义---------------*/float Kp=1.0;float Ti=10.0;float Td=3.0;float Tf0=15.0;float Tf=0;float T=0.1; /*---------------采样时间------------*/float ad,e,pv0;float u=0.0;float pv=0.0;float sp=0.0;char A_H='H';int key=0;int time_counter=0; /*times of interrupt*/int cj_counter=0; /*sampling counter*/int Q_counter=800; /* 采集步长赋初始值*/int stepdata[800];int slopedata[800];int error[800];/*--------------函数声明-----------------*/void interrupt (*fadd1C)(void);void loop();float AD(unsigned char channal); /*A/D*/void DA(float pv1); /*D/A*/void interrupt INT_1C(void); /*8259,reset interrupt controller*/int scankey();float DelayAction(float y0);void PIDset(void);float PID(float sp1,float pv1,float Kp1,float Ti1,float Td1,float Tf1,char A_H1,float T1); float Object(float u1,float T1);void Initial_Sys(void); /*Initiate graph display*/void axis(void);void Drawline(int cj,float pv1,float sp1,float u1,float e1);/*主函数*/void main(void){for(i=0;i<500;i++){stepdata[i]=10;slopedata[i]=i;error[i]=0;}/* Set new INT_1C and save old */disable();fadd1C=getvect(0x1C);/*1C为定时器控制的软中断,平均一秒发生18.2次,即周期为55ms 中断程序*//* 开启中断服务*/setvect(0x1C,INT_1C);enable();axis();loop();}/*主函数结束下面为定时采值输出程序*/void loop(){ do{if((cj_counter*T)<(time_counter/18.2)){PIDset(); /*Introduction:Exit-E/ESC,A_H-A/H,Ideal/Parallel PID,sp-U/D*/u=PID(sp,pv,Kp,Ti,Td,Tf,A_H,T);/*DA(u); */pv=Object(u,T);/*ad=AD(O);*//*pv=DelayAction(u);*/e=error[cj_counter];Drawline(cj_counter,pv,sp,u,e);manu=0;if(Tf==0)printf("IdealPID,Mode:%c,sp=%.1f,pv=%2.1f,u=%.1f,error=%.1f,Kp=%.1f,Ti=%.1f,Td=%.1f\t\r",A_H,sp,pv,u,e, Kp,Ti,Td);else if(Tf>0)printf("Parallal,Mode:%c,sp=%.1f,pv=%2.1f,u=%.1f,error=%.1f,Kp=%.1f,Ti=%.1f,Td=%.1f\t\r",A_H,sp,pv,u,e,K p,Ti,Td);else printf("\t\tTf got a wrong value! Please exit and restart this program.\r");cj_counter++;}}while(cj_counter<500);disable();/* 恢复中断*/setvect(0x1C,fadd1C);enable();}/*D/A conversion program,0 to 4095 -- 0to +5*/float AD(unsigned char channal){float result=0;int i;unsigned char hb=0,lb=0,ok=0x10;/*12bit AD/high 4 bits and low 8 bits*/outportb(BASE+11,REG); /*软件程序触发*/delay(10); /*here,'delay(int ms)' is used long before,and we just need some time for hardware working*/outportb(BASE+10,channal); /*进行通道设置.选择通道0*/delay(10);outportb(BASE+12,0); /*触发A/D转换*/delay(10);do{ok=inportb(BASE+5);}while(ok&0x10);hb=inportb(BASE+5);delay(10);lb=inportb(BASE+4);result = lb + ((hb&0x0F)<<8); /*0 to 4095*/result=result*5/4096; /*0 V to +5V*/return result;}/*A/D conversion program,0 to +5 -- 0 to 4095*/void DA(float pv1){int temp,i;unsigned char hb,lb;if (pv1>5) /* make the output real */pv1=5;else if (pv1<0)pv1=0;temp=(int)(4095*pv1/5.0);hb=temp<<8;lb=temp-(hb<<8);outportb(BASE+4,lb); /* low 8 */delay(10);outportb(BASE+5,hb); /* high 4 */}void interrupt INT_1C(void){time_counter++;outportb(0x20,0x20);}/*键盘控制*/int scankey(void){int key0;key0=bioskey(1); /* function 1 returns 0 until a key is pressed */ if(key0!=0)key0=bioskey(0); /* function 0 returns the key that is waiting */ return key0;}/*DelayAction*//*tao=(int)(18.2*2) Delay action=2 seconds*/float DelayAction(float y0){float y_out;int cyc;y_out=y_old[36-1];for(cyc=1;cyc<36;cyc++)y_old[36-cyc]=y_old[36-cyc-1];y_old[0]=y0;return y_out;}/*PID 主程序*/void PIDset(void){key=scankey();if(A_H=='H'){if(key==key_up)Kp+=0.2;else if(key==key_down)Kp-=0.2;else if(key==key_left)Ti-=0.2;else if(key==key_right)Ti+=0.2;else if(key==key_pgup)Td+=0.2;else if(key==key_pgdown)Td-=0.2;else if(key==key_U)manu='+';else if(key==key_D)if(A_H=='A'){if(key==key_U)sp+=10;if(key==key_D)sp-=10;}if(key==key_E||key==key_ESC)exit(1);if(key==key_A)A_H='A';if(key==key_H)A_H='H';if(key==key_I)Tf=0;if(key==key_P)Tf=Tf0;}/*PID-default:IdealPID*/float PID(float sp1,float pv1,float Kp1,float Ti1,float Td1,float Tf1,char A_H1,float T1){float delta_u,u0,e,C1,C2,C3,C4;static float e1,e2,u1,delta_u1; /*here,u1 stands for the previous value of u*/if(Kp1<0) p rintf("Kp becomes a negative number,please restart.");else if(Ti1<0) printf("Ti becomes a negative number,please press restart.");else if(Td1<0) printf("Td becomes a negative number,please press restart.");.{C1=Tf1/(T1+Tf1);C2=Kp1*T1*(1+T1/Ti1+Td1/T1)/(T1+Tf1);C3=-Kp1*T1*(1+2*Td1/T1)/(T1+Tf1);C4=Kp1*Td1/(T1+Tf1);/*自动控制*/if(A_H1=='A'){e=sp1-pv1;delta_u=C1*delta_u1+C2*e+C3*e1+C4*e2; /*here,delta_u1 stands for the previous value of delta_u*/u0=u1+delta_u;e2=e1;e1=e;delta_u1=delta_u;u1=u0;error[cj_counter]=sp1-pv1;return u0;}/*手动控制*/else if(A_H1=='H'){if(manu=='+')u+=10;if(manu=='-')u-=10;sp1=pv1;u1=u;sp=pv1;e1=0;.e2=0;delta_u1=0;error[cj_counter]=sp1-pv1;return u;}}}/*object:二阶惯性环节*/float Object(float u1,float T1){/*G(s)=1/(4.7s+1)^2,K=1,Tp1=Tp2=4.7*/static float y1,y2;float y;y=1/(31.49+T1)*(-22.09*y2+53.58*y1+T1*u1);y2=y1;y1=y;return y;}/*显示与画图*//*初始化CRT*/void Initial_Sys(void){int GraphDriver;int GraphMode;detectgraph(&GraphDriver,&GraphMode);initgraph(&GraphDriver,&GraphMode,"C:\\TC201E\\BGI"); }/*draw basic coordinate axis*/void axis(void){int i;Initial_Sys();.setbkcolor(15); /*white0/black15*/setcolor(9); /*linght blue*/rectangle(10,20,630,470); /*zone of drawing*/line(ox,oy,xx,xy); /*axis and arrow*/line(xx-5,xy-5,xx,xy);line(xx,xy,xx-5,xy+5);line(ox,oy,yx,yy);line(yx-5,yy+5,yx,yy);line(yx+5,yy+5,yx,yy);settextstyle(2,1,5); /*Small font,vert,5 times bigger*/outtextxy(20,100,"The Output (Response)");outtextxy(20,40,"U(t)");settextstyle(2,0,5); /*Small font,horiz,5 times bigger*/outtextxy(300,455,"Time");outtextxy(590,455,"t/sec");setlinestyle(1,0,1); /*dot line,none,width*/for(i=1;i<4;i++) /*each inport starting position*/ line(ox,oy-100*i,ox+500,oy-100*i);outtextxy(ox-16,oy-100*0,"0");outtextxy(ox-16,oy-100*1,"1");outtextxy(ox-16,oy-100*2,"2");outtextxy(ox-16,oy-100*3,"3");for(i=1;i<=500/50;i++) /*scale*/{line(ox+50*i,oy,ox+50*i,oy-400); /*1/T=10,that is 10px=1sec*/line(ox+50*i-25,oy,ox+50*i-25,oy-10);}setlinestyle(0,0,1); /*real line,none,width*/outtextxy(ox-4+50,oy+2,"5");outtextxy(ox-8+100,oy+2,"10");.outtextxy(ox-8+150,oy+2,"15");outtextxy(ox-8+200,oy+2,"20");outtextxy(ox-8+250,oy+2,"25");outtextxy(ox-8+300,oy+2,"30");outtextxy(ox-8+350,oy+2,"35");outtextxy(ox-8+400,oy+2,"40");outtextxy(ox-8+450,oy+2,"45");outtextxy(ox-8+500,oy+2,"50");}/*line-drawing and notes*/void Drawline(int cj1,float pv1,float sp1,float u1,float e1){static int cj_0=0,cj_1=0,pv_0=0.0,pv_1=0.0,sp_0=0.0,sp_1=0.0,u_0=0.0,u_1=0.0,e_0=0.0,e_1=0.0;cj_1=cj1; /*X coordinate*/pv_1=(int)(pv1*10);sp_1=(int)(sp1*10);u_1=(int)(u1*10);e_1=(int)(e1*10);setcolor(2); /*green*/line(ox+cj_0,oy-pv_0,ox+cj_1,oy-pv_1);outtextxy(590,30,"pv");setcolor(8); /*dark gray*/line(ox+cj_0,oy-sp_0,ox+cj_1,oy-sp_1);outtextxy(590,45,"sp");setcolor(3); /*cyan*/line(ox+cj_0,oy-u_0,ox+cj_1,oy-u_1);outtextxy(590,60,"u");setcolor(4); /*red*/line(ox+cj_0,oy-e_0,ox+cj_1,oy-e_1);outtextxy(590,75,"error");cj_0=cj_1;.pv_0=pv_1;sp_0=sp_1;u_0=u_1;e_0=e_1;}三、课程设计(综合实验)总结或结论1.正文为宋体,五号字行间距为211.1------------1.2------------四、参考文献[1] 作者1, 作者2 书名. 出版单位, 版本. 出版日期附录(设计流程图、程序、表格、数据等)注:根据课程设计、综合实验的容将标题任选其一。