home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 15 / 15.iso / s / s300 / 1.ddi / CHAP5 / CNTRL4.C < prev    next >
Encoding:
C/C++ Source or Header  |  1988-05-02  |  18.3 KB  |  645 lines

  1. /***********************************************************************
  2.  
  3. FILE
  4.     cntrl4.c  -  PI Control of Servo Motor Speed for Multiple Motors
  5.  
  6. ENTRY ROUTINES
  7.     control  -  execute control algorithm
  8.     init     -  interpret command line for initialization 
  9.     gain     -  interpret command line for PI gains
  10.     setv     -  interpret command line for setpoint
  11.     setsmp   -  interpret command line for sampling rate
  12.     setmot   -  allocate and initialize motor structures
  13.  
  14. PRIVATE ROUTINES
  15.     pimotor  -  apply motor control
  16.     cpmotor  -  copy motor descriptors
  17.     getv     -  get velocity reading
  18.     mact     -  send controller output to actuator
  19.  
  20. REMARKS
  21.     This version implements multiple motor control; each motor has 
  22.     completely independent sampling interval, control parameters, etc.
  23.  
  24. LAST UPDATE
  25.     20 March 1988
  26.         include ANSI features
  27.     
  28.     Copyright (c) 1984-1988  D.M.Auslander and C.H. Tham
  29.     
  30. ***********************************************************************/
  31.  
  32. /***********************************************************************
  33.                     I M P O R T S
  34. ***********************************************************************/
  35.  
  36. #include <stdio.h>
  37. #include <stdlib.h>
  38. #include <math.h>
  39.  
  40. #include "envir.h"      /* environment definitions */
  41. #include "dash8.h"      /* declarations for dash8.c */
  42. #include "dac2.h"       /* declarations for dac2.c */
  43. #include "time0.h"      /* declarations for time0.c */
  44. #include "cntrl3.h"     /* exported declarations for this module */
  45.  
  46. #include "user.h"
  47.  
  48. #ifdef SIMRT
  49. #ifdef ANSI
  50. extern void tstep(void);
  51. extern void sim_init(int);
  52. extern void sim_reset(char *);
  53. #else
  54. extern void tstep();
  55. extern void sim_init();
  56. extern void sim_reset();
  57. #endif
  58. #endif
  59.  
  60. /***********************************************************************
  61.            MODULE  PRIVATE  DATA  STRUCTURES  AND  VARIABLES
  62. ***********************************************************************/
  63.  
  64. #define ADCTOV      1.0         /* ADC units to velocity units */
  65. #define MTODAC      1.0         /* output units to DAC units */
  66.  
  67. #define TIME_GRAIN  0.001       /* left time granularity */
  68.  
  69. /*-----------------------------------------------------------*/
  70. /*  This structure describes the control and configurational */
  71. /*  parameters for velocity control of a d.c. motor.         */
  72. /*-----------------------------------------------------------*/
  73.  
  74. typedef struct motor {
  75.     int ichan;      /* A/D channel number */
  76.     int mchan;      /* D/A channel number */
  77.     double v;       /* velocity */
  78.     double vref;    /* velocity setpoint */
  79.     double kp;      /* proportional gain */
  80.     double ki;      /* integral gain */
  81.     double ival;    /* integrator accumulated value */
  82.     double vcon;    /* constant in control equation */
  83.     double m;       /* controller output */
  84.     double mmin;    /* lower limit for controller output */
  85.     double mmax;    /* upper limit for controller output */
  86.     double tsamp;   /* sample interval */
  87.     double tleft;   /* time left to next sample */
  88. } MOTOR;
  89.  
  90. static MOTOR *pm0;      /* pointer to the structure for motor #0 */
  91. static int nmotors;     /* number of motors */
  92.  
  93. /*----------------------------------------------------------------*/
  94. /*  motdef is a set of default values that is used to initialize  */
  95. /*  newly allocated motor control structure.                      */
  96. /*----------------------------------------------------------------*/
  97.  
  98. static MOTOR motdef = {
  99.           0,    /* A/D channel defaults to 0 */
  100.           0,    /* D/A channel defaults to 0 */
  101.         0.0,    /* zero initial velocity */
  102.      1000.0,    /* default velocity setpoint */
  103.         1.0,    /* kp defaults to 1.0 */
  104.         0.0,    /* no integrator by default, P control by default */
  105.         0.0,    /* zero integration sum */
  106.         0.0,    /* constant term in control equation */
  107.         0.0,    /* no controller output initially */
  108.     -2048.0,    /* lower output limit */
  109.      2047.0,    /* upper output limit */
  110.         0.5,    /* default sampling interval of 0.5 seconds */
  111.        0.0      /* time left to next sample */
  112. };
  113.  
  114. /***********************************************************************
  115.                F O R W A R D    D E C L A R A T I O N S
  116. ***********************************************************************/
  117.  
  118. #ifdef ANSI
  119.  
  120. double  getv(int);
  121. void    mact(int, double);
  122. void    pimotor(MOTOR *);
  123. void    cpmotor(MOTOR *, MOTOR *);
  124.  
  125. #else
  126.  
  127. double  getv();
  128. void    mact();
  129. void    pimotor();
  130. void    cpmotor();
  131.  
  132. #endif
  133.  
  134. /***********************************************************************
  135.                     E N T R Y    R O U T I N E S
  136. ***********************************************************************/
  137.  
  138. /*----------------------------------------------------------------------
  139. PROCEDURE
  140.     CONTROL  -  this function does the control
  141.  
  142. SYNOPSIS
  143.     void control(cline)
  144.     char cline[];
  145.  
  146. PARAMETER
  147.     cline  -  user's input line that specifies the number of iterations
  148.               and whether to trace the control action.
  149.  
  150. REMARKS
  151.     The control output is calculated in two parts - first with only P
  152.     action and then with I action added.  The output is limited to what
  153.     the actuator can put out, with provisions against reset windup.
  154.  
  155. LAST UPDATE
  156.     20 March 1988
  157.         use ANSI features
  158. ----------------------------------------------------------------------*/
  159.  
  160. void control(cline)
  161. char *cline;
  162. {
  163.     MOTOR *pm;          /* pointer to motor data structure */
  164.     long i;             /* iteration counter */
  165.     int m;              /* motor number index */
  166.  
  167.  
  168.     prologue();         /* prepare scheduling kernel */
  169.  
  170.     pm = pm0;           /* set pm to point at first motor structure */
  171.  
  172.     for (m = 0; m < nmotors; m++)
  173.     {
  174.         pm->ival = 0.0;     /* initialize the integrator */
  175.  
  176.         SetCntrlTask("motor", pimotor, (void *)pm, 1000,
  177.                         (long)(pm->tsamp * 1000.0  +  0.5));
  178.  
  179.         pm++;               /* next motor */
  180.     }
  181.  
  182.     start();            /* start the scheduler */
  183.         
  184.  
  185.  
  186.  
  187. /*----------------------------------------------------------------------
  188. PROCEDURE
  189.     INIT  -  process system initialization command
  190.  
  191. SYNOPSIS
  192.     void init(cline)
  193.     char *cline;
  194.  
  195. PARAMETER
  196.     cline  -  data part of input command line, specifying step size
  197.  
  198. REMARKS
  199.     This routine must be called before control() is called.
  200.  
  201. LAST UPDATE
  202.     20 March 1988
  203.         change sim_init()
  204. ----------------------------------------------------------------------*/
  205.  
  206. void init(cline)
  207. char *cline;
  208. {
  209.  
  210.     ad_init();              /* initialize A/D */
  211.     da_init();              /* initialize D/A */
  212.  
  213. #ifdef SIMRT
  214.     sim_reset(cline);
  215. #endif
  216.  
  217. }
  218.  
  219.  
  220.  
  221. /*----------------------------------------------------------------------
  222. PROCEDURE
  223.     GAIN  -  interpret input line for controller gains specification
  224.  
  225. SYNOPSIS
  226.     void gain(cline)
  227.     char *cline;
  228.  
  229. PARAMETER
  230.     cline  -  pointer to null terminated input command line
  231.  
  232. REMARKS
  233.     Note that in gain(), setv() and setsmp(), the input data is put
  234.     into temporary storage first.  This is because the specified
  235.     motor number may be out of range.
  236.  
  237. LAST UPDATE
  238.     20 March 1988
  239.         use ANSI features
  240. ----------------------------------------------------------------------*/
  241.  
  242. void gain(cline)
  243. char *cline;
  244. {
  245.     int mn;                 /* motor number */
  246.     double kp, ki, vcon;    /* temporary gain and constant terms */
  247.  
  248.  
  249.     if (sscanf(cline, "%d %lf %lf %lf", &mn, &kp, &ki, &vcon) == 4)
  250.     {
  251.         if ((mn >= 0) && (mn < nmotors))
  252.         {
  253.             /*
  254.              *  Set the appropriate fields of the motor descriptor.
  255.              *  Note that (pm0 + mn) is a pointer to the descriptor
  256.              *  for the mn-th motor; hence the use of "->" to
  257.              *  dereference the descriptor fields.
  258.              */
  259.  
  260.             (pm0 + mn)->kp = kp;
  261.             (pm0 + mn)->ki = ki;
  262.             (pm0 + mn)->vcon = vcon;
  263.  
  264. #ifdef DEBUG
  265.             printf("<gain> motor = %d, ", mn);
  266.             printf("kp = %lf, ki = %lf, vcon = %lf\n", kp, ki, vcon);
  267. #endif
  268.         }
  269.         else
  270.             printf("gain: invalid motor number: %d\n", mn);
  271.     }
  272.     else
  273.         printf("gain: invalid or insufficient parameters\n");
  274.  
  275. }
  276.  
  277.  
  278.  
  279. /*----------------------------------------------------------------------
  280. PROCEDURE
  281.     SETV  -  get velocity setpoint from command line
  282.  
  283. SYNOPSIS
  284.     void setv(cline)
  285.     char *cline;
  286.  
  287. PARAMETER
  288.     cline  -  pointer to null terminated input command line
  289.  
  290. LAST UPDATE
  291.     20 March 1988
  292.         use ANSI features
  293. ----------------------------------------------------------------------*/
  294.  
  295. void setv(cline)
  296. char *cline;
  297. {
  298.     int mn;         /* motor number */
  299.     double vref;    /* temporary holder for setpoint value */
  300.  
  301.  
  302.     if (sscanf(cline, "%d %lf", &mn ,&vref) == 2)
  303.     {
  304.         if ((mn >= 0) && (mn < nmotors))
  305.         {
  306.             (pm0 + mn)->vref = vref;        /* record velocity reference */
  307.  
  308. #ifdef DEBUG
  309.             printf("<setv> motor = %d, vref = %lf\n", mn, vref);
  310. #endif
  311.         }
  312.         else
  313.             printf("setv: bad motor number: %d\n", mn);
  314.     }
  315.     else
  316.         printf("setv: invalid or insufficient parameters\n");
  317.  
  318. }
  319.  
  320.  
  321.  
  322. /*----------------------------------------------------------------------
  323. PROCEDURE
  324.     SETSMP  -  set sampling interval from command line specification
  325.  
  326. SYNOPSIS
  327.     void setsmp(cline)
  328.     char *cline;
  329.  
  330. PARAMETER
  331.     cline  -  pointer to null terminated input command line
  332.  
  333. LAST UPDATE
  334.     20 March 1988
  335.         use ANSI features
  336. ----------------------------------------------------------------------*/
  337.  
  338. void setsmp(cline)
  339. char *cline;
  340. {
  341.     int mn;             /* motor number */
  342.     double tsamp;       /* temporary holder for sample time */
  343.  
  344.  
  345.     if (sscanf(cline,"%d %lf", &mn, &tsamp) == 2)
  346.     {
  347.         if ((mn >= 0) && (mn < nmotors))
  348.         {
  349.             (pm0 + mn)->tsamp = tsamp;      /* set sample interval */
  350.  
  351. #ifdef DEBUG
  352.             printf("<setsmp> motor = %d, tsamp = %f\n", mn, tsamp);
  353. #endif
  354.         }
  355.         else
  356.             printf("setsmp: bad motor number: %d\n", mn);
  357.     }
  358.     else
  359.         printf("setsmp: invalid or insufficient parameters\n");
  360.  
  361. }
  362.  
  363.  
  364.  
  365. /*----------------------------------------------------------------------
  366. PROCEDURE
  367.     SETMOT  -  allocate and initialize data structures for the number
  368.                 of motors requested by the user.
  369. SYNOPSIS
  370.     void setmot(void)
  371.  
  372. REMARKS
  373.     This routine must be called as the first action of the main program.
  374.  
  375.     Motor descriptors initialized from defaults specified in "motdef"
  376.  
  377. LAST UPDATE
  378.     20 March 1988
  379.         use ANSI features
  380. ----------------------------------------------------------------------*/
  381.  
  382. void setmot()
  383. {
  384.     MOTOR *pm;          /* pointer to motor data structures */
  385.     int nm;             /* number of motors */
  386.     char cbuf[20];      /* input line */
  387.     int damin, damax;   /* D/A output limits */
  388.     int done;           /* flag, if set, o.k. to return */
  389.     int i;              /* loop index */
  390.  
  391.  
  392.     do
  393.     {
  394.         done = 1;
  395.  
  396.         /*
  397.          *  Prompt user and get response line.  Because scanf() does not
  398.          *  remove the newline character at the end of lines, fgets() is
  399.          *  used instead since the newline character may cause trouble
  400.          *  with other console functions.
  401.          */
  402.  
  403.         fputs("How many motors? ", stdout);
  404.         fgets(cbuf, 20, stdin);         /* get at most 19 characters */
  405.  
  406.         if (sscanf(cbuf, "%d", &nm) == 1)   /* decode input */
  407.         {
  408.             da_limits(&damin, &damax);      /* find DAC limits */
  409.  
  410.             motdef.mmin = (double)damin / MTODAC;   /* set output limits */
  411.             motdef.mmax = (double)damax / MTODAC;
  412.  
  413.             /*
  414.              *  Dynamically allocate memory for the required number of
  415.              *  motor structures.  Note that calloc() is used instead of
  416.              *  malloc() since calloc() is suppose to guarantee that the
  417.              *  allocated block of memory has the correct alignment.
  418.              *  Note also the use of "casts" in the allocation statement;
  419.              *  though not strictly necessary, it is recommended as good
  420.              *  programming style.
  421.              */
  422.  
  423.             if ((pm0 = (MOTOR *)calloc(nm, sizeof(MOTOR))) == (MOTOR *)NULL)
  424.             {
  425.                 printf("INSUFFICIENT MEMORY: ");
  426.                 printf("cannot allocate %d motor blocks\n", nm);
  427.                 exit(1);
  428.             }
  429.  
  430.             for (i = 0, pm = pm0; i < nm; i++, pm++)
  431.             {
  432.                 cpmotor(&motdef, pm);   /* init with defaults from motdef */
  433.             
  434.                 pm->ichan = i;  /* set both A/D and D/A channels to i; the */
  435.                 pm->mchan = i;  /*   actual channels depends on your setup */
  436.             }
  437.  
  438.             nmotors = nm;       /* record number of motor descriptors */
  439.  
  440. #ifdef SIMRT
  441.             sim_init(nm);   /* if real-time simulation is to be used, */
  442. #endif                      /*   initialize the simulation module.    */
  443.  
  444.             if (nm == 1)
  445.                 printf("\nmotor 0 has been initialized\n");
  446.             else
  447.                 printf("\nmotors 0 to %d have been initialized\n", nm - 1);
  448.         }
  449.         else
  450.         {
  451.             printf("invalid entry\n");
  452.             done = 0;
  453.         }
  454.     }
  455.     while (!done);
  456.  
  457. }
  458.  
  459.  
  460. /***********************************************************************
  461.                     P R I V A T E    R O U T I N E S 
  462. ***********************************************************************/
  463.  
  464. /*----------------------------------------------------------------------
  465. PROCEDURE
  466.     PIMOTOR  -  apply PI control for motor
  467.  
  468. SYNOPSIS
  469.     static void pimotor(pm, prnt)
  470.     MOTOR *pm;
  471.     int prnt;
  472.  
  473. PARAMETERS
  474.     pm    -  pointer to motor structure requiring control
  475.     prnt  -  if 1, print control output
  476.  
  477. LAST UPDATE
  478.     20 March 1988
  479.         use ANSI features
  480.     10 April 1988
  481.         modify for clotho background scheduler
  482. ----------------------------------------------------------------------*/
  483.  
  484. static void pimotor(pm)
  485. MOTOR *pm;
  486. {
  487.     double m1;      /* intermediate result in control calculation. */
  488.     double error;   /* error between reference and measured */
  489.     int mn;         /* motor number */
  490.  
  491.  
  492.     pm->v = getv(pm->ichan);            /* obtain motor velocity */
  493.  
  494.     error = pm->vref - pm->v;           /* calculate error term */
  495.     pm->ival += error;                  /* update integral of error */
  496.     m1 = pm->kp * error + pm->vcon;     /* do P control first */
  497.  
  498.     /*
  499.      *  Check for actuator limits and apply "reset windup" control.
  500.      *  If the output would exceed actuator limits, set the integral
  501.      *  term to zero to prevent the integral term from dominating
  502.      *  the output and slow down corrective action.
  503.      */
  504.  
  505.     if (m1 > pm->mmax)
  506.     {
  507.         pm->m = pm->mmax;
  508.         pm->ival = 0.0;
  509.     }
  510.     else if (m1 < pm->mmin)
  511.     {
  512.         pm->m = pm->mmin;
  513.         pm->ival = 0.0;
  514.     }
  515.     else    /* output withing actuator limits */
  516.     {
  517.         pm->m = m1 + (pm->ki * pm->ival);   /* now add integral term */
  518.     }
  519.  
  520.     mn = (pm - pm0);    /* compute motor number for output */
  521.  
  522.     printf("motor = %d, v = %7.3lf, m = %7.3lf\n", mn, pm->v, pm->m);
  523.  
  524.     mact(pm->mchan, pm->m); /* send controller output to actuator */
  525.  
  526. }
  527.  
  528.  
  529.  
  530. /*----------------------------------------------------------------------
  531. PROCEDURE
  532.     CPMOTOR  -  copy one motor descriptor to another
  533.  
  534. SYNOPSIS
  535.     static void cpmotor(from, to)
  536.     MOTOR *from, *to;
  537.  
  538. PARAMETERS
  539.     from  -  pointer to source
  540.     to    -  pointer to destination
  541.  
  542. REMARKS
  543.     Standard (K&R) C does not allow entire structures to be assigned
  544.     (this will change with the proposed ANSI X3J11 standards).  This
  545.     restriction is very inconvenient and this routine gets around this
  546.     by doing a byte-by-byte copy of the descriptor contents.  This
  547.     technique may not work on all computers, but it is reasonably
  548.     robust and is much easier than doing field by field assignments.
  549.  
  550.     If you are using an ANSI conforming C compiler, just do a structure
  551.     assignment.  The compiler will take care of the rest.
  552.  
  553. LAST UPDATE
  554.     20 March 1988
  555.         use ANSI features
  556. ----------------------------------------------------------------------*/
  557.  
  558. static void cpmotor(from, to)
  559. MOTOR *from, *to;
  560. {
  561.  
  562. #ifdef ANSI     /* can do structure assignment */
  563.  
  564.     *to = *from;
  565.  
  566. #else           /* do explicit byte by byte copy */
  567.  
  568.     register char *src, *dst;   /* source and destination pointers */
  569.     unsigned count;             /* byte count */
  570.  
  571.  
  572.     src = (char *)from;     /* make fast copy of source pointer */
  573.     dst = (char *)to;       /* make fast copy of destination pointer */
  574.  
  575.     for (count = 0; count < sizeof(MOTOR); count++)
  576.         *dst++ = *src++;
  577.  
  578. #endif
  579.  
  580. }
  581.  
  582.  
  583.  
  584. /*----------------------------------------------------------------------
  585. FUNCTION
  586.     GETV  -  get current motor velocity
  587.  
  588. SYNOPSIS
  589.     static double getv(channel)
  590.     int channel;
  591.  
  592. PARAMETER
  593.     channel  -  A/D channel from which to read the velocity
  594.  
  595. RETURNS
  596.     current motor velocity
  597.  
  598. REMARKS
  599.     Note that the conversion of raw ADC units to velocity units
  600.     uses the ADCTOV conversion factor.
  601.  
  602. LAST UPDATE
  603.     20 March 1988
  604.         use ANSI features
  605. ----------------------------------------------------------------------*/
  606.  
  607. static double getv(channel)
  608. int channel;
  609. {
  610.  
  611.     return((double)ad_wread(channel) * ADCTOV);
  612. }
  613.  
  614.  
  615.  
  616. /*----------------------------------------------------------------------
  617. PROCEDURE
  618.     mact  -  send controller output to D/A converter
  619.  
  620. SYNOPSIS
  621.     static void mact(channel, output)
  622.     int channel;
  623.     double output;
  624.  
  625. PARAMETERS
  626.     channel -  D/A channel number
  627.     output  -  controller output
  628.  
  629. REMARKS
  630.     Multiply by conversion factor and add 0.5 for rounding.
  631.  
  632. LAST UPDATE
  633.     20 March 1988
  634.         use ANSI features
  635. ----------------------------------------------------------------------*/
  636.  
  637. static void mact(channel, output)
  638. int channel;
  639. double output;
  640. {
  641.     
  642.     da_write(channel, (int)((output * MTODAC) + 0.5));
  643. }
  644.