Self-Balancing Robot Package for MAE 433.

Dependencies:   MAE433_Library

main.cpp

Committer:
Electrotiger
Date:
2016-06-30
Revision:
2:098bb1322f0e
Parent:
1:4afc0dd95be9

File content as of revision 2:098bb1322f0e:

/**
 * @file main.cpp
 * @date June 9th, 2015
 * @author Weimen Li
 * @mainpage RobotBalancer
 * This program is a demo lab for MAE 433. It controls a robot to balance it.
 * @see main.cpp
 */

/* Inclusions */
#include "mbed.h"
#include "rtos.h"
#include "HBridge.hpp"
#include "QuadEnc.hpp"
#include "HC06Bluetooth.hpp"
#include "FXOS8700CQ.hpp"
#include "dsp.h"
#include "FixedRefresh.hpp"

/* Constants */
/// The CPR of the encoder.
const float encoderCPR = 100.98f * 12.0f;
/// The transmission period of the bluetooth module.
const uint32_t BTTransmitPeriodMS = 1;
/// The sampling period of the control system.
const uint32_t controlSampleRateUS = 1250;
/// The sampling period of the motor control subsystem
const uint32_t motorControlSampleRateUS = 333;
/// The diameter of the wheels in inches
const float wheelDiameter = 1.9f;

/* Declaring objects */
/// The quad encoder of the left motor.
QuadEnc leftQuadEnc(PTB0, PTB1, encoderCPR);
/// The quad encoder of the right motor.
QuadEnc rightQuadEnc(PTC2, PTC1, encoderCPR);
/// The H-Bridge driving the left motor.
HBridge leftMotor(D9, D7);
/// The H-Bridge driving the right motor.
HBridge rightMotor(D10, D8);
/// The accelerometer on the board
FXOS8700CQ accelerometer(PTB3, PTB2, PTD0, PTD1);

/// The Bluetooth link used to transmit data.
HC06Bluetooth bluetooth(PTD3, PTD2);
/// Timer to transmit timing information.
Timer timer;

/* The acceleration commanded by control_thread and used as input for motorControl_thread() */
float setAccel;
Mutex setAccelMutex;

/* Threads */
/** @brief Bluetooth Transmission:
 * This thread reads the quadrature encoder and
 * output data and transmits it as comma-separated values over Bluetooth.
 */
 float angle;
void bluetooth_thread(void const *argument) {
    // Timer to control how long to wait.
    FixedRefresh refresh;
    // 64-byte buffer is used to store the data. May be expanded as needed.
    char buffer[128];
    // The accelerometer readings.
    float xAccel;
    float yAccel;
    float zAccel;
    

    while(true) {
        accelerometer.readAccelerometer(&xAccel, &yAccel, &zAccel);
        // Place the CSV information in the buffer:
        sprintf(buffer, "%f,%f,%f,%f,%f,%f,%f,%f\n\r", timer.read(), xAccel, yAccel, zAccel, angle, setAccel, leftMotor.read(), rightMotor.read());
        // Transmit the information.
        bluetooth.print(buffer);
        // Refresh at a specified interval.
        refresh.refresh_us(BTTransmitPeriodMS * 1000);
    }

}

/** @brief Control Thread:
 * This thread reads the current quadrature counts and actuates
 * the motor through the H-Bridge.
 */

void control_thread(void const *argument) {
    // FixedRefresh object to wait at fixed time
    FixedRefresh fixedRefresh;
    // The accelerometer readings.
    float xAccel;
    float yAccel;
    float zAccel;

    /* Feedback constants: */
    // Accelerometer feedback at n = 0;
    const float a0 = 0.15;
    // Accelerometer feedback at n = -1;
    const float a1 = 0;
    // Accelerometer feedback at n = -2;
    const float a2 = 0;
    
    /* State Variables: */
    // System's angle at n = 0
    // float angle = 0;
    // System's angle at n = -1
    float angle_n1 = 0;
    // System's angle at n = -2
    float angle_n2 = 0;

    /* Filtering Constants: */
    // The block size is the number of samples processed by each tick of the digital filter.
    // Since we work sample-by-sample in real time, the block size is 1.
    const uint32_t BLOCK_SIZE = 1;
 
    /* FIR Coefficients buffer generated using fir1() MATLAB function. */
    const uint32_t NUM_TAPS = 121;
    const float32_t firCoeffs32[NUM_TAPS] = { 
    -0.0000000000f, -0.0002560113f, -0.0004312516f, -0.0004555333f, -0.0003012438f, 
    +0.0000000000f, +0.0003556327f, +0.0006322583f, +0.0006981380f, +0.0004780164f, 
    -0.0000000000f, -0.0005895668f, -0.0010599506f, -0.0011770516f, -0.0008069672f, 
    +0.0000000000f, +0.0009886023f, +0.0017658793f, +0.0019458113f, +0.0013225924f, 
    -0.0000000000f, -0.0015907108f, -0.0028149413f, -0.0030732930f, -0.0020702914f, 
    +0.0000000000f, +0.0024483239f, +0.0042990157f, +0.0046595251f, +0.0031177229f, 
    -0.0000000000f, -0.0036437681f, -0.0063662040f, -0.0068700365f, -0.0045798332f, 
    +0.0000000000f, +0.0053245081f, +0.0092889853f, +0.0100177610f, +0.0066800457f, 
    -0.0000000000f, -0.0077943369f, -0.0136463577f, -0.0147897471f, -0.0099262614f, 
    +0.0000000000f, +0.0117998710f, +0.0209259298f, +0.0230400730f, +0.0157663561f, 
    -0.0000000000f, -0.0197710935f, -0.0363819152f, -0.0419784039f, -0.0305190869f, 
    +0.0000000000f, +0.0463622585f, +0.1004643664f, +0.1511729509f, +0.1872140169f, 
    +0.2002504170f, +0.1872140169f, +0.1511729509f, +0.1004643664f, +0.0463622585f, 
    +0.0000000000f, -0.0305190869f, -0.0419784039f, -0.0363819152f, -0.0197710935f, 
    -0.0000000000f, +0.0157663561f, +0.0230400730f, +0.0209259298f, +0.0117998710f, 
    +0.0000000000f, -0.0099262614f, -0.0147897471f, -0.0136463577f, -0.0077943369f, 
    -0.0000000000f, +0.0066800457f, +0.0100177610f, +0.0092889853f, +0.0053245081f, 
    +0.0000000000f, -0.0045798332f, -0.0068700365f, -0.0063662040f, -0.0036437681f, 
    -0.0000000000f, +0.0031177229f, +0.0046595251f, +0.0042990157f, +0.0024483239f, 
    +0.0000000000f, -0.0020702914f, -0.0030732930f, -0.0028149413f, -0.0015907108f, 
    -0.0000000000f, +0.0013225924f, +0.0019458113f, +0.0017658793f, +0.0009886023f, 
    +0.0000000000f, -0.0008069672f, -0.0011770516f, -0.0010599506f, -0.0005895668f, 
    -0.0000000000f, +0.0004780164f, +0.0006981380f, +0.0006322583f, +0.0003556327f, 
    +0.0000000000f, -0.0003012438f, -0.0004555333f, -0.0004312516f, -0.0002560113f, 
    -0.0000000000f
    };

    /* Filter Object */
    // The filter object used to perform the filtering.
    FIR_f32<NUM_TAPS, BLOCK_SIZE> fir(firCoeffs32);
    // The filtered z Acceleration
    float32_t zAccelFiltered;
    
    while (true) {
        // Read the state from the accelerometer:
        accelerometer.readAccelerometer(&xAccel, &yAccel, &zAccel);
        fir.process(&zAccel, &zAccelFiltered);


        // Acquire the system's angle:
        // angle = atan2f(yAccel, zAccel);
        arm_sqrt_f32(2*zAccelFiltered, &angle);
        if(yAccel < 0) {
            angle = -angle;
        }

        // Control Law to regulate system:
        float output = a0 * angle + a1 * angle_n1 + a2 * angle_n2;
                // Filter the accelerometer state:

        // Set the output for motorControl_thread to handle.
        setAccelMutex.lock();
        setAccel = output;
        setAccelMutex.unlock();

        // Update the state variables
        angle_n1 = angle;
        angle_n2 = angle_n1;

        // Thread::wait(100);
        fixedRefresh.refresh_us(controlSampleRateUS);
    }
}


/**
 * @brief This thread controls the motors to have a specified acceleration, with the value
 * coming from control_thread.
 */
void motorControl_thread(void const* argument) {
    // Control variable constants
    const float kConst = 0.5;
    const float iConst = 4 * motorControlSampleRateUS / 1000000.;

    // Left Wheel State Variables
    float leftPosition = 0;
    float prevLeftPosition = 0;
    float leftPositionDeriv = 0;
    float prevLeftPositionDeriv = 0;
    float leftPositionDDeriv = 0;
    float leftErrorInt = 0;

    // Right Wheel State Variables
    float rightPosition = 0;
    float prevRightPosition = 0;
    float rightPositionDeriv = 0;
    float prevRightPositionDeriv = 0;
    float rightPositionDDeriv = 0;
    float rightErrorInt = 0;

    FixedRefresh refresh;

    while(true) {
        // Calculate new values for the state variables. Note that the steps are performed in reverse.
        leftPositionDDeriv = leftPositionDeriv - prevLeftPositionDeriv;
        rightPositionDDeriv = rightPositionDeriv - prevRightPositionDeriv;
        leftPositionDeriv = leftPosition - prevLeftPosition;
        rightPositionDeriv = rightPosition - prevRightPosition;
        leftPosition = leftQuadEnc.getRevs();
        rightPosition = rightQuadEnc.getRevs();

        // Get the commanded acceleration:
        setAccelMutex.lock();
        float localSetAccel = setAccel;
        setAccelMutex.unlock();

        // Calculate the error:

        float leftError = localSetAccel - leftPositionDDeriv;
        float rightError = localSetAccel - rightPositionDDeriv;

        // Calculate the output:
        float leftOutput = kConst * leftError + iConst * leftErrorInt;
        float rightOutput = kConst * leftError + iConst * rightErrorInt;
        leftErrorInt += leftError;
        rightErrorInt += rightError;
        // Write the output:
        leftMotor.write(leftOutput);
        rightMotor.write(rightOutput);
        // Update the previous state variable values:
        prevLeftPosition = leftPosition;
        prevRightPosition = rightPosition;
        prevLeftPositionDeriv = leftPositionDeriv;
        prevRightPositionDeriv = rightPositionDeriv;
        refresh.refresh_us(motorControlSampleRateUS);
    }
}

/** @brief Main thread:
 * This thread performs any additional initialization necessary for the declared objects.
 * Then, it starts the threads.
 */
int main() {
    /* Initialize objects */
    // Set the offset value for the accelerometer.
    accelerometer.setOffset(0.012, -0.032, 0.032);
    // Start the global timer.
    timer.start();

    /* Initialize threads. */
    // Thread priority is set as normal.
    Thread thread(bluetooth_thread, NULL, osPriorityNormal);
    // Thread priority is set as above normal: If the control_thread and bluetooth_thread
    // ever want to happen at the same time, control_thread wins.
    Thread thread2(control_thread, NULL, osPriorityAboveNormal);
    Thread thread3(motorControl_thread, NULL, osPriorityAboveNormal);
    while (true) {
        // Main thread does nothing else, so we tell it to wait forever.
        Thread::wait(osWaitForever);
    }
}