home *** CD-ROM | disk | FTP | other *** search
- /*********************************************************************
-
- FILE
- cascade1.c - position and velocity control using cascaded PID
-
- ENTRY ROUTINES
- control - the cascade controller
-
- pgain - get position gains
- vgain - get velocity gains
- smpset - set sampling interval
-
- getv - get velocity
- getp - get position
-
- PRIVATE ROUTINES
- initpid - initialize cloop fields
- pid - perform PID control
-
- INITIALIZATION ROUTINE
- init_cascade - allocate and initialize data structures
-
- REMARKS
- This module implements the PID cascade control configuration of
- figure 3.7 in the book. This implementation differs from the
- previous cascade0.c in that the velocity and position control
- loops are described by a special data structure of type PIDLOOP.
-
- Representing each control loop by a structure variable of type
- PIDLOOP leads to a more elegant and flexible control system. It
- allows one to easily implement complex and deeply nested cascade
- control systems by merely connecting the various control loops
- through the main control program.
-
- In this demonstration program, the two loops are connected by
- control() such that the input to the innner velocity loop is the
- output of the outer position loop.
-
- This file contains only the control implementation. You have
- to implement the user interface and perhaps the simulation
- module. Please refer to cntrl1.c and rtsim1.c for examples of
- user interface and simulation modules.
-
- LAST UPDATE
- 20 March 1988
- check number of fields converted by sscanf()
-
- ***********************************************************************/
-
- /***********************************************************************
- I M P O R T S
- ***********************************************************************/
-
- #include <stdio.h>
- #include <stdlib.h> /* standard library function declarations */
- #include <string.h> /* standard string function declarations */
-
- #include "envir.h" /* environment definitions */
- #include "dash8.h" /* declarations for dash8.c */
- #include "dac2.h" /* declarations for dac2.c */
- #include "time0.h" /* declarations for time0.c */
- #include "pidloop.h" /* PIDLOOP data strcuture declarations */
- #include "cascade1.h" /* export declarations for this module */
-
- /***********************************************************************
- F O R W A R D D E C L A R A T I O N S
- ***********************************************************************/
-
- #ifdef ANSI
-
- static double getv(void); /* get velocity */
- static double getp(void); /* get position */
- static void vmact(void); /* send velocity control output to actuator */
- static void pid(PIDLOOP *); /* basic PID control */
- static void init_cascade(void); /* initialize this module */
- static void init_pid(PIDLOOP *, char *, int, int, double, double, double,
- double, double, double, double, double, double);
-
- #else /* no function prototypes */
-
- double getv();
- double getp();
- void vmact();
-
- void pid();
-
- void init_cascade();
- void init_pid();
-
- #endif
-
- /**********************************************************************
- P R I V A T E D A T A
- **********************************************************************/
-
- static int initflag = 0; /* has system been initialized? */
-
- static PIDLOOP *vloop; /* pointer to velocity control loop */
- static PIDLOOP *ploop; /* pointer to position control loop */
-
- static double tsamp = 1.0; /* sampling interval in seconds */
-
- #define VINCHAN 0 /* velocity input ADC channel */
- #define VOUTCHAN 0 /* velocity output DAC channel */
- #define PINCHAN 1 /* posiiton input ADC channel */
- #define POUTCHAN -1 /* dummy position DAC channel */
-
- #define ADCTOP 1.0 /* ADC units to position units */
- #define ADCTOV 1.0 /* ADC units to velocity units */
- #define MTODAC 1.0 /* output units to DAC units */
-
- #define MPMIN -1.0e+6 /* min position actuator output */
- #define MPMAX 1.0e+6 /* max position actuator output */
-
- /********* default PID gains and other control parameters **********/
-
- #define VKP 5.0 /* velocity PID gains */
- #define VKI 0.1
- #define VKD 0.0
-
- #define VREF 0.0 /* default velocity setpoint */
-
- #define VERR0 0.0 /* velocity errors at k, k-1 and k-2 */
- #define VERR1 0.0
- #define VERR2 0.0
-
- #define PREF 0.0 /* default position setpoint */
-
- #define PKP 2.0 /* position PID gains */
- #define PKI 0.1
- #define PKD 0.0
-
- #define PERR0 0.0 /* position errors at k, k-1 and k-2 */
- #define PERR1 0.0
- #define PERR2 0.0
-
- /**********************************************************************
- E N T R Y R O U T I N E S
- **********************************************************************/
-
- /*----------------------------------------------------------------------
- PROCEDURE
- CONTROL - implements cascade position-velocity control
-
- SYNOPSIS
- void control(cline)
- char *cline;
-
- PARAMETER
- cline - pointer to input line.
-
- REMARKS
- The error terms are initialized to zero. When this routine starts,
- it is equivalent to applying a step position reference input.
-
- LAST UPDATE
- 10 March 1988
- fixup for ANSI features
- 20 March 1988
- check number of fields converted by sscanf()
- ----------------------------------------------------------------------*/
-
- void control(cline)
- char *cline;
- {
- int nitr; /* # of iterations */
- int prnt; /* 1 - print control output, 0 - do not print */
- int i; /* iteration counter */
-
-
- if (!initflag) /* if system not initialized, do so */
- init_cascade();
-
- /* get user specified iteration limit and print switch */
-
- if (sscanf(cline, "%d %d", &nitr, &prnt) != 2)
- {
- printf("invalid or insufficient parameters\n");
- return;
- }
-
- for (i = 0; i < nitr; i++)
- {
- /*------------------------------------------------------------
- Set countdown timer to sampling period in milliseconds.
- -------------------------------------------------------------*/
-
- SetTimer((long)(tsamp * 1000L));
-
- /*--------------------------------------------------------------
- Obtain the current position and call pid() to calculate
- the position loop's control output value.
- ---------------------------------------------------------------*/
-
- ploop->cl_feedback = getp(); /* get current position */
- vloop->cl_feedback = getv(); /* get current velocity */
-
- pid(ploop); /* calc. position control o/p */
-
- /*---------------------------------------------------------------
- The output of the position loop is the setpoint for the
- velocity loop. Here we copy the position output to the
- velocity setpoint, get the current velocity and pass the
- parameters to pid() to calculate the control output for
- the velocity loop.
- ----------------------------------------------------------------*/
-
- vloop->cl_setpoint = ploop->cl_output;
-
- pid(vloop); /* calculate velocity loop output */
-
- vmact(); /* send control output to velocity actuator */
-
- if (prnt)
- printf("%d: p = %.3lf, v = %.3lf, mp = %.2lf, mv = %.2lf\n",
- i, ploop->cl_feedback, vloop->cl_feedback,
- ploop->cl_output, vloop->cl_output);
-
- if (TimeUp()) /* premature time-out */
- {
- printf("sample time too short\n");
- exit(1);
- }
-
- while (!TimeUp())
- {
- #if SIMRT
- tstep(); /* advance simulation time step */
- #endif
- }
- }
-
- }
-
-
-
- /*----------------------------------------------------------------------
- PROCEDURES
- PGAIN - interpret the input line specifying position gains
- VGAIN - interpret the input line specifying velocity gains
-
- SYNOPSIS
- void pgain(cline)
- char *cline;
-
- void vgain(cline)
- char *cline;
-
- PARAMETERS
- cline - input line
-
- REMARKS
- Checks the initflag to ensure that the vloop and ploop data
- structures are allocated and if not, to allocate and initialize
- them through init_cascade().
-
- LAST UPDATE
- 1 December 1987
- declare return type as void
- 20 March 1988
- check number of fields converted by sscanf()
- ----------------------------------------------------------------------*/
-
- void pgain(cline)
- char *cline;
- {
- double kp, ki, kd; /* P, I and D gains */
-
-
- if (sscanf(cline, "%lf %lf %lf", &kp, &ki, &kd) == 3)
- {
- #ifdef DEBUG
- printf("<pgain> kp = %.3lf, ki = %.3lf, kd = %.3lf\n", kp, ki, kd);
- #endif
-
- if (!initflag)
- init_cascade();
-
- ploop->cl_kp = kp;
- ploop->cl_ki = ki;
- ploop->cl_kd = kd;
- }
- else
- printf("invalid or insufficient position gains\n");
-
- }
-
-
- void vgain(cline)
- char *cline;
- {
- double kp, ki, kd; /* P, I and D gains */
-
-
- if (sscanf(cline, "%lf %lf %lf", &kp, &ki, &kd) == 3)
- {
- #ifdef DEBUG
- printf("<vgain> kp = %.3lf, ki = %.3lf, kd = %.3lf\n", kp, ki, kd);
- #endif
- if (!initflag)
- init_cascade();
-
- vloop->cl_kp = kp;
- vloop->cl_ki = ki;
- vloop->cl_kd = kd;
- }
- else
- printf("invalid or insufficient velocity gains\n");
-
- }
-
-
-
- /*----------------------------------------------------------------------
- PROCEDURE
- SETPOS - get position setpoint from command line
-
- SYNOPSIS
- void setpos(cline)
- char *cline;
-
- PARAMETERS
- cline - user's command line
-
- REMARKS
- Note how this differs from pgain() and vgain() in that the value
- is sscanf'ed directly into the cl_setpoint field instead of going
- through temporary variables.
-
- LAST UPDATE
- 1 December 1987
- declare return type as void
- 20 March 1988
- check number of fields converted by sscanf()
- ----------------------------------------------------------------------*/
-
- void setpos(cline)
- char *cline;
- {
-
- if (!initflag)
- init_cascade(); /* initialize controller */
-
- if (sscanf(cline,"%lf", &(ploop->cl_setpoint)) == 1)
- {
- #ifdef DEBUG
- printf("<setpos> position setpoint = %.3lf\n", ploop->cl_setpoint);
- #endif
- }
- else
- printf("invalid position setpoint\n");
-
- }
-
-
-
- /*----------------------------------------------------------------------
- PROCEDURE
- SETSMP - set sample time
-
- SYNOPSIS
- void setsmp(cline)
- char *cline;
-
- PARAMETER
- cline - user's command line
-
- DESCRIPTION
- Extract sample time from user's command line and use it to set the
- sample time on the timer.
-
- LAST UPDATE
- 20 March 1988
- check number of fields converted by sscanf()
- ----------------------------------------------------------------------*/
-
- void setsmp(cline)
- char *cline;
- {
-
- if (sscanf(cline, "%lf", &tsamp) == 1)
- {
- if (!initflag)
- {
- init_cascade(); /* initialize controller */
- }
- else /* set both position and velocity tsamps to be the same */
- {
- ploop->cl_tsamp = tsamp;
- vloop->cl_tsamp = tsamp;
- }
-
- #ifdef DEBUG
- printf("<setsmp> tsamp = %lf\n", tsamp);
- #endif
- }
- else
- printf("invalid sampling period\n");
-
- }
-
-
-
- /*----------------------------------------------------------------------
- PROCEDURE
- INIT - process system initialization command
-
- SYNOPSIS
- void init()
-
- REMARKS
- This routine must be called before control() is called.
-
- LAST UPDATE
- 10 March 1988
- add call to init_cascade() to reset parameters
- ----------------------------------------------------------------------*/
-
- void init(cline)
- char *cline;
- {
-
- ad_init(); /* initialize A/D */
- da_init(); /* initialize D/A */
-
- init_cascade(); /* initialize or reset parameters */
-
- #ifdef SIMRT
- sim_init(cline); /* initialize simulation module */
- #endif
-
- }
-
-
- /***********************************************************************
-
- The following section contains routines which interface between
- high level control code and functions which perform the hardware
- dependent functions of operating the A/D and D/A. As such, they
- are necessarily dependent on the device driver interface.
-
- ***********************************************************************/
-
- /*---------------------------------------------------------------------
- FUNCTIONS
- GETV - returns motor velocity
- GETP - returns motor position
-
- SYNOPSIS
- static double getv()
- static double getp()
-
- RETURNS
- current motor velocity (units unspecified)
- current motor position (units unspecified)
-
- REMARKS
- Reads the ADC channel specified by the cl_inchan field.
-
- Note that the source for the channel number is assumed here, but
- that's O.K. because this is a static function that services this
- module only.
-
- The alternative is to pass the channel number as an argument, but
- that means that the control() function must know how velocity and
- position is obtained, which is less desirable as it spreads the
- application specific parameters into three places: the PIDLOOP data
- structure, the control() routine and these two routines, instead
- of just the data structure and these two routines.
-
- Note that the conversion of raw ADC units to velocity and position
- units uses parameterized ADCTOV and ADCTOP conversion factors.
-
- LAST UPDATE
- 1 March 1988
- ---------------------------------------------------------------------*/
-
- static double getv()
- {
-
- return((double)ad_wread(vloop->cl_inchan) * ADCTOV);
- }
-
-
- static double getp()
- {
-
- return((double)ad_wread(ploop->cl_inchan) * ADCTOP);
- }
-
-
-
- /*----------------------------------------------------------------------
- PROCEDURE
- VMACT - send velocity control output to actuator
-
- SYNOPSIS
- static void vmact()
-
- REMARKS
- Output is converted from logical to physical units required by the
- DAC (using MTODAC) and rounded to the nearest integer.
-
- Note that the DAC channel is specified in the cl_outchan field.
-
- LAST UPDATE
- 1 March 1988
- ----------------------------------------------------------------------*/
-
- static void vmact()
- {
- int channel;
- int value;
-
-
- channel = vloop->cl_outchan;
- value = (int)((vloop->cl_output * MTODAC) + 0.5);
-
- da_write(channel, value);
-
- }
-
-
- /***********************************************************************
- P R I V A T E R O U T I N E S
- /***********************************************************************
-
- /*----------------------------------------------------------------------
- PROCEDURE
- PID - apply PID control for 1 iteration
-
- SYNOPSIS
- static void pid(param)
- PIDLOOP *param;
-
- PARAMETERS
- param - pointer to control loop data structure
-
- REMARKS
- Using data in control loop, calculate controller output and store
- it in the cl_output field.
-
- LAST UPDATE
- 11 May 1985
- ----------------------------------------------------------------------*/
-
- static void pid(param)
- PIDLOOP *param;
- {
- double delta_m; /* change in controller output */
-
-
- param->cl_err2 = param->cl_err1; /* update error terms */
- param->cl_err1 = param->cl_err0;
- param->cl_err0 = param->cl_setpoint - param->cl_feedback;
-
- delta_m = param->cl_kp * (param->cl_err0 - param->cl_err1)
- + param->cl_ki * param->cl_err0
- + param->cl_kd * (param->cl_err0 - (2.0 * param->cl_err1)
- + param->cl_err2);
-
- param->cl_output += delta_m; /* update output value */
-
- if (param->cl_output > param->cl_outmax) /* apply output limits */
- {
- param->cl_output = param->cl_outmax;
- }
- else if (param->cl_output < param->cl_outmin)
- {
- param->cl_output = param->cl_outmin;
- }
-
- }
-
-
- /*********************************************************************
- I N I T I A L I Z A T I O N R O U T I N E S
- *********************************************************************/
-
- /*--------------------------------------------------------------------
- PROCEDURE
- INIT_CASCADE - allocate and initialize data structures
-
- SYNOPSIS
- void init_cascade()
-
- REMARKS
- Sets init flag to true to indicate system initialized.
-
- The vloop and ploop structures are only allocated the first
- time this routine is called. The PID gains and setpoints are
- preserved on subsequent calls. This is to allow init() to call
- this routine when the user wants to reset/restart the program.
-
- LAST UPDATE
- 10 March 1988
- modify to preserve gains on subsequent calls.
- --------------------------------------------------------------------*/
-
- static void init_cascade()
- {
- int damin, damax; /* min and max DAC output values */
- double mvmin, mvmax; /* min and max of velocity control values */
- double kp, ki, kd; /* copies of P, I and D gains */
-
-
- if (!initflag) /* allocate velocity loop control structure */
- {
- if ((vloop = (PIDLOOP *)calloc(1, sizeof(PIDLOOP))) == (PIDLOOP *)NULL)
- {
- printf("cannot allocate velocity parameter structure\n");
- exit(1);
- }
- else /* initialize PID gains to defaults */
- {
- kp = VKP;
- ki = VKI;
- kd = VKD;
- }
- }
- else /* preserve current PID gains */
- {
- kp = vloop->cl_kp;
- ki = vloop->cl_ki;
- kd = vloop->cl_kd;
- }
-
- /*--------------------------------------------------------
- Initialize velocity PID loop control parameters.
- ----------------------------------------------------------*/
-
- da_limits(&damin, &damax); /* find D/A output limits */
-
- mvmin = (double)damin / MTODAC;
- mvmax = (double)damax / MTODAC;
-
- init_pid(vloop, "vloop", VINCHAN, VOUTCHAN, kp, ki, kd,
- VERR0, VERR1, VERR2, mvmin, mvmax, tsamp);
-
- if (!initflag) /* allocate position PID control structure */
- {
- if ((ploop = (PIDLOOP *)calloc(1, sizeof(PIDLOOP))) == (PIDLOOP *)NULL)
- {
- printf("cannot allocate position parameter structure\n");
- exit(1);
- }
- else
- {
- kp = PKP;
- ki = PKI;
- kd = PKD;
- }
- }
- else
- {
- kp = ploop->cl_kp;
- ki = ploop->cl_ki;
- kd = ploop->cl_kd;
- }
-
- /*------------------------------------------------------------
- Since the output of the position loop does not directly
- drive an actuator, we used pre-defined output limits
- MPMIN and MPMAX which are set for a very wide range.
- -------------------------------------------------------------*/
-
- init_pid(ploop, "ploop", PINCHAN, POUTCHAN, kp, ki, kd,
- PERR0, PERR1, PERR2, MPMIN, MPMAX, tsamp);
-
- initflag = 1; /* system initialized */
-
- }
-
-
-
- /*----------------------------------------------------------------------
- PROCEDURE
- INIT_PID - initialize data structure parameters for PID control
-
- SYNOPSIS
- static void init_pid(param, name, ichan, ochan, kp, ki, kd,
- e0, e1, e2, omin, omax, tsamp)
- PIDLOOP *param;
- char *name;
- int ichan, ochan;
- double kp, ki, kd, e0, e1, e2, omin, omax, tsamp;
-
- PARAMETERS
- param - pointer to control loop parameter structure
- name - pointer to name or id string
- ichan, ochan - input and output i/o channel numbers
- kp, ki, kd - P, I and D gains
- e0, e1, e2 - error terms at k, k-1 and k-2
- omin, omax - lower and upper output limits
- tsamp - sampling interval
-
- REMARKS
- The basic function of this routine is to copy the initialization
- argument parameters into the appropriate fields of the control
- loop data structure.
-
- The name field is not used at present, but is maintained for
- possible future use as a means to find or reference particular
- control loops in an application with a large number of control
- loops.
-
- The error terms can be initialized to different values for
- different starting or initial conditions.
-
- LAST UPDATE
- 10 March 1988
- explicitly NUL terminate name string
- ----------------------------------------------------------------------*/
-
- static void init_pid(param,name,ichan,ochan,kp,ki,kd,e0,e1,e2,omin,omax,tsamp)
- PIDLOOP *param;
- char *name;
- int ichan, ochan;
- double kp, ki, kd, e0, e1, e2, omin, omax, tsamp;
- {
-
- /*-------------------------------------------------------------
- strncpy() may not add a NUL terminator if name length
- exceeds NAMESIZE, so to make sure, we NUL terminate it.
- --------------------------------------------------------------*/
-
- strncpy(param->cl_name, name, NAMESIZE);
- param->cl_name[NAMESIZE] = '\0'; /* NUL terminate name */
-
- param->cl_inchan = ichan;
- param->cl_outchan = ochan;
- param->cl_kp = kp;
- param->cl_ki = ki;
- param->cl_kd = kd;
- param->cl_err0 = e0;
- param->cl_err1 = e1;
- param->cl_err2 = e2;
- param->cl_outmin = omin;
- param->cl_outmax = omax;
- param->cl_tsamp = tsamp;
-
- param->cl_feedback = 0.0;
- param->cl_output = 0.0;
-
- }
-
-