Code for autonomous rover for Sparkfun AVC. DataBus won 3rd in 2012 and the same code was used on Troubled Child, a 1986 Jeep Grand Wagoneer to win 1st in 2014.

Dependencies:   mbed Watchdog SDFileSystem DigoleSerialDisp

Revision:
0:a6a169de725f
Child:
2:fbc6e3cf3ed8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sensors/Sensors.cpp	Mon May 27 13:26:03 2013 +0000
@@ -0,0 +1,418 @@
+/*
+
+MinIMU-9-Arduino-AHRS
+Pololu MinIMU-9 + Arduino AHRS (Attitude and Heading Reference System)
+
+Copyright (c) 2011 Pololu Corporation.
+http://www.pololu.com/
+
+MinIMU-9-Arduino-AHRS is based on sf9domahrs by Doug Weibel and Jose Julio:
+http://code.google.com/p/sf9domahrs/
+
+sf9domahrs is based on ArduIMU v1.5 by Jordi Munoz and William Premerlani, Jose
+Julio and Doug Weibel:
+http://code.google.com/p/ardu-imu/
+
+MinIMU-9-Arduino-AHRS is free software: you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation, either version 3 of the License, or (at your option)
+any later version.
+
+MinIMU-9-Arduino-AHRS is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with MinIMU-9-Arduino-AHRS. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "Sensors.h"
+#include "debug.h"
+#include "stdio.h"
+
+#define GYRO_SCALE 14.49787 // TODO: is the sign right here?? yes, see g_sign
+
+#define VFF 50.0 // voltage filter factor
+
+///////////////////// MAGNETOMETER CALIBRATION
+
+Sensors::Sensors():
+    //gps(p9, p10),              // Ublox6 GPS
+    gps(p26, p25),               // Venus GPS
+    _voltage(p19),               // Voltage from sensor board
+    _current(p20),               // Current from sensor board
+    _left(p30),                  // left wheel encoder
+    _right(p29),                 // Right wheel encoder
+    _gyro(p28, p27),             // MinIMU-9 gyro
+    _compass(p28, p27),          // MinIMU-9 compass/accelerometer
+    _rangers(p28, p27),          // Arduino ADC to I2C
+    _cam(p28, p27)
+{
+    for (int i=0; i < 3; i++) {
+        m_offset[i] = 0;
+        m_scale[i] = 1;
+    }
+
+    // TODO: parameterize
+    g_scale[0] = 1;
+    g_scale[1] = 1;
+    g_scale[2] = GYRO_SCALE;
+
+    g_sign[0] = 1;
+    g_sign[1] = -1;
+    g_sign[2] = -1;
+
+    a_sign[0] = -1;
+    a_sign[1] = 1;
+    a_sign[2] = 1;
+
+    m_sign[0] = 1;
+    m_sign[1] = -1;
+    m_sign[2] = -1;
+
+    // upside down mounting
+    //g_sign[3] = {1,1,1};        
+    //a_sign[3] = {-1,-1,-1};
+    //m_sign[3] = {1,1,1}; 
+}
+
+/* Compass_Calibrate
+ *
+ * Set the offset and scale calibration for the compass
+ */
+void Sensors::Compass_Calibrate(float offset[3], float scale[3])
+{
+    for (int i=0; i < 3; i++) {
+        m_offset[i] = offset[i];
+        m_scale[i] = scale[i];
+    }    
+ 
+    return;
+}
+
+
+void Sensors::Read_Encoders()
+{
+    // Odometry            
+    leftCount = _left.read();
+    rightCount = _right.read();
+    leftTotal += leftCount;
+    rightTotal += rightCount;
+            
+    // TODO--> sanity check on encoders; if difference between them
+    //  is huge, what do we do?  Slipping wheel?  Skidding wheel?
+    //  front encoders would be ideal as additional sanity check
+    
+    // TODO: move this into scheduler??
+    
+    // TODO: how do we track distance, should we only check distance everytime we do a nav/pos update?
+    // TODO: get rid of state variable
+    lrEncDistance  = (WHEEL_CIRC / WHEEL_STRIPES) * (double) leftCount;
+    rrEncDistance = (WHEEL_CIRC / WHEEL_STRIPES) * (double) rightCount;
+    encDistance = (lrEncDistance + rrEncDistance) / 2.0;
+    // compute speed from time between ticks
+    int leftTime = _left.readTime();
+    int rightTime = _right.readTime();
+
+    // We could also supplement this with distance/time calcs once we get up to a higher
+    // speed and encoder quantization error is lower
+    // To reduce asymmetry of the encoder operation, we're only reading time
+    // between rising ticks. So that means half the wheel stripes
+
+    if (leftTime <= 0) {
+        lrEncSpeed = 0;
+    } else {
+        lrEncSpeed = (2.0 * WHEEL_CIRC / WHEEL_STRIPES) / ((float) leftTime * 1e-6);
+    }
+    
+    if (rightTime <= 0) {
+        rrEncSpeed = 0;
+    } else {
+        rrEncSpeed = (2.0 * WHEEL_CIRC / WHEEL_STRIPES) / ((float) rightTime * 1e-6);
+    }
+        
+    // Dead band
+    if (lrEncSpeed < 0.1) lrEncSpeed = 0;
+    if (rrEncSpeed < 0.1) rrEncSpeed = 0;
+    // TODO: 3 use more sophisticated approach where we count if any ticks have happened lately
+    // and if not, reset the speed
+    encSpeed = (lrEncSpeed + rrEncSpeed) / 2.0;
+}
+
+
+void Sensors::Read_Gyro()
+{
+    _gyro.read(g);
+    gTemp = (int) _gyro.readTemp();
+    
+    gyro[_x_] = g_sign[_x_] * (g[_x_] - g_offset[_x_]); // really need to scale this too
+    gyro[_y_] = g_sign[_y_] * (g[_y_] - g_offset[_y_]); // really need to scale this too
+    gyro[_z_] = (g_sign[_z_] * (g[_z_] - g_offset[_z_])) / g_scale[_z_];
+
+    return;
+}
+
+// Reads x,y and z accelerometer registers
+void Sensors::Read_Accel()
+{
+    _compass.readAcc(a);
+
+    accel[_x_] = a_sign[_x_] * (a[_x_] - a_offset[_x_]);
+    accel[_y_] = a_sign[_y_] * (a[_y_] - a_offset[_y_]);
+    accel[_z_] = a_sign[_z_] * (a[_z_] - a_offset[_z_]);
+
+    return;  
+}
+
+void Sensors::Read_Compass()
+{
+    _compass.readMag(m);
+    // TODO: parameterize sign
+    // adjust for compass axis offsets and scale
+    for (int i=0; i < 3; i++) {
+        mag[i] = ((float) m[i] - m_offset[i]) * m_scale[i] * m_sign[i];
+    }
+    //fprintf(stdout, "m_offset=(%5.5f,%5.5f,%5.5f)\n", ahrs.c_magnetom[0],ahrs.c_magnetom[1],ahrs.c_magnetom[2]);
+  
+    return;
+}
+
+/* doing some crude gryo and accelerometer offset calibration here
+ *
+ */
+void Sensors::Calculate_Offsets()
+{
+    int samples=128;
+    
+    for (int i=0; i < 3; i++) {
+        g_offset[i] = 0;
+        a_offset[i] = 0;
+    }
+
+    for(int i=0; i < samples; i++) {  // We take some readings...
+        Read_Gyro();
+        Read_Accel();
+        wait(0.010); // sample at 100hz
+        for(int y=0; y < 3; y++) {   // accumulate values
+            g_offset[y] += g[y];
+            a_offset[y] += a[y];
+        }
+    }
+
+    for(int y=0; y < 3; y++) {
+        g_offset[y] /= samples;
+        a_offset[y] /= samples;
+    }
+
+    //TODO: if we ever get back to using accelerometer, do something about this.
+    //a_offset[_z_] -= GRAVITY * a_sign[_z_]; // I don't get why we're doing this...?
+}
+
+
+
+void Sensors::Compass_Heading()
+{
+  /*
+  float MAG_X;
+  float MAG_Y;
+  float cos_roll;
+  float sin_roll;
+  float cos_pitch;
+  float sin_pitch;
+  
+  // See DCM.cpp Drift_correction()
+
+  // umm... doesn't the dcm already have this info in it?!
+  cos_roll = cos(ahrs.roll);
+  sin_roll = sin(ahrs.roll);
+  cos_pitch = cos(ahrs.pitch);
+  sin_pitch = sin(ahrs.pitch);
+    
+  // Tilt compensated Magnetic filed X:
+  MAG_X = mag[_x_] * cos_pitch + mag[_y_] * sin_roll * sin_pitch + mag[_z_] * cos_roll * sin_pitch;
+  // Tilt compensated Magnetic filed Y:
+  MAG_Y = mag[_y_] * cos_roll - mag[_z_] * sin_roll;
+  // Magnetic Heading
+  ahrs.MAG_Heading = atan2(-MAG_Y,MAG_X);
+  */
+}
+
+
+void Sensors::getRawMag(int rawmag[3])
+{
+    Read_Compass();
+    for (int i=0; i < 3; i++) {
+        rawmag[i] = m[i];
+    }        
+}
+
+
+// getCurrent is a macro defined above
+
+float Sensors::getVoltage() {
+    static float filter = -1.0;
+    float v = _voltage * 3.3 * 4.12712;        // 242.3mV/V
+
+    // TODO: median filter to eliminate spikes
+    if (filter < 0) {
+        filter = v * VFF;
+    } else {
+        filter += (v - filter/VFF);
+    }
+    return (filter / VFF);
+}
+
+
+
+float Sensors::getCurrent() {
+    /*static bool first=true;
+    static float history[3];
+    float tmp;
+    float sort[3];
+
+    if (first) {
+        first = false;
+        history[0] = history[1] = history[2] = _current;
+    } else {
+        sort[0] = history[0] = history[1];
+        sort[1] = history[1] = history[2];
+        sort[2] = history[2] = _current;
+    }
+    BubbleSort(sort, 3);*/
+    return (_current * 3.3 * 13.6612); // 73.20mV/A        
+}
+
+
+void Sensors::Read_Power()
+{
+    // TODO: exponential or median filtering on these to get rid of spikes
+    //
+    voltage = getVoltage();
+    current = getCurrent();
+
+    return;
+}
+
+float clampIRRanger(float x)
+{
+    float y=x;
+    
+    if (y < 0.1) 
+        y = 0.1;
+    if (y > 5.0) 
+        y = 5.0;
+    
+    return y;
+}
+
+void Sensors::Read_Camera()
+{
+    char count;
+    //char data[128];
+    //char addr=0x80;
+    /*
+    struct {
+        int color;
+        int x1;
+        int y1;
+        int x2;
+        int y2;
+    } blob;
+    */
+
+    /* 
+    I2C START BIT
+    WRITE: 0xA0 ACK 
+    WRITE: 0x00 ACK 
+    I2C START BIT
+    WRITE: 0xA1 ACK 
+    READ: 0x01  ACK 0x02  ACK 0x03  ACK 0x04  ACK 0x05  ACK 0x06  ACK 0x07  ACK 0x08  ACK 0x09  ACK 0x0A 
+    NACK
+    I2C STOP BIT
+    */
+
+    _cam.start();
+    _cam.write(0x11<<1);
+    _cam.write(0x01); // command to send box data
+    _cam.start();
+    _cam.write((0x11<<1 | 0x01));
+    count = _cam.read(1); // number of boxes tracked
+    if (count > 8) count = 8;
+    //fprintf(stdout, "----------\n%d\n", count);
+    int x=0;
+    for (int i=0; i < count; i++) {
+        //fprintf(stdout, "%d: ", i);
+        for (int j=0; j < 5; j++) {
+            //data[x] = _cam.read(1);
+            //fprintf(stdout, "%d ", data[x]);
+            x++;
+        }
+        //fprintf(stdout, "\n");
+    }
+    _cam.read(0); // easiest to just read one more byte then NACK it
+    _cam.stop();
+}
+
+
+void Sensors::Read_Rangers()
+{
+    char data[2];
+    // Sharp GP2Y0A710YK0F
+    static float m=288.12;  // slope adc=m(dist) + b
+    static float b=219.3;
+    static float m_per_lsb=0.004883/0.38583; // 0.004883mV/lsb * 0.0098in/lsb * 1in/0.0254m = m/lsb
+    
+    _rangers.start();
+    data[0] = (0x11<<1 | 0x01); // send address + read = 1
+    _rangers.write(data[0]);       // send address
+    ranger[0] = (int) _rangers.read(1) | ((int) _rangers.read(1)) << 8;
+    ranger[1] = (int) _rangers.read(1) | ((int) _rangers.read(1)) << 8;
+    ranger[2] = (int) _rangers.read(1) | ((int) _rangers.read(0)) << 8; // don't ack the last byte
+    _rangers.stop();
+
+    /*
+    for (int q=0; q<3; q++)
+        fprintf(stdout, "%04x ", ranger[q]);    
+    fprintf(stdout, "\n");
+    */
+    
+    if (ranger[0] < 86) {
+        rightRanger = 999.0;
+    } else if (ranger[0] > 585) {
+        rightRanger = 20.0;
+    } else {
+        // Sharp GP2Y0A02YK0F
+        rightRanger = 1/(8.664e-005*ranger[0]-0.0008); // IR distance, meters
+    }
+
+    // Compute distances
+    leftRanger = clampIRRanger( m/(ranger[1]-b) );  // IR distance, meters
+    // Sonar
+    centerRanger = ranger[2] * (m_per_lsb); // Sonar distance, metersmv/in=0.0098, mv/lsb=0.0049
+}
+
+
+
+/** perform bubble sort
+ * for median filtering
+ */
+void Sensors::BubbleSort(float *num, int numLength)
+{
+    float temp;             // holding variable
+    bool flag=true;
+    
+    for(int i = 1; (i <= numLength) && flag; i++) {
+        flag = false;
+        for (int j = 0; j < (numLength -1); j++) {
+            if (num[j+1] > num[j]) {      // ascending order simply changes to <
+                temp = num[j];             // swap elements
+                num[j] = num[j+1];
+                num[j+1] = temp;
+                flag = true;
+            }
+        }
+    }
+    return;   //arrays are passed to functions by address; nothing is returned
+}