www.pudn.com > truetime-1.2-compiled.zip > servo_init.cpp


// PID-control of a DC servo process.
//
// This example shows four ways to implement a periodic controller
// activity in TrueTime. The task implements a standard
// PID-controller to control a DC-servo process (2nd order system). 

#define S_FUNCTION_NAME servo_init

#include "ttkernel.cpp"

// PID data structure used in Implementations 1a, 2, and 3 below.
class PID_Data {
public:
  struct { // states
    double u, Iold, Dold, yold, t; // t used only in Implementation 2 below 
  } s;
  
  struct { // params
    double K, Ti, Td, N, h;
    int rChan, yChan, uChan; 
  } p;
};

// calculate PID control signal and update states
void pidcalc(PID_Data* d, double r, double y) {
  double P = d->p.K*(r-y);
  double I = d->s.Iold;
  double D = d->p.Td/(d->p.N*d->p.h+d->p.Td)*d->s.Dold+d->p.N*d->p.K*d->p.Td/(d->p.N*d->p.h+d->p.Td)*(d->s.yold-y); 

  d->s.u = P + I + D;
  d->s.Iold = d->s.Iold + d->p.K*d->p.h/d->p.Ti*(r-y);
  d->s.Dold = D;
  d->s.yold = y;
};

// Variables used in Implementation 1b below.
const int nInp = 2;                 // nbr of inputs to controller block 
const int nOutp = 2;                // nbr of outputs to controller block 
static double inp[] = {0.0, 0.0};   // block inputs 
static double outp[] = {0.0, 0.0};  // block outputs 


// ---- PID code function for Implementation 1a ----
double pidcode1(int seg, void* data) {

  double r, y;
  PID_Data* d = (PID_Data*) data;

  switch (seg) {
  case 1:  
    r = ttAnalogIn(d->p.rChan);
    y = ttAnalogIn(d->p.yChan);
    pidcalc(d, r, y); 
    return 0.002;
  case 2:    
    ttAnalogOut(d->p.uChan, d->s.u);
    return FINISHED;
  }
}

// ---- PID code function for Implementation 1b ----
double blockcode(int seg, void* data) {

  switch (seg) {
  case 1: 
    inp[0] = ttAnalogIn(1);
    inp[1] = ttAnalogIn(2);

    ttCallBlockSystem(nOutp, outp, nInp, inp, "controller");

    return outp[1];     // execution time returned from block 
  case 2:    
    ttAnalogOut(1, outp[0]);
    return FINISHED;
  }
}

// ---- PID code function for Implementation 2 ----
double pidcode2(int seg, void* data) {

  double r, y;
  PID_Data* d = (PID_Data*) data;

  switch (seg) {
  case 1:
    d->s.t = ttCurrentTime();
    return 0.0;
  case 2:  
    r = ttAnalogIn(d->p.rChan);
    y = ttAnalogIn(d->p.yChan);
    pidcalc(d, r, y); 
    return 0.002;
  case 3:    
    ttAnalogOut(d->p.uChan, d->s.u);
    // Sleep
    d->s.t += d->p.h;
    ttSleepUntil(d->s.t);
    return 0.0;
  case 4:
    ttSetNextSegment(2); // loop
    return 0.0;
  }
}

// ---- PID code function for Implementation 3 ----
double pidcode3(int seg, void* data) {

  double r;
  double *y;
  PID_Data* d = (PID_Data*) data;

  switch (seg) {
  case 1:  
    r = ttAnalogIn(d->p.rChan);
    y = (double*) ttTryFetch("Samples");
    pidcalc(d, r, *y); 
    delete y;
    return 0.0018;
  case 2:    
    ttAnalogOut(d->p.uChan, d->s.u);
    return FINISHED;
  }
}

// ---- Sampler code function for Implementation 3 ----
double samplercode(int seg, void* data) {

  double y;
  int* d = (int*) data;

  switch (seg) {
  case 1:  
    y = ttAnalogIn(*d);
    ttTryPost("Samples", new double(y)); // put sample in mailbox
    ttCreateJob("pid_task");  // trigger task job
    return 0.0002;
  case 2:    
    return FINISHED;
  }
}

#define NBROFINPUTS 2
#define NBROFOUTPUTS 1
#define SCHEDULER FP

PID_Data *d;
double *d2 = NULL;
int *hdl_data = NULL;

void init() {
  
  // Initialize TrueTime kernel
  ttInitKernel(NBROFINPUTS, NBROFOUTPUTS, SCHEDULER);

  // Task attributes
  double period = 0.006;
  double deadline = period;
  double offset = 0.0; // start of first task instance
  double prio = 1.0;

  // Create task data (local memory)
  d = new PID_Data;
  d->p.K = 0.96;
  d->p.Ti = 0.12;
  d->p.Td = 0.049;
  d->p.N = 10.0;
  d->p.h = period;
  d->s.u = 0.0;
  d->s.t = 0.0; // only used in Implementation 2 below
  d->s.Iold = 0.0;
  d->s.Dold = 0.0;
  d->s.yold = 0.0;
  d->p.rChan = 1;
  d->p.yChan = 2;
  d->p.uChan = 1;

  // IMPLEMENTATION 1a: using the built-in support for periodic tasks
  //
  ttCreatePeriodicTask("pid_task", offset, period, prio, pidcode1, d);

  // IMPLEMENTATION 1b: calling Simulink block within code function
  //
  // d2 = new double(0.0); // Only the control signal needs to be 
                           // stored between segments. Controller 
                           // states are stored internally by TrueTime.
  // ttCreatePeriodicTask("pid_task", offset, period, prio, blockcode, d2);

  // IMPLEMENTATION 2: sleepUntil and loop back
  //
  // ttCreateTask("pid_task", deadline, prio, pidcode2, d);
  // ttCreateJob("pid_task");
  
  // IMPLEMENTATION 3: sampling in timer handler, triggers task job
  //
  // hdl_data = new int(2); // y_chan for reading samples
  // ttCreateInterruptHandler("timer_handler", prio, samplercode, hdl_data);
  // ttCreatePeriodicTimer("timer", offset, period, "timer_handler");
  //
  // ttCreateMailbox("Samples", 10);
  //
  // ttCreateTask("pid_task", deadline, prio, pidcode3, d);
}

void cleanup() {
 
  delete d;
  if (d2) delete d2;
  if (hdl_data) delete hdl_data;
}