The subsystem design/basis for the final project

Dependencies:   mbed-rtos mbed-src pixylib

robot.cpp

Committer:
balsamfir
Date:
2017-04-24
Revision:
19:05b8123905fb
Parent:
18:501f1007a572

File content as of revision 19:05b8123905fb:

#include "robot.h"
#include "global.h"

enum System {
    MOTOR = '0', 
    SPEED = '1', 
    STEERING = '2'
};

// Function prototypes
// ----------------------------------------------------------------
void MotorISR(void);
void NavigationISR(void);
void CollisionISR(void);
void WatchdogISR(void const *n);
void MotorThread(void const *argument);
void NavigationThread(void const *argument);
void ShutDown(void);

// Interrupt and thread control
osTimerId oneShotId;
osThreadId motorId, navigationId, wdtId;
osThreadDef(MotorThread, osPriorityRealtime, DEFAULT_STACK_SIZE); 
osThreadDef(NavigationThread, osPriorityAboveNormal, DEFAULT_STACK_SIZE); 
osTimerDef(Wdtimer, WatchdogISR);
int32_t motorSignal, navigationSignal;
Ticker motorPeriodicInt;  
Ticker sensorPeriodicInt;   

// Mutex to protect left and right motor setpoints
osMutexId motorMutex;
osMutexDef(motorMutex);

// Variables
// ----------------------------------------------------------------

// For tunning and response measurement
bool isTunning;
int motorSample;
int navigationSample;
float leftMotorResponse[MOTOR_SAMPLES];
float rightMotorResponse[MOTOR_SAMPLES];
float speedResponse[NAVIGATION_SAMPLES];
float steeringResponse[NAVIGATION_SAMPLES];


// Set points and display variables
int x, height;
float speed, steering;
float leftMotor, rightMotor;


// Functions
// ----------------------------------------------------------------
void InitRobot(void) { 
    leftPwm.period(PWM_PERIOD);
    leftPwm.pulsewidth(0);
    rightPwm.period(PWM_PERIOD);
    rightPwm.pulsewidth(0);

    motorMutex = osMutexCreate(osMutex(motorMutex));
    
    // Start execution of the threads MotorThread, and NavigationThread:
    navigationId = osThreadCreate(osThread(NavigationThread), NULL);
    motorId = osThreadCreate(osThread(MotorThread), NULL);
 
    // Start the watch dog timer and enable the watch dog interrupt
    oneShotId = osTimerCreate(osTimer(Wdtimer), osTimerOnce, (void *)0);
    led3 = 0; // Clear watch dog led3 status
    
    //SPI Initialization
    pc.printf("\n\rStarting SPI...");
    deSpi.format(16,1); // 16 bit mode 1
    deSpi.frequency(500000);
    ioReset = 0; ioReset = 1; 
    wait_us(10); ioReset = 0; spiReset = 0; spiReset = 1; 
    wait_us(10); spiReset = 0; 
    pc.printf("\n\rDevice Id: %d \r\n", deSpi.write(0x8004)); // Read count & time for both motors
    FlushBuffer();
}

// Setup robot for auto tracking and listen for commands
void AutoTrack(void) {
    char key;
    motorPeriodicInt.attach(&MotorISR, MOTOR_PERIOD);
    sensorPeriodicInt.attach(&NavigationISR, NAVIGATION_PERIOD);
    
    while (1) {
        osTimerStart(oneShotId, 2000); // Start or restart the watchdog timer interrupt and set to  2000ms.
        if (pc.readable()) {
            key = pc.getc();
            if (key == 'q') {
                ShutDown();
                return;
            } else if (key == 'm') {
                ShutDown();
                ManualControl();
            } 
        }
        
        pc.printf("X: %d, Height: %d \r\n", x, height);
        pc.printf("Speed: %f \r\nSteering: %f \r\n", speed, steering);

        Thread::wait(250);
    }
}

// Setup robot for manual control and listen for commands
void ManualControl(void) {
    char key;
    motorPeriodicInt.attach(&MotorISR, MOTOR_PERIOD);
    float speedRate, steeringRate;
    
    while (1) {
        osTimerStart(oneShotId, 2000); // Start or restart the watchdog timer interrupt and set to  2000ms.
        if (pc.readable()) {
            key = pc.getc();
            if (key == 'q') {
                ShutDown();
                return;
            } else if (key == 'a') {
                ShutDown();
                AutoTrack();
            } else if (key == '8') {
                speedRate = (speedRate < 1)? speedRate + 0.1 : 1;
            } else if (key == '5') {
                speedRate = (speedRate > -1)? speedRate - 0.1 : -1;
            } else if (key == '6') {
                steeringRate = (steeringRate < 1)? steeringRate + 0.1 : 1;
            } else if (key == '4') {
                steeringRate = (steeringRate > -1)? steeringRate - 0.1 : -1;
            } 
            
            pc.printf("%.2f,%.2f\r\n", speedRate, steeringRate); 
        }
        
        osMutexWait(motorMutex, osWaitForever);  
        leftMotor = SPEED_MAX*(speedRate + steeringRate);
        rightMotor = SPEED_MAX*(speedRate - steeringRate);
        osMutexRelease(motorMutex); 
    }
}

void Tunning(void) {
    System system;
    int i;
    char key;
    float increment = 1;
    
    isTunning = true;
    
    pc.printf("\e[1;1H\e[2J");
    pc.printf("Motor (0), Speed (1), Steering (2), Print (p) \r\n");
    pc.printf("=> ");
    
    while (1) {
        osTimerStart(oneShotId, 20000); // Start or restart the watchdog timer interrupt and set to  20000ms.
        if (pc.readable()) {
            key = pc.getc();
            if ((key=='0')||(key=='1')||(key=='2')) {
                system = (System)key;
            } else if (key == 'q') {
                ShutDown();
                return;
            } else if (key == 'p') {
                
                pc.printf("\r\n\r\n  Left Motor \t Right Motor \t Speed   \t Steering \r\n");
                
                while ((i < MOTOR_SAMPLES)||(i < NAVIGATION_SAMPLES)) {
                    if (i < MOTOR_SAMPLES) {
                        pc.printf("  %f, \t %f, \t ", 
                            rightMotorResponse[i],
                            leftMotorResponse[i]
                        );
                    } else pc.printf("           \t             \t");
                    
                    if (i < NAVIGATION_SAMPLES) {
                        pc.printf("%f, \t %f ", 
                            speedResponse[i],
                            steeringResponse[i]
                        );
                    } else pc.printf("           \t   ");
                    pc.printf("\r\n");
                    i++;
                }
                
            } else {
                switch (system) {
                    case MOTOR:
                        if (key == 'a') leftMotorPI.kP = leftMotorPI.kP + increment;   
                        else if (key == 'z') leftMotorPI.kP = leftMotorPI.kP - increment; 
                        else if (key == 's') leftMotorPI.kI = leftMotorPI.kI + increment; 
                        else if (key == 'x') leftMotorPI.kI = leftMotorPI.kI - increment; 
                        break;
                    case SPEED:
                        if (key == 'a') heightPI.kP = heightPI.kP + increment;   
                        else if (key == 'z') heightPI.kP = heightPI.kP - increment; 
                        else if (key == 's') heightPI.kI = heightPI.kI + increment; 
                        else if (key == 'x') heightPI.kI = heightPI.kI - increment; 
                        break;
                   case STEERING:
                        if (key == 'a') xPI.kP = xPI.kP + increment;   
                        else if (key == 'z') xPI.kP = xPI.kP - increment; 
                        else if (key == 's') xPI.kI = xPI.kI + increment; 
                        else if (key == 'x') xPI.kI = xPI.kI - increment; 
                        break;
                }
                
                if (key == 'd') increment = increment*10;   
                else if (key == 'c') increment = increment/10; 
            }
            
            pc.printf("\e[1;1H\e[2J");
            pc.printf("Current system: %c \r\n\r\n", system);
            
            pc.printf("Motor (0) \t Speed (1) \t Steering (2) \t Print (p) \r\n");
            pc.printf("Kp (a/z)  \t Ki (s/x)  \t inc (d/c) \r\n\r\n");
            
            pc.printf("Increment \t %f \r\n", increment);
            pc.printf("Motor     \t KP: %f, KI: %f \r\n", leftMotorPI.kP, leftMotorPI.kI);
            pc.printf("Speed     \t KP: %f, KI: %f \r\n", heightPI.kP, heightPI.kI); 
            pc.printf("Steering  \t KP: %f, KI: %f \r\n\r\n", xPI.kP, xPI.kI);
            pc.printf("=> ");
            
            if (key != 'p') {
                motorPeriodicInt.attach(&MotorISR, MOTOR_PERIOD);
                sensorPeriodicInt.attach(&NavigationISR, NAVIGATION_PERIOD);
                motorSample = 0;
                navigationSample = 0;
                i = 0;
            }
        }
        
        // Stop when all samples are collected
        if ((navigationSample >= NAVIGATION_SAMPLES)&&(motorSample >= MOTOR_SAMPLES)) {
            motorPeriodicInt.detach();
            sensorPeriodicInt.detach();   
            leftPwm.pulsewidth(0);
            rightPwm.pulsewidth(0);
            xPI.Reset();
            heightPI.Reset();
            leftMotorPI.Reset();
            rightMotorPI.Reset();
        }
        Thread::wait(500); // Go to sleep for 500 ms
    }
}

void ShutDown(void) {
    motorPeriodicInt.detach();
    sensorPeriodicInt.detach();
    leftPwm.pulsewidth(0);
    rightPwm.pulsewidth(0);
    isTunning = false;
    motorSample = 0;
    navigationSample = 0;
    xPI.Reset();
    heightPI.Reset();
    leftMotorPI.Reset();
    rightMotorPI.Reset();
    pc.printf("Robot Shutdown... \r\n\r\n");
    FlushBuffer();
}

// Threads
// ----------------------------------------------------------------
void NavigationThread(void const *argument) {
    int count;
    
    while (1) {
        osSignalWait(navigationSignal, osWaitForever); // Go to sleep until navigation signal is set high by ISR

        count = pixy.getBlocks(1);
        
        // If target returned
        if (count && (pixy.blocks[0].signature == TARGET_DECIMAL)) {
            height = pixy.blocks[0].height;
            x = pixy.blocks[0].x;
            
            speed = heightPI.Run(HEIGHT_SETPOINT-height, SPEED_MAX);
            steering = xPI.Run(X_SETPOINT-x, SPEED_MAX);

            // Give setpoints to MotorThread
            osMutexWait(motorMutex, osWaitForever); 
            leftMotor = speed - steering;
            rightMotor = speed + steering;
            osMutexRelease(motorMutex); 
        } 
        
        if (isTunning && (navigationSample < NAVIGATION_SAMPLES)) {
            speedResponse[navigationSample] = speed; 
            steeringResponse[navigationSample] = steering;
            navigationSample++;
        }
    } 
}

void MotorThread(void const *argument) {
    float leftSet, rightSet, angVel;
    float timeOnLeft, timeOnRight;
    short loop;
    short dP, dt;

    while (1) {
        osSignalWait(motorSignal, osWaitForever); // Go to sleep until motor signal is set high by ISR
        
        // Get setpoints from navigation
        osMutexWait(motorMutex, osWaitForever); 
        leftSet = leftMotor;
        rightSet = rightMotor;
        osMutexRelease(motorMutex); 
        
        // Run PI control on right motor
        dP = deSpi.write(0);
        dt = deSpi.write(0); 
        angVel = RadsPerSec(dP, dt); // motors have opposite orientations
        timeOnRight = rightMotorPI.Run(rightSet-angVel, PWM_PERIOD/2);
        
        // Run PI control on left motor
        dP = deSpi.write(0);
        dt = deSpi.write(0); 
        angVel = -RadsPerSec(dP, dt);
        timeOnLeft = leftMotorPI.Run(leftSet-angVel, PWM_PERIOD/2);
        
        
        // Output new PWM and direction 
        if (timeOnLeft >= 0) leftDir = 0;   
        else leftDir = 1;
        
        if (timeOnRight >= 0) rightDir = 0;   
        else rightDir = 1;
        
        leftPwm.pulsewidth(fabs(timeOnLeft));
        rightPwm.pulsewidth(fabs(timeOnRight));
        
        if (isTunning && (motorSample < MOTOR_SAMPLES)) {
            leftMotorResponse[motorSample] = timeOnLeft; 
            rightMotorResponse[motorSample] = timeOnRight;
            motorSample++;
        } 
        
        #ifdef DEBUG
        loop = (loop < 1/MOTOR_PERIOD)? loop + 1 : 0;       
        #endif
    } 
}


// Interrupt Service Routines
// ----------------------------------------------------------------

void WatchdogISR(void const *n) {
    led3=1; // Activated when the watchdog timer times out
    ShutDown();
}

void MotorISR(void) {
    osSignalSet(motorId,0x1); // Activate the signal, MotorControl, with each periodic timer interrupt
}

void NavigationISR(void) {
    osSignalSet(navigationId,0x1); // Activate the signal, SensorControl, with each periodic timer interrupt
}