软件课程设计.docx
《软件课程设计.docx》由会员分享,可在线阅读,更多相关《软件课程设计.docx(12页珍藏版)》请在冰点文库上搜索。
软件课程设计
软件课程设计
一、目的
1、掌握C#进行程序设计的基本思路和方法
2、能利用C#编程实现简单的任务
3、结合控制系统理论用C#进行计算机控制仿真
二、设计内容
1、对一阶系统实现PID算法控制并进行仿真,具体功能如下:
1)基本要求:
实现PID算法和一阶系统差分方程仿真,PID算法中的四个参数和一阶系统的参数都可以通过菜单进行设定,系统对阶越函数的响应以图形方式实时显示在窗口中。
2)附加功能:
将系统的时间响应数据保存到数据库中,具体应包括下列属性:
时间,输出值。
将系统的历史响应重现。
使用Teechart控件作为显示输出。
三、计划
1、熟悉了解C#要包括C#、界面设计、绘图功能、数据库访问技术、ActiveX控件的注册和使用。
2、回顾控制系统原理中的PID算法及PID算法的数字化。
3、实现PID算法对一阶系统的控制并显示系统的时间响应。
4、将系统的时间响应数据存储到数据库中,然后重现整个响应。
五、图形界面及代码
usingSystem;
usingSystem.Collections.Generic;
usingSystem.ComponentModel;
usingSystem.Data;
usingSystem.Drawing;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Windows.Forms;
namespaceWindowsFormsApplication3
{
publicpartialclassForm1:
Form
{
privateForm2form2;
publicPIDpid;
publicstructPID
{
publicdoublekp,ki,kd,T1,deltat,startpoi,endpoi,lasterror,preverror,sumerror,setpoint;
}
//publicPIDpid;
//publicdouble[]data=newdouble[20000];
publicForm1()
{
InitializeComponent();
}
privatevoidForm1_Load(objectsender,EventArgse)
{
}
privatevoidbutton1_Click(objectsender,EventArgse)
{
//doublec;
//PIDpid;
pid.kp=Convert.ToDouble(textBox1.Text);
pid.ki=Convert.ToDouble(textBox2.Text);
pid.kd=Convert.ToDouble(textBox3.Text);
pid.T1=Convert.ToDouble(textBox4.Text);
pid.deltat=Convert.ToDouble(textBox5.Text);
pid.startpoi=Convert.ToDouble(textBox6.Text);
pid.endpoi=Convert.ToDouble(textBox7.Text);
this.Hide();
form2=newForm2();
form2.Show();
MessageBox.Show("您¨²好?
,ê?
欢?
迎®-使º1用®?
本À?
PID控?
制?
时º¡À域®¨°仿¤?
真?
软¨¨ª件t!
ê?
");
//MessageBox.Show("successful!
");
}
privatevoidbutton2_Click(objectsender,EventArgse)
{
this.Hide();
}
privatevoid历史查询ToolStripMenuItem_Click(objectsender,EventArgse)
{
form2=newForm2();
form2.Show();
form2.ReadDataRec(pid);
}
privatevoid修改参数ToolStripMenuItem_Click(objectsender,EventArgse)
{
pid.kp=Convert.ToDouble(textBox1.Text);
pid.ki=Convert.ToDouble(textBox2.Text);
pid.kd=Convert.ToDouble(textBox3.Text);
pid.T1=Convert.ToDouble(textBox4.Text);
pid.deltat=Convert.ToDouble(textBox5.Text);
pid.startpoi=Convert.ToDouble(textBox6.Text);
pid.endpoi=Convert.ToDouble(textBox7.Text);
this.Hide();
form2=newForm2();
form2.Show();
}
privatevoid退出ToolStripMenuItem_Click(objectsender,EventArgse)
{
this.Close();
}
privatevoid修改查询ToolStripMenuItem_Click(objectsender,EventArgse)
{
pid.startpoi=Convert.ToDouble(textBox6.Text);
pid.endpoi=Convert.ToDouble(textBox7.Text);
this.Hide();
form2=newForm2();
form2.Show();
}
}
}
当按下确定的时候:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.ComponentModel;
usingSystem.Data;
usingSystem.Drawing;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Windows.Forms;
namespaceWindowsFormsApplication3
{
publicpartialclassForm2:
Form
{
privateForm1form1;
//privateGraphicsgx;
publicstructPID
{
publicdoublekp,ki,kd,T1,deltat,startpoi,endpoi,lasterror,preverror,sumerror,setpoint;
}
publicPIDpid;
privatePointP0;
privatePointP1;
publicdoubleXnt=0,Ynt=0;
publicdouble[]Data=newdouble[20000];
publicForm2()
{
InitializeComponent();
}
privatevoidForm2_Load(objectsender,EventArgse)
{
pid.kp=200;
pid.ki=1;
pid.kd=1;
pid.setpoint=10;
pid.T1=0.0001;
pid.deltat=40;
P0.X=260;
P0.Y=260;
//Penthepen;
}
privatevoid返回Click(objectsender,EventArgse)
{
this.Hide();
form1=newForm1();
form1.Show();
}
privatevoidReadDataRec(PIDp)
{
Graphicsg;
Penthepen=newPen(Color.Red,2);
Form2.Data1.Recordset.MoveFirst();
for(inti=1;i<=p.startpoi;i++)
{
Form2.Data1.Recordset.MoveNext();
}
for(inti=1;i<=p.endpoi-p.startpoi;i++)
{
Data[i]=Form2.Data1.Recordset.Fields("value");
Form2.Data1.Recordset.MoveNext();
}
Form1.TChart1.AddSeries(scLine);
Form1.TChart1.Series(0).Clear();
for(inti=1;i<=p.endpoi-p.startpoi;i++)
{
doublej=i+p.startpoi;
P1.X=(int)(j/60);
P1.Y=(int)Data[i-1];
g.DrawLine(thepen,P0,P1);
P0=P1;
if(i%20==0)
{
Form2.TChart1.AutoRepaint=false;
Form2.TChart1.Series(0).Add(Data[i-1],i,BackColor);
Form1.TChart1.AutoRepaint=true;
Form1.TChart1.Repaint();
}
label1.Text="x="+j;
label2.Text="y="+Data[i-1];
timer1_Tick();
}
}
protectedoverridevoidOnPaintAndWriteDataRec(PaintEventArgse)
{
Data1.Recordset.MoveFirst();
while(!
Data1.Recordset.Eof())
{
Data1.Recordset.Delete();
Data1.Recordset.MoveNext();
}
for(inti=1;i<=20000;i++)
{
Xnt=Compute_PID(pid,Ynt);
Data[i-1]=Last_Val(Ynt,Xnt,pid);
Data1.Recordset.AddNew();
Data1.Recordset.Fields("time")=i;
Data1.Recordset.Fields("value")=Data[i-1];
Data1.Recordset.Update();
}
TChart1.AddSeries(scLine);
TChart1.Series(0).Clear();
for(inti=1;i<=20000;i++)
{
if(i%20==0)
{
Form2.TChart1.AutoRepaint=true;
Form2.TChart1.Series(0).Add(Data[i-1],i,BackColor);
Form1.TChart1.AutoRepaint=true;
Form1.TChart1.Repaint();
}
label1.Text="x="+j;
label2.Text="y="+Data[i-1];
timer1_Tick();
}
}
privatedoubleCompute_PID(PIDp,doublenextpoint)
{
doublederror,error;
error=p.setpoint-nextpoint;//偏?
差?
p.sumerror=error+p.sumerror;//积y分¤?
derror=p.lasterror-p.preverror;//当Ì¡À前¡ã微¡é分¤?
p.preverror=p.lasterror;
p.lasterror=error;
return(p.kp*error+p.ki*p.sumerror+p.kd*derror);
}
privatedoubleLast_Val(doubleXt,doubleYt,PIDp)
{
return(p.deltat/p.T1*Xt-(p.deltat/p.T1-1)*Yt);
}
privatevoidtimer1_Tick(objectsender,EventArgse)
{
}//定¡§时º¡À器¡Â每?
0.01s自Á?
己o调Ì¡Â用®?
一°?
次ä?
,ê?
在¨²此ä?
相¨¤当Ì¡À于®¨²延¨®时º¡À作Á¡Â用®?
privatevoidcheckBox1_CheckedChanged(objectsender,EventArgse)
{
TChart1.Panel.Gradient.Visible=Check1.Value;
}
privatevoidcheckBox2_CheckedChanged(objectsender,EventArgse)
{
TChart1.Aspect.View3D=Check2.Value;
}
}
}
加上背景和3D效果后:
五、调试心得和感受
本次课程收获很大,从专业知识到编程水平都有一定的巩固和提高,曾经只是在课本上学习了PID控制理论,只能解一些理论的题目,从未深入思考PID在软件上该如何实现,虽然在Matlab中经常调用PID模块,但是对真正的PID函数内部没有深入了解,也没有对Simulink里面实现PID做过任何深层次的探究,此次恰好有这样一个机会自己编写自己的PID算法,为今后参加飞思卡尔小车竞赛提供了很大帮助。
其次,根据老师提供的PID算法公式,可以发现很多连续时域系统的方程都可以转化成离散的差分方程,为以后连续时域系统的分析也可以自己用软件编译实现仿真,验证自己用理论运算的结果。
最后,由于之前有做过关于Kinect相关项目的开发,跟着学长学习了C#很长一段时间,但是后来暑假就荒废了,很多东西都忘了,由于生疏的原因导致在编程的时候遇到种种小问题,比如安装Teechart控件的时候没有注意与VB的区别装的是C++版本总是调用不了,还有本可以用WPF文件来写立体显示,效果比TeeChart控件达到的效果能更好,但是最后还是没有实现,只能退而求其次的用TeeChart,再比如在C#中可以用foreach语句,开始总是把格式写错,现在就不会,还有C#中独有的委托也是生疏的原因总是用不好。
经过此次的训练,重新燃烧了我对C#的热爱,打算寒假再苦心研究,争取开学的时候能够给自己的飞思卡尔小车做好上位机程序,能够实现在线调试,串口通信等功能。
在最最后,感谢老师能够看完我的报告。