The subsystem design/basis for the final project

Dependencies:   mbed-rtos mbed-src pixylib

main.cpp

Committer:
JamesMacLean
Date:
2016-03-09
Revision:
0:80a37292f6b2
Child:
1:cc5636894b95

File content as of revision 0:80a37292f6b2:

// Description: combines the functionality of both the PIcontrol program that controls a motor and the Pixy subsystem
//              to create a program that will control the robot

#include "rtos.h"
#include "mbed.h"
#include "Robot_Control.h"
#include "Pixy.h"

#define MAX_BLOCKS 1
#define TARGET 12 //the code of our target, very important, should this be octal...?

//controls whether or not motors are a part of the program
//for our test of the PIXY, they will not be
#define MOTORS_ON 0

//steering inputs, may want to change to variables or something
#define STEER_SETPOINT 160
#define STEER_KP 1
#define STEER_KI 0
#define STEER_MAX 2147483648

//speed inputs
#define HEIGHT_SETPOINT 160
#define SPEED_KP 1
#define SPEED_KI 0
#define SPEED_MAX 2147483648

//shared variables between sensor thread and motor thread, protected by mutex
float left_setpoint, right_setpoint;

//the setpoint mutex
osMutexId setpoint_mutex;
osMutexDef(setpoint_mutex);

//motorPI function variables
float L_xState, R_xState; //the states for the left and right pi control loop (must have them outside of function to make them cumulative)

//PI function variables
float steer_xState, speed_xState; //the states for the steering and speed pi control loop

//main()variables
short idCode;
char isMeasuring;
char inKey;
float pwmPeriod = 0.001;

//global variables
float Kp, Ki; //kp and ki for motors

// Various debugging variables
float vels[60], times[60], errs[60], xvals[60], uvals[60]; // Used to measure step response
int x_global, height_global;
float speed_global, steer_global;

// ******** Main Thread ********
int main() {  // This thread executes first upon reset or power-on.
    
    if(MOTORS_ON){
        // left
        PWM0.period(pwmPeriod);
        PWM0.pulsewidth(0);
        Thread::wait(500);

        // right
        PWM1.period(pwmPeriod);
        PWM1.pulsewidth(0);
        Thread::wait(500);
   
        //mutex for the left and right motor setpoints
        setpoint_mutex = osMutexCreate(osMutex(setpoint_mutex));
    }
    
    Bumper.rise(&ExtCollisionISR); // Attach the address of the interrupt handler to the rising edge of Bumper
    
    // Start execution of the threads: PiControlThread,ExtCollisionThread, and SensorControlThread:
    SensorControl = osThreadCreate(osThread(SensorControlThread), NULL);
    if(MOTORS_ON) PiControl = osThreadCreate(osThread(PiControlThread), NULL);
    ExtCollision = osThreadCreate(osThread(ExtCollisionThread), NULL);
    
    // Start the watch dog timer and enable the watch dog interrupt
    osTimerId OneShot = osTimerCreate(osTimer(Wdtimer), osTimerOnce, (void *)0);
    led3=0; // Clear watch dog led3 status
    
    if(MOTORS_ON){
        //SPI Section
        pc.printf("\n\rStarting SPI...");
        // Restart with a new SPI protocol.
        DE0.format(16,1); // 16 bit mode 1
        IoReset = 0; 
        IoReset = 1; 
        wait_us(10);
        IoReset = 0;
        SpiReset = 0; 
        SpiReset = 1; 
        wait_us(10);
        SpiReset = 0; 
        idCode = DE0.write(0x8004); //changed from 8002 for one motor to 8004 for two
        pc.printf("\n\rDevice Id: %d", idCode);
    
        // motor Kp and Ki
        Kp = 0.000120;
        Ki = 0.0000000001;
    }
    
    // Specify address of the PeriodicInt ISR as PiControllerISR, specify the interval 
    // in seconds between interrupts, and start interrupt generation:
    if(MOTORS_ON) PeriodicInt.attach(&PiControllerISR, .001);
    PeriodicInt2.attach(&SensorControllerISR, .0167); //trigger sensor thread 60 times/sec
    
    //isMeasuring = 1; 
    //Setpoint = 6; 
    
    while (1) {
        
        osTimerStart(OneShot, 2000); // Start or restart the watchdog timer interrupt and set to  2000ms.

        if(!MOTORS_ON){
            pc.printf("X Coordinate: %d, Steering: %d,", x_global, steer_global);
            pc.printf("Height: %d, Speed: %d,", height_global, speed_global);
            pc.printf(" L setpoint: %d, R setpoint: %d \n\r", left_setpoint, right_setpoint);
        }
        /*
        if (!isMeasuring && (Setpoint!=0)) {
            pc.printf("\n\r-----------------------------------\n\r");
            printf("times (ms) \t vel (rads/s) \t error \t\t x value \t u value \n\r");  
            for (i=0; i<60; i++) {
                  printf("%f, \t %f, \t %f, \t %f, \t %f \n\r", times[i], vels[i], errs[i], xvals[i], uvals[i]);  
            }
            pc.printf("\n\r-----------------------------------\n\r");
            Setpoint = 0; 
            i = 0;
        }
        */
        Thread::wait(500); // Go to sleep for 500 ms
    }
}

// ******** Control Thread ********
void SensorControlThread(void const *argument) {
    float speed, steer;
    int x, count, height;
    
    Pixy pixy(Pixy::SPI, p11, p12, p13);
    
    while (1) {
        osSignalWait(SignalSensor, osWaitForever); //note: SignalSensor is just zero, waits for any signal dirrected at thread.

        count = pixy.getBlocks(130); //get as many blocks as are available

        //find the target block in all of the blocks that were sent
        for(x=0;x<count;x++){
            if (pixy.blocks[x].signature == TARGET) break;
            if (x==count) pc.printf("no target in frame");
        }

        // if blocks were returned
        if (count){

            //height = CalcHeight() //find the feedback from the pixy for the height
            height = pixy.blocks[(x-1)].height; //use this for now

            steer = PI(0, STEER_SETPOINT, STEER_KP, STEER_KI, pixy.blocks[(x-1)].x, STEER_MAX); //the feedback for the steering is the x location of the target block
            speed = PI(1, HEIGHT_SETPOINT, SPEED_KP, SPEED_KI, height, SPEED_MAX);

            if(!MOTORS_ON){
                x_global = pixy.blocks[x-1].x;
                height_global = height;
                steer_global = steer;
                speed_global = speed;
            }

            // give setpoints to PiControlThread
            osMutexWait(setpoint_mutex, osWaitForever); //enter mutex
            left_setpoint = speed - steer;
            right_setpoint = speed + steer;
            osMutexRelease(setpoint_mutex); //exit mutex
        }

    } 
}
    
// ******** Control Thread ********
void PiControlThread(void const *argument) {
    short dP_L, dt_L, dP_R, dt_R;
    float uL,uR;
    float left_set,right_set;

    while (1) {
        osSignalWait(SignalPi, osWaitForever); // Go to sleep until signal, SignalPi, is received. SignalPi is just zero lol, waits for any signal to thread
        led2= !led2; // Alive status - led2 toggles each time PiControlThread is signaled.
        
        dP_L = DE0.write(0); // Read QEI-0 position register
        dt_L = DE0.write(0);  // Read QEI-0 time register
        dP_R = DE0.write(0); // Read QEI-0 position register
        dt_R = DE0.write(0);  // Read QEI-0 time register
        
        osMutexWait(setpoint_mutex, osWaitForever);//enter mutex
        left_set= left_setpoint; //copy setpoint
        right_set= right_setpoint;
        osMutexRelease(setpoint_mutex);//exit mutex

        uL = motorPI(0, left_set, Kp, Ki, dP_L, dt_L);
        uR = motorPI(1, right_set, Kp, Ki, dP_R, dt_R);
        
        PWM0.pulsewidth(fabs(uL));
        PWM1.pulsewidth(fabs(uR));
    } 
}

// ******** Collision Thread ********
void ExtCollisionThread(void const *argument) {
    while (1) {
        osSignalWait(SignalExtCollision, osWaitForever); // Go to sleep until signal, SignalExtCollision, is received
        led4 = 1;
    }
}

// ******** Watchdog Interrupt Handler ********
void Watchdog(void const *n) {
    led3=1; // led3 is activated when the watchdog timer times out
}

// ******** Period Timer Interrupt Handler ********
void PiControllerISR(void) {
    osSignalSet(PiControl,0x1); // Activate the signal, PiControl, with each periodic timer interrupt.
}

// ******** Period Timer Interrupt Handler ********
void SensorControllerISR(void) {
    osSignalSet(SensorControl,0x1); // Activate the signal, SensorControl, with each periodic timer interrupt. (basically sends signal to id of thread)
}
    
// ******** Collision Interrupt Handler ********
void ExtCollisionISR(void) {
    osSignalSet(ExtCollision,0x1); // Activate the signal, ExtCollision, with each pexternal interrupt.
}

// motorPI - motor pi control extacted from previous work
// Input:   0 for Left motor, 1 for right motor
// Output: pwm pulsewidth for corresponding motor

float motorPI(int RL_Motor, float Setpoint, float Kp, float Ki, short dP1, short dt1){
    int dP32, i;
    float angVel, e, u, xState;
    
    if (dP1 & 0x00008000) {
        dP32 = dP1 | 0xFFFF0000;
    } else {
        dP32 = dP1;
    }   
        
    angVel = QE2RadsPerSec(dP32, dt1);
        
    e = Setpoint - angVel;

    RL_Motor ? xState = R_xState : xState = L_xState; //state is cumulative, select left or right state global variable
        
    // Avoid integrator wind up
     if((u>=pwmPeriod)||(u<=-pwmPeriod)){
        //xState= xState; - turns off integrator when u maxed
    }else{
        xState = xState + e;
    }

    RL_Motor ? R_xState = xState : L_xState = xState; //update state variable
        
    u = Ki*xState + Kp*e;
        
    // Set direction and pwm
    if (u >= 0) {
       RL_Motor ? DIR1 = 1 : DIR0 = 1;   
    } else {
       RL_Motor ? DIR1 = 0 : DIR0 = 0;
    }
        
    if (isMeasuring) {
        vels[i] = angVel;
        times[i] = i;
        errs[i] = e;
        xvals[i] = xState;
        uvals[i] = u;
        if (i>=59) {
            isMeasuring = 0;
        }
        i++;
    }
        
    if (fabs(u) > pwmPeriod) u = pwmPeriod;
    
    return u;
}

// PI() - A generic PI controller function used by the sensor control thread
// st_or_sp - 0 for steering, 1 for speed
float PI(int st_or_sp, float setpoint, float Kp, float Ki, int feedback, float max_val){
    float e, xState, u;
    
    e = setpoint - (float)feedback;
    
    st_or_sp ? xState = speed_xState : xState = steer_xState; 

    // Avoid integrator wind up
     if((u>=max_val)||(u<=-max_val)){
        //xState= xState; - turns off integrator when u maxed
    }else{
        xState = xState + e;
    }
    
    st_or_sp ? speed_xState = xState : steer_xState = xState; //update state variable

    u = Ki*xState + Kp*e;
    
    if (fabs(u) > max_val) u = max_val;
    
    return u;
}

float QE2RadsPerSec(int counts, int time) {
    return (counts*122.62)/time;
}