Go Down

Topic: Stabilizing Accelerometer (ADXL335) to measure displacement? (Read 133 times) previous topic - next topic

cloudurchin

Hi,
I would like some help with stabilizing / filtering the data of the 3 axis, from the ADXL335 accelerometer.

What I want:
I am trying to print out the displacement values of the 3 axis. The displacement data will be used in another software to produce sound.

Issue:
When the accelerometer is static, all of the 3 output values fluctuate between 0 and 3-4. How can I make the output values (Xval, Yval, Zval) more stable (stay closer to zero) when it's at a station point (when the accelerometer is not moving)?

What I've done:
As seen in the code below, I have tried getting the "displacement" value (how much the accelerometer has moved - comparing to the last reading and the current reading, subtracting the former from the latter). I have learned that there is a method called "double integration", but I had a hard time understanding it, and I came up with this method of just calculating the difference in previous & current readings. There might be critical mistakes - I'd really appreciate your corrections/suggestions.

The set-up:

The accelerometer is hanging down loose on a string from the ceiling, and it's meant to swing randomly in mid-air (in all 3 axis directions) from 10cm to around 1m (imagine something like a pinata) - by human hand interaction.

Thanks for reading.


My code:


Code: [Select]
const int ap1 = A5;
const int ap2 = A4;
const int ap3 = A3;
const int numReadings = 30;
int inByte = 0;         // incoming serial byte

int readingsX[numReadings];      // the readings from the analog input
int readingsY[numReadings];      // the readings from the analog input
int readingsZ[numReadings];      // the readings from the analog input

int readIndexX = 0;              // the index of the current reading
int readIndexY = 0;              // the index of the current reading
int readIndexZ = 0;              // the index of the current reading
int totalX = 0;                  // the running total
int totalY = 0;                  // the running total
int totalZ = 0;                  // the running total
int averageX = 0;                // the average
int averageY = 0;                // the average
int averageZ = 0;                // the average
int lastX = 0;
int lastY = 0;
int lastZ = 0;



void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readingsX[thisReading] = 0;
    readingsY[thisReading] = 0;
    readingsZ[thisReading] = 0;
  }
}

void loop() {


  int Xval = getXval();
  int Yval = getYval();
  int Zval = getZval();
 
  Serial.print(Xval);
  Serial.print(",");
  Serial.print(Yval);
  Serial.print(",");
  Serial.print(Zval);
  Serial.print("\n");
  delay(2);

} //------End of Loop-----


int getXval(){
 
  //  X------------------------------
  // subtract the last reading:
  totalX = totalX - readingsX[readIndexX];
 
//read from the sensor:
  int x = abs(analogRead(ap1));
  int xMapped = map(x,0,10,0,1000);
  readingsX[readIndexX] = xMapped;
 
  // add the reading to the total:
  totalX = totalX + readingsX[readIndexX];
  // advance to the next position in the array:
  readIndexX = readIndexX + 1;

  // if we're at the end of the array...
  if (readIndexX >= numReadings) {
    // ...wrap around to the beginning:
    readIndexX = 0;
  }

  // calculate the average:
  averageX = totalX / numReadings;
  int accelerationX = abs(averageX - lastX);
  lastX = averageX;
  return accelerationX;
}


int getYval(){

  //  Y------------------------------
   // subtract the last reading:
  totalY = totalY- readingsY[readIndexY];
 
//read from the sensor:
  int y = abs(analogRead(ap2));
  int yMapped = map(y,0,10,0,1000);
  readingsY[readIndexY] = yMapped;
 
  // add the reading to the total:
  totalY = totalY + readingsY[readIndexY];
  // advance to the next position in the array:
  readIndexY = readIndexY + 1;

  // if we're at the end of the array...
  if (readIndexY >= numReadings) {
    // ...wrap around to the beginning:
    readIndexY = 0;
  }

  // calculate the average:
  averageY = totalY / numReadings;
  int accelerationY = abs(averageY - lastY);
  lastY = averageY;
  return accelerationY;
}


int getZval(){
  //  Z------------------------------
   // subtract the last reading:
  totalZ = totalZ - readingsZ[readIndexZ];
 
//read from the sensor:
  int z = abs(analogRead(ap3));
  int zMapped = map(z,0,10,0,1000);
  readingsZ[readIndexZ] = zMapped;
 
  // add the reading to the total:
  totalZ = totalZ + readingsZ[readIndexZ];
  // advance to the next position in the array:
  readIndexZ = readIndexZ + 1;

  // if we're at the end of the array...
  if (readIndexZ >= numReadings) {
    // ...wrap around to the beginning:
    readIndexZ = 0;
  }

  // calculate the average:
  averageZ = totalZ / numReadings;
  int accelerationZ = abs(averageZ - lastZ);
  lastZ = averageZ;
  return accelerationZ;
}

pylon

Quote
I would like some help with stabilizing / filtering the data of the 3 axis, from the ADXL335 accelerometer.
Get you Vcc stabilized. The more stable the power supply voltage the better readings you get from a sensor with analog outputs.

Then there are several actions you can take on the Arduino to get a more stable result. Put the processor to sleep during the ADC measurement (there's a special sleep state for this). Read the input several times and use the average of the read values. Throw away the first value read after switching the MUX.

Quote
When the accelerometer is static, all of the 3 output values fluctuate between 0 and 3-4.
I would expect at least one axis to be substantially greater than 0 (at least if you're not measuring in space).

Quote
I have learned that there is a method called "double integration",
As the sensor is measuring acceleration a double integration is the way to go to get a distance (after the first integration you get a speed, the second integration results in a distance). The problem is that you don't ever get exact results with this method as the time slots between measurements should be infinitely small. So the faster your measuring the better the integration, but with faster measurements the measurement error from the ADC will increase so you probably have to play with the values to get the best settings for your case.

The posted code doesn't contain a double integration. Was that planed for a later version?

jremington

This post explains why you cannot measure displacement using a consumer grade accelerometer.

cloudurchin

This post explains why you cannot measure displacement using a consumer grade accelerometer.
Hi jremington,
Thanks for the article. I have read it briefly - it helps me understand the sensor, since I'm still at a beginner level.
What if I don't need the sensor to be that much accurate? (I would even be happy if the sensor actually works to some extent and get a rough estimate results - to get values closer to zero when there is smaller movements/displacement, and to get a larger value when there is a greater displacement).

Or, am I just trying to something impossible? Should I just give up on the +-4 values?

jremington

Try it out. Perhaps it will work well enough for you.

Quote
to get values closer to zero when there is smaller movements/displacement
This does not follow, because displacement is independent of acceleration.

Large accelerations can bring a moving object to a dead stop over a very short distance.

wvmarle

The accelerometer is hanging down loose on a string from the ceiling, and it's meant to swing randomly in mid-air (in all 3 axis directions) from 10cm to around 1m (imagine something like a pinata) - by human hand interaction.
The Z axis should give you a very good indicator.

When no movement, it's equal to gravity. It's just hanging there.

When swinging, it's equal to gravity at the end points - well, the Z of your accelerometer may be a bit less due to the angle your device will be at - and higher at the mid point. As it swings through the mid point the X and Y accelerations should be zero (a little above zero due to friction).

The highest acceleration in the Z direction gives you a measure of the speed at the mid point, and that in turn tells you quite accurately how far out your device swings. A bit of dead reckoning should get you quite close to the actual point of the swing curve your device is. Noticing the dip in Z values tells you you're on your way back (one calibration point), the peak in Z values gives the mid point of the swing (second calibration point).

Ignore sharp peaks in the acceleration: that's people hitting it. Or maybe you actually want to do something with those hits. Anyway, it's not something that's relevant for your calculation, and those peaks I expect to see mostly in the X and Y directions as the Z is restrained by the string it's swinging from.

The actual lateral direction of the swing provide a much greater problem, as there will be little to no velocity change in the X and Y direction as it swings. If it can rotate (hanging on a single wire) then all bets are off for those two anyway, you have no idea which direction your object is oriented, and with it no idea which direction it's being pushed.
Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

Go Up