RC Boat using a C# GUI over XBee communication.

Dependencies:   MODSERIAL LSM9DS0 Motordriver PID mbed-rtos mbed

RC Boat Wiki Page

Revision:
0:58395e885409
Child:
4:6f6a9602e92d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Tue Apr 28 20:02:30 2015 +0000
@@ -0,0 +1,438 @@
+#include "mbed.h"
+#include "LSM9DS0.h"
+#include "rtos.h"
+#include "PID.h"
+#include "motordriver.h"
+//#include "IMUfilter.h"
+
+//Gravity at Earth's surface in m/s/s
+#define g0 9.812865328
+//Number of samples to average.
+#define SAMPLES 4
+//Number of samples to be averaged for a null bias calculation
+//during calibration.
+#define CALIBRATION_SAMPLES 1024
+//Convert from radians to degrees.
+#define toDegrees(x) (x * 57.2957795)
+//Convert from degrees to radians.
+#define toRadians(x) (x * 0.01745329252)
+//LSM9DS0 gyroscope sensitivity is 8.75 (millidegrees/sec)/digit when set to +-2G
+#define GYROSCOPE_GAIN (0.007476806640625)
+//Full scale resolution on the accelerometer is 0.0001220703125g/LSB.
+#define ACCELEROMETER_GAIN (0.00006103515625 * g0)
+//Sampling gyroscope at 100Hz.
+#define GYRO_RATE   0.01
+//Sampling accelerometer at 200Hz.
+#define ACC_RATE    0.005
+// SDO_XM and SDO_G are both grounded, so our addresses are:
+#define LSM9DS0_XM  0x1E // Would be 0x1E if SDO_XM is LOW
+#define LSM9DS0_G   0x6A // Would be 0x6A if SDO_G is LOW
+#define RATE  0.1 // Define rate for PID loop
+
+
+Serial pc(USBTX, USBRX);
+LSM9DS0 imu(p28, p27, LSM9DS0_G, LSM9DS0_XM);
+Mutex pcMutex;
+PID linearController(1.0, 0.0, 0.0, RATE);
+PID angularController(1.0, 0.0, 0.0, RATE);
+DigitalOut led1(LED1);
+DigitalOut led2(LED2);
+DigitalOut led3(LED3);
+DigitalOut led4(LED4);
+bool usePID = 1;
+bool commRecieved = 0;
+
+//Gloabal Variables for comm system below
+Ticker commTicker;
+
+
+//Global Variables for motor control below
+Motor  leftMotor(p21, p22, p23, 1); // pwm, fwd, rev
+Motor rightMotor(p26, p25, p24, 1); // pwm, fwd, rev
+
+//Offsets for the gyroscope.
+//The readings we take when the gyroscope is stationary won't be 0, so we'll
+//average a set of readings we do get when the gyroscope is stationary and
+//take those away from subsequent readings to ensure the gyroscope is offset
+//or "biased" to 0.
+double w_xBias;
+double w_yBias;
+double w_zBias;
+ 
+//Offsets for the accelerometer.
+//Same as with the gyroscope.
+double a_xBias;
+double a_yBias;
+double a_zBias;
+ 
+//Accumulators used for oversampling and then averaging.
+double a_xAccumulator = 0;
+double a_yAccumulator = 0;
+double a_zAccumulator = 0;
+double w_xAccumulator = 0;
+double w_yAccumulator = 0;
+double w_zAccumulator = 0;
+ 
+//Accelerometer and gyroscope readings for x, y, z axes.
+double a_x;
+double a_y;
+double a_z;
+double w_x;
+double w_y;
+double w_z;
+
+//Previous Acceleromerter and gyroscope readings for integration
+double last_a_x;
+double last_a_y;
+double last_a_z;
+ 
+//Buffer for accelerometer readings.
+int readings[3];
+//Number of accelerometer samples we're on.
+int accelerometerSamples = 0;
+//Number of gyroscope samples we're on.
+int gyroscopeSamples = 0;
+
+//current readings of linear velocity
+double v_x = 0;
+double v_y = 0;
+double v_z = 0;
+double v_mag2 = 0; // linear velocity squared. sqrt() is comupationally difficult
+ 
+//values for pwm wave to motors
+float linearOutput = 0;
+float angularOutput = 0;
+/**
+ * Prototypes
+ */
+//Calculate the null bias.
+void calibrateAccelerometer(void);
+//Take a set of samples and average them.
+void sampleAccelerometer(void);
+//Calculate the null bias.
+void calibrateGyroscope(void);
+//Take a set of samples and average them.
+void sampleGyroscope(void);
+
+
+ 
+void parseInput(void const *args) {
+ while(1) {
+     // Andrew to parse code here
+     
+     // if: manual boat control
+     // stopPID();
+     //leftMotor.speed(intput [-1,1]);
+     //rightMotor.speed(input [-1,1]);
+     
+     //if come back from coast stop
+     // startPID();
+
+     
+     //if coast
+     //
+     //stopPID();
+     //leftMotor.coast();
+     //rightMotor.coast();
+     commRecieved = 1;
+     }
+ }
+ 
+void startPID() {
+    linearController.setMode(AUTO_MODE);
+    angularController.setMode(AUTO_MODE);
+    usePID = 1;
+    }
+    
+void stopPID() {
+    linearController.setMode(MANUAL_MODE);
+    angularController.setMode(MANUAL_MODE);
+    usePID = 0;
+    }
+
+void checkCOMRecieved() {
+ if (commRecieved == 0) {
+    stopPID();
+    leftMotor.coast();
+    rightMotor.coast();
+    led1 = led2 = led3 = led4 = 1;
+    }
+     commRecieved = 0;
+}
+
+void sampleAccelerometer(void const *args) {
+ while(1) {
+    while(usePID != 1) {
+    Thread::wait(1000); //wait 1 sec and check if we are using the PID loop again.
+    }
+    //Have we taken enough samples?
+    if (accelerometerSamples == SAMPLES) {
+ 
+        //Average the samples, remove the bias, and calculate the acceleration
+        //in m/s/s.
+        a_x = ((a_xAccumulator / SAMPLES) - a_xBias) * ACCELEROMETER_GAIN;
+        a_y = ((a_yAccumulator / SAMPLES) - a_yBias) * ACCELEROMETER_GAIN;
+        a_z = ((a_zAccumulator / SAMPLES) - a_zBias) * ACCELEROMETER_GAIN;
+ 
+        a_xAccumulator = 0;
+        a_yAccumulator = 0;
+        a_zAccumulator = 0;
+        accelerometerSamples = 0;
+        pcMutex.lock();
+        pc.printf("a_x: %f  a_y: %f  a_z: %f \n",a_x,a_y,a_z);
+        pcMutex.unlock();
+        
+        // integrate to get velocity. make sure to subtract gravity downwards!
+        
+        v_x = v_x + (a_x + last_a_x)/2 * ACC_RATE * SAMPLES;
+        v_y = v_y + (a_y + last_a_y)/2 * ACC_RATE * SAMPLES;
+        v_z = v_z + (a_z + last_a_z)/2 * ACC_RATE * SAMPLES;
+        
+        last_a_x = a_x;
+        last_a_y = a_y;
+        last_a_z = a_z;
+        pcMutex.lock();
+        pc.printf("v_x: %f  v_y: %f  v_z: %f \n",v_x,v_y,v_z);
+        pcMutex.unlock();
+ 
+    } else {
+        //Take another sample.
+        imu.readAccel();
+        pcMutex.lock();
+        pc.printf("A: ");
+        pc.printf("%d", imu.ax);
+        pc.printf(", ");
+        pc.printf("%d",imu.ay);
+        pc.printf(", ");
+        pc.printf("%d\n",imu.az);
+        pcMutex.unlock();
+        
+        a_xAccumulator += imu.ax;
+        a_yAccumulator += imu.ay;
+        a_zAccumulator += imu.az;
+ 
+        accelerometerSamples++;
+        Thread::wait(ACC_RATE * 1000);
+    }
+
+
+}
+}
+ 
+void calibrateAccelerometer(void) {
+
+    led1 = 1;
+    
+    a_xAccumulator = 0;
+    a_yAccumulator = 0;
+    a_zAccumulator = 0;
+    
+    
+    //Take a number of readings and average them
+    //to calculate the zero g offset.
+    for (int i = 0; i < CALIBRATION_SAMPLES; i++) {
+ 
+        imu.readAccel();
+ 
+        a_xAccumulator += (double) imu.ax;
+        a_yAccumulator += (double) imu.ay;
+        a_zAccumulator += (double) imu.az;
+
+        wait(ACC_RATE);
+ 
+    }
+ 
+    a_xAccumulator /= CALIBRATION_SAMPLES;
+    a_yAccumulator /= CALIBRATION_SAMPLES;
+    a_zAccumulator /= CALIBRATION_SAMPLES;
+ 
+    //At 4mg/LSB, 250 LSBs is 1g.
+    a_xBias = a_xAccumulator;
+    a_yBias = a_yAccumulator;
+    //a_zBias = (a_zAccumulator - (1/0.00006103515625));
+    a_zBias = a_zAccumulator; // calibrate so that gravity is already out of the equation
+    
+    pcMutex.lock();
+    pc.printf("A_Bias: ");
+    pc.printf("%f", a_xBias);
+    pc.printf(", ");
+    pc.printf("%f",a_yBias);
+    pc.printf(", ");
+    pc.printf("%f\n",a_zBias);
+    pcMutex.unlock();
+    
+    a_xAccumulator = 0;
+    a_yAccumulator = 0;
+    a_zAccumulator = 0;
+    pc.printf("done calibrating accel\n");
+    led1 = 0;
+}
+ 
+ 
+void calibrateGyroscope(void) {
+    led2 = 1;
+    w_xAccumulator = 0;
+    w_yAccumulator = 0;
+    w_zAccumulator = 0;
+ 
+    //Take a number of readings and average them
+    //to calculate the gyroscope bias offset.
+    for (int i = 0; i < CALIBRATION_SAMPLES; i++) {
+        imu.readGyro();
+        w_xAccumulator += (double) imu.gx;
+        w_yAccumulator += (double) imu.gy;
+        w_zAccumulator += (double) imu.gz;
+        Thread::wait(GYRO_RATE * 1000);
+ 
+    }
+ 
+    //Average the samples.
+    w_xAccumulator /= CALIBRATION_SAMPLES;
+    w_yAccumulator /= CALIBRATION_SAMPLES;
+    w_zAccumulator /= CALIBRATION_SAMPLES;
+ 
+    w_xBias = w_xAccumulator;
+    w_yBias = w_yAccumulator;
+    w_zBias = w_zAccumulator;
+     
+    pcMutex.lock();
+    pc.printf("G_Bias: ");
+    pc.printf("%f", w_xBias);
+    pc.printf(", ");
+    pc.printf("%f",w_yBias);
+    pc.printf(", ");
+    pc.printf("%f\n",w_zBias);
+    pcMutex.unlock();
+    
+    w_xAccumulator = 0;
+    w_yAccumulator = 0;
+    w_zAccumulator = 0;
+    pc.printf("done calibrating gyro\n");
+    led2 = 0;
+}
+ 
+void sampleGyroscope(void const *args) {
+ while(1){
+    while(usePID != 1) {
+        Thread::wait(1000); //wait 1 sec and check if we are using the PID loop again.
+        }
+    //Have we taken enough samples?
+    if (gyroscopeSamples == SAMPLES) {
+ 
+        //Average the samples, remove the bias, and calculate the angular
+        //velocity in deg/s.
+        w_x = ((w_xAccumulator / SAMPLES) - w_xBias) * GYROSCOPE_GAIN;
+        w_y = ((w_yAccumulator / SAMPLES) - w_yBias) * GYROSCOPE_GAIN;
+        w_z = ((w_zAccumulator / SAMPLES) - w_zBias) * GYROSCOPE_GAIN;
+        pcMutex.lock();
+        pc.printf("w_x: %f  w_y: %f  w_z: %f \n",w_x,w_y,w_z);
+        pcMutex.unlock();
+        w_xAccumulator = 0;
+        w_yAccumulator = 0;
+        w_zAccumulator = 0;
+        gyroscopeSamples = 0;
+ 
+    } else {
+        imu.readGyro();
+        pcMutex.lock();
+        pc.printf("G: ");
+        pc.printf("%d", imu.gx);
+        pc.printf(", ");
+        pc.printf("%d",imu.gy);
+        pc.printf(", ");
+        pc.printf("%d\n",imu.gz);
+        pcMutex.unlock();
+        
+        w_xAccumulator += imu.gx;
+        w_yAccumulator += imu.gy;
+        w_zAccumulator += imu.gz;
+        
+        gyroscopeSamples++;
+        Thread::wait(GYRO_RATE * 1000);
+    }
+
+Thread::wait(GYRO_RATE * 1000);
+}
+}
+ 
+
+ 
+ 
+ void setup()
+{
+    pc.baud(9600); // Start serial at 115200 bps
+    // Use the begin() function to initialize the LSM9DS0 library.
+    // You can either call it with no parameters (the easy way):
+    //uint16_t status = dof.begin();
+    
+    //Or call it with declarations for sensor scales and data rates:
+    //uint16_t status = imu.begin(dof.G_SCALE_2000DPS,
+    //                            dof.A_SCALE_2G, dof.M_SCALE_2GS);
+                                
+    uint16_t status = imu.begin(imu.G_SCALE_245DPS, imu.A_SCALE_2G, imu.M_SCALE_2GS,
+                                imu.G_ODR_760_BW_100, imu.A_ODR_400, imu.M_ODR_50);
+    
+    // begin() returns a 16-bit value which includes both the gyro
+    // and accelerometers WHO_AM_I response. You can check this to
+    // make sure communication was successful.
+    pc.printf("LSM9DS0 WHO_AM_I's returned: 0x");
+    pc.printf("%x\n",status);
+    pc.printf("Should be 0x49D4\n");
+    pc.printf("\n");
+    
+    // Set up PID Loops
+    linearController.setInputLimits(-1.0, 1.0);
+    angularController.setInputLimits(-1.0, 1.0);
+    linearController.setOutputLimits(0.0, 1.0);
+    angularController.setOutputLimits(0.0, 1.0);
+    linearController.setSetPoint(0.0);
+    angularController.setSetPoint(0.0);
+    linearController.setMode(AUTO_MODE);
+    angularController.setMode(AUTO_MODE);
+    
+}
+
+
+int main() {
+ 
+    pc.printf("Starting IMU filter test...\n");
+    setup();
+    
+    
+    //Initialize inertial sensors.
+    calibrateAccelerometer();
+    calibrateGyroscope();
+
+    Thread accelThread(sampleAccelerometer);
+    Thread gyroThread(sampleGyroscope);
+    Thread commThread(parseInput);
+    commTicker.attach(&checkCOMRecieved, 2.0); // the address of the function to be attached (checkCOMRecieved) and the interval (2 seconds)
+    /*
+    pcMutex.lock();
+   pc.printf("done attaching tickers\n\n");
+   pcMutex.unlock();
+*/
+    while (1) {
+        //pc.printf("in loop\n");    
+        if(usePID != 1) {
+            v_mag2 = v_x * v_x + v_y * v_y;
+            linearController.setProcessValue(v_mag2);
+            angularController.setProcessValue(w_z); // w_z = degrees per second 
+            linearOutput = linearController.compute();
+            angularOutput = angularController.compute();
+            float leftMotorCO = 0.5 * (linearOutput + angularOutput);
+            float rightMotorCO = 0.5 * (linearOutput - angularOutput);
+            leftMotor.speed(leftMotorCO);
+            rightMotor.speed(rightMotorCO);
+            }
+        Thread::wait(RATE * 1000);
+ /*
+        pcMutex.lock();
+        pc.printf("%f,%f,%f\n",toDegrees(imuFilter.getRoll()),
+                  toDegrees(imuFilter.getPitch()),
+                  toDegrees(imuFilter.getYaw()));
+        pcMutex.unlock();
+*/
+    }
+
+}
\ No newline at end of file