本文分两部分,一部分是讲PID算法的实现,另一部分是讲如何用动态的曲线绘制出PID运算的结果。
首先,PID算法的理论模型请参考自动控制理论,最早出现的是模拟PID控制,后来计算机成为控制器,由于计算机控制是一种采样控制,需把模拟PID转换成数字PID,就是模拟PID的离散化,两者中间是香浓定理。当然这些和编程是没关系的,我们只需要有个数字模型就能开展后面的工作了。
private float prakp, praki, prakd, prvalue, err, err_last, err_next, setvalue;
int MAXLIM, MINLIM;
//PID valculate
public float pidvalc()
{
err_next = err_last; //前两次的误差
err_last = err; //前一次的误差
err = setvalue - prvalue; //现在的误差
//增量式计算
prvalue += prakp * ((err - err_last) + praki * err + prakd * (err - 2 * err_last + err_next));
//输出上下限值
if (prvalue > MAXLIM)
prvalue = MAXLIM;
if (prvalue < MINLIM)
prvalue = MINLIM;
return prvalue;
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PIDtest
{
class Class1
{
private float prakp, praki, prakd, prvalue, err, err_last, err_next, setvalue, deadband;
int index, UMAX, UMIN, MAXLIM, MINLIM;
public float Prakp
{
set
{
prakp = value;
}
get
{
return prakp;
}
}
public float Praki
{
set
{
praki = value;
}
get
{
return praki;
}
}
public float Prakd
{
set
{
prakd = value;
}
get
{
return prakd;
}
}
public float Setvalue
{
set
{
setvalue = value;
}
get
{
return setvalue;
}
}
public Class1()
{
pidinit();
}
//PID参数初始化
public void pidinit()
{
prakp = 0;
praki = 0;
prakd = 0;
prvalue = 0;
err = 0;
err_last = 0;
err_next = 0;
MAXLIM = 800;
MINLIM = -200;
UMAX = 310;
UMIN = -100;
deadband = 2;
}
//PID valculate
public float pidvalc()
{
err_next = err_last;
err_last = err;
err = setvalue - prvalue;
//抗积分饱和
if (prvalue > UMAX)
{
if (err < 0)
index = 1;
else
index = 0;
}
else if (prvalue < UMIN)
{
if (err > 0)
index = 1;
else
index = 0;
}
//积分分离
else
{
if (Math.Abs(err) > 0.8 * setvalue)
index = 0;
else
index = 1;
}
//死区
if (Math.Abs(err) > deadband)
prvalue += prakp * ((err - err_last) + index * praki * err + prakd * (err - 2 * err_last + err_next));
else
prvalue += 0;
//输出上下限制
if (prvalue > MAXLIM)
prvalue = MAXLIM;
if (prvalue < MINLIM)
prvalue = MINLIM;
return prvalue;
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsControlLibrary2
{
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
Graphics g;
//List l = new List();//储存要绘制的数据
Pen p = new Pen(Color.Green, 1);
Pen p1 = new Pen(Color.Red, 1);
//PointF ptfront = new PointF();
//PointF ptbehond = new PointF();
private int jiange = 86;//网格间距
private int pianyi = 2;//绘图两点之间间隔
private float value1;
//Random r=new Random ();
PointF[] data;
public float Value
{
get
{
return value1;
}
set
{
this.value1 = value;
}
}
//获得一个数据
private void getdata()
{
data[data.Length - 1].Y = value1;
for (int i = 0; i < data.Length - 1; i++)
data[i].Y = data[i + 1].Y;
//放数据到LIST
//if (l.Count >= 80)
//{
// l.RemoveAt(0);
// l.Add(value1 );
//}
}
//初始化数据存放数组
private void UserControl1_Load_1(object sender, EventArgs e)
{
timer1.Enabled = true;
timer1.Interval = 100;
//l.Add(0);
data = new PointF[pictureBox1.Width / pianyi];
for (int i = 0; i < data.Length; i++)
data[i].X += pianyi * i;
/*
for (int i = 0; i < pictureBox1.Width / pianyi; i++)
{
l.Add(r.Next(50));
}
*/
}
private void pictureBox1_Paint_1(object sender, PaintEventArgs e)
{
g = e.Graphics;
//画网格线
//for (int i = this.pictureBox1.Width; i >= 0; i -= jiange)
//g.DrawLine(p, i, 0, i, this.pictureBox1.Width);
//for (int i = this.pictureBox1.Height; i >= 0; i -= jiange)
//g.DrawLine(p, 0, i, this.pictureBox1.Width , i);
for (int i = 0; i < pictureBox1.Width; i++)
if (i % jiange == 0)
g.DrawLine(p, i, 0, i, this.pictureBox1.Height);
for (int i = 0; i < pictureBox1.Height; i++)
if (i % jiange == 0)
g.DrawLine(p, 0, i, pictureBox1.Width, i);
//画数据曲线
// ptbehond.X = 0;
/*
for (int i = 0; i < l.Count - 1; i++)
{
ptfront.X = ptbehond.X;
ptfront.Y = l[i];
ptbehond.X += pianyi;
ptbehond.Y = l[i + 1];
g.DrawLine(p1, ptfront, ptbehond);
}
*/
g.DrawCurve(p1, data);
}
//绘图刷新周期
private void timer1_Tick_1(object sender, EventArgs e)
{
getdata();
this.pictureBox1.Refresh();
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace PIDtest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
timer1.Interval = 5;
timer1.Enabled = true;
}
Class1 pid = new Class1();
//Random r = new Random();
private void timer1_Tick(object sender, EventArgs e)
{
userControl11.Value = pid.pidvalc();
}
private void button1_Click(object sender, EventArgs e)
{
pid.pidinit();
pid.Setvalue = float.Parse(textBox1setvalue.Text);
pid.Prakp = float.Parse(textBox1prakp.Text);
pid.Praki = float.Parse(textBox2praki.Text);
pid.Prakd = float.Parse(textBox3prakd.Text);
}
}
}