test angle measurement LSM6DS3

Dependencies:   ESC IMUfilter LSM6DS3 mbed

Files at this revision

API Documentation at this revision

Comitter:
rsmits
Date:
Sat Feb 10 12:42:40 2018 +0000
Parent:
2:405f8e1a01d3
Commit message:
calibration

Changed in this revision

main.cpp Show annotated file Show diff for this revision Revisions of this file
diff -r 405f8e1a01d3 -r 78cf56b8eb21 main.cpp
--- a/main.cpp	Sat Feb 03 15:21:25 2018 +0000
+++ b/main.cpp	Sat Feb 10 12:42:40 2018 +0000
@@ -11,21 +11,84 @@
 #define toRadians(x) (x * 0.01745329252f)
 //Convert from radians to degrees.
 #define toDegrees(x) (x * 57.2957795)
-
+//Number of samples to average.
+#define SAMPLES 4
+//Number of samples to be averaged for a null bias calculation
+//during calibration.
+#define CALIBRATION_SAMPLES 128
+//Sampling gyroscope at 200Hz.
+#define GYRO_RATE   0.005
+//Sampling accelerometer at 200Hz.
+#define ACC_RATE    0.005
+//Updating filter at 40Hz.
+#define FILTER_RATE 0.1
 const int   LSM6DS3_ADDRESS = 0xD4;
-float       DEGREES[3];
-float       ACCELERATIONS[3];
-float       ANGLES[3];
 //const int   LSM6DS3_ADDRESS = 0x69;
 
 Serial      pc(SERIAL_TX, SERIAL_RX); 
 AnalogIn    PRESSURE_SENSOR(PA_0);
 LSM6DS3     imu(PB_9, PB_8, LSM6DS3_ADDRESS);
-IMUfilter   imuFilter(0.1, 10);
+//At rest the gyroscope is centred around 0 and goes between about
+//-5 and 5 counts. As 1 degrees/sec is ~15 LSB, error is roughly
+//5/15 = 0.3 degrees/sec.
+IMUfilter   imuFilter(FILTER_RATE, 0.3);
+Ticker      filterTicker;
 Ticker      ScreenTicker; 
 
+//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.
+float w_xBias;
+float w_yBias;
+float w_zBias;
+ 
+//Offsets for the accelerometer.
+//Same as with the gyroscope.
+float a_xBias;
+float a_yBias;
+float a_zBias;
+ 
+//Accumulators used for oversampling and then averaging.
+volatile float a_xAccumulator = 0;
+volatile float a_yAccumulator = 0;
+volatile float a_zAccumulator = 0;
+volatile float w_xAccumulator = 0;
+volatile float w_yAccumulator = 0;
+volatile float w_zAccumulator = 0;
+ 
+//Accelerometer and gyroscope readings for x, y, z axes.
+volatile float a_x;
+volatile float a_y;
+volatile float a_z;
+volatile float w_x;
+volatile float w_y;
+volatile float w_z;
+ 
+//Buffer for accelerometer readings.
+float readings[3];
+//Number of accelerometer samples we're on.
+int accelerometerSamples = 0;
+//Number of gyroscope samples we're on.
+int gyroscopeSamples = 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);
+//Update the filter and calculate the Euler angles.
+void filter(void);
+
 //Init Serial port and LSM6DS3 chip
-void setup()
+void setup_LSM6DS3()
 {
     // Use the begin() function to initialize the LSM9DS0 library.
     // You can either call it with no parameters (the easy way):
@@ -47,21 +110,136 @@
     pc.printf("Should be 0x69\r\n");
 }
 
-void screenUpdate(){
-    pc.printf("Accel (m/s^2):\r\n");
-    pc.printf("AX: %2f\r\n", ACCELERATIONS[0]);
-    pc.printf("AY: %2f\r\n", ACCELERATIONS[1]);
-    pc.printf("AZ: %2f\r\n", ACCELERATIONS[2]);
+void screenUpdate(){    
+    pc.printf("%f,%f,%f\r\n",toDegrees(imuFilter.getRoll()),
+          toDegrees(imuFilter.getPitch()),
+          toDegrees(imuFilter.getYaw()));
+}
+
+void calibrateAccelerometer(void) {
+ 
+    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 += imu.ax;
+        a_yAccumulator += imu.ay;
+        a_zAccumulator += imu.az;  
+ 
+        wait(ACC_RATE);
+    }
+ 
+    a_xAccumulator /= CALIBRATION_SAMPLES;
+    a_yAccumulator /= CALIBRATION_SAMPLES;
+    a_zAccumulator /= CALIBRATION_SAMPLES;
+ 
+    a_xBias = a_xAccumulator;
+    a_yBias = a_yAccumulator;
+    // When laying on the table, default a_z is 1g
+    a_zBias = (a_zAccumulator - 1.0f);
+ 
+    a_xAccumulator = 0;
+    a_yAccumulator = 0;
+    a_zAccumulator = 0;
+}
+
+void sampleAccelerometer(void) {
+ 
+    //Have we taken enough samples?
+    if (accelerometerSamples == SAMPLES) {
+ 
+        //Average the samples, remove the bias and convert to m/s/s.
+        a_x = ((a_xAccumulator / SAMPLES) - a_xBias) * g0;
+        a_y = ((a_yAccumulator / SAMPLES) - a_yBias) * g0;
+        a_z = ((a_zAccumulator / SAMPLES) - a_zBias) * g0;
+ 
+        a_xAccumulator = 0;
+        a_yAccumulator = 0;
+        a_zAccumulator = 0;
+        accelerometerSamples = 0;
+ 
+    } else {
+        //Take another sample.
+        imu.readAccel();
+        a_xAccumulator += imu.ax;
+        a_yAccumulator += imu.ay;
+        a_zAccumulator += imu.az;        
+ 
+        accelerometerSamples++;
+    }
+}
 
-    pc.printf("Gyro (degrees/s):\r\n");
-    pc.printf("GX: %f\r\n", DEGREES[0]);
-    pc.printf("GY: %f\r\n", DEGREES[1]);
-    pc.printf("GZ: %2f\r\n", DEGREES[2]);
-    
-    pc.printf("Angle (degrees):\r\n");
-    pc.printf("X: %f\r\n", ANGLES[0]);
-    pc.printf("Y: %f\r\n", ANGLES[1]);
-    pc.printf("Z: %f\r\n\r\n", ANGLES[2]); 
+void calibrateGyroscope(void) {
+ 
+    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 += imu.gx;
+        w_yAccumulator += imu.gy;
+        w_zAccumulator += imu.gz;
+        
+        wait(GYRO_RATE);
+    }
+ 
+    //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;
+ 
+    w_xAccumulator = 0;
+    w_yAccumulator = 0;
+    w_zAccumulator = 0;
+}
+ 
+void sampleGyroscope(void) {
+ 
+    //Have we taken enough samples?
+    if (gyroscopeSamples == SAMPLES) {
+ 
+        //Average the samples, remove the bias, and calculate the angular
+        //velocity in rad/s.
+        w_x = toRadians(((w_xAccumulator / SAMPLES) - w_xBias));
+        w_y = toRadians(((w_yAccumulator / SAMPLES) - w_yBias));
+        w_z = toRadians(((w_zAccumulator / SAMPLES) - w_zBias));
+ 
+        w_xAccumulator = 0;
+        w_yAccumulator = 0;
+        w_zAccumulator = 0;
+        gyroscopeSamples = 0;
+ 
+    } else {
+        //Take another sample.
+        imu.readGyro();
+        w_xAccumulator += imu.gx;
+        w_yAccumulator += imu.gy;
+        w_zAccumulator += imu.gz;
+ 
+        gyroscopeSamples++;
+    }
+}
+ 
+void 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();
+ 
 }
  
 int main()
@@ -72,37 +250,28 @@
         if(PRESSURE_SENSOR > 0.9f){
             
             if (not started){
-                setup();  //Setup sensor and Serial
+                setup_LSM6DS3();  //Setup sensor and Serial
                 pc.printf("\r\n------ LSM6DS3 Demo -----------\r\n");
                 started=true;
-                ScreenTicker.attach(&screenUpdate, 1);
+                
+                calibrateAccelerometer();
+                calibrateGyroscope();
+                pc.printf("\r\n------ Calibrated -----------\r\n");
+                
+                //Set up timers.
+                filterTicker.attach(&filter, FILTER_RATE);
+                ScreenTicker.attach(&screenUpdate, FILTER_RATE);
             }
             
-            wait_ms(REFRESH_TIME_MS);     
-            imu.readAccel();
-            imu.readGyro();
-            
-            
-            DEGREES[0] = imu.gx;
-            DEGREES[1] = imu.gy;
-            DEGREES[2] = imu.gz;
-            
-            // m/(s^2) = g0*g
-            ACCELERATIONS[0] = g0*imu.ax;
-            ACCELERATIONS[1] = g0*imu.ay;
-            ACCELERATIONS[2] = g0*imu.az;
-                        
-            imuFilter.updateFilter(toRadians(imu.gx), toRadians(imu.gy), toRadians(imu.gz), 
-                                   g0*imu.ax, g0*imu.ay, g0*imu.az);
-            imuFilter.computeEuler();
-            
-            ANGLES[0] = toDegrees(imuFilter.getRoll());
-            ANGLES[1] = toDegrees(imuFilter.getPitch());
-            ANGLES[2] = toDegrees(imuFilter.getYaw());            
+            wait(0.005);
+            sampleAccelerometer();
+            sampleGyroscope();
+               
         }
         else {
             started=false;
             imuFilter.reset();
+            filterTicker.detach();
             ScreenTicker.detach();
         }
     }