Accelerometer drifting solutions?

I got an accelerometer module, and each axis needs to be calibrated differently every few minutes. Different centers and different acceleration slope. Its kind of a hassle and It makes it useless for any accurate acceleration experiments. Is there a way to compensate for the drifting or should I be looking for a higher quality accelerometer module?

Hi,
Link to accelerometer module please...

Tom.... :slight_smile:

from what I know, it shouldn't drift. Could you send your data capture. Do you use gyroscope to increase accuracy ?

nurimo:
Do you use gyroscope to increase accuracy ?

Normally it's the other way around: the gyros need constant correction from the accelerometers. There must be something else going on here.

Are you changing the orientation of the accelerometer?

When stationary, it will measure 1g acceleration due to gravity.

Depending on how it is orientated this could all be in the x axis, all in the y axis or all in the z axis. If it is in some random orientation there will be components of this 1g in all three axes.

This is explained in fig. 24 of the datasheet.

If you were to rotate the device the components in each axis will change even though the accelerometer has not been physically accelerated.

Could this effect be what you are believing is drift?

If the device is kept perfectly stationary does it still "drift"?

Perhaps I should have been more specific. It's not drifting in the sense that over time the values change. The reference values needed to get an accurate reading and different every time I restart the arduino. At first I figured, each would be centered at half, 512, and 1G on the vertical axis. Using that to calibrate each values, it was wildly inaccurate. I figured since this is how it was done in an academic tutorial I saw, it was my accelerometer. I came up with a new calibration strategy that involves looking for the max possible on the vertical axis, which will always be 1G, and from there I can use some trigonometry to assume the other two axis are zero. But this max value changes on every reset. Every axis has a different slope and a different center point. I'm attempting to calibrate all 3 axis simultaneously, but I see I may need to search for the max and min of every axis independently every time I restart it. I'll post my code when I get to my computer. In the mean time, how would I go about searching for the maximum value over some period of time? So I don't have to input it myself.

It sounds to me like your method is suspect. You need to calculate an offset and scale factor for each axis, and those usually won't change with time. Adafruit has a decent tutorial.

jremington:
It sounds to me like your method is suspect. You need to calculate an offset and scale factor for each axis, and those usually won't change with time. Adafruit has a decent tutorial.

Unfortunately, this accelerometer uses SPI and I2C communication and stays calibrated. The ADXL335 only has a 3 pin analog output and appears to need a re calibration at every startup. The tutorial does use max and min values for each axis, which is what I'm now attempting to do. If I have some set of values, how do I take the maximum and minimum values? I imagine some kind of an array.

Follow the general outline of Adafruit tutorial, modifying the interface details for your sensor.

Unfortunately, this accelerometer uses SPI and I2C communication and stays calibrated.

No, it does not "stay calibrated". The calibration procedure calculates offset and scale factors that you must apply separately.

jremington:
No, it does not "stay calibrated"

"The ADXL chips are calibrated at the factory to a level of precision sufficient for most purposes" Why do they bother with a factory calibration if it needs to be recalibrated every time anyway?

Hi,
How do you calibrate it.
How do you store the calibration offset figures?

Please show us some figures to indicate how much it is drifting.

Tom... :slight_smile:

“The ADXL chips are calibrated at the factory to a level of precision sufficient for most purposes”

If it is calibrated sufficiently well for your purposes, fine. If not, you need to determine an additional correction.

If it is possible for a user to change the calibration internally, that information is not revealed in the data sheet. Most people use a variation on the Adafruit scheme to make the extra correction. Anyone who needs the full accuracy of the accelerometer has to do so and fortunately, it is a rather simple, one time process.

When reading the values on the serial monitor, I look for the maximum, minimum, and center values. With those vales it's very easy to calculate acceleration. The problem is, those values are different every time the arduino restarts. That's what I mean by drifting. So it's not exactly drifting, maybe more like skipping or jumping. For this particular accelerometer, calibration is not a not time process because of this. Here's an example from the accelrometer.

x, y, z:
flat
489, 484, 620

upside down
489, 484, 440

On it's side
489, 596, 530

On the other side:
576, 484, 530

I'm not going to go through all the positions, but from this I can tell that the centers are: 489, 484, 530 and 1G is :576, 596, 620. Using those two sets of values I can do (max - center) to get a constant value k so that (Reading - Center)G/k. G being gravity. This works once and after I restart it, every value has changed. Some changes can be as large as 20. Because of this, I'll need to find these values for all 3 axis every time I turn it on. If I need to do this, I should find a way to do it without using the serial monitor.

I plan to gently rotate it along all three axis. The maximum values will all be 1G, and the minimums will be -1G. From there I can find all the values I need to calculate acceleration on all 3 axis. But I don't know how I would program this. How do I find which values read during calibration are the maximums and minimums? Is there some way for it to know when it's found these values, or do I need to give myself a time window to perform the calibration?

Because of this, I'll need to find these values for all 3 axis every time I turn it on.

You've given no evidence that the latest readings are any better or more accurate than earlier ones.

Noisy readings are in fact causing the confusion, and the "axial" approach is overly simplistic anyway.

For the most advanced and accurate type of correction, follow this tutorial (intended for magnetometers, but works just as well for accelerometers provided that the accelerometer is held still for each data point). In that approach, every data point counts, not just the min/max values.

jremington:
Noisy readings are in fact causing the confusion, and the "axial" approach is overly simplistic anyway.

Noise is not the issue. I'm using an average of 100 samples, and when the accelerometer is still, the values are very consistent.

jremington:
You've given no evidence that the latest readings are any better or more accurate than earlier ones.

I'm not claiming any change in the accuracy. I've already explained what the issue is as clearly as I can, please reread post #13.

jremington:
For the most advanced and accurate type of correction, follow this tutorial (intended for magnetometers, but works just as well for accelerometers provided that the accelerometer is held still for each data point). In that approach, every data point counts, not just the min/max values.

I'm sure this software will give me a decent calibration, but I can't do this every time I turn it on.

Noise is not the issue. I'm using an average of 100 samples, and when the accelerometer is still, the values are very consistent.

This is your first mention of averaging. Had you mentioned this earlier, I would have recommended throwing the device away, particularly if the averages differ by up to 20 LSBs.

You seem to be having very unusual difficulties with that accelerometer. I've been working with similar models, as they've evolved over about a decade, and have never seen the sort of instability you describe.

The averages differ by up to 20, but not all at one time. If I calibrate it and leave still, the values are consistent. If I rotate it and bring it back to the same position, it will go back to the same value. Those benchmark values only change when the arduino looses power or restarts. I referred back to the link provided by TomGeorge and use some of the info there to make this:

#include <SD.h>                                         //SD card library
#include <SPI.h>

boolean Calibrated = false;                            //Check if it's calibrating

const int xAxis = A0;                                  //Define axis inputs
const int yAxis = A1;
const int zAxis = A2;
const int CS_pin = 10;                                //Chip select for SD module
const int reCal = 9;                                  //Button pin to recalibrate
const int CalLED = 8;                                 //Pin to indicate calibratrion
const int samples = 100;                              //Number of samples for smooting
const int blinkInterval = 40;                         //How fast the indicator blinks, ms
const int blinkInterval2 = 250;
const float G = 9.81;                                 //Gs to m/s^2
const float thousand = 1000;                          //Milliseconds to seconds

unsigned long xTotal;                                 //Store total values
unsigned long yTotal;
unsigned long zTotal;
unsigned int xAverage;                                //Store average values
unsigned int yAverage;
unsigned int zAverage;
float xAccel;                                         //Acceleration
float yAccel;
float zAccel;
float xZero;                                          //Zero acceleration values
float yZero;
float zZero;
float xK;                                             //Axis constants
float yK;
float zK;
float xMax;                                           //Max/Mins
float yMax;
float zMax;
float xMin;
float yMin;
float zMin;

String dataString;                                    //String to be stored on the SD card
String values;                                        //Data string for serial output
String minMax;                                        //String for calibration checking
unsigned long dataID = 1;                             //DataID to keep track of the data
unsigned long previousMillis;                         //Timer mark
float timeSeconds;                                    //Time elapsed since calibration

void setup() {
  SD.begin();                                 //Initialize SD library
  Serial.begin(9600);                         //Initialize serial monitor
  Serial.println("Initializing");

  pinMode(xAxis, INPUT);                      //Set inputs
  pinMode(yAxis, INPUT);
  pinMode(zAxis, INPUT);
  pinMode(reCal, INPUT);                      //Recalibrate button
  pinMode(CalLED, OUTPUT);                    //Indicates Calibrating
  digitalWrite(CalLED, LOW);                  //Not calibrating
  analogReference(EXTERNAL);                  //3.3v ref for accelerometer output

  for (int i = 0; i < samples; i++) {        //Allow input to stablalize
    analogRead(xAxis);
    analogRead(yAxis);
    analogRead(zAxis);
  }
  while (Calibrated == false) {                         //Wait for first calibration before continuing
    if (millis() - previousMillis >= blinkInterval2) {  //Slow blink
      if (digitalRead(CalLED) == LOW) {
        digitalWrite(CalLED, HIGH);
      }
      else {
        digitalWrite(CalLED, LOW);
      }
      previousMillis = millis();
    }
    if (digitalRead(reCal) == HIGH) {
      Calibrate();
    }
  }

  if (!SD.begin(CS_pin)) {                    //SD card stuff
    Serial.println("Card Error");
    return;
  }
  Serial.println("Card Ready");

  File Data = SD.open("Data.csv", FILE_WRITE);
  if (Data) {
    Data.println(", , , , , , , , , , , , ");
    String header = "Data ID, Seconds, X Axis, Y Axis, Z Axis";
    Data.println (header);
    Data.close();
  }
  else {
    Serial.println("Couln't Open Data File");
    return;
  }
}

void loop() {

  if (digitalRead(reCal) == HIGH) {         //Recalibrate if the button is HIGH
    Calibrate();
  }

  readAcceleration();                       //Take readings and mark time
  timeSeconds = (millis() - previousMillis) / thousand;

  //Organize averages into a string, save, and println
  values = String(dataID) + ", " + String(timeSeconds) + ", " + String(xAverage) + ", " + String(yAverage) + ", " + String(zAverage) + ", " + String(xAccel) + ", " + String(yAccel) + ", " + String(zAccel);
  Serial.println(values);
  dataString = String(dataID) + String(timeSeconds) + ", " + String(xAccel) + ", " + String(yAccel) + ", " + String(zAccel);
  File Data = SD.open("Data.csv", FILE_WRITE);
  Data.println(dataString);
  Data.close();
  dataID++;
}

void Calibrate() {
  Serial.println("Calibrating Axis");        //Initial Calibration

  xK = 0;                                    //Reset values
  yK = 0;
  zK = 0;
  xMax = 0;
  yMax = 0;
  zMax = 0;
  xMin = 1023;
  yMin = 1023;
  zMin = 1023;
  xZero = 0;
  yZero = 0;
  zZero = 0;

  while (digitalRead(reCal) == HIGH) {
    lightBlink();                             //Fast blink
    readAcceleration();                       //Get raw average values

    if (xAverage > xMax) {                    //Find minimums and maximums, then calculate zeros and constants
      xMax = xAverage;
    }
    if (xAverage < xMin) {
      xMin = xAverage;
    }
    xK = (xMax - xMin) / 2;
    xZero = xMax - xK;

    if (yAverage > yMax) {
      yMax = yAverage;
    }
    if (yAverage < yMin) {
      yMin = yAverage;
    }
    yK = (yMax - yMin) / 2;
    yZero = yMax - yK;

    if (zAverage > zMax) {
      zMax = zAverage;
    }
    if (zAverage < zMin) {
      zMin = zAverage;
    }
    zK = (zMax - zMin) / 2;
    zZero = zMax - zK;

    minMax = String(xK) + ", " + String(yK) + ", " + String(zK) + ", " + String(xZero) + ", " + String(yZero) + ", " + String(zZero) + ", " + String(xMin) + ", " + String(xMax) + ", " + String(yMin) + ", " + String(yMax) + ", " + String(zMin) + ", " + String(zMax);
    Serial.println(minMax);                     //Print all numbers found from calibration
  }

  digitalWrite(CalLED, LOW);                    //Switch off led incase it lands on HIGH
  Calibrated = true;                            //Calibrating done
  previousMillis = millis();                    //Reset the clock
  Serial.println("Done Calibrating");           //Notifaction of completion
}

void readAcceleration() {

  for (int i = 0; i < samples; i++) {         //Loop 10x
    xTotal = xTotal + analogRead(xAxis);      //Read each axis and incrament total
    yTotal = yTotal + analogRead(yAxis);
    zTotal = zTotal + analogRead(zAxis);
  }

  xAverage = xTotal / samples;                //Calculate averages
  yAverage = yTotal / samples;
  zAverage = zTotal / samples;

  xTotal = 0;                                 //Reset totals
  yTotal = 0;
  zTotal = 0;

  xAccel = G * (xAverage - xZero) / xK;       //Calculate the acceleration
  yAccel = G * (yAverage - yZero) / yK;
  zAccel = G * (zAverage - zZero) / zK;
}

void lightBlink() {                           //Probably doesn't need it's own function, but light blinker
  if (millis() - previousMillis >= blinkInterval) {
    if (digitalRead(CalLED) == LOW) {
      digitalWrite(CalLED, HIGH);
    }
    else {
      digitalWrite(CalLED, LOW);
    }
    previousMillis = millis();
  }
}

I recalibrate it each time it starts up and I’m getting within 1m/s^2 or better on all axis.

1m/s^2

Only 10% accuracy (g = 9.8 m/s^2)? That is quite poor.

After careful calibration, I expect +/- .002 g or better accuracy from the LSM303DLHC, and it doesn't change with time.

This accelerometer was only $1, and on a good calibration I can be within 3%. I think I've gone as far as I can go with this one. I'll definitely buy a better one next time. Thanks for all the help