Stabilizing Accelerometer (ADXL335) to measure displacement?

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:

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

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.

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).

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?

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

jremington:
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?

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

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.

cloudurchin:
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.

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:

pylon:
Put the processor to sleep during the ADC measurement (there's a special sleep state for this).

pylon:
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

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 :sob:
I just know that method double integration exists, but I don't know how to execute it.

I'm using this code instead:

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?

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.)

pylon:
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!

jremington:
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 -

wvmarle:
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.

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

cloudurchin:
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:

cloudurchin:
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.

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.

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.

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 Accelerometer Basics - SparkFun Learn
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).

wvmarle:
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.

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.

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??

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"??

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!

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.

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.

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:

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

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.

spits out the number roughly between 425 and 427 (for the z axis). So this corresponds to the "9.8 m/s2"??

Yes.

Flip it over and get the negative (-9.8 m/s^2).

Align either X or Y vertically, and what do you see?

pylon:
Get you Vcc stabilized. The more stable the power supply voltage the better readings you get from a sensor with analog outputs.

This is a general question but - is the regular 5v or 3.3v power supply from computer via usb cable to Arduino board not stable enough???
I'm just using the 3.3v on Arduino UNO??

Thanks.

jremington:
Yes.

Flip it over and get the negative (-9.8 m/s^2).

Align either X or Y vertically, and what do you see?

Ah ok, that makes sense. When I align X or Y, the 425-427 just increases or decreases. But this raw data is not what I want.

After reading through documentations / forums of the sensor, I guess what I really need to do is first convert the values to G forces and calibrate the sensor, then start from there.

I made a new forum that's more specific and better tailored to my project now, so please take a look if you have time - thanks for everything.

eezJerome:
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.

Hi, thanks for your comment and the video url!
Oh yes, I do know that it's about finding the area under a curve...
The issue is more like... I don't know why I need to do that and how that fits into the whole process of finding acceleration...

But I feel like I don't even need to do a double integration, because I just want to track acceleration...
All the mixed answers from everyone on the forum is frying my brain but it seems like, all I need to do is just combine the vectors??? of the 3-axises, x, y, z, and don't need any of the double integration stuff.

Does it have a voltage regulator? Normally the breakout board has one but there are also some that don't have it. I'm not so familiar with the ADXL3355 but if it has a tiny 3 pin looking black "chip" then I guess it's safe to use 5v. Otherwise use 3.3v

Ensure that enough voltage is supplied by your Arduino to the sensor; if you choose to use 3.3v straight from the Arduino, a nano sometimes won't be enough, however an uno will be good.

I'm not like one of the experts here but if I give advice it should help :slight_smile:

Cheers