Integrating accelerometer data

Hey guys!

I had the idea of using my accelerometer to calculate change in velocity and change in position. But even after just one integral the values I am getting are all over the place. I do know that I will never achieve precise measurements and that the values will drift over time but what I am getting is just straight unusable.

For example: When I slide the sensor alongside an edge of my table all three values change.
The values drift with about 1cm/s every 2-3 seconds.
When I stop the motion alongside the table the values don't go down to 0 or even close to that.

Is what I am trying to do simply not feasible or am I doing something wrong?

My setup: LSM303DLHC (https://cdn-shop.adafruit.com/datasheets/LSM303DLHC.PDF)
@+/- 2g for max precision
@ 200Hz

#include <Wire.h>

#define SAMPLES 1000
#define ACCELEROMETER_ADDRESS 0x19

double velocity[3] = {0,0,0};
double compensation_values[3] = {0,0,0};

void setup()
{
  Wire.begin();
  Serial.begin(250000);
  Serial.println("Setup");
  init_accelerometer();
}

void loop()
{
  long start_time = millis();
  integrate_acc();
  Serial.print("X:");
  Serial.print(velocity[0]);
  Serial.print("\t\tY:");
  Serial.print(velocity[1]);
  Serial.print("\t\tZ:");
  Serial.println(velocity[2]);
  while(millis() < start_time + 5);
}

int i2c_write(byte i2c_address, byte register_address, byte data)
{
  Wire.beginTransmission(i2c_address);
  Wire.write(register_address);
  Wire.write(data);
  return Wire.endTransmission();
}

inline void init_accelerometer()
{
  if(i2c_write(ACCELEROMETER_ADDRESS, 0x20, 0b01100111) != 0) //200Hz
    Serial.println("Fehler beim Schreiben ins CTRL_REG1_A");

  if(i2c_write(ACCELEROMETER_ADDRESS, 0x23, 0b00001000) != 0) //
    Serial.println("Fehler beim Schreiben ins CTRL_REG4_A");

  long sums[3] = {0,0,0};
  double acc_data[3];
  
  for(int i=0; i<SAMPLES; i++)
  {
    read_acc_data(acc_data);
    sums[0] += acc_data[0];
    sums[1] += acc_data[1];
    sums[2] += acc_data[2];
    delay(5);
  }

  compensation_values[0]  = sums[0] / SAMPLES;
  compensation_values[1]  = sums[1] / SAMPLES;
  compensation_values[2]  = sums[2] / SAMPLES;

  Serial.println("Finished compensating.");
  Serial.println(compensation_values[0]);
  Serial.println(compensation_values[1]);
  Serial.println(compensation_values[2]);
  delay(3000);
}

void integrate_acc()
{
  double acc_data[3];
  read_acc_data(acc_data);

  velocity[0] += (acc_data[0] - compensation_values[0]) / 16000 * 100 * 0.005; //16000 in order to get from reading to Gs, 
  velocity[1] += (acc_data[1] - compensation_values[1]) / 16000 * 100 * 0.005; //100 because I want to have cm/s not m/s,
  velocity[2] += (acc_data[2] - compensation_values[2]) / 16000 * 100 * 0.005; //0,005 for the 200Hz
}

void read_acc_data(double acc_data[])
{
  Wire.beginTransmission(ACCELEROMETER_ADDRESS);
  Wire.write(0x28 | 1<<7);
  Wire.endTransmission();
  Wire.requestFrom(ACCELEROMETER_ADDRESS, 6);
  while(Wire.available() < 6);

  acc_data[0] = Wire.read() | Wire.read()<<8;
  acc_data[1] = Wire.read() | Wire.read()<<8;
  acc_data[2] = Wire.read() | Wire.read()<<8;
}

Do you get stable values from the accelerometer when it is sitting still?

PaulS:
Do you get stable values from the accelerometer when it is sitting still?

Do you mean the acceleration data or the integrated data?

The acceleration data seems to be pretty stable: ~0.004 Gs max
The intragrated data of that drifts a bit with the z axis being the worst: 1m/s every ~4 seconds

Have you confirmed gravity is working correctly where you are?

AWOL:
Have you confirmed gravity is working correctly where you are?

https://www.google.at/url?sa=i&rct=j&q=&esrc=s&source=images&cd=&cad=rja&uact=8&ved=0ahUKEwjG0KHLkIrQAhXKfRoKHYjXBecQjRwIBw&url=http%3A%2F%2Fknowyourmeme.com%2Fphotos%2F546001-demotivational-posters&bvm=bv.137132246,d.d2s&psig=AFQjCNG66r-zpxQ2gskmIYXyskncvNzdtA&ust=1478178033462810

I'd suggest taking a step back and just dump raw measurements to a PC file under your test conditions and play with that data on a spreadsheet or some such tool.

The problem is probably in your "compensation" approach. My guess is that small changes in sensor orientation off vertical cause large biases relative to your calibration. This is inherently a problem in working in an accelerating reference frame, that is, gravity is (unfortunately) working at your location.

MrMark:
I'd suggest taking a step back and just dump raw measurements to a PC file under your test conditions and play with that data on a spreadsheet or some such tool.

The problem is probably in your "compensation" approach. My guess is that small changes in sensor orientation off vertical cause large biases relative to your calibration. This is inherently a problem in working in an accelerating reference frame, that is, gravity is (unfortunately) working at your location.

you mean that when when I compensate for gravity lets say in the z direction perfectly and then turn the sensor a bit the "compensation" screws off all three values?

I thought of that and plan to combine my current setup with an gyro in order to account for changes in the attitude of the accelerometer but I wanted to test the integration of the accelerometer data first.

Do you think that this is my whole problem?

I think small changes in sensor orientation being a problem make sense. In a 9.8 m/s^2 environment, drifting 1 cm/s^2 is only 0.1%.

As a point of reference, I have an app on my Android cell phone that displays it's accelerometer data. It's likely a lower performance device than what you're using. Sitting stationary on the desktop, I see second to second variation on the order of a few hundredths m/s^2. Approximate average values are (0.067, -0.421, 9.682) in one orientation and after rotating 180 degrees around vertical (0.057, -0.182, 9.673), so there's a significant bias change in the Y axis at least.

Here is a good explanation for why this idea won't work, but in summary, consumer grade IMUs are too noisy and inaccurate.

jremington:
Here is a good explanation for why this idea won't work, but in summary, consumer grade IMUs are too noisy and inaccurate.

To be fair, whether the "idea works" or not depends on the application. If one's goal is to estimate the velocity of a model rocket accelerating at ~10 G after a ~1 second burn, then the accumulated few meters/second error is probably acceptable. On the other hand if one is trying to do open loop navigation of a quadcopter, this approach is hopelessly inaccurate.

For a model rocket in the coast phase accelerometer readings are likely to be predominately due to airframe attitude, but the residual of RSS(x,y,z) minus 9.8 m/s^2 might be a useful enough measure of drag induced deceleration, at least while it's still moving fast. You also have a priori knowledge of the dominant axis of drag induced deceleration which greatly simplifies the problem.

*** Edit: On further thought, the second paragraph is wrong. In the coast phase, the rocket is nominally zero-G except for aerodynamic drag, so the residual is mostly drag . . . ***

jremington:
Here is a good explanation for why this idea won't work, but in summary, consumer grade IMUs are too noisy and inaccurate.

Thats is some excellent reading material. Thank you you for that!

MrMark:
To be fair, whether the "idea works" or not depends on the application. If one's goal is to estimate the velocity of a model rocket accelerating at ~10 G after a ~1 second burn, then the accumulated few meters/second error is probably acceptable. On the other hand if one is trying to do open loop navigation of a quadcopter, this approach is hopelessly inaccurate.

What if I wanted to just measure velocity without knowing the direction along the axis? could I combine the acceleration forces along the three axis, subtract 1g and then integrate this? would that be possible?

And could you maybe recommend some literature about IMUs? I am very interested in that topic but all my experiments in that direction have failed so far.

What if I wanted to just measure velocity without knowing the direction along the axis?

If you want to measure velocity, the easiest way is to measure position and take its rate of change. Wheel encoders, ultrasonic or laser rangers come to mind.

Accelerometers cannot detect uniform motion.

You need accelerometers and attitude (typically from a gyroscope) to solve the unconstrained problem. The issue, as per jremington's link, is that errors will accumulate over time and the MEMS sensors have relatively high errors. Thus the errors get pretty big pretty quickly. The model rocket example had very high dynamics (high signal to noise) over a very short duration, so MEMS sensors are good enough. Long term low dynamic inertial tracking even with the best available technology (e.g. submarine navigation) is problematic.

If you search on terms like "open source inertial measurement unit" there are documented projects out there.

Interesting topic, discussed few times here, afaik.
I think you need to have the accelerometer placed on a gyro-stabilized platform (ie. V2 had accelerometers mounted such way).
The modern mems gyros are not the same as the old mechanical one (with the heavy rotating momentum wheel) - the mems gyros measure "the angular velocity", while the old one measure "the attitude". The mems gyros drift a few degrees per second, the old one gyros do a few degrees per hour (afaik the best Sperry mechanical gyros drifted 1.5degree per hour, or something like that).
So in order to make "dead reckoning" with your accelerometer you have to integrate the data from your ie. 3axis accelerometer say 1000x per second (as fast as possible) and the accelerometer itself has to be placed on a gyroscopically stabilized platform (frame).

MrMark:
*** Edit: On further thought, the second paragraph is wrong. In the coast phase, the rocket is nominally zero-G except for aerodynamic drag, so the residual is mostly drag . . . ***

Upon reading this a second time - isn't that wrong? The coasting phase shouldn't be a zero-G situation because the rocket is still rising but gravity is obviously acting upon it and slowing it down. When reaching it reached apogee and falls back down - sure thats 0g but coasting too?

MrMark:
If you search on terms like "open source inertial measurement unit" there are documented projects out there.

I will see what I can find! Thank you for all your help! Unfortunately, the world isn't always as easy as I think it is but I will see what I can do haha (=

pito:
So in order to make dead reckoning with your accelerometer you have to integrate the data from your 3axis accel say 1000x per second (as much as possible) and the accel itself has to be placed on a gyroscopically stabilized platform (frame).

the only remaining question is where do I get a high precision gyro on the cheap :wink: but thanks for your help (=

If a rocket were rising and falling in a vacuum after burnout, that would be a zero-g situation with respect to anything in the rocket (passengers and accelerometers). Passengers would be in free fall but would consider themselves to be "floating in space" within the rocket.

However, air resistance would be an external force that an accelerometer and a passenger would easily detect, especially when the rocket reaches terminal velocity. In that case, a passenger inside the rocket would experience 1 g, and fall toward the rocket nose.

When the 3axis accelerometer would be mounted on a gyroscopically stabilized platform (for example the x-y plane will be stabilized such -z points to Earth's middle) the x- and y- accelerometers will still integrate the rocket trajectory properly, only the z-accelerometer will be influenced by the "free fall and air drag effects".
So the x-y trajectory would be known and free of those effects.. WvonBraun knew it well..
Without the gyrostabilized platform the x- and y- accels will mess with Earth's gravity effects as well - (btw Earth's G depends on the altitude over Earth's surface as well) and therefore x- and y- accels will not be able to distinguish between the forces they have to integrate in order to provide the trajectory, and the gravity forces (or generally any forces in z axis).

People on this post who are saying that its impossible then take a look at ardupilot source code they are doing it.

https://github.com/ArduPilot/ardupilot/blob/master/libraries/AP_AHRS/AP_AHRS_NavEKF.cpp

in this file they are getting raw accelerometer data and they converting it into velocity then using kalman filter they are getting exact position of drone. and also then they fuse it with gps data and get exact position of drone.
i still don't understand how they got this amount precision.

kishan_joshi:
People on this post who are saying that its impossible then take a look at ardupilot source code they are doing it.

https://github.com/ArduPilot/ardupilot/blob/master/libraries/AP_AHRS/AP_AHRS_NavEKF.cpp

in this file they are getting raw accelerometer data and they converting it into velocity then using kalman filter they are getting exact position of drone. and also then they fuse it with gps data and get exact position of drone.
i still don't understand how they got this amount precision.

Because they are fusing with the GPS data. Without that its impossible as the accelerometer drift
after two stages of integration will diverge enormously in a time scale of seconds or tens of seconds.

The GPS will kill such drift dead, the IMU sensors handle the short-term, the GPS provides the long term.