BlocNotes

Notepad of a tinker, maker, hacker or whatever you call it :)

Some fun with an accelerometer

I recently discussed with some friends about sensors to record jumps height and hang time when kitesurfing, which are really expensive and need to wear a phone in a waterproof case.
As it is a useless sensor and just to have some fun, I wanted to try with a cheap sensor, an µC and some EEPROM, no need to have an accuracy of 2 centimeters...

So I wrote a really fast (30 min before going to bed), and dirty code using an old accelerometer : ADXL345. And the result is not so bad : if reseted every 2 seconds to avoid drift, 75 cm delta height is measured with a precision of +/- 6 cm.

But I also need a gyroscope to be able to know the height variations no matter which orientation is the sensor: I ordered a MPU-6050 IMU which combine accelero+gyro and has some co-processor to avoid huge calculations on host processor. I might also try an ARM Cortex-M3 to speed up the refresh frequency in order to have a better tracking.

Also, there is lot of thesis on such subjects, often with algorithms made for huge computers, and every time the drawback is drift due to double integration... As I will use it to measure jumps height, I might be able to get rid of it because lift time is small.

The following code is for Arduino with I2CDevLib library, I2C speed must be set to 400 kHz.

/* ============================================
Uses I2Cdev device library : http://www.i2cdevlib.com  
*/

// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
// is used in I2Cdev.h
#include "Wire.h"

// I2Cdev and ADXL345 must be installed as libraries, or else the .cpp/.h files
// for both classes must be in the include path of your project
#include "I2Cdev.h"
#include "ADXL345.h"

// Declare objects
ADXL345 accel;

// Defines
#define G_MS2 9.80665

void setup() {  
    // join I2C bus (I2Cdev library doesn't do this automatically)
    Wire.begin();

    Serial.begin( 115200 );

    // Initialize device and setup
    Serial.println( "Initializing I2C devices..." );
    accel.initialize();
    accel.setRate( ADXL345_RATE_200 );
    accel.setRange( ADXL345_RANGE_2G );

    // verify connection
    Serial.println( "Testing device connections..." );
    Serial.println( accel.testConnection() ? "ADXL345 connection successful" : "ADXL345 connection failed" );
}

void loop() {  
  // Variables declaration
  float ax, ay, az, speedZOld, speedZThreshold, speedZ, positionZ, speedDelta;

  unsigned long timerAccumulation, timerSerial; // the timer
  unsigned long INTERVAL = 5; // the repeat interval
  unsigned long INTERVAL_SERIAL = 2000;
  unsigned long i;

  speedZOld = 0;
  speedZ = 0;
  positionZ = 0;
  speedDelta = 0;
  speedZThreshold = 0.1;

  // Start timers
  timerAccumulation = millis();
  timerSerial = timerAccumulation;

  Serial.println( "\t\tStarting main loop" );

  while (1) {

    // Compute data
    i = millis()-timerAccumulation;
    if ( i > INTERVAL ) {
      timerAccumulation += INTERVAL;      // Reset timer
      az = accel.getAccelerationZ();
      az = (az-231) * G_MS2 / 256.0;      // Convertion to m/s2
      speedZ += az * i/1e3;               // Integration to get speed
      // distance moved in deltaTime, s = 1/2 a t^2 + vt
      double sz = 0.5 * az * i * i/1e6 + speedZ * i/1e3;
      positionZ += sz;
    }


    if ( abs( speedZ ) < speedZThreshold ) {                      // If speed is behind given threshold, discard it to avoid noise accumulation
      if ( abs( speedZOld ) > speedZThreshold ) {                 // If previous speed was high, it was moving
        if ( abs( positionZ ) > 0.15 ) {                          // If we moved more than 0.15 cm, display it
          Serial.print( az ); Serial.print( "\t" );
          Serial.print( speedZ ); Serial.print( "\t" );
          Serial.print( positionZ );
        }
        positionZ = 0;
        speedZ = 0;
        Serial.println( "\tReset" );
      }
    }
    speedZOld = speedZ;

    if ( (millis()-timerSerial) > INTERVAL_SERIAL ) {             // Periodic reset to clear values from noise
      Serial.println( "\tReset time" );
      timerSerial += INTERVAL_SERIAL;
      positionZ = 0;
      speedZ = 0;
    }

  }
}