Roving robot using the RS-EDP.

Dependencies:   mbed RSEDP_AM_MC1_lib SDFileSystem

Files at this revision

API Documentation at this revision

Comitter:
aberk
Date:
Thu Aug 26 14:41:08 2010 +0000
Parent:
0:8d15dc761944
Commit message:
Added additional comments and documentation.

Changed in this revision

IMU.cpp Show annotated file Show diff for this revision Revisions of this file
IMU.h Show annotated file Show diff for this revision Revisions of this file
IMU.lib Show diff for this revision Revisions of this file
IMUfilter.cpp Show annotated file Show diff for this revision Revisions of this file
IMUfilter.h Show annotated file Show diff for this revision Revisions of this file
IMUfilter_lib.lib Show diff for this revision Revisions of this file
Rover.cpp Show annotated file Show diff for this revision Revisions of this file
Rover.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IMU.cpp	Thu Aug 26 14:41:08 2010 +0000
@@ -0,0 +1,336 @@
+/**
+ * @section LICENSE
+ *
+ * Copyright (c) 2010 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ * IMU consisting of ADXL345 accelerometer and ITG-3200 gyroscope using
+ * orientation filter developed by Sebastian Madgwick.
+ *
+ * Find more details about his paper here:
+ *
+ * http://code.google.com/p/imumargalgorithm30042010sohm/
+ */
+
+
+/**
+ * Includes
+ */
+#include "IMU.h"
+
+IMU::IMU(float imuRate,
+         double gyroscopeMeasurementError,
+         float accelerometerRate,
+         float gyroscopeRate) : accelerometer(p5, p6, p7, p8),
+        gyroscope(p9, p10), imuFilter(imuRate, gyroscopeMeasurementError) {
+
+    imuRate_ = imuRate;
+    accelerometerRate_ = accelerometerRate;
+    gyroscopeRate_ = gyroscopeRate;
+
+    //Initialize sampling variables.
+    a_xAccumulator = 0;
+    a_yAccumulator = 0;
+    a_zAccumulator = 0;
+    w_xAccumulator = 0;
+    w_yAccumulator = 0;
+    w_zAccumulator = 0;
+
+    accelerometerSamples = 0;
+    gyroscopeSamples = 0;
+
+    //Initialize and calibrate sensors.
+    initializeAccelerometer();
+    calibrateAccelerometer();
+
+    initializeGyroscope();
+    calibrateGyroscope();
+
+    //To reduce the number of interrupts we'll remove the separate tickers for
+    //the accelerometer, gyro and filter update and combine them all into one.
+
+    //accelerometerTicker.attach(this, &IMU::sampleAccelerometer, accelerometerRate_);
+    //gyroscopeTicker.attach(this, &IMU::sampleGyroscope, gyroscopeRate_);
+    sampleTicker.attach(this, &IMU::sample, accelerometerRate_);
+    //filterTicker.attach(this, &IMU::filter, imuRate_);
+
+}
+
+double IMU::getRoll(void) {
+
+    return toDegrees(imuFilter.getRoll());
+
+}
+
+double IMU::getPitch(void) {
+
+    return toDegrees(imuFilter.getPitch());
+
+}
+
+double IMU::getYaw(void) {
+
+    return toDegrees(imuFilter.getYaw());
+
+}
+
+void IMU::initializeAccelerometer(void) {
+
+    //Go into standby mode to configure the device.
+    accelerometer.setPowerControl(0x00);
+    //Full resolution, +/-16g, 4mg/LSB.
+    accelerometer.setDataFormatControl(0x0B);
+    //200Hz data rate.
+    accelerometer.setDataRate(ADXL345_200HZ);
+    //Measurement mode.
+    accelerometer.setPowerControl(0x08);
+    //See http://www.analog.com/static/imported-files/application_notes/AN-1077.pdf
+    wait_ms(22);
+
+}
+
+void IMU::sampleAccelerometer(void) {
+
+    //If we've taken a certain number of samples,
+    //average them, remove the bias and convert the units.
+    if (accelerometerSamples == SAMPLES) {
+
+        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;
+
+    }
+    //Otherwise, accumulate another reading. 
+    else {
+
+        accelerometer.getOutput(readings);
+
+        a_xAccumulator += (int16_t) readings[0];
+        a_yAccumulator += (int16_t) readings[1];
+        a_zAccumulator += (int16_t) readings[2];
+
+        accelerometerSamples++;
+
+    }
+
+}
+
+void IMU::calibrateAccelerometer(void) {
+
+    a_xAccumulator = 0;
+    a_yAccumulator = 0;
+    a_zAccumulator = 0;
+
+    //Accumulate a certain number of samples.
+    for (int i = 0; i < CALIBRATION_SAMPLES; i++) {
+
+        accelerometer.getOutput(readings);
+
+        a_xAccumulator += (int16_t) readings[0];
+        a_yAccumulator += (int16_t) readings[1];
+        a_zAccumulator += (int16_t) readings[2];
+
+        wait(accelerometerRate_);
+
+    }
+
+    //Average the samples.
+    a_xAccumulator /= CALIBRATION_SAMPLES;
+    a_yAccumulator /= CALIBRATION_SAMPLES;
+    a_zAccumulator /= CALIBRATION_SAMPLES;
+
+    //These are our zero g offsets.
+    //250 = 9.81m/s/s @ 4mg/LSB.
+    a_xBias = a_xAccumulator;
+    a_yBias = a_yAccumulator;
+    a_zBias = (a_zAccumulator - 250);
+
+    //Reset accumulators.
+    a_xAccumulator = 0;
+    a_yAccumulator = 0;
+    a_zAccumulator = 0;
+
+}
+
+void IMU::initializeGyroscope(void) {
+
+    //Low pass filter bandwidth of 42Hz.
+    gyroscope.setLpBandwidth(LPFBW_42HZ);
+    //Internal sample rate of 200Hz.
+    gyroscope.setSampleRateDivider(4);
+
+}
+
+void IMU::calibrateGyroscope(void) {
+
+    w_xAccumulator = 0;
+    w_yAccumulator = 0;
+    w_zAccumulator = 0;
+
+    //Accumulate a certain number of samples.
+    for (int i = 0; i < CALIBRATION_SAMPLES; i++) {
+
+        w_xAccumulator += gyroscope.getGyroX();
+        w_yAccumulator += gyroscope.getGyroY();
+        w_zAccumulator += gyroscope.getGyroZ();
+        wait(gyroscopeRate_);
+
+    }
+
+    //Average the samples.
+    w_xAccumulator /= CALIBRATION_SAMPLES;
+    w_yAccumulator /= CALIBRATION_SAMPLES;
+    w_zAccumulator /= CALIBRATION_SAMPLES;
+
+    //Set the null bias.
+    w_xBias = w_xAccumulator;
+    w_yBias = w_yAccumulator;
+    w_zBias = w_zAccumulator;
+
+    //Reset the accumulators.
+    w_xAccumulator = 0;
+    w_yAccumulator = 0;
+    w_zAccumulator = 0;
+
+}
+
+void IMU::sampleGyroscope(void) {
+
+    //If we've taken the required number of samples then,
+    //average the samples, removed the null bias and convert the units
+    //to rad/s.
+    if (gyroscopeSamples == SAMPLES) {
+
+        w_x = toRadians(((w_xAccumulator / SAMPLES) - w_xBias) * GYROSCOPE_GAIN);
+        w_y = toRadians(((w_yAccumulator / SAMPLES) - w_yBias) * GYROSCOPE_GAIN);
+        w_z = toRadians(((w_zAccumulator / SAMPLES) - w_zBias) * GYROSCOPE_GAIN);
+
+        w_xAccumulator = 0;
+        w_yAccumulator = 0;
+        w_zAccumulator = 0;
+        gyroscopeSamples = 0;
+
+    }
+    //Accumulate another sample. 
+    else {
+
+        w_xAccumulator += gyroscope.getGyroX();
+        w_yAccumulator += gyroscope.getGyroY();
+        w_zAccumulator += gyroscope.getGyroZ();
+
+        gyroscopeSamples++;
+
+    }
+
+}
+
+void IMU::sample(void) {
+
+    //If we've taken enough samples then,
+    //average the samples, remove the offsets and convert to appropriate units.
+    //Feed this information into the filter to calculate the new Euler angles.
+    if (accelerometerSamples == SAMPLES) {
+
+        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;
+
+        w_x = toRadians(((w_xAccumulator / SAMPLES) - w_xBias) * GYROSCOPE_GAIN);
+        w_y = toRadians(((w_yAccumulator / SAMPLES) - w_yBias) * GYROSCOPE_GAIN);
+        w_z = toRadians(((w_zAccumulator / SAMPLES) - w_zBias) * GYROSCOPE_GAIN);
+
+        w_xAccumulator = 0;
+        w_yAccumulator = 0;
+        w_zAccumulator = 0;
+        gyroscopeSamples = 0;
+
+        //Update the filter variables.
+        imuFilter.updateFilter(w_y, w_x, w_z, a_y, a_x, a_z);
+        //Calculate the new Euler angles.
+        imuFilter.computeEuler();
+
+    }
+    //Accumulate another sample. 
+    else {
+
+        accelerometer.getOutput(readings);
+
+        a_xAccumulator += (int16_t) readings[0];
+        a_yAccumulator += (int16_t) readings[1];
+        a_zAccumulator += (int16_t) readings[2];
+
+        w_xAccumulator += gyroscope.getGyroX();
+        w_yAccumulator += gyroscope.getGyroY();
+        w_zAccumulator += gyroscope.getGyroZ();
+
+        accelerometerSamples++;
+
+    }
+
+}
+
+void IMU::filter(void) {
+
+    //Update the filter variables.
+    imuFilter.updateFilter(w_y, w_x, w_z, a_y, a_x, a_z);
+    //Calculate the new Euler angles.
+    imuFilter.computeEuler();
+
+}
+
+void IMU::reset(void) {
+
+    //Disable interrupts.
+    sampleTicker.detach();
+    
+    //Recalibrate sensors.
+    calibrateAccelerometer();
+    calibrateGyroscope();
+    
+    //Reset the IMU filter.
+    imuFilter.reset();
+    
+    //Reset the working variables.
+    a_xAccumulator = 0;
+    a_yAccumulator = 0;
+    a_zAccumulator = 0;
+    w_xAccumulator = 0;
+    w_yAccumulator = 0;
+    w_zAccumulator = 0;
+    accelerometerSamples = 0;
+    gyroscopeSamples = 0;
+    
+    //Enable interrupts.
+    sampleTicker.attach(this, &IMU::sample, accelerometerRate_);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IMU.h	Thu Aug 26 14:41:08 2010 +0000
@@ -0,0 +1,208 @@
+/**
+ * @section LICENSE
+ *
+ * Copyright (c) 2010 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ * IMU consisting of ADXL345 accelerometer and ITG-3200 gyroscope using
+ * orientation filter developed by Sebastian Madgwick.
+ *
+ * Find more details about his paper here:
+ *
+ * http://code.google.com/p/imumargalgorithm30042010sohm/
+ */
+
+#ifndef MBED_IMU_H
+#define MBED_IMU_H
+
+/**
+ * Includes
+ */
+#include "mbed.h"
+#include "ADXL345.h"
+#include "ITG3200.h"
+#include "IMUfilter.h"
+
+/**
+ * Defines
+ */
+#define IMU_RATE           0.025
+#define ACCELEROMETER_RATE 0.005
+#define GYROSCOPE_RATE     0.005
+#define GYRO_MEAS_ERROR    0.3 //IMUfilter tuning parameter.
+
+//Gravity at Earth's surface in m/s/s
+#define g0 9.812865328
+//Number of samples to average
+#define SAMPLES 4
+#define CALIBRATION_SAMPLES 128
+//Multiply radians to get degrees.
+#define toDegrees(x) (x * 57.2957795)
+//Multiply degrees to get radians.
+#define toRadians(x) (x * 0.01745329252)
+//Full scale resolution on the ADXL345 is 4mg/LSB.
+//Multiply ADC count readings from ADXL345 to get acceleration in m/s/s.
+#define toAcceleration(x) (x * (4 * g0 * 0.001))
+//14.375 LSB/(degrees/sec)
+#define GYROSCOPE_GAIN (1 / 14.375)
+#define ACCELEROMETER_GAIN (0.004 * g0)
+
+/**
+ * IMU consisting of ADXL345 accelerometer and ITG-3200 gyroscope to calculate
+ * roll, pitch and yaw angles.
+ */
+class IMU {
+
+public:
+
+    /**
+     * Constructor.
+     *
+     * @param imuRate Rate which IMUfilter update and Euler angle calculation
+     *                occurs.
+     * @param gyroscopeMeasurementError IMUfilter tuning parameter.
+     * @param accelerometerRate Rate at which accelerometer data is sampled.
+     * @param gyroscopeRate Rate at which gyroscope data is sampled.
+     */
+    IMU(float imuRate,
+        double gyroscopeMeasurementError,
+        float accelerometerRate,
+        float gyroscopeRate);
+
+    /**
+     * Get the current roll angle.
+     *
+     * @return The current roll angle in degrees.
+     */
+    double getRoll(void);
+
+    /**
+     * Get the current pitch angle.
+     *
+     * @return The current pitch angle in degrees.
+     */
+    double getPitch(void);
+
+    /**
+     * Get the current yaw angle.
+     *
+     * @return The current yaw angle in degrees.
+     */
+    double getYaw(void);
+
+    /**
+     * Sample the sensors, and if enough samples have been taken,
+     * take an average, and compute the new Euler angles.
+     */
+    void sample(void);
+    
+    /**
+     * Recalibrate the sensors and reset the IMU filter.
+     */
+    void reset(void);
+
+private:
+
+    /**
+     * Set up the ADXL345 appropriately.
+     */
+    void initializeAccelerometer(void);
+
+    /**
+     * Calculate the zero g offset.
+     */
+    void calibrateAccelerometer(void);
+
+    /**
+     * Take a set of samples and average them.
+     */
+    void sampleAccelerometer(void);
+
+    /**
+     * Set up the ITG-3200 appropriately.
+     */
+    void initializeGyroscope(void);
+
+    /**
+     * Calculate the bias offset.
+     */
+    void calibrateGyroscope(void);
+
+    /**
+     * Take a set of samples and average them.
+     */
+    void sampleGyroscope(void);
+
+    /**
+     * Update the filter and calculate the Euler angles.
+     */
+    void filter(void);
+
+    ADXL345   accelerometer;
+    ITG3200   gyroscope;
+    IMUfilter imuFilter;
+
+    Ticker accelerometerTicker;
+    Ticker gyroscopeTicker;
+    Ticker sampleTicker;
+    Ticker filterTicker;
+
+    float accelerometerRate_;
+    float gyroscopeRate_;
+    float imuRate_;
+
+    //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;
+
+    double a_xBias;
+    double a_yBias;
+    double a_zBias;
+
+    volatile double a_xAccumulator;
+    volatile double a_yAccumulator;
+    volatile double a_zAccumulator;
+    volatile double w_xAccumulator;
+    volatile double w_yAccumulator;
+    volatile double w_zAccumulator;
+
+    //Accelerometer and gyroscope readings for x, y, z axes.
+    volatile double a_x;
+    volatile double a_y;
+    volatile double a_z;
+    volatile double w_x;
+    volatile double w_y;
+    volatile double w_z;
+
+    //Buffer for accelerometer readings.
+    int readings[3];
+    int accelerometerSamples;
+    int gyroscopeSamples;
+
+};
+
+#endif /* MBED_IMU_H */
--- a/IMU.lib	Mon Aug 16 09:46:28 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-http://mbed.org/users/aberk/code/IMU/#51d01aa47c71
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IMUfilter.cpp	Thu Aug 26 14:41:08 2010 +0000
@@ -0,0 +1,226 @@
+/**
+ * @section LICENSE
+ *
+ * Copyright (c) 2010 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ * IMU orientation filter developed by Sebastian Madgwick.
+ *
+ * Find more details about his paper here:
+ *
+ * http://code.google.com/p/imumargalgorithm30042010sohm/
+ */
+
+/**
+ * Includes
+ */
+#include "IMUfilter.h"
+
+IMUfilter::IMUfilter(double rate, double gyroscopeMeasurementError) {
+
+    firstUpdate = 0;
+
+    //Quaternion orientation of earth frame relative to auxiliary frame.
+    AEq_1 = 1;
+    AEq_2 = 0;
+    AEq_3 = 0;
+    AEq_4 = 0;
+
+    //Estimated orientation quaternion elements with initial conditions.
+    SEq_1 = 1;
+    SEq_2 = 0;
+    SEq_3 = 0;
+    SEq_4 = 0;
+
+    //Sampling period (typical value is ~0.1s).
+    deltat = rate;
+
+    //Gyroscope measurement error (in degrees per second).
+    gyroMeasError = gyroscopeMeasurementError;
+
+    //Compute beta.
+    beta = sqrt(3.0 / 4.0) * (PI * (gyroMeasError / 180.0));
+
+}
+
+void IMUfilter::updateFilter(double w_x, double w_y, double w_z, double a_x, double a_y, double a_z) {
+
+    //Local system variables.
+
+    //Vector norm.
+    double norm;
+    //Quaternion rate from gyroscope elements.
+    double SEqDot_omega_1;
+    double SEqDot_omega_2;
+    double SEqDot_omega_3;
+    double SEqDot_omega_4;
+    //Objective function elements.
+    double f_1;
+    double f_2;
+    double f_3;
+    //Objective function Jacobian elements.
+    double J_11or24;
+    double J_12or23;
+    double J_13or22;
+    double J_14or21;
+    double J_32;
+    double J_33;
+    //Objective function gradient elements.
+    double nablaf_1;
+    double nablaf_2;
+    double nablaf_3;
+    double nablaf_4;
+
+    //Auxiliary variables to avoid reapeated calcualtions.
+    double halfSEq_1 = 0.5 * SEq_1;
+    double halfSEq_2 = 0.5 * SEq_2;
+    double halfSEq_3 = 0.5 * SEq_3;
+    double halfSEq_4 = 0.5 * SEq_4;
+    double twoSEq_1 = 2.0 * SEq_1;
+    double twoSEq_2 = 2.0 * SEq_2;
+    double twoSEq_3 = 2.0 * SEq_3;
+
+    //Compute the quaternion rate measured by gyroscopes.
+    SEqDot_omega_1 = -halfSEq_2 * w_x - halfSEq_3 * w_y - halfSEq_4 * w_z;
+    SEqDot_omega_2 = halfSEq_1 * w_x + halfSEq_3 * w_z - halfSEq_4 * w_y;
+    SEqDot_omega_3 = halfSEq_1 * w_y - halfSEq_2 * w_z + halfSEq_4 * w_x;
+    SEqDot_omega_4 = halfSEq_1 * w_z + halfSEq_2 * w_y - halfSEq_3 * w_x;
+
+    //Normalise the accelerometer measurement.
+    norm = sqrt(a_x * a_x + a_y * a_y + a_z * a_z);
+    a_x /= norm;
+    a_y /= norm;
+    a_z /= norm;
+
+    //Compute the objective function and Jacobian.
+    f_1 = twoSEq_2 * SEq_4 - twoSEq_1 * SEq_3 - a_x;
+    f_2 = twoSEq_1 * SEq_2 + twoSEq_3 * SEq_4 - a_y;
+    f_3 = 1.0 - twoSEq_2 * SEq_2 - twoSEq_3 * SEq_3 - a_z;
+    //J_11 negated in matrix multiplication.
+    J_11or24 = twoSEq_3;
+    J_12or23 = 2 * SEq_4;
+    //J_12 negated in matrix multiplication
+    J_13or22 = twoSEq_1;
+    J_14or21 = twoSEq_2;
+    //Negated in matrix multiplication.
+    J_32 = 2 * J_14or21;
+    //Negated in matrix multiplication.
+    J_33 = 2 * J_11or24;
+
+    //Compute the gradient (matrix multiplication).
+    nablaf_1 = J_14or21 * f_2 - J_11or24 * f_1;
+    nablaf_2 = J_12or23 * f_1 + J_13or22 * f_2 - J_32 * f_3;
+    nablaf_3 = J_12or23 * f_2 - J_33 * f_3 - J_13or22 * f_1;
+    nablaf_4 = J_14or21 * f_1 + J_11or24 * f_2;
+
+    //Normalise the gradient.
+    norm = sqrt(nablaf_1 * nablaf_1 + nablaf_2 * nablaf_2 + nablaf_3 * nablaf_3 + nablaf_4 * nablaf_4);
+    nablaf_1 /= norm;
+    nablaf_2 /= norm;
+    nablaf_3 /= norm;
+    nablaf_4 /= norm;
+
+    //Compute then integrate the estimated quaternion rate.
+    SEq_1 += (SEqDot_omega_1 - (beta * nablaf_1)) * deltat;
+    SEq_2 += (SEqDot_omega_2 - (beta * nablaf_2)) * deltat;
+    SEq_3 += (SEqDot_omega_3 - (beta * nablaf_3)) * deltat;
+    SEq_4 += (SEqDot_omega_4 - (beta * nablaf_4)) * deltat;
+
+    //Normalise quaternion
+    norm = sqrt(SEq_1 * SEq_1 + SEq_2 * SEq_2 + SEq_3 * SEq_3 + SEq_4 * SEq_4);
+    SEq_1 /= norm;
+    SEq_2 /= norm;
+    SEq_3 /= norm;
+    SEq_4 /= norm;
+
+    if (firstUpdate == 0) {
+        //Store orientation of auxiliary frame.
+        AEq_1 = SEq_1;
+        AEq_2 = SEq_2;
+        AEq_3 = SEq_3;
+        AEq_4 = SEq_4;
+        firstUpdate = 1;
+    }
+
+}
+
+void IMUfilter::computeEuler(void) {
+
+    //Quaternion describing orientation of sensor relative to earth.
+    double ESq_1, ESq_2, ESq_3, ESq_4;
+    //Quaternion describing orientation of sensor relative to auxiliary frame.
+    double ASq_1, ASq_2, ASq_3, ASq_4;
+
+    //Compute the quaternion conjugate.
+    ESq_1 = SEq_1;
+    ESq_2 = -SEq_2;
+    ESq_3 = -SEq_3;
+    ESq_4 = -SEq_4;
+
+    //Compute the quaternion product.
+    ASq_1 = ESq_1 * AEq_1 - ESq_2 * AEq_2 - ESq_3 * AEq_3 - ESq_4 * AEq_4;
+    ASq_2 = ESq_1 * AEq_2 + ESq_2 * AEq_1 + ESq_3 * AEq_4 - ESq_4 * AEq_3;
+    ASq_3 = ESq_1 * AEq_3 - ESq_2 * AEq_4 + ESq_3 * AEq_1 + ESq_4 * AEq_2;
+    ASq_4 = ESq_1 * AEq_4 + ESq_2 * AEq_3 - ESq_3 * AEq_2 + ESq_4 * AEq_1;
+
+    //Compute the Euler angles from the quaternion.
+    phi = atan2(2 * ASq_3 * ASq_4 - 2 * ASq_1 * ASq_2, 2 * ASq_1 * ASq_1 + 2 * ASq_4 * ASq_4 - 1);
+    theta = asin(2 * ASq_2 * ASq_3 - 2 * ASq_1 * ASq_3);
+    psi = atan2(2 * ASq_2 * ASq_3 - 2 * ASq_1 * ASq_4, 2 * ASq_1 * ASq_1 + 2 * ASq_2 * ASq_2 - 1);
+
+}
+
+double IMUfilter::getRoll(void) {
+
+    return phi;
+
+}
+
+double IMUfilter::getPitch(void) {
+
+    return theta;
+
+}
+
+double IMUfilter::getYaw(void) {
+
+    return psi;
+
+}
+
+void IMUfilter::reset(void) {
+
+    firstUpdate = 0;
+
+    //Quaternion orientation of earth frame relative to auxiliary frame.
+    AEq_1 = 1;
+    AEq_2 = 0;
+    AEq_3 = 0;
+    AEq_4 = 0;
+
+    //Estimated orientation quaternion elements with initial conditions.
+    SEq_1 = 1;
+    SEq_2 = 0;
+    SEq_3 = 0;
+    SEq_4 = 0;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IMUfilter.h	Thu Aug 26 14:41:08 2010 +0000
@@ -0,0 +1,141 @@
+/**
+ * @section LICENSE
+ *
+ * Copyright (c) 2010 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ * IMU orientation filter developed by Sebastian Madgwick.
+ *
+ * Find more details about his paper here:
+ *
+ * http://code.google.com/p/imumargalgorithm30042010sohm/
+ */
+
+#ifndef IMU_FILTER_H
+#define IMU_FILTER_H
+
+/**
+ * Includes
+ */
+#include "mbed.h"
+
+/**
+ * Defines
+ */
+#define PI 3.1415926536
+
+/**
+ * IMU orientation filter.
+ */
+class IMUfilter {
+
+public:
+
+    /**
+     * Constructor.
+     *
+     * Initializes filter variables.
+     *
+     * @param rate The rate at which the filter should be updated.
+     * @param gyroscopeMeasurementError The error of the gyroscope in degrees
+     *  per second. This used to calculate a tuning constant for the filter.
+     *  Try changing this value if there are jittery readings, or they change
+     *  too much or too fast when rotating the IMU.
+     */
+    IMUfilter(double rate, double gyroscopeMeasurementError);
+
+    /**
+     * Update the filter variables.
+     *
+     * @param w_x X-axis gyroscope reading in rad/s.
+     * @param w_y Y-axis gyroscope reading in rad/s.
+     * @param w_z Z-axis gyroscope reading in rad/s.
+     * @param a_x X-axis accelerometer reading in m/s/s.
+     * @param a_y Y-axis accelerometer reading in m/s/s.
+     * @param a_z Z-axis accelerometer reading in m/s/s.
+     */
+    void updateFilter(double w_x, double w_y, double w_z,
+                      double a_x, double a_y, double a_z);
+
+    /**
+     * Compute the Euler angles based on the current filter data.
+     */
+    void computeEuler(void);
+
+    /**
+     * Get the current roll.
+     *
+     * @return The current roll angle in radians.
+     */
+    double getRoll(void);
+
+    /**
+     * Get the current pitch.
+     *
+     * @return The current pitch angle in radians.
+     */
+    double getPitch(void);
+
+    /**
+     * Get the current yaw.
+     *
+     * @return The current yaw angle in radians.
+     */
+    double getYaw(void);
+    
+    /**
+     * Reset the filter.
+     */
+    void reset(void);
+
+private:
+
+    int firstUpdate;
+
+    //Quaternion orientation of earth frame relative to auxiliary frame.
+    double AEq_1;
+    double AEq_2;
+    double AEq_3;
+    double AEq_4;
+
+    //Estimated orientation quaternion elements with initial conditions.
+    double SEq_1;
+    double SEq_2;
+    double SEq_3;
+    double SEq_4;
+
+    //Sampling period
+    double deltat;
+
+    //Gyroscope measurement error (in degrees per second).
+    double gyroMeasError;
+
+    //Compute beta (filter tuning constant..
+    double beta;
+
+    double phi;
+    double theta;
+    double psi;
+
+};
+
+#endif /* IMU_FILTER_H */
--- a/IMUfilter_lib.lib	Mon Aug 16 09:46:28 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-http://mbed.org/users/aberk/programs/IMUfilter_lib/latest
\ No newline at end of file
--- a/Rover.cpp	Mon Aug 16 09:46:28 2010 +0000
+++ b/Rover.cpp	Thu Aug 26 14:41:08 2010 +0000
@@ -61,8 +61,6 @@
  */
 #include "Rover.h"
 
-Serial pc2(USBTX, USBRX);
-
 Rover::Rover(PinName leftMotorPwm,
              PinName leftMotorBrake,
              PinName leftMotorDirection,
@@ -91,7 +89,7 @@
         rightController(Kc, Ti, Td, PID_RATE),
         stateTicker(),
         logTicker(),
-        imu(IMU_RATE,
+        imu(IMU_RATE_,
             GYRO_MEAS_ERROR,
             ACCELEROMETER_RATE,
             GYROSCOPE_RATE) {
@@ -150,6 +148,7 @@
     degreesTurned_    = 0.0;
     leftStopFlag_     = 0;
     rightStopFlag_    = 0;
+    logIndex          = 0;
 
     //--------
     // BEGIN!
@@ -160,7 +159,14 @@
 
 }
 
-void Rover::move(int distance) {
+void Rover::move(float distance) {
+
+    //Convert from metres into millimetres.
+    distance *= 1000;
+    //Work out how many pulses are required to go that many millimetres.
+    distance *= PULSES_PER_MM;
+    //Make sure we scale the number of pulses according to our encoding method.
+    distance /= ENCODING;
 
     positionSetPoint_ = distance;
 
@@ -183,7 +189,15 @@
 
 void Rover::turn(int degrees) {
 
-    headingSetPoint_ = abs(degrees);
+    //Correct the amount to turn based on deviation during last segment.
+    headingSetPoint_ = abs(degrees) + (endHeading_ - startHeading_);
+    
+    //In case the rover tries to [pointlessly] turn >360 degrees.
+    if (headingSetPoint_ > 359.8){
+    
+        headingSetPoint_ -= 359.8;
+    
+    }
 
     //Rotating clockwise.
     if (degrees > 0) {
@@ -210,22 +224,28 @@
 
 void Rover::startLogging(void) {
 
-    logFile = fopen("/sd/roverlog.csv", "w");
-    fprintf(logFile, "leftPulses, rightPulses, leftVelocity, rightVelocity, heading, degreesTurned\n");
-    logTicker.attach(this, &Rover::log, LOG_RATE);
+    logFile = fopen("/local/roverlog.csv", "w");
+    fprintf(logFile, "leftPulses, rightPulses, leftVelocity, rightVelocity, heading\n");
+    //logTicker.attach(this, &Rover::log, LOG_RATE);
 
 }
 
 void Rover::stopLogging(void) {
 
+    //logFile = fopen("/local/roverlog.csv", "w");
+    //fprintf(logFile, "leftPulses, rightPulses, leftVelocity, rightVelocity, heading, degreesTurned\n");
+    //fprintf(logFile, "leftVelocity, rightVelocity\n");
+    //for(int i = 0; i < 1024; i++){
+    //    fprintf(logFile, "%f, %f\n", leftVelocityBuffer[i], rightVelocityBuffer[i]);
+    //}
     fclose(logFile);
 
 }
 
 void Rover::log(void) {
 
-    fprintf(logFile, "%i,%i,%f,%f,%f,%f\n",
-            leftPulses_, rightPulses_, leftVelocity_, rightVelocity_, imu.getYaw(), degreesTurned_);
+    //fprintf(logFile, "%i,%i,%f,%f,%f,%f\n",
+    //        leftPulses_, rightPulses_, leftVelocity_, rightVelocity_, imu.getYaw(), degreesTurned_);
 
 }
 
@@ -235,6 +255,7 @@
 
             //We're not moving so don't do anything!
         case (STATE_STATIONARY):
+
             break;
 
         case (STATE_MOVING_FORWARD):
@@ -247,7 +268,7 @@
                 leftVelocity_ = (leftPulses_ - leftPrevPulses_) / PID_RATE;
                 leftPrevPulses_ = leftPulses_;
                 leftController.setProcessValue(leftVelocity_);
-                leftPwmDuty_ = leftController.getRealOutput();
+                leftPwmDuty_ = leftController.compute();
 
             } else {
                 leftStopFlag_ = 1;
@@ -261,8 +282,8 @@
                 rightVelocity_ = (rightPulses_ - rightPrevPulses_) / PID_RATE;
                 rightPrevPulses_ = rightPulses_;
                 rightController.setProcessValue(rightVelocity_);
-                rightPwmDuty_ = rightController.getRealOutput();
-                
+                rightPwmDuty_ = rightController.compute();
+
             } else {
                 rightStopFlag_ = 1;
             }
@@ -275,6 +296,7 @@
                 rightPwmDuty_ = 1.0;
                 leftMotors.setPwmDuty(leftPwmDuty_);
                 rightMotors.setPwmDuty(rightPwmDuty_);
+                endHeading_ = imu.getYaw();
                 enterState(STATE_STATIONARY);
             }
 
@@ -289,7 +311,7 @@
                 leftVelocity_ = (leftPulses_ - leftPrevPulses_) / PID_RATE;
                 leftPrevPulses_ = leftPulses_;
                 leftController.setProcessValue(fabs(leftVelocity_));
-                leftPwmDuty_ = leftController.getRealOutput();
+                leftPwmDuty_ = leftController.compute();
             } else {
                 leftStopFlag_ = 1;
             }
@@ -301,7 +323,7 @@
                 rightVelocity_ = (rightPulses_ - rightPrevPulses_) / PID_RATE;
                 rightPrevPulses_ = rightPulses_;
                 rightController.setProcessValue(fabs(rightVelocity_));
-                rightPwmDuty_ = rightController.getRealOutput();
+                rightPwmDuty_ = rightController.compute();
 
             } else {
                 rightStopFlag_ = 1;
@@ -334,13 +356,13 @@
                 leftVelocity_ = (leftPulses_ - leftPrevPulses_) / PID_RATE;
                 leftPrevPulses_ = leftPulses_;
                 leftController.setProcessValue(leftVelocity_);
-                leftPwmDuty_ = leftController.getRealOutput();
+                leftPwmDuty_ = leftController.compute();
 
                 rightPulses_ = rightQei.getPulses();
                 rightVelocity_ = (rightPulses_ - rightPrevPulses_) / PID_RATE;
                 rightPrevPulses_ = rightPulses_;
                 rightController.setProcessValue(fabs(rightVelocity_));
-                rightPwmDuty_ = rightController.getRealOutput();
+                rightPwmDuty_ = rightController.compute();
 
                 leftMotors.setPwmDuty(leftPwmDuty_);
                 rightMotors.setPwmDuty(rightPwmDuty_);
@@ -371,13 +393,13 @@
                 leftVelocity_ = (leftPulses_ - leftPrevPulses_) / PID_RATE;
                 leftPrevPulses_ = leftPulses_;
                 leftController.setProcessValue(fabs(leftVelocity_));
-                leftPwmDuty_ = leftController.getRealOutput();
+                leftPwmDuty_ = leftController.compute();
 
                 rightPulses_ = rightQei.getPulses();
                 rightVelocity_ = (rightPulses_ - rightPrevPulses_) / PID_RATE;
                 rightPrevPulses_ = rightPulses_;
                 rightController.setProcessValue(rightVelocity_);
-                rightPwmDuty_ = rightController.getRealOutput();
+                rightPwmDuty_ = rightController.compute();
 
                 leftMotors.setPwmDuty(leftPwmDuty_);
                 rightMotors.setPwmDuty(rightPwmDuty_);
@@ -405,6 +427,15 @@
 
     }
 
+    if (logIndex < 1024) {
+        headingBuffer[logIndex] = imu.getYaw();
+        leftVelocityBuffer[logIndex] = leftVelocity_;
+        rightVelocityBuffer[logIndex] = rightVelocity_;
+        leftPositionBuffer[logIndex] = leftPulses_;
+        rightPositionBuffer[logIndex] = rightPulses_;
+        logIndex++;
+    }
+
 }
 
 void Rover::enterState(State state) {
@@ -449,6 +480,18 @@
             leftStopFlag_     = 0;
             rightStopFlag_    = 0;
 
+            for (int i = 0; i < logIndex; i++) {
+                fprintf(logFile, "%i, %i, %f, %f, %f\n", leftPositionBuffer[i],
+                        rightPositionBuffer[i],
+                        leftVelocityBuffer[i],
+                        rightVelocityBuffer[i],
+                        headingBuffer[i]);
+            }
+
+            logIndex = 0;
+            
+            imu.reset();
+
             state_ = STATE_STATIONARY;
 
             break;
@@ -466,6 +509,10 @@
             leftController.setSetPoint(1000);
             rightController.setSetPoint(1000);
 
+            logIndex = 0;
+            
+            startHeading_ = imu.getYaw();
+
             state_ = STATE_MOVING_FORWARD;
 
             break;
@@ -483,6 +530,8 @@
             leftController.setSetPoint(1000);
             rightController.setSetPoint(1000);
 
+            logIndex = 0;
+
             state_ = STATE_MOVING_BACKWARD;
 
             break;
@@ -504,6 +553,8 @@
             heading_ = fabs(imu.getYaw());
             prevHeading_ = heading_;
 
+            logIndex = 0;
+
             state_ = STATE_ROTATING_CLOCKWISE;
 
             break;
@@ -525,12 +576,16 @@
             heading_ = fabs(imu.getYaw());
             prevHeading_ = heading_;
 
+            logIndex = 0;
+
             state_ = STATE_ROTATING_COUNTER_CLOCKWISE;
 
             break;
 
         default:
 
+            state_ = STATE_STATIONARY;
+
             break;
 
     }
--- a/Rover.h	Mon Aug 16 09:46:28 2010 +0000
+++ b/Rover.h	Thu Aug 26 14:41:08 2010 +0000
@@ -39,7 +39,7 @@
  * ---------------
  *
  * The set up assumes the H-bridge being used has pins for:
- * 
+ *
  * - PWM input
  * - Brake
  * - Direction
@@ -68,6 +68,7 @@
 #include "PID.h"
 #include "IMU.h"
 #include "SDFileSystem.h"
+#include "HMC6352.h"
 
 /**
  * Defines
@@ -81,6 +82,9 @@
 #define REVS_PER_ROTATION     (ROTATION_DISTANCE / WHEEL_DIAMETER)
 #define PULSES_PER_ROTATION   (REVS_PER_ROTATION * PULSES_PER_REV)
 #define PULSES_PER_MM         (PULSES_PER_REV / WHEEL_DIAMETER)
+#define DISTANCE_PER_PULSE    (WHEEL_DIAMETER / PULSES_PER_REV)
+#define ENCODING              2 //Use X2 encoding
+#define WHEEL_DISTANCE        (ROTATION_DISTANCE / DISTANCE_PER_PULSE)
 //-----
 // PID
 //-----
@@ -96,13 +100,13 @@
 //---------
 // Logging
 //---------
-#define LOG_RATE     0.01
+#define LOG_RATE     0.025
 //-----
 // IMU
 //-----
 #define ACCELEROMETER_RATE 0.005
 #define GYROSCOPE_RATE     0.005
-#define IMU_RATE           0.025
+#define IMU_RATE_          0.025
 #define GYRO_MEAS_ERROR    0.3
 
 class Rover {
@@ -175,12 +179,17 @@
      * +ve distance -> forward
      * -ve distance -> backward
      *
-     * @param distance The distance to move.
+     * @param distance The distance to move in metres.
      */
-    void move(int distance);
+    void move(float distance);
 
     /**
      * Turn the rover left or right a certain number of degrees.
+     *
+     * +ve degrees -> clockwise
+     * -ve degrees -> counter-clockwise
+     *
+     * @param degrees The number of degrees to rotate.
      */
     void turn(int degrees);
 
@@ -246,12 +255,12 @@
     IMU           imu;
 
     FILE*          logFile;
-    
+
     volatile int   leftStopFlag_;
     volatile int   rightStopFlag_;
 
     volatile int   positionSetPoint_;
-    volatile int   headingSetPoint_;
+    volatile float headingSetPoint_;
     volatile float degreesTurned_;
 
     volatile int   leftPulses_;
@@ -269,6 +278,16 @@
 
     volatile State state_;
 
+    volatile float headingBuffer[1024];
+    volatile int   leftPositionBuffer[1024];
+    volatile int   rightPositionBuffer[1024];
+    volatile float leftVelocityBuffer[1024];
+    volatile float rightVelocityBuffer[1024];
+    volatile int   logIndex;
+    
+    volatile float startHeading_;
+    volatile float endHeading_;
+    
 };
 
 #endif /* ROVER_H */
--- a/main.cpp	Mon Aug 16 09:46:28 2010 +0000
+++ b/main.cpp	Thu Aug 26 14:41:08 2010 +0000
@@ -37,6 +37,7 @@
 
 //Debugging.
 Serial pc(USBTX, USBRX);
+DigitalOut led1(LED1);
 
 //Globals.
 LocalFileSystem local("local");
@@ -55,34 +56,44 @@
     //-----------------------
     // Simple command parser
     //-----------------------
-    char cmd0[16]; //{"move", "turn"}
-    char cmd1[16]; //{{"forward", "backward"}, {"left", "right"}}
-    int  cmd2 = 0; //{distance, angle}
+    char  cmd0[16]; //{"move", "turn"}
+    char  cmd1[16]; //{{"forward", "backward"}, {"left", "right"}}
+    float cmd2 = 0; //{distance in METRES, angle in DEGREES}
 
     pc.printf("Starting...\n");
 
+    //Wait a bit before we start moving.
     wait(3);
 
+    //Open the command script.
     FILE *fp = fopen("/local/commands.txt", "r");
 
+    //Check if we were successful in opening the command script.
     if (fp == NULL) {
         pc.printf("Opening file failed...\n");
     } else {
         pc.printf("Opening file succeeded!\n");
     }
 
-    while (fscanf(fp, "%s%s%d", cmd0, cmd1, &cmd2) >= 0) {
+    //While there's another line to read from the command script.
+    while (fscanf(fp, "%s%s%f", cmd0, cmd1, &cmd2) >= 0) {
+    
+        led1 = 1;
+    
+        pc.printf("Start of new command\n");
 
         wait(1);
 
-        pc.printf("%s %s %d\n", cmd0, cmd1, cmd2);
+        pc.printf("%s %s %f\n", cmd0, cmd1, cmd2);
 
         //move command.
         if (strcmp(cmd0, "move") == 0) {
             if (strcmp(cmd1, "forward") == 0) {
                 myRover.move(cmd2);
                 while (myRover.getState() != Rover::STATE_STATIONARY) {
+                
                 }
+                pc.printf("Finished!\n");
             } else if (strcmp(cmd1, "backward") == 0) {
                 myRover.move(-cmd2);
                 while (myRover.getState() != Rover::STATE_STATIONARY) {
@@ -101,6 +112,9 @@
                 }
             }
         }
+        
+        pc.printf("End of command\n");
+        led1 = 0;
 
     }