Pascal code for running control system

[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.

Tom Bourbon [941102.1635]

[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 . . ..

Nifty, Bill. This program runs nicely in TP6.0. It should help keep all of
us (who can run it) talking about the same things, at least when we discuss
the basic model.

Bruce Abbott -- Your programs run in TP6.0, but I'm about to debug the
graphics initialization. When I run either of the programs (Ecoli1 or
Ecoli2) for the first time, it is fine. If I try to re-run one of the
programs without first running the other one, or some other program that
initializes the graphics, it doesn't work.

For those who don't have a DOS machine, you need to get one. A machine that
will run all of the PCT DOS programs can be had for well under $1000. I
can't think of a more productive way for you to use your cash, or that of
someone who knows you and loves you -- spouse, chairperson, boss, your
senator or congressional representative (if you are in the USA and one of
these people is up for re-election next week) . . .. :slight_smile:

Tom

Tom Bourbon [941102.1635]

For those who don't have a DOS machine, you need to get one. A machine that
will run all of the PCT DOS programs can be had for well under $1000. I
can't think of a more productive way for you to use your cash, or that of
someone who knows you and loves you -- spouse, chairperson, boss, your
senator or congressional representative (if you are in the USA and one of
these people is up for re-election next week) . . .. :slight_smile:

That depends on where you live. Here in the cold north is more like $2000
(Canadians, that is). Still, for those of us who have little money and use
Macintosh, there is the alternative of using SoftPC ($110, American that
is). A little bit slow, but I tried it las week with the PCTDEMOS and they
seem to work fine.

Francisco

J Francisco Arocha Tel: (514) 398-4985
1110 Pine Avenue W. Fax (514) 398-7246
Centre for Medical Education
McGill University
Montreal, QC H3A 1A3
Canada