课程设计报告
(2006
--
2007
年度第2
学期)
名
称:
计算机控制系统A
题
目:
DDC单回路PID闭环
控制系统的设计及实时仿真
院
系:
自动化系
班
级:
自动班
学
号:
学生姓名:
指导教师:
设计周数:
一
周成绩:
日期:2007
年X月X
日
《计算机控制系统A》课程设计
任
务
书
一、目的与要求
1.学习并了解用高级语言(C语言)实现数字PID控制算法模块程序的方法;
2.比较验证理想微分PID和实际微分PID控制算法阶跃响应,加深对上述两种算法各自特点的认识;
3.学习了解用模拟计算机使用方法;
4.学习掌握A/D、D/A转换接口板的使用方法;
5.了解一种微机中断定时的方法;
6.学习掌握通过A/D、D/A转换用计算机获取被控对象动态特性的方法;
7.通过实时仿真实验掌握DDC单回路控制程序编制及调试方法。
二、主要内容
1.用C语言分别编写理想微分PID和实际微分PID控制算法模块,在微机中调试实现,并编写简单的计算机绘图程序,分别绘制并打印出上述两种算法的单位阶跃响应曲线(课外上机完成);
2.用模拟计算机搭接成一个二阶惯性环节,作为一个模拟仿真的被控对象;
3.用C语言编写使用HY-6060进行定时采样、定时输出的接口程序,并在微机中调试实现;
4.由D/A输出阶跃信号,同时由A/D采集模拟的被控对象的响应,绘制并打印出采集获得的飞升曲线,并初步计算出对象模型;
y
5.由模拟计算机搭接的二阶惯性环节作为系统中的被控对象,用计算机作为DDC控制器,通过HY-6060接口板实现对模拟机的实时控制。仿真实验系统的框图如下:
r
+
u
PID
模拟机
D/A
A/D
6.整定控制器PID参数,在设定值阶跃情况下,打印控制量u和被控量y的曲线。
三、进度计划
序号
设计内容
完成时间
备注
编写数字PID控制算法模块
本设计开始前
在课程要求的8学时课外上机时间完成2
本设计其它内容按小组分工协作完成设计周五
验收、答辩
设计周末前
四、设计(实验)成果要求
1.根据个人在设计小组中的分工,完成设计内容;
2.分析实时仿真结果,每人完成设计报告。设计报告除C语言源程序及打印的结果曲线外,必须手写完成,不得使用打印稿;
3.提交设计报告,参加设计答辩。
五、考核方式
按小组对设计内容进行演示,由教师检查验收,答辩按学生个人进行,给出综合成绩。
学生姓名:
指导教师:
2007
年
X月X
日
正
文
一.设计思想
运用编程软件Turboc2.0,开发名为Administrators的监控软件。Administrators包含HY-6060进行定时采样、定时输出的接口模块,手/自动切换模块及控制界面显示模块。对PID参数进行整定后,通过HY-6060对用模拟计算机所搭接二阶惯性环节数据的采集,在可视化界面中获得直观的响应曲线,并通过HY-6060接口板实现对模拟机的实时控制。
二.设计步骤
1.前期准备工作
(1)需要一台具有PC插槽的微型计算机并安装具有直接I/O能力的Windows
98系统以及Turboc2.0软件,HY-6060及接线板一套,模拟计算机一台。
(2)复习Turboc2.0并参照说明书学习
HY-6060的使用
2.PID的设计:
(1)采用连续传递函数的离散化方法,如向后差分法及双线性变换法,将理想微分PID算式和实际微分PID算式离散化,写成差分方程的形式,从而便于用计算机实现。
理想微分PID:
c0=kp*(1+T/Ti+Td/T);
c1=-kp*(1+2*Td/T);
c2=kp*Td/T;
u[0]=u[0]+q0*e[2]+q1*e[1]+q2*e[0];
实际微分PID:
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);
a=c0*(u[0]-u[1])+c1*e[0]+c2*e[1]+c3*e[2]+u[0];
(4)设置死区:在实际控制系统中,并不要求被控量十分精确的与设定值相等,完全无差,而是容许偏差在一定范围内变化,在这种情况下,计算机控制为了避免控制动作过于频繁,以消除系统振荡,就会采用带死区的PID算法。
该算法是在原PID算法前加一个不灵敏区来实现,即
设置不灵敏区C值
当|e(k)|>C时,|e(k)|=|e(k)|;
当|e(k)| (5) 手动/自动跟踪和无扰切换 自动到手动过程:系统处于自动时,手操器实时跟踪PID调节器的输出,当切换到手动时,被控对象处于手操器的开环控制,调节器跟踪手操器的输出。 通过设置键盘,使的按下手动键H时,系统处于手动状态,按下自动键A时,系统处于自动状态,同时设置相应的功能键,P为暂停,E为退出。 3.二阶惯性环节的搭接 利用模拟计算机中的电容电阻及运算放大器,搭接已经设计好的二阶惯性环节,仿真一个被控对象。其传递函数为,为使增益为1左右并且对象的惯性时间足够大 令R1=R2=R3=R4=R=1M R5=R6=510K,C1=C2=C=4.7uF 则K=(R2/R1)*(R4/R3)=1 T1=T2=R*C=1000000*0.0000047=4.7s 所以G(s)=1/(4.7s+1)*(4.7s+1) 将HY-6060插入微型计算机的PC插槽,用硬盘线、接线板及导线将HY-6060与所搭二阶惯性环节连接,实现计算机与二阶系统的硬件连接,为通信做好准备。 接线电路图如下:(手抄) 4.PID参数整定 通过使用matlab获得对象的阶跃响应曲线,从而确定出对象的延迟时间、时间常数T,开环增益K,根据 内模整定法得出PID调节器参数的初步整定值。再通过实验调整初步整定值最终得出一组PID参数使系统在给定值阶跃扰动下的响应曲线比较理想。 于是有:T=11.5 =2 K=1 则 Ti=T=11.5 Td=0.5*=1 =0.5*K*/T=1/11.5 Kp=11.5 Tf=0.2 5.A/D采集与D/A转换 (1)过程:采集二阶惯性环节输出的电压并通过HY-6060对该模拟量进行A/D转换,然后将其送入微型计算机,将转化后的数字信号与设定值进行比较,利用偏差进行PID控制,将PID调节器的输出量送给HY-6060进行D/A转换得到模拟信号,即电压值,再送给二阶惯性环节,从而得到新的二阶惯性环节的输出量,该输出量再次由HY-6060采集并进行A/D转换。 采样时间取0.055s,由中断实现。 (2)HY-6060的使用: 通过调整PID使板基地址base为260 H Base+0为模拟输入信号通道选择寄存器,实验中选用通道1 base+1为转换完成位寄存器,最高位置1转换完成,置0转换未完成base+2为转换低8位数据寄存器 base+3为转换高4位数据寄存器 base+4为命令字寄存器,实验中置0从而屏蔽定时触发和外触发。 通过调整JP1、JP2、JP5、JP6使模拟电压输入范围为单极性输入0--+10 通过调整JP3、JP4使模拟电压输出范围为单极性输出0--+10 (3)数模标度变换 采集的模拟量A是最大范围为0--+10的电压,而经转换后在电脑中的数字量D是12位二进制数,对应范围是0—FFFH,即数字量D=A*FFFH/10。 例如0V的电压对应数字量是000H。 6.控制界面设计 本小组使用Turboc2.0基本控制界面实现对被控对象的PID参数设置及调整,通过本控制界面可实现手/自动控制切换。 三.实验结果 本小组采用实际PID调节器控制具有迟延的二阶对象(其中二阶对象由模拟计算机实现),获得了良好的效果。其中整定的实际PID调节器参数如下: kp=11.5 Ti=11.5 Td=1 Tf=0.2 峰值时间(s) 超调量 (%) 调节时间(s) 原系统阶跃响应 0 整定后系统阶跃响应 3.9 15.1% 14.7 原系统阶跃响应曲线,整定后系统的阶跃响应曲线,系统运行曲线,手/自动切换PID调节器输出曲线见附录。 四.实验总结 1.实验过程中,因本小组遇到的难点主要集中在硬件方面如HY-6060的使用、A/D通道和D/A通道连接测试等硬件方面的问题及软件的查错调试等,因本小组成员在这些方面经验不足,所以在解决这些问题的过程中本小组成员虚心向老师及周围同学请教,获得了解决问题的基本思路后积极讨论,从而使问题得到了较为圆满的解决。 2.本小组设计最突出的创新特色是整定PID的过程中使用了内模整定法,使得整定过程非常简洁轻松,并且整定参数在实际使用整定效果非常出色,但因时间及所学知识有限无法实现比较人性化的操作界面及其他一些较复杂的功能,使得本次设计中仍存在一些缺憾,本小组成员将以这次设计过程中获得经验教训为契机在以后的学习中更加注重将所学知识与实践结合,使以后的作品更加完善。 五.参考文献 六.附录 1.硬件连接图 2.PID响应曲线 理想PID响应曲线: 实际PID响应曲线: 3.对象飞升特性曲线 4.给定值扰动曲线 .程序清单 //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- #include #pragma hdrstop #include “Unit1.h“ //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource “*.dfm“ TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- #include #include static double e[3],u[2],y[3],sp,manual,Ucc,realstep,idealstep,tall; int t,t1,height,width; double T; double kp=5; double Ti=10; double Tf=10; double Td=3; typedef unsigned char UINT8; typedef unsigned short UINT16; void outportb(UINT16 Port,UINT8 x) { asm mov dx,Port asm mov al,x asm out dx,al } UINT8 inportb(UINT16 Port) { UINT8 r; asm mov dx,Port asm in al,dx asm mov r,al return r; } void pidout(); void sample() { int i,j; float k; unsigned char h,l; outportb(0x264,0); for(i=0;i<1000;i++); outportb(0x260,0x0f); for(i=0;i<1000;i++); outportb(0x261,0); for(i=0;i<100;i++) for(j=0;j<100;j++); l=inportb(0x262); for(i=0;i<1000;i++); h=inportb(0x263); h=h&0x0f; i=(int)h*256; i=i+(int)l; Ucc=5.0; //y数组用于纯迟延,即y[2]保存当前时刻的对象输出的采样值,//经过两拍,由y[2]变为y[1],y[1]变为y[0]才用于计算偏差信号 e[0]=sp-y[0]; y[0]=y[1]; y[1]=y[2]; //被控对象的电压输出范围为-5V~+5V,//通过该转换公式将输出电压值转换为000H~FFFH y[2]=(i-2047)*2.0*Ucc/4095; return; } void pidout() { int i; unsigned char h,l; i=(u[0]+5)*4095/10; h=i/256; l=i-h*256; outportb(0x262,l); for(i=0;i<100;i++); outportb(0x263,h); for(i=0;i<100;i++); outportb(0x261,0); return; } void idealpid() { double q0,q1,q2; q0=kp*(1+T/Ti+Td/T); q1=-kp*(1+2*Td/T); q2=kp*Td/T; u[0]=u[0]+q0*e[2]+q1*e[1]+q2*e[0]; e[2]=e[1]; e[1]=e[0]; return ; } void realpid() { int i; double a,k,q1,q2,q3,q4,q5,A=11,death=0.001,saturate=10; //加死区 if(fabs(e[0]) return ; //积分分离 if (fabs(e[0])>A) k=10000; else k=1; Ti=Ti*k; /*q0=Td/(Td+Kd*T); q1=kp*Td*(1+Kd)/(Td+Kd*T); q2=-kp*(2*Ti*Td*(1+Kd)+Td*(Kd*Ti+Td))/(Ti*(Td+Kd*T)); q3=kp*(Ti*Td*(1+Kd)+T*(Kd*Ti+Td)+Kd*T*T)/(Ti*(Td+Kd*T)); q0=Tf/(T+Tf); q1=(kp*T+T*T*kp/Ti+Td*kp)/(T+Tf); q2=-(2*kp*Td+T*kp)/(T+Tf); q3=Td/(kp*T+kp*Tf); a=u[0]+q0*(u[0]-u[1])+q1*e[2]+q2*e[1]+q3*e[0]; 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); a=c0*(u[0]-u[1])+c1*e[0]+c2*e[1]+c3*e[2]+u[0]; if(a>saturate) { k=10000; Ti=Ti*k; 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); a=c0*(u[0]-u[1])+c1*e[0]+c2*e[1]+c3*e[2]+u[0]; } if(a>saturate) a=saturate; */ //采用PID递推算法 q1=4*Tf/(T+2*Tf); q2=(T-2*Tf)/(T+2*Tf); q3=(4*kp*Ti*Td+2*Ti*kp*T+kp*T*T)/(2*Ti*T+4*Ti*Tf); q4=(2*kp*T-8*kp*Ti*Td)/(2*Ti*T+4*Ti*Tf); q5=(4*kp*Ti*Td-2*Ti*kp*T+kp*T*T)/(2*Ti*T+4*Ti*Tf); a=q1*u[0]+q2*u[1]+q3*e[0]+q4*e[1]+q5*e[2]; //抗积分饱和 if (abs(a)>saturate) { k=10000; Ti=Ti*k; q1=4*Tf/(T+2*Tf); q2=(T-2*Tf)/(T+2*Tf); q3=(4*kp*Ti*Td+2*Ti*kp*T+kp*T*T)/(2*Ti*T+4*Ti*Tf); q4=(2*kp*T-8*kp*Ti*Td)/(2*Ti*T+4*Ti*Tf); q5=(4*kp*Ti*Td-2*Ti*kp*T+kp*T*T)/(2*Ti*T+4*Ti*Tf); a=q1*u[0]+q2*u[1]+q3*e[0]+q4*e[1]+q5*e[2]; } if (abs(a)>saturate) //如果出现积分饱和,对PID控制器输出u[k]限幅 { if (a>saturate) a=saturate; else a=(-1)*saturate; } u[1]=u[0]; u[0]=a; e[2]=e[1]; e[1]=e[0]; Ti=10; return ; } void realpidstep() { int i; double a,k,q1,q2,q3,q4,q5,A=11,death=0.001,saturate=10; //加死区 if(fabs(e[0]) return ; //积分分离 if (fabs(e[0])>A) k=10000; else k=1; Ti=Ti*k; //采用PID递推算法 q1=4*Tf/(T+2*Tf); q2=(T-2*Tf)/(T+2*Tf); q3=(4*kp*Ti*Td+2*Ti*kp*T+kp*T*T)/(2*Ti*T+4*Ti*Tf); q4=(2*kp*T-8*kp*Ti*Td)/(2*Ti*T+4*Ti*Tf); q5=(4*kp*Ti*Td-2*Ti*kp*T+kp*T*T)/(2*Ti*T+4*Ti*Tf); a=q1*u[0]+q2*u[1]+q3*e[0]+q4*e[1]+q5*e[2]; //抗积分饱和 if (abs(a)>saturate) { k=10000; Ti=Ti*k; q1=4*Tf/(T+2*Tf); q2=(T-2*Tf)/(T+2*Tf); q3=(4*kp*Ti*Td+2*Ti*kp*T+kp*T*T)/(2*Ti*T+4*Ti*Tf); q4=(2*kp*T-8*kp*Ti*Td)/(2*Ti*T+4*Ti*Tf); q5=(4*kp*Ti*Td-2*Ti*kp*T+kp*T*T)/(2*Ti*T+4*Ti*Tf); a=q1*u[0]+q2*u[1]+q3*e[0]+q4*e[1]+q5*e[2]; } if (abs(a)>saturate) //如果出现积分饱和,对PID控制器输出u[k]限幅 { if (a>saturate) a=saturate; else a=(-1)*saturate; } u[1]=u[0]; u[0]=a; e[2]=e[1]; e[1]=e[0]; Ti=10; return ; } void __fastcall TForm1::Button1Click(TObject *Sender) { //initial(); T=Timer1->Interval; T=T/1000.; manual=0;e[0]=0;e[1]=0;e[2]=0;u[0]=0;u[1]=0; y[0]=0;y[1]=0;y[2]=0;Ucc=5;t=0;t1=0;tall=0; sp=StrToFloat(Edit4->Text); realstep=0; idealstep=0; pidout(); height=PaintBox2->Height; width=PaintBox2->Width; PaintBox1->Canvas->Pen->Color=clRed; PaintBox2->Canvas->Pen->Color=clBlue; PaintBox1->Canvas->MoveTo(8,height/2); PaintBox2->Canvas->MoveTo(8,height/2); PaintBox1->Invalidate(); PaintBox2->Invalidate(); Timer1->Enabled=true; } //--------------------------------------------------------------------------- void __fastcall TForm1::Button2Click(TObject *Sender) { Timer1->Enabled=false; } //--------------------------------------------------------------------------- void __fastcall TForm1::BitBtn1Click(TObject *Sender) { t1=t1+1; } //--------------------------------------------------------------------------- void __fastcall TForm1::BitBtn2Click(TObject *Sender) { t1=t1-1; } //--------------------------------------------------------------------------- void __fastcall TForm1::Timer1Timer(TObject *Sender) { int ypos,upos; tall=tall+T; if(realstep==1) { realpidstep(); upos=height-(u[0]+5)*height/10; if(t>72) { t=-1; PaintBox2->Invalidate(); PaintBox2->Canvas->MoveTo(0,upos); } t=t+1; PaintBox2->Canvas->LineTo(10*t+10,upos); Edit3->Text=FloatToStr(u[0]); Edit5->Text=FloatToStr(tall); } else if (idealstep==1) { idealpid(); upos=height-(u[0]+5)*height/10; if(t>72) { t=-1; PaintBox2->Invalidate(); PaintBox2->Canvas->MoveTo(0,upos); } t=t+1; PaintBox2->Canvas->LineTo(10*t+10,upos); Edit3->Text=FloatToStr(u[0]); Edit5->Text=FloatToStr(tall); } else { int unchange=0,s1,s2,s3,upos,ypos; //采集A/D转换的系统输出数据y[k] sample(); Edit2->Text=FloatToStr(y[0]); //PID调节器处理偏差信号 if (manual==MANUAL->Checked) unchange=20; manual=MANUAL->Checked; if (MANUAL->Checked) s2=10; else s2=5; if(RealPID->Checked) s1=1; else s1=0; s3=s1+s2+unchange; switch (s3) { case 5: Form1->Edit1->Text=““; //手动到自动 for (int i=0;i<3;i++) e[i]=0; u[1]=u[0]; idealpid(); break; case 6: for (int i=1;i<3;i++) e[i]=0; sample(); u[1]=u[0]; sp=StrToFloat(Edit4->Text); realpid(); if(fabs(u[0]-u[1])>0.2) { Form1->Edit1->Text=“偏差太大,无法切换“; MANUAL->Checked=true; u[0]=u[1]; } else Form1->Edit1->Text=““; break; case 10: u[0]=u[0]+t1; //自动到手动 t1=0; Form1->Edit1->Text=u[0]; break; case 11: u[0]=u[0]+t1; t1=0; Form1->Edit1->Text=u[0]; break; case 25: idealpid();break; // 不切换 case 26: realpid(); break; case 30: u[0]=u[0]+t1; t1=0; Form1->Edit1->Text=u[0]; break; case 31: u[0]=u[0]+t1; t1=0; Form1->Edit1->Text=u[0]; break; } //图形显示当前y[k]和u[k] ypos=height-(y[0]+5)*height/10; upos=height-(u[0]+5)*height/10; if(t>72) { t=-1; PaintBox1->Invalidate(); PaintBox2->Invalidate(); PaintBox1->Canvas->MoveTo(0,ypos); PaintBox2->Canvas->MoveTo(0,upos); } t=t+1; PaintBox1->Canvas->LineTo(10*t+10,ypos); PaintBox2->Canvas->LineTo(10*t+10,upos); //将u[k]通过D/A转换输送给被控对象 pidout(); Edit3->Text=FloatToStr(u[0]); Edit5->Text=FloatToStr(tall); } } //--------------------------------------------------------------------------- void __fastcall TForm1::Button4Click(TObject *Sender) { realstep=1; idealstep=0; T=Timer1->Interval; T=T/1000.; e[0]=1;e[1]=0;e[2]=0; u[0]=0;u[1]=0;t=0; height=PaintBox2->Height; PaintBox2->Canvas->Pen->Color=clBlue; PaintBox2->Canvas->MoveTo(8,height/2); tall=0; PaintBox1->Invalidate(); PaintBox2->Invalidate(); Timer1->Interval=300; Timer1->Enabled=true; } //--------------------------------------------------------------------------- void __fastcall TForm1::BitBtn3Click(TObject *Sender) { sp=sp+1.00; Edit4->Text=FloatToStr(sp); } //--------------------------------------------------------------------------- void __fastcall TForm1::BitBtn4Click(TObject *Sender) { sp=sp-1.00; Edit4->Text=FloatToStr(sp); } //--------------------------------------------------------------------------- void __fastcall TForm1::Button3Click(TObject *Sender) { idealstep=1; T=Timer1->Interval; T=T/1000.; realstep=0; e[0]=1;e[1]=0;e[2]=0; u[0]=0;u[1]=0;t=0; height=PaintBox2->Height; PaintBox2->Canvas->Pen->Color=clBlue; PaintBox2->Canvas->MoveTo(8,height/2); tall=0; PaintBox1->Invalidate(); PaintBox2->Invalidate(); Timer1->Interval=300; Timer1->Enabled=true; } //---------------------------------------------------------------------------