Simple PID controller for a DC motor with an attached QEI and potentiometer.

Dependencies:   QEI mbed

Basic_SM_PID.cpp

Committer:
snaranjo
Date:
2018-08-02
Revision:
0:31ccfbaf5bff

File content as of revision 0:31ccfbaf5bff:

#include "mbed.h"
#include "QEI.h"

#define pi  3.14159265358979323846


// typedef struct
 struct sPID
{
    double dState; // Last position input
    double iState; // Integrator state
    double iMax, iMin; // Maximum and minimum allowable integrator state
    double iGain, // integral gain
           pGain, // proportional gain
           dGain; // derivative gain
};



double UpdatePID(sPID * pid, double error, double position)
{
    double pTerm, dTerm, iTerm;
    
    pTerm = pid->pGain * error; // calculate the proportional term
    
   
    pid->iState += error;  // calculate the integral state with appropriate limiting

    if (pid->iState > pid->iMax) pid->iState = pid->iMax;
    else if (pid->iState < pid->iMin) pid->iState = pid->iMin;

    iTerm = pid->iGain * pid->iState; // calculate the integral term
    dTerm = pid->dGain * (pid->dState - position);

    pid->dState = position;

    return pTerm + dTerm + iTerm;
}

void setPID(sPID *pid, double P, double I, double D,
                      double iMx, double iMn, double dS, double iS){
 
    pid->pGain = P;
    pid->iGain = I;
    pid->dGain = D;
    pid->iMax = iMx;
    pid->iMin = iMn;
    pid->dState = dS;
    pid->iState = iS;
 
 }
 
Serial pc(USBTX, USBRX);

int baudRate = 460800;



int steps_per_rev = 1200; //Encoder CPR * gearbox ratio
int pulseRead = 0;

float ctrlDeg = 0.0;
float shaftDeg = 0.0;
float cmdSig = 0.0;
float degErr = 0.0;

Timer timer;

//Use X4 encoding.
QEI wheel(p11, p12, NC, steps_per_rev, QEI::X4_ENCODING);       //(encoder channel 1, encoder channel 2, index (n/a here), counts per revolution, mode (X4, X2))

//Output to motors. PWM signal is sent to either one, depending on desired rotational direction. CW or CCW.
DigitalOut cwCH(p22);             //Ouput pin to h-bridge 1
DigitalOut ccwCH(p26);
PwmOut motor(p24);            //Output pin to h-bridge 2
AnalogIn Current (p19);         //Current sense pin
AnalogIn ctrlPot (p16);
AnalogIn currPot (p18);


float pulsesToRadians(float pulses) //Encoder steps to revolutions of output shaft in radians
{
    return ((pulses/steps_per_rev)*(2*pi));
}

float pulsesToDegrees(float pulses) //Encoder steps to revolutions of output shaft in degrees
{

    return ((pulses/steps_per_rev)*360);
}

void signal_to_hbridge( float signal)   //Input of -1 means full reverse, 1 means full forward
{
    //An input of magnitude > 1 gets reduced to 1 (or -1) by the pwm.write() function
    if (signal < 0) {
          cwCH  = 0;
          ccwCH = 1;
          motor.write(signal * -1);
//        Motor1.write(0);
//        Motor2.write(signal * -1);
    } else if (signal > 0) {
          ccwCH = 0;
          cwCH  = 1; 
          motor.write(signal);
//        Motor1.write(signal);
//        Motor2.write(0);
    } else {
        cwCH = 0;
        ccwCH = 0;
//        Motor1.write(0);
//        Motor2.write(0);
    }
}


int main() {
      
      pc.baud(baudRate); 
      //will run a loop to apply the PID controller. I will command a movement with the 
      //pot, then will run the updatePID function to find the error difference and
      //learn how to get good feedback with the PID controller
      sPID mPid;
      sPID* pmPid;
      float currVal, prev_curr;
//      float currFactor;
      pmPid = &mPid;    // .1
           //      P   , I, D, Imax, Imin, Dstate, Istate
//      setPID(pmPid,0.07, 0, .6, 0.45, -0.1, 0, 0); //gains for when working with Degs
      setPID(pmPid,0.9, 0, 7, 0.45, -0.1, 0, 0); //gains when working in radians
    
      float t, t_ref;

      //initialize PID controller
      motor.period(0.00005f);       //Set PWM frequency.  MC33926 h-bridge limited 20 kHz (.00005 seconds)

      timer.start();
      t_ref = 0;
      while (1) {
            //Ctrl pot range will be -30 to 30 degrees, just to have high point resolution
            //Gather inputs
            ctrlDeg = ctrlPot*(1.0472f)- 0.5236f;            
            shaftDeg = pulsesToRadians(wheel.getPulses());
            degErr = ctrlDeg - shaftDeg;
            
//            currFactor = currPot*3.3*(1/3.3);
            currVal = Current.read()*3.3/.140f;
            
            
            //Update PID vars and send CMD signal to motor
            cmdSig = UpdatePID(pmPid, degErr, shaftDeg);
            signal_to_hbridge(cmdSig);
            t = timer.read();
            //Print out result
           // pc.printf("t: %010lu\t SP: %3.3f\t PV: %3.3f\t CV: %3.3f\n",
//                      t, ctrlDeg, shaftDeg, cmdSig);
            if (t - t_ref > 0.1) {
                pc.printf("%6.3f\t %5.2f\t %3.3f\t %3.3f\t %3.3f\n",
                  //t, ctrlDeg, shaftDeg, cmdSig);
                   t, ctrlDeg, shaftDeg, degErr, currVal);
                   t_ref = t;
               //Current protection of motor
               if (((currVal > 6.0f) &&  (abs(currVal - prev_curr) <0.20))) break;
               
               prev_curr = currVal;
                      }//end if t_ref
            wait(0.0001f);
        }//end while loop
        //assuming motor needs braknig
        signal_to_hbridge(0);
}