This paper is divided into two parts, part of the implementation of the PID algorithm, the other part is about how to use the dynamic curve to draw the results of the PID operation.
First of all, the theoretical model of the PID algorithm, please refer to the theory of automatic control, the first occurrence of analog PID control, then the computer became a controller, because the computer control is a sampling control, the need to convert analog PID to digital PID, is the simulation of PID discretization, the middle is the Shannon theorem. Of course, these are not related to programming, we only need to have a digital model to carry out the work behind.
When you are programming, you can write:
Absolute formula Uo (n) = P *e (n) + i*[e (n) +e (n-1) +...+e (0)]+ D *[e (N)-E (n-1)] Uo (n-1) = P *e (n-1) + i*[e (n-1) +e (n-2) +...+e (0)]+ D * [E (n-1)-E (n-2)]
Subtract to get an incremental calculation formula Uo = P * (e (n)-E (n-1)) + i*e (n) + D *[e (n) -2*e (n-1) +e (n-2)]
E (n)--------------------------this error
The next task is to use the code to implement the formula above, I put the PID operations into a class CLASS1 for other programs to call, start only to achieve the most basic PID operation, did not consider from the integral separation and dead zone processing, the most important code is as follows:
Private float PRAKP, Praki, PRAKD, Prvalue, err, Err_last, Err_next, SetValue;
int Maxlim, Minlim;
PID valculate public
float Pidvalc ()
{
err_next = err_last; The first two errors
err_last = err; The previous error
err = Setvalue-prvalue; Now error
//increment calculation
Prvalue + = PRAKP * (err-err_last) + Praki * err + PRAKD * (err-2 * err_last + err_next));
//output upper and lower limit
if (Prvalue > Maxlim)
prvalue = Maxlim;
if (Prvalue < Minlim)
prvalue = Minlim;
return prvalue;
}
The results of the simulation are as follows, the output value convergence oscillation after the stability.
Now we have a bit of code improvement for the PID operation, adding the integral separation and dead zone function, the complete code is as follows:
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, SE
TValue, Deadband;
int index, UMAX, Umin, Maxlim, Minlim;
Public float PRAKP {set {PRAKP = value;
get {return PRAKP;
The public float Praki {set {Praki = value;
get {return Praki;
The public float PRAKD {set {prakd = value;
get {return prakd;
The public float Setvalue {set {Setvalue = value;
} get {return setvalue;
} public Class1 () {pidinit ();
The//pid parameter initializes 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;
Resistance to integral saturation if (Prvalue > UMAX) {if (Err < 0) index = 1;
else index = 0;
else if (Prvalue < umin) {if (Err > 0) index = 1;
Else index = 0;
}//Integral separate else {if (Math.Abs (ERR) > 0.8 * SetValue)
index = 0;
else index = 1; }//Dead-area if (Math.Abs (ERR) > Deadband) prvalue + = PRAKP * (err-err_last) + IND
ex * Praki * err + PRAKD * (err-2 * err_last + err_next));
else Prvalue = 0;
Output up/down limit if (Prvalue > Maxlim) prvalue = Maxlim;
if (Prvalue < Minlim) Prvalue = Minlim;
return prvalue; }
}
}
Then using the same parameters to simulate the test, the results of the following figure, to achieve a stable set value of time a lot faster, and there is no oscillation phenomenon, there is a significant improvement
The first part completes the PID algorithm realization, then starts the second part to dynamically draw the PID graph.
The beginning is also entangled in where to draw, on the form, or on the PictureBox, the dynamically acquired data in a sort of array or list. Then how do you make the curve translate from left to right. Reference to other people's procedures and data, in fact, not so tangled, separate to see this problem although this is a very small problem, but with big project thinking is the same, divided into graphic display and data update two parts to see, it suddenly enlightened.
The whole idea is to draw the background--draw the grid line--Get the data--draw the loops like the curve, and the loops can be put into a timer. For what containers to use to store graphics and curve data, only the implementation details are different.
I use the drawing in the PictureBox, with a pointf[] array to hold all the data to be displayed at once, which triggers the PictureBox paint event and refreshes the y-axis of the point coordinate in pointf[. If you use the list to hold the coordinate value of point, you can use Method Lremoveat () to remove the first dot, add () to implement at the end plus the most recent point, so that the dynamic translation of the curve. To make it easier to reuse later, I made a form of a user control UserControl1
The code to modify the reference is as follows:
<pre name= "code" class= "CSharp" >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 usercontr
Ol1 () {InitializeComponent ();
} Graphics G;
list<float> L = new list<float> ()//save data to be drawn 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;//grid spacing private int Pianyi = 2;//Drawing two points between the interval private float value1;
Random r=new Random ();
pointf[] data;
Public float Value {get {return value1; set {this.value1 = ValUe }//Get a data 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
Put data to List//if (L.count >=)//{//l.removeat (0);
L.add (value1);
}///Initialize data storage array 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; Draw grid line//for (int i = This.pictureBox1.Width i >= 0; I-= Jiange)//g.drawline (p, I, 0, I, thi
S.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); Draw data curves//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);
}//drawing refresh cycle private void Timer1_tick_1 (object sender, EventArgs e) {GetData ();
This.pictureBox1.Refresh (); }
}
}
Finally, the class Class1 used in FORM1 and the invocation of the user control UserControl1 to display the data curve are implemented in the code as follows:
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);}}