The PID Emulator (emul.g)

Cogent Tools Demo and Tutorials: Version 4.0
Prev |  | Next

# Chapter 5. The PID Emulator (`emul.g`)

The PID Emulator creates a mathematically simulated PID loop. This chapter presents the code in several annotated sections. The complete code is shown in the section emul.g`` - The PID Emulator program in the View the Code chapter.

# 5.1. The PID Controller Class - `PID_Controller`

This class (created with the Gamma `class` statement) contains the variables and methods used to create the PID Controller, which controls the `MV_001` datahub point corresponding to the MV: Control output display in the Monitor.

       /********************************************************
        *                PID Controller Class                  *
        ********************************************************/

       /*--------------------------------------------------------------------
        * Class:       PID_Controller
        * Description: Contains data and methods for creating and maintaining
        *              a simulated PID loop.
        *------------------------------------------------------------------*/
       class PID_Controller
       {
         name;

         errorPrev;
         errorInt = 0;
         errorDrv = 0;

         kProp;
         kInt;
         kDrv;
       }

Using a Gamma `method` statement, we create the `.init` method, which sets up the three datahub points corresponding to the proportional, integral, and derivative values (`.kProp`, `.kInt`, and `.kDrv`). With Gamma's `symbol` and `string` functions, it names these points by using the class `name` instance variable conjoined with a suffix (`_Kp`, `_Ki`, or `_Kd`). This allows for the creation of multiple controller classes, each with their own unique datahub points.

Here also is the first use of Gamma's `local` statement, which allows us to define variables in a local scope.

       /*--------------------------------------------------------------------
        * Method:      PID_Controller.init
        * Returns:     a name
        * Description: Initializes the PID control loop by creating three
        *              variables representing proportion, integration, and
        *              derivative.
        *------------------------------------------------------------------*/
       method PID_Controller.init (name)
       {
         local val;

         .name = name;

         .kProp = symbol(string(name, "_Kp"));
         val = register_point(.kProp);
         if (number_p(val) && (conf(val) != 0))
           set(.kProp, val);
         else
           write_point(.kProp, 0.05);

We use the Gamma function `register_point` to register the points with the datahub. Then we check to be sure that the value of the point is a number (using the Gamma `number_p` predicate function) and that it has a confidence greater than `0` (using Gamma's `conf` function). If so, it is OK to set our variable to the value of the point. In doing this, we need to evaluate `.kProp`, so we use Gamma's `set` function. If the value of the point is not a number or has a confidence of `0`, we overwrite the point with a default value corresponding to Poor in the Monitor's PID Loop options, using the Gamma function `write_point` The other two points are done in similar fashion.

| The Gamma function `conf` is used to check the confidence of each of these points in the datahub, and if necessary write a default value whose confidence is `100`. If we don't do this, when the program first starts and the datahub hasn't run yet, the confidence for these points will be `0`, and their values won't appear in the Monitor.

         .kInt = symbol(string(name, "_Ki"));
         val = register_point(.kInt);
         if (number_p(val) && (conf(val) != 0))
           set(.kInt, val);
         else
           write_point(.kInt, 0.7);

         .kDrv = symbol(string(name, "_Kd"));
         val = register_point(.kDrv);
         if (number_p(val) && (conf(val) != 0))
           set(.kDrv, val);
         else
           write_point(.kDrv, 0.05);
         .name;
       }

This next method performs the calculations for changing the value of `MV_001`. It finds the difference between `SP_001` and `PV_001`, approximates an integral and a derivative, and uses this information to create a new value for `MV_001`.

       /*--------------------------------------------------------------------
        * Method:      PID_Controller.calculate
        * Returns:     an approximate value for MV, as a real number
        * Description: Sets and writes a new MV value.
        *------------------------------------------------------------------*/
       method PID_Controller.calculate (sp, mv, pv)
       {
         local error, res;

         error = eval(sp) - eval(pv);

         /* Appproximate the integral. */
         .errorInt += error;

         /* Prevent error "wind-up" effect. */
         if (.errorInt < 0)
           .errorInt = 0;

         /* Approximate the derivative. */
         if (.errorPrev)
           .errorDrv = error - .errorPrev;
         .errorPrev = error;

         res = (eval(.kProp) * error
                + eval(.kInt) * .errorInt
                + eval(.kDrv) * .errorDrv);
         res = floor (res * 100) / 100;

The names for the three points are passed in by the `change_values` function protected from evaluation. In order to get their current values, we use the Gamma `eval` function.

To best emulate a real-world situation, we set boundaries for our controller at `0` and `200`. This gives us enough leeway to easily control the plant between values of `0` and `100`, and it keeps all the values positive.

However, this has an effect on the integral error value (`.errorInt`). If `SP_001` changes value suddenly from very high to very low, the error "winds up" to a very high negative value (such as `-400`) and it takes half a minute or so to return to a positive value. This causes a delay in the updating of `MV_001` and `PV_001`. To prevent this effect, we set a lower limit of `0` on the integral error.

We also set the confidence of `res` to `100` so that the datahub point `MV_001` will have a confidence of `100`.

         /* Set boundary conditions. */
         if (res > 200)
           res = 200;
         if (res <= 0)
           res = 0;
         write_point (mv, res);
         res;
       }

Prev | Home | Next
Data and Control Flow | Up | The Plant Model - `change_procvar`

Copyright 1995-2002 by Cogent Real-Time Systems, Inc.