# Arduino 101 accelerometer drift or calculation error?

I want to calculate the displacement of the Arduino 101 board compared to the starting point (only in the x direction so far).

In below data I'm swinging the board multiple times in circles around my laptop and then putting it back at the origin a few seconds later.

Regardless if 1ms-100ms between the loops, the numbers show no sign of the board returning to the origin. I've calibrated the accelerometer x-axis as closely as possible. Am I doing something wrong in deriving the acceleration/velocity/distance, or is this the level of compounding error to expect?

If so, can combining gyro data correct acceleration (thought it mainly worked the other way around)?

I have a GPS meant for correcting position but was hoping any drift wouldn't be this quick.

EDIT: I was incorrect re. use of gyro data; the attitude of the board, which was not consistent in my swings, will effect the acceleration output. Gyro data can be used to calculate and "cancel out" this gravitational impact, and get a more accurate (linear) acceleration (http://www.cl.cam.ac.uk/research/dtg/www/files/publications/public/abr28/ojw28_thesis.pdf). Will report back in a few days.

Code:

``````#include "CurieIMU.h"

float gravity = 9.80665; // to convert G forces (1 m/s2 = 0.101972 g; 1 g = 9.80665 m/s2)
long t1; // create object for first time reference, set time just before the loop later on

// Variables that are dependent on value from last loop
float vx, vy, vz;
float imu_x, imu_y, imu_z;

void setup() {
Serial.begin(9600);
while (!Serial);  // wait for serial to open

Serial.println("Initializing IMU device...");
CurieIMU.begin(); // start accelerometer / gyro
CurieIMU.setAccelerometerRange(2);  // accelerometer range to 2G - might need to be greater later on (18 m/s2 highest possible now)

/*

// Auto-calibration to ensure accelerometer doesn't drift
CurieIMU.autoCalibrateAccelerometerOffset(X_AXIS, 0); // 89.69999 creates both positive and negative spikes
delay(1000);
CurieIMU.autoCalibrateAccelerometerOffset(Y_AXIS, 0);
delay(1000);
CurieIMU.autoCalibrateAccelerometerOffset(Z_AXIS, 1);
delay(1000);

*/

// Hardcoded accelerometer offsets after testing
CurieIMU.setAccelerometerOffset(X_AXIS,89);

// Fetch the offsets from the IMU
float x_offset = CurieIMU.getAccelerometerOffset(X_AXIS);
float y_offset = CurieIMU.getAccelerometerOffset(Y_AXIS);
float z_offset = CurieIMU.getAccelerometerOffset(Z_AXIS);

Serial.println("Current x_offset, y offset, z offset");
Serial.print(x_offset, 6);
Serial.print("\t");
Serial.print(y_offset, 6);
Serial.print("\t");
Serial.println(z_offset, 6);
delay(5000);

t1 = millis(); // set the first time reference

Serial.println("ax_g (g), ax (m/s2), t1 (ms), t2 (ms), td (ms), tds (s), vx (m/s), imu_posx (m)");

}

void loop() {

// Fetch G forces
float ax_g, ay_g, az_g;   //scaled accelerometer values
CurieIMU.readAccelerometerScaled(ax_g, ay_g, az_g); // read accelerometer measurements from device, scaled to the configured range

// Convert G force to m/s2
float ax = ax_g / gravity;
float ay = ay_g / gravity;
float az = az_g / gravity;

// Calculate time since last loop
long t2 = millis();
float td = t2 - t1; // time delta in ms
float tds = td / 1000; // delta in seconds for easier logic

// Calculate current velocity: velocity (m/s) = velocity (m/s) + acceleration (m/s2) * deltaTime (s)
vx = vx + ax * tds;
vy = vy + ay * tds;
vz = vz + az * tds;

// Calculate position: position (m) = position (m) + velocity (m/s) * deltaTime (s)
imu_x = imu_x + vx * tds;
imu_y = imu_y + vy * tds;
imu_z = imu_z + vz * tds;

// Print the values
Serial.print(ax_g, 6);
Serial.print("\t");
Serial.print(ax, 6);
Serial.print("\t");
Serial.print(t1);
Serial.print("\t");
Serial.print(t2);
Serial.print("\t");
Serial.print(td, 6);
Serial.print("\t");
Serial.print(tds, 6);
Serial.print("\t");
Serial.print(vx, 6);
Serial.print("\t");
Serial.println(imu_x, 6);

// Reset start values for next loop
t1 = millis();

delay(1);

}
``````
• I don't think millis is going to be accurate enough, try micros instead and grab it only once per loop. – ratchet freak Sep 6 '17 at 18:23
• This is dead reckoning and is very difficult. When (few) cars do this (after loosing their GPS signal) they usually count the number of wheel rotations and turning direction. Just consider if you stopped moving your Arduino. Then your acceleration would go to zero but your integral calculation of velocity may not due to noise or round off error. If this happens, the integral of your velocity (position) would continue to change. – st2000 Sep 7 '17 at 2:14
• @ratchetfreak thanks, will try in combination with the edited conclusion above – Henrik Bohman Sep 7 '17 at 10:12
• Do you have to use 9600 baud? That's quite slow, and is probably introducing 70ms of that 100ms delay. Why not use 115200? Also, did you try using doubles instead of floats - although I guess this would only help with minor drifts. – Kingsley Oct 31 '17 at 21:20

The Arduino 101 contains the Intel® Curie™ Module, this module contains an Inertial Measurements Unit or IMU for short among other sensors and peripherals The IMU of this module is Bosch BMI160 which is a low cost MEMS IMU, this IMUs typically suffer from systematic errors and random/stochastic errors, the following diagram summarize them: 