Go Down

Topic: Stabilizing Accelerometer (ADXL335) to measure displacement? (Read 581 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.

cloudurchin

Everyone, sorry for the radio silence (was dealing with some personal issues).
Thanks for all your insight.
I'm back now with some more questions after reading your comments - I'd really appreciate your help once again.

pylon,
In regards to these comments:
Put the processor to sleep during the ADC measurement (there's a special sleep state for this).
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.
do you mean by, analog to digital conversion process (done by the processor???) is the cause of potential inaccuracy of the double integration?
What exactly is "measurement error from the ADC"? Does that happen all the time? (for example, does the ADC process to readings from simple analog input such as a potentiometer and using that to dim LED - also has some errors!?) Sorry if this is common sense, but it would be great if you could explain what/how/why that happens?

Also regarding
Quote
The posted code doesn't contain a double integration. Was that planed for a later version?
My code doesn't contain double integration. I don't know how to do double integration :smiley-cry:
I just know that method double integration exists, but I don't know how to execute it.

I'm using this code instead:

Code: [Select]
int accelerationX = abs(averageX - lastX);
  lastX = averageX;
  return accelerationX;


^This is how I (probably inaccuratly) calculate acceleration, as a replacement for going through all the fuss of double integration, as of now (still struggling to find out how to do double integration).

averageX is the "current" reading and lastX is the "previous" reading of the x-value of the sensor.
1) I subtract lastX from averageX and make sure it's a positive value using "abs". Store that as "accelerationX"
2) I update lastX with the "current" reading after, so it can be used in the next round of loop.
3) "accelerationX" is returned. It's used as the acceleration value for x-axis.
Please point out if you think this won't work to calculate acceleration/if there's a fundamental error?


Quote
I would expect at least one axis to be substantially greater than 0 (at least if you're not measuring in space).
This does not happen (when adxl335 is static) because I am subtracting the "previous" reading from the "current" reading. (e.g. if the previous reading was 100 and current reading is 100.1, 100.1 - 100 = 0.1, meaning virtually zero difference.)

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.
Ah Okay - so how double integration works is - by frequently checking how much the gravitational pull has changed from "last reading" to "current reading", I can figure out how much velocity has changed over a certain distance, thus figure out acceleration??? And, because the Arduino has to check this so frequently, the process is too heavy for the processor of Arduino???

Sorry for so many questions, but I really want to get the accelerometer to work.
Thanks so much again for your help!

cloudurchin

Try it out. Perhaps it will work well enough for you.
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.
Hi jremington,
thanks for your comment!
After reading your comment, I realised I was unclear about the term "displacement"!
What I meant by "displacement" was, simply "acceleration" - apologies for the confusion!
I don't need to find the distance of how much the accelerometer moved.
I want to know wether the accelerometer moved or not - meaning if there was any acceleration.

So what wvmarle said has a point -
Ignore sharp peaks in the acceleration: that's people hitting it. Or maybe you actually want to do something with those hits.d.
I want to know when people are hitting it! I want to get a reading of ANY acceleration applied to the sensor. Does this make more sense??
Again, I'm really sorry I was unclear.

wvmarle

In #0 you quite unambiguously described "displacement" as the distance moved:

the "displacement" value (how much the accelerometer has moved - comparing to the last reading and the current reading, subtracting the former from the latter).
The new explanation is actually more confusing:

After reading your comment, I realised I was unclear about the term "displacement"!
What I meant by "displacement" was, simply "acceleration" - apologies for the confusion!
I don't need to find the distance of how much the accelerometer moved.
I want to know wether the accelerometer moved or not - meaning if there was any acceleration.

Displacement and acceleration are two very different things!

So you want to know whether there's acceleration - well that's exactly what an accelerometer tells you, doesn't it? Hang it still in the air and you will read 0 m/s2 in the x and y axis and 9.8 m/s2 in the Z direction when the sensor is placed so that x and y are in the horizontal plane. That 9.8 m/s2 is the gravitational acceleration.

If you have your accelerometer at some random angle the vector of the three would give you a total of 9.8 m/s2.

Displacement is distance moved, and that's where the double integration comes in play as you have to get rid of that /s2 part. Acceleration times time gives you speed, and speed times time gives you the distance it has moved.

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.

MorganS

Read The Scientist and Engineer's Guide to
Digital Signal Processing
. I think you want to implement a bandpass filter to remove gravity and remove high-frequency noise.

Removing gravity from accelerometer readings is a subject all of its own and usually requires gyros as well as accelerometers. Look at using a chip like the MPU9020 or LSM9DS1.
"The problem is in the code you didn't post."

cloudurchin

Read The Scientist and Engineer's Guide to
Digital Signal Processing
. I think you want to implement a bandpass filter to remove gravity and remove high-frequency noise.

Removing gravity from accelerometer readings is a subject all of its own and usually requires gyros as well as accelerometers. Look at using a chip like the MPU9020 or LSM9DS1.
Hi MorganS,
Thanks for your comment.
Since I'm aiming to get this project done in the next week or two, I might not have a time read the book before that- but will do when I have time, thank you for the suggestion!

In the meantime, I have another question for you.
Why do you think I should remove gravity from the readings?? What does it do??

MPU9020 / LSM9DS1 is a gyro is it? I read on the link https://learn.sparkfun.com/tutorials/accelerometer-basics
that
" [gyros]... are commonly used in motion tracking applications and UAV guidance systems, where location and orientation of an object is important."

But I don't need location or orientation I think? I just want to know the acceleration of the sensor, and use that data (map the output values to soundscape).

cloudurchin

In #0 you quite unambiguously described "displacement" as the distance moved:

The new explanation is actually more confusing:

Displacement and acceleration are two very different things!

So you want to know whether there's acceleration - well that's exactly what an accelerometer tells you, doesn't it? Hang it still in the air and you will read 0 m/s2 in the x and y axis and 9.8 m/s2 in the Z direction when the sensor is placed so that x and y are in the horizontal plane. That 9.8 m/s2 is the gravitational acceleration.

If you have your accelerometer at some random angle the vector of the three would give you a total of 9.8 m/s2.

Displacement is distance moved, and that's where the double integration comes in play as you have to get rid of that /s2 part. Acceleration times time gives you speed, and speed times time gives you the distance it has moved.


Hi wvmarle,
Thanks for your comment.
Oh my god, I am really sorry - I didn't know displacement and acceleration are different things. I was using the term interchangeably. Sorry I feel very embarrassed. Thanks for clarifying this difference.

Now I know

Displacement = Distance of the accelerometer's movement
Acceleration = Change in velocity.


Quote
So you want to know whether there's acceleration
Yes you are right! I want to know if there's acceleration / how much acceleration there are.


Quote
well that's exactly what an accelerometer tells you, doesn't it?
I thought it did, but it doesn't seem like it!?

As I tilt the sensor in the 3 axis, the raw readings of the axis just increases or decreases according to the angle, not how fast (acceleration) I'm moving the sensor.
When the sensor is parallel to a table (flat) and static, the readings of the 3 axises show the value ranging between 300-450. When I tilt the sensor on an angle and hold it still in that angle, the values changed correspondingly. But that's not acceleration, is it??

I would like the accelerometer to show the reading "0" when I'm holding it still, whether on a flat surface or the sensor is in an angle.
When I'm moving the sensor very slowly, the reading should be close to 0.
When I'm moving and shaking the sensor very quickly (in random directions), the reading should be very high.

Does this... make sense??

Quote
Hang it still in the air and you will read 0 m/s2 in the x and y axis and 9.8 m/s2 in the Z direction
When I hang it still in the air, as you've mentioned, it just spits out the number roughly between 425 and 427 (for the z axis). So this corresponds to the "9.8 m/s2"??

Quote
If you have your accelerometer at some random angle the vector of the three would give you a total of 9.8 m/s2.
What is vector? Do you mean by the three raw values of the axis (the 300-450 reading mentioned previously)?


I'm really sorry again about the confusion regarding "displacement". My original post was a very badly formulated question!


wvmarle

What you're measuring is the continuous acceleration of gravity.

Vector = value with direction. Speed and acceleration are typical examples. Pretty basic algebra, at least it was in my secondary school.

What numbers correspond to what exact acceleration you can almost certainly find in the datasheet of your sensor.
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.

MorganS

Here is another fun thought experiment: jump off a building with your accelerometer. (Since it is a thought experiment, you don't need to carry a parachute.) What does the accelerometer show when you are in free-fall? It shows zero.

So yes, you absolutely do need an algorithm to remove gravity to show you the true acceleration.
"The problem is in the code you didn't post."

eezJerome

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;
}

NICE!

Anyway, I'm currently working on something similar and we might be on the same stage in our projects. I'm a high schooler in precalculus, so I have no idea how to integrate; all I know is that it's something about finding the area under a curve, which provides values [?] of a lower order object (i.e. velocity is "lower" than acceleration);

Check this out and work out the math in your code. It's not as daunting as it sounds.
The Organic Chemist -- Definite Integrals
https://www.youtube.com/watch?v=rCWOdfQ3cwQ

EDIT: Never mind....the other posts didn't load so I thought I was the 3rd poster. But nonetheless if your project is not gonna be used for some industry, I think it's possible to achieve your goal. It's just not going to be accurate.

Go Up