[From Bill Powers (941101.1515 MST)]
Here is the Turbo Pascal code for a simple model of a control system. It puts
three plots on the screen showing the behavior of
plot 1: reference signal, perceptual signal
plot 2: input quantity, output quantity, disturbance
Plot 3: error signal.
The standard diagram of the control system is drawn on the screen. The user
can move a pointer to indicate
ki: input function multiplier
ko: output function multiplier
kf: feedback function multiplier
kd: disturbance function multiplier
r: reference signal
qd: disturbance
The + and - keys increase and decrease the value of the item at the pointer.
There's nothing fancy about this code; it should run with any version of
Turbo Pascal. Be sure to compile the Unit "csdiagrm.pas" to the disk.
This should help us in talking about the properties of control systems.
Bill P.
···
=======================================================================
Program ctbasic;
{
This program runs a basic control system for 150 time units, and
plots the behavior of the variables. The user can change the values
of the parameters using the + and - keys. After each change the
plots are redrawn. Note that the Unit "csdiagrm" must be in the
Units directory.
WTP 941101
}
Uses Dos,Crt,Graph,grUtils,csdiagrm;
const
left = 203; right = 205; up = 200; down = 208; PgUp = 201; PgDn = 209;
Ins = 210; Del = 211; EndKey = 207; Home = 199; EscKey = 27; Cr = 13;
maxlimit: array[0..5] of real = (10.0,50.0,10.0,10.0,100.0,100.0);
minlimit: array[0..5] of real = (0.0,0.0,0.0,0.0,-100.0,-100.0);
delta: array[0..5] of real = (1.0,2.0,1.0,1.0,5.0,5.0);
menux = 20; menuy = 160;
type
csystype = record
ki,p,r,e,o,ko,s: real;
end;
envtype = record
qo,qi,qd,kd,kf: real;
end;
plottype = record
basex,basey,zero: integer;
scalex,scaley: real;
end;
var
csys: csystype;
env: envtype;
varref: array[0..5] of ^real;
frame: array[1..3] of plottype;
maxx,maxy: integer;
ch: char;
clock: integer;
MaxColor: word;
menuptr: integer;
done: boolean;
procedure Control(var ctsys: csystype);
begin
with ctsys do
begin
p := ki*env.qi;
e := r - p;
o := o + s*(ko*e - o); {Leaky integrator, s < 1.0}
end;
end;
procedure Environment(var environ: envtype);
begin
with environ do
begin
qo := csys.o;
qi := kf*qo + kd*qd;
end;
end;
function GetCmd: byte;
var ch: byte;
begin
ch := ord(readkey);
if (ch = 0) and keypressed then
ch := ord(readkey) or $80;
getcmd := ch;
end;
procedure InitScreen;
begin
ClrScr;
InitGraphics;
MaxX := GetMaxX; MaxY := GetMaxY;
maxColor := getmaxcolor;
Rectangle(0, 0, MaxX, MaxY);
OutTextXY(MaxX div 2 - 170, 5,
'BASIC CONTROL SYSTEM MODEL');
OutTextXY(20, MaxY-50, 'Press ESC or END to Quit...');
end;
procedure MakeFrames(framenum: integer);
var v,zz,i: integer;
begin
with frame[framenum] do
begin
zz := (zero div 10) * 10;
setcolor(darkgray);
for i := 1 to 10 do
begin
v := maxy - basey - 10*i;
moveto(1+basex,v);
lineto(1+basex+150,v);
end;
for i := 1 to 15 do
begin
v := basex+10*i;
moveto(v,maxy - basey - 1);
lineto(v,maxy - basey - 100);
end;
setcolor(white);
moveto(basex,maxy - basey-100);
lineto(basex,maxy - basey);
lineto(basex+150,maxy - basey);
moveto(basex,maxy - basey - zz);
lineto(basex+150,maxy - basey - zz);
end;
end;
procedure InitPlots;
var i: integer;
begin
with frame[1] do
begin
scalex := 1.0; scaley := 0.5;
basex := maxx - 152; basey := maxy - 102; zero := 50;
MakeFrames(1);
end;
with frame[2] do
begin
scalex := 1.0; scaley := 0.5;
basex := maxx - 152; basey := (maxy div 2) - 50; zero := 50;
MakeFrames(2);
end;
with frame[3] do
begin
scalex := 1.0; scaley := 0.5;
basex := maxx - 152; basey := 15; zero := 50;
MakeFrames(3);
end;
end;
procedure LabelPlots;
begin
with frame[1] do
begin
setcolor(lightred);
outtextxy(basex - 25,maxy - basey - 60,'r');
setcolor(lightgreen);
outtextxy(basex - 25,maxy - basey - 48,'p');
setcolor(white);
outtextxy(basex + 50,maxy - basey + 4,'TIME');
end;
with frame[2] do
begin
setcolor(lightred);
outtextxy(basex - 25,maxy - basey - 66,'qo');
setcolor(lightgreen);
outtextxy(basex - 25,maxy - basey - 54,'qi');
setcolor(yellow);
outtextxy(basex - 25,maxy - basey - 42,'qd');
setcolor(white);
outtextxy(basex + 50,maxy - basey + 4,'TIME');
end;
with frame[3] do
begin
setcolor(lightred);
outtextxy(basex - 25,maxy - basey - 52,'e');
setcolor(white);
outtextxy(basex + 50,maxy - basey + 4,'TIME');
end;
end;
procedure PlotGraph(framenum,x,y,color: integer);
begin
with frame[framenum] do
putpixel(basex + round(scalex*x),
maxy - basey - round(scaley*y) - zero,
color);
end;
procedure InitControlSys;
Begin
with csys do
begin
ki := 1.0;
ko := 10.0;
o := 0.0;
r := 85.0;
s := 0.01;
end;
end;
procedure InitEnv;
begin
with env do
begin
qo := csys.o;
kf := 1.0;
kd := 1.0;
qd := 5.0;
end;
end;
procedure ShowValue(x,y,num: integer);
var numstr: string;
locx,locy: integer;
begin
setcolor(white);
locx := x+70; locy := y + 15*num;
moveto(locx,locy);
str(varref[num]^:4:2,numstr);
bar(locx,locy,locx+textwidth(numstr),locy+textheight('1'));
outtext(numstr);
end;
procedure ChangeValue(x,y: integer);
var cmd: byte;
locx,locy: integer;
begin
repeat
locx := x; locy := y + 15*menuptr;
cmd := getcmd;
moveto(locx,locy);
bar(locx,locy,locx + textwidth('>'),locy + textheight('>'));
case cmd of
up: if menuptr > 0 then dec(menuptr) else menuptr := 5;
down: if menuptr < 5 then inc(menuptr) else menuptr := 0;
ord('+'): if varref[menuptr]^ < maxlimit[menuptr] then
varref[menuptr]^ := varref[menuptr]^ + delta[menuptr];
ord('-'): if varref[menuptr]^ > minlimit[menuptr] then
varref[menuptr]^ := varref[menuptr]^ - delta[menuptr];
EndKey,EscKey: done := true;
end;
if chr(cmd) in ['+','-'] then showvalue(x,y,menuptr);
locy := y + 15*menuptr;
outtextxy(locx,locy,'>');
until (cmd in [Cr,ord('+'),ord('-')]) or done;
end;
procedure MakeMenu(x,y: integer);
var xx: integer;
begin
setcolor(white);
outtextxy(x+12,y, 'ki = ');
outtextxy(x+12,y + 15, 'ko = ');
outtextxy(x+12,y + 30, 'kf = ');
outtextxy(x+12,y + 45, 'kd = ');
outtextxy(x+12,y + 60, 'qd = ');
outtextxy(x+12,y + 75, 'r = ');
outtextxy(x+12,y + 120,'UP,DOWN ARROWS TO SELECT');
outtextxy(x+12,y + 135,'PLUS, MINUS KEYS TO CHANGE');
end;
procedure InitMenu(x,y: integer);
var i: integer;
begin
for i := 0 to 5 do showvalue(x,y,i);
outtextxy(x,y + 15*menuptr,'>');
end;
procedure initvarref;
begin
varref[0] := @csys.ki;
varref[1] := @csys.ko;
varref[2] := @env.kf;
varref[3] := @env.kd;
varref[4] := @env.qd;
varref[5] := @csys.r;
end;
begin
done := false;
InitVarref;
InitScreen;
InitControlSys;
InitEnv;
setfillstyle(0,0);
drawcs(300,140);
menuptr := 0;
MakeMenu(menux,menuy);
InitMenu(menux,menuy);
repeat
setviewport(maxx - 152,1,maxx - 1,maxy-1,false);
clearviewport;
setviewport(0,0,maxx,maxy,false);
InitPlots;
LabelPlots;
csys.o := 0.0;
clock := 0;
repeat
control(csys);
environment(env);
with csys,env do
begin
PlotGraph(1,clock,round(r),lightred);
PlotGraph(1,clock,round(p),lightgreen);
PlotGraph(2,clock,round(qi),lightgreen);
PlotGraph(2,clock,round(qo),lightred);
PlotGraph(2,clock,round(qd),yellow);
PlotGraph(3,clock,round(e),lightred);
end;
inc(clock);
until clock = 150;
while not keypressed do ;
ChangeValue(menux,menuy);
until done;
closegraph;
end.
Here's the Unit that draws the control system diagram.
unit csdiagrm;
interface
uses Crt,graph;
procedure drawcs(x,y: integer);
implementation
procedure drawcs(x,y: integer);
begin
setviewport(x,y,x+130,y+170,true);
rectangle(30,20,60,40);
rectangle(0,60,30,80);
rectangle(60,60,90,80);
rectangle(30,100,60,120);
rectangle(30,140,60,160);
settextjustify(centertext,centertext);
outtextxy(45,30,'COMP');
outtextxy(15,70,'ki');
outtextxy(75,70,'ko');
outtextxy(45,110,'kf');
outtextxy(45,150,'kd');
settextjustify(lefttext,toptext);
circle(15,110,3);
circle(75,110,3);
circle(75,150,3);
outtextxy(5,40,'p');
outtextxy(50,5,'r');
outtextxy(80,40,'e');
outtextxy(0,100,'qi');
outtextxy(80,100,'qo');
outtextxy(80,140,'qd');
line(45,0,45,20);
line(72,150,60,150);
line(30,150,15,150);
line(15,150,15,113);
line(15,107,15,80);
line(18,110,30,110);
line(60,110,72,110);
line(75,107,75,80);
line(15,60,15,30);
line(15,30,30,30);
line(60,30,75,30);
line(75,30,75,60);
setviewport(0,0,639,479,true);
end;
end.