frclib/TrcPIDCtrl.h
author Trc492/3543
Wed Apr 11 21:49:15 2012 -0700 (2012-04-11)
changeset 242 8f5f116afc38
parent 222 786e16659b3d
child 258 746dfa65aecc
permissions -rw-r--r--
Added color font to debug tracing
     1 #if 0
     2 /// Copyright (c) Titan Robotics Club. All rights reserved.
     3 ///
     4 /// <module name="TrcPIDCtrl.h" />
     5 ///
     6 /// <summary>
     7 ///     This module contains the definition and implementation of the
     8 ///     TrcPIDCtrl class.
     9 /// </summary>
    10 ///
    11 /// <remarks>
    12 ///     Environment: Wind River C++ for National Instrument cRIO based Robot.
    13 /// </remarks>
    14 #endif
    15 
    16 #ifndef _TRCPIDCTRL_H
    17 #define _TRCPIDCTRL_H
    18 
    19 #ifdef MOD_ID
    20     #undef MOD_ID
    21 #endif
    22 #define MOD_ID                  MOD_PIDCTRL
    23 #ifdef MOD_NAME
    24     #undef MOD_NAME
    25 #endif
    26 #define MOD_NAME                "TrcPIDCtrl"
    27 
    28 //
    29 // PIDCtrl options.
    30 //
    31 #define PIDCTRLO_INVERSE        0x00000001
    32 #define PIDCTRLO_ABS_SETPT      0x00000002
    33 #define PIDCTRLO_SPEED_CTRL     0x00000004
    34 #define PIDCTRLO_NO_OSCILLATE   0x00000008
    35 
    36 class TrcPIDCtrl;
    37 
    38 /**
    39  * This abstract class defines the PIDInput object. The object is
    40  * a callback interface. It is not meant to be created as an object.
    41  * Instead, it should be inherited by a subclass who needs to provide
    42  * input data to a PID controller.
    43  */
    44 class PIDInput
    45 {
    46 public:
    47     /**
    48      * This function is provided by the subclass to provide input to the PID
    49      * controller object.
    50      *
    51      * @param pidCtrl Points to the PIDCtrl object that requires input.
    52      */
    53     virtual
    54     float
    55     GetInput(
    56         TrcPIDCtrl *pidCtrl
    57         ) = 0;
    58 };  //class PIDInput
    59 
    60 /**
    61  * This class defines and implements the TrcPIDCtrl object. This object
    62  * replaces the PIDController object from the WPI library. The PIDController
    63  * object in the WPI library is not flexible enough because it dealt with
    64  * PID input and PID output directly. In contrast, the TrcPIDCtrl object is
    65  * a simple primitive that calculates the PID output according to the given
    66  * PID input. So it is agnostic to the kind of input, output or even how the
    67  * calculated output is used. This is especially important because the
    68  * TrcPIDDrive object is going to combine three PID controllers to
    69  * calculate the drive output. One PID controller is for driving straight
    70  * distance in the X direction, one PID controller is for driving straight
    71  * distance in the Y direction, and one for rotation. In addition, the WPI
    72  * PIDController is creating a periodic loop per controller. In the TRC
    73  * library, we will do periodic loop at a higher level in the TrcPIDDrive
    74  * object instead.
    75  */
    76 class TrcPIDCtrl
    77 {
    78 private:
    79 
    80     double    m_Kp;
    81     double    m_Ki;
    82     double    m_Kd;
    83     float     m_tolerance;
    84     UINT32    m_settlingTime;
    85     UINT32    m_pidCtrlOptions;
    86     float     m_minInput;
    87     float     m_maxInput;
    88     float     m_minOutput;
    89     float     m_maxOutput;
    90     float     m_prevError;
    91     float     m_totalError;
    92     UINT32    m_startSettling;
    93     float     m_setPoint;
    94     float     m_prevOutput;
    95 
    96 public:
    97     /**
    98      * Constructor: Create an instance of the TrcPIDCtrl object.
    99      *
   100      * @param idString Identifying the PID controller, mainly for logging
   101      *        purpose.
   102      * @param Kp Specifies the proportional coefficient.
   103      * @param Ki Specifies the integral coefficient.
   104      * @param Kd Specifies the derivative coefficient.
   105      * @param tolerance Specifies the on-target tolerance.
   106      * @param settlingTime Specifes the on-target settling time in msec.
   107      * @param pidCtrlOptions Specifies the option flags.
   108      * @param initialOutput Specifies the initial prevOutput value,
   109      *        applicable only in speed control mode.
   110      */
   111     TrcPIDCtrl(
   112         char  *idString,
   113         double Kp,
   114         double Ki,
   115         double Kd,
   116         float  tolerance = 0.0,
   117         UINT32 settlingTime = 0,
   118         UINT32 pidCtrlOptions = 0,
   119         float  initialOutput = 0.0
   120         ): m_Kp(Kp)
   121          , m_Ki(Ki)
   122          , m_Kd(Kd)
   123          , m_tolerance(tolerance)
   124          , m_settlingTime(settlingTime)
   125          , m_pidCtrlOptions(pidCtrlOptions)
   126          , m_minInput(0.0)
   127          , m_maxInput(0.0)
   128          , m_minOutput(-1.0)
   129          , m_maxOutput(1.0)
   130          , m_prevError(0.0)
   131          , m_totalError(0.0)
   132          , m_startSettling(0)
   133          , m_setPoint(0.0)
   134          , m_prevOutput(initialOutput)
   135     {
   136         TLevel(INIT);
   137         TEnterMsg(("ID=%s,Kp=%f,Ki=%f,Kd=%f,tolerance=%f,settlingTime=%d,"
   138                    "options=%x,initialOuptut=%f",
   139                    idString, Kp, Ki, Kd, tolerance, settlingTime,
   140                    pidCtrlOptions, initialOutput));
   141 
   142 #ifdef _LOGDATA_PIDCTRL
   143         DataLogger *dataLogger = DataLogger::GetInstance();
   144         dataLogger->AddDataPoint(MOD_NAME, idString, "setPt", "%f",
   145                                  DataFloat, &m_setPoint);
   146         dataLogger->AddDataPoint(MOD_NAME, idString, "output", "%f",
   147                                  DataFloat, &m_prevOutput);
   148         dataLogger->AddDataPoint(MOD_NAME, idString, "error", "%f",
   149                                  DataFloat, &m_prevError);
   150         dataLogger->AddDataPoint(MOD_NAME, idString, "totalError", "%f",
   151                                  DataFloat, &m_totalError);
   152 #endif
   153 
   154         TExit();
   155     }   //TrcPIDCtrl
   156 
   157     /**
   158      * Destructor: Destroy an instance of the TrcPIDCtrl object.
   159      */
   160     ~TrcPIDCtrl(
   161         void
   162         )
   163     {
   164         TLevel(INIT);
   165         TEnter();
   166         TExit();
   167     }   //~TrcPIDCtrl
   168 
   169     /**
   170      * This function resets the PID controller.
   171      */
   172     void
   173     Reset(
   174         void
   175         )
   176     {
   177         TLevel(API);
   178         TEnter();
   179 
   180         m_prevError = 0.0;
   181         m_totalError = 0.0;
   182 
   183         TExit();
   184         return;
   185     }   //Reset
   186 
   187     /**
   188      * This function gets the proportional constant.
   189      *
   190      * @return Proportional coefficient constant.
   191      */
   192     float
   193     GetKp(
   194         void
   195         )
   196     {
   197         TLevel(API);
   198         TEnter();
   199         TExitMsg(("=%f", m_Kp));
   200         return m_Kp;
   201     }   //GetKp
   202 
   203     /**
   204      * This function gets the integral constant.
   205      *
   206      * @return Integral coefficient constant.
   207      */
   208     float
   209     GetKi(
   210         void
   211         )
   212     {
   213         TLevel(API);
   214         TEnter();
   215         TExitMsg(("=%f", m_Ki));
   216         return m_Ki;
   217     }   //GetKi
   218 
   219     /**
   220      * This function gets the differential constant.
   221      *
   222      * @return Differential coefficient constant.
   223      */
   224     float
   225     GetKd(
   226         void
   227         )
   228     {
   229         TLevel(API);
   230         TEnter();
   231         TExitMsg(("=%f", m_Kd));
   232         return m_Kd;
   233     }   //GetKd
   234 
   235     /**
   236      * This function gets the PID controller constants.
   237      *
   238      * @param pKp Points to the variable to hold the proportional constant.
   239      * @param pKi Points to the variable to hold the integral constant.
   240      * @param pKd Points to the variable to hold the differential constant.
   241      */
   242     void
   243     GetPID(
   244         double *pKp = NULL,
   245         double *pKi = NULL,
   246         double *pKd = NULL
   247         )
   248     {
   249         TLevel(API);
   250         TEnterMsg(("pKp=%p,pKi=%p,pKd=%p", pKp, pKi, pKd));
   251 
   252         if (pKp != NULL)
   253         {
   254             *pKp = m_Kp;
   255         }
   256 
   257         if (pKi != NULL)
   258         {
   259             *pKi = m_Ki;
   260         }
   261 
   262         if (pKd != NULL)
   263         {
   264             *pKd = m_Kd;
   265         }
   266 
   267         TExitMsg(("Kp=%f,Ki=%f,Kd=%f", m_Kp, m_Ki, m_Kd));
   268         return;
   269     }   //GetPID
   270 
   271     /**
   272      * This function sets the PID controller constants.
   273      *
   274      * @param Kp Specifies the proportional constant.
   275      * @param Ki Specifies the integral constant.
   276      * @param Kd Specifies the differential constant.
   277      */
   278     void
   279     SetPID(
   280         double Kp,
   281         double Ki,
   282         double Kd
   283         )
   284     {
   285         TLevel(API);
   286         TEnterMsg(("Kp=%f,Ki=%f,Kd=%f", Kp, Ki, Kd));
   287 
   288         m_Kp = Kp;
   289         m_Ki = Ki;
   290         m_Kd = Kd;
   291 
   292         TExit();
   293         return;
   294     }   //SetPID
   295 
   296     /**
   297      * This function gets the last error.
   298      *
   299      * @return the last error.
   300      */
   301     float
   302     GetError(
   303         void
   304         )
   305     {
   306         TLevel(API);
   307         TEnter();
   308         TExitMsg(("=%f", m_prevError));
   309         return m_prevError;
   310     }   //GetError
   311 
   312     /**
   313      * This function gets the PID controller setpoint.
   314      *
   315      * @return the current setpoint.
   316      */
   317     float
   318     GetTarget(
   319         void
   320         )
   321     {
   322         TLevel(API);
   323         TEnter();
   324         TExitMsg(("=%f", m_setPoint));
   325         return m_setPoint;
   326     }   //GetTarget
   327 
   328     /**
   329      * This function sets the PID controller setpoint.
   330      *
   331      * @param setPoint Specifies the target setpoint.
   332      * @param currInput Specifies the current input value.
   333      */
   334     void
   335     SetTarget(
   336         float setPoint,
   337         float currInput
   338         )
   339     {
   340         TLevel(API);
   341         TEnterMsg(("setpt=%f,currInput=%f", setPoint, currInput));
   342 
   343         if (!(m_pidCtrlOptions & PIDCTRLO_ABS_SETPT))
   344         {
   345             setPoint += currInput;
   346         }
   347 
   348         if (m_maxInput > m_minInput)
   349         {
   350             if (setPoint > m_maxInput)
   351             {
   352                 m_setPoint = m_maxInput;
   353             }
   354             else if (setPoint < m_minInput)
   355             {
   356                 m_setPoint = m_minInput;
   357             }
   358             else
   359             {
   360                 m_setPoint = setPoint;
   361             }
   362         }
   363         else
   364         {
   365             m_setPoint = setPoint;
   366         }
   367         m_prevError = m_setPoint - currInput;
   368         m_totalError = 0.0;
   369         m_startSettling = GetMsecTime();
   370 
   371         TExit();
   372         return;
   373     }   //SetTarget
   374 
   375     /**
   376      * This function determines if we are on target by checking if the
   377      * previous error is within target tolerance and remain within tolerance
   378      * for at least the settling period.
   379      *
   380      * @return True if we are on target, false otherwise.
   381      */
   382     bool
   383     OnTarget(
   384         void
   385         )
   386     {
   387         bool fOnTarget = false;
   388 
   389         TLevel(API);
   390         TEnter();
   391 
   392         if (m_pidCtrlOptions & PIDCTRLO_NO_OSCILLATE)
   393         {
   394             if (fabs(m_prevError) < m_tolerance)
   395             {
   396                 fOnTarget = true;
   397             }
   398         }
   399         else if (fabs(m_prevError) > m_tolerance)
   400         {
   401             m_startSettling = GetMsecTime();
   402         }
   403         else if (GetMsecTime() - m_startSettling >= m_settlingTime)
   404         {
   405             fOnTarget = true;
   406         }
   407 
   408         TExitMsg(("=%x", fOnTarget));
   409         return fOnTarget;
   410     }   //OnTarget
   411 
   412     /**
   413      * This function sets the minimum and maximum values expected from the
   414      * input.
   415      *
   416      * @param minInput Specifies the minimum value.
   417      * @param maxInput Specifies the maximum value.
   418      */
   419     void
   420     SetInputRange(
   421         float minInput,
   422         float maxInput
   423         )
   424     {
   425         TLevel(API);
   426         TEnterMsg(("min=%f,max=%f", minInput, maxInput));
   427 
   428         m_minInput = minInput;
   429         m_maxInput = maxInput;
   430 
   431         TExit();
   432         return;
   433     }   //SetInputRange
   434 
   435     /**
   436      * This function limits the minimum and maximum values of the output.
   437      *
   438      * @param minOutput Specifies the minimum value.
   439      * @param maxOutput Specifies the maximum value.
   440      */
   441     void
   442     SetOutputRange(
   443         float minOutput,
   444         float maxOutput
   445         )
   446     {
   447         TLevel(API);
   448         TEnterMsg(("min=%f,max=%f", minOutput, maxOutput));
   449 
   450         m_minOutput = minOutput;
   451         m_maxOutput = maxOutput;
   452 
   453         TExit();
   454         return;
   455     }   //SetOutputRange
   456 
   457     /**
   458      * This function returns the calculated PID output according to the input
   459      * value from the input source.
   460      *
   461      * @param currInput Specifies the current input value.
   462      *
   463      * @return Returns the PID control source input value.
   464      */
   465     float
   466     CalcPIDOutput(
   467         float currInput
   468         )
   469     {
   470         float output;
   471         float error;
   472         float adjTotalError;
   473         
   474         TLevel(API);
   475         TEnterMsg(("currInput=%f", currInput));
   476         
   477         error = m_setPoint - currInput;
   478         if (m_pidCtrlOptions & PIDCTRLO_INVERSE)
   479         {
   480             error = -error;
   481         }
   482 
   483         adjTotalError = m_Ki*(m_totalError + error);
   484         if ((adjTotalError > m_minOutput) && (adjTotalError < m_maxOutput))
   485         {
   486             m_totalError += error;
   487         }
   488 
   489         output = m_Kp*error + m_Ki*m_totalError + m_Kd*(error - m_prevError);
   490 
   491         if (m_pidCtrlOptions & PIDCTRLO_SPEED_CTRL)
   492         {
   493             output += m_prevOutput;
   494         }
   495 
   496         output = BOUND(output, m_minOutput, m_maxOutput);
   497         
   498         m_prevError = error;
   499         m_prevOutput = output;
   500 
   501         TExitMsg(("=%f", output));
   502         return output;
   503     }   //CalcPIDOutput
   504 
   505 };  //class TrcPIDCtrl
   506 
   507 #endif  //ifndef _TRCPIDCTRL_H