Sampling Accelerometer data to detect a pothole need help and guidance

I've always wanted to build something that can detect potholes in the car. Basically I have an Arduino and I have the KXSP5 accelerometer hooked up to it in analog mode. Then I have two push buttons one to stop and start the logging process, so right before I drive over a bump I hit start then afterwards hit stop.

I want to count potholes, plot the GPS location of the pothole on Google Maps, the hardest part of the project is detecting a pothole. When I get pothole data eventually I want to score how "rough" it was based on a subjected scale like a 1 to 5 rough to the driver/passenger. I'm not funded by too much money I work full time and go to school full time.

I basically put the accelerometer flat and got the baseline voltage of no "vibration" I am measure and logging voltage and converting it to G force. Vertical acceleration of the sprung mass in the car

I know when the car hits a pothole, the springs and shocks should behave similar to a damped harmonic oscillator, from research it seems that most cars will oscillate below 20HZ even with bad shocks. I'm just not sure how my sampling rate should be for the Arduino to get data from the accelerometer. I know from Nyqusit it should be at-least double this right so 40 samples/sec which the Arduino should be able to do, I believe the ADC clock is main clock (16MHz)/2

I just found out the Accelerometer I'm using has a built in 1KHZ low pass filter but I need to measure below 20HZ it seems.... Not sure if there is a way to use it still, the Datasheet saids you can change capacitors to modify the cutoff, but I can't figure out how to get it

I'm plotting Accelerometer data from the serial port, but its just the output of the accelerometer with no time reference. I think I need to have a time reference for the X axis right? To be able to use the data? I'm not sure if FFT is even the way to go here I like the idea of finding a signature pothole and matching patterns though!

this sounds cool im gonna have to keep tabs on this one, although since the roads here are pothole free id have to modify it to people i run over :fearful:

It's subjective what you're going to call a pothole, but it seems to me that they have in common a brief negative acceleration followed by a brief and much larger positive acceleration. I think you should be able to tell the difference between a pothole and normal road noise very easily by sampling relatively frequently and integrating the total impulse over (say) a tenth of a second. It will be up to you to decide how long a period to sample over and how high to set the threshold for considering it a pothole, but I think if you capture the acceleration profile of a typical pothole you will find it easy enough to recognise.

Hey Peter yeah my experiment has a start and stop button so I can drive holding a button in my hand and can turn on and off the logging right before and right after the "pothole" or bump in the road. I'm wondering what the sample rate should be, also for a time scale

I was going to do maybe a milis() then do an analogread then do another millis, then the difference would be the time stamp for the data to plot?

It seems like the maximum I can get my car to "oscillate is about 9HZ, the bandwidth of the accelerometer is 50Hz so I should be sampling 100 times a second???

Also

"integrating the total impulse over (say) a tenth of a second."

Is the arduino capable of doing this fast enough? I'm also going to be running GPS data and other things

How would you Integrate the impulse on the arduino is there a library to use ?

I don't know how fast your accelerometer can be read, but if you can manage it I'd have thought that up to 1KHz would be worth while.

I don't know of a library implementing integration (there may be one; I haven't looked) but it is relatively easy to do yourself.

Integration is essentially just repeated addition, and you can integrate an accelerometer signal to calculate the impulse just by adding your samples over the time interval of interest. For example, you might calculate the rolling average over 100ms and compare that to a threshold. Since you are only interested in a general indication and not an accurate calculation, you can use a decaying average rather than work the average out exactly; this means you don't need to preserve a large number of data samples as part of your calculation.

I think you are overcomplicating your situation. All you need it to calculate running average and standard deviation. When you hit a pothole, the average over say the course of 2 second is probably 1g since you oscillate up and down. But the stdev is a large number, maybe 0.3g. You just have a stdev threshold to record a pothole, then clear running average, wait 3 second, restart running average. This is what I do to detect a knock on a box and it works nicely.
I don't think detailed oscillation curve will help you find a pothole unless you want to later manually identify them on a curve. It's too complicated. Once you can do the threshold thing, have a routine to find maximal deviation to use as a pothole depth measure before resetting running average.

"You just have a stdev threshold to record a pothole, then clear running average, wait 3 second, restart running average."

Not sure I understand how to calculated the stdev of what? the acceleration? Do you have any example code from the knock detection? I think I am overcomplicating the problem I was thinking of having to use patterns of FFT to match a signature of a pothole, what is the stdev method?

Thank you

a running average is calculated with a FIFO queue, you keep adding new data and tossing old. You then calculate the standard deviation of the data in the queue. This standard deviation will grow big when you have much change of your measurement caused by the pothole and will return to a baseline value when the change is gone.

It's been two years since I did that project I call music box:

The main part of checking is here. Every time you do a measurement, you add it to the stat object for running average calc and then find standard deviation to compare with a threshold.

void checkBox()
{
  ax_stats.add(x_level);
  ay_stats.add(y_level);
  az_stats.add(z_level);
//  SerialDebug0();
  if ((ax_stats.get_stdev()<gx_factor*0.1)&&(ay_stats.get_stdev()<gy_factor*0.1)&&(az_stats.get_stdev()<gz_factor*0.1)) // Stable condition
  {
    if (ax_stats.within_eb(gx_factor,gx_factor*0.15)&&last_number!=2) last_number=2;
    else if (ax_stats.within_eb(-gx_factor,gx_factor*0.15)&&last_number!=1) last_number=1;
    else if (ay_stats.within_eb(-gy_factor,gy_factor*0.15)&&last_number!=4) last_number=4;
    else if (ay_stats.within_eb(gy_factor,gy_factor*0.15)&&last_number!=3) last_number=3;
    else if (az_stats.within_eb(gz_factor,gz_factor*0.15)&&last_number!=5) last_number=5;
    else if (az_stats.within_eb(-gz_factor,gz_factor*0.15)&&last_number!=6) last_number=6;
//    else last_number=0;
  }
  else if ((ax_stats.get_stdev()>gx_factor*0.35)||(ay_stats.get_stdev()>gy_factor*0.35)||(az_stats.get_stdev()>gz_factor*0.35))
  {
    playTone(last_number);
  }
}

stat.h

#include <Arduino.h>
#ifndef stat_h
#define stat_h
class stat
{
  public:
  int buffer[5]; // This stores analog level for running average.
  int buffer_pointer; // This is the pointer for analog level for running average.
  void add(int number);
  float get_avg(); // Calculate for running average.
  float get_stdev(); // Calculate for running average and then standard deviation.
  boolean within_eb(float center, float dev); // Calculate whether the running average is within center +- dev.
  stat();
};
#endif

stat.cpp

#include "stat.h"
#include "math.h"
stat::stat()
{
  for (short i=0;i<5;i++)
  {
    buffer[i]=0;
  }
  buffer_pointer=0;
}

void stat::add(int number)
{
  if (buffer_pointer==5) buffer_pointer=0;
  buffer[buffer_pointer++]=number;
}

float stat::get_avg()
{
  float avg=0;
  for (short i=0;i<5;i++)
  {
    avg+=buffer[i];
  }
  avg/=5.0;
  return avg;
}

float stat::get_stdev()
{
  float avg=0;
  float stdev=0;
  for (short i=0;i<5;i++)
  {
    avg+=buffer[i];
  }
  avg/=5.0;

  for (short i=0;i<5;i++)
  {
    stdev+=(buffer[i]-avg)*(buffer[i]-avg);
  }

  stdev/=5.0;
  stdev=sqrt(stdev);
  return stdev;
}

boolean stat::within_eb(float center, float dev)
{
  float avg=0;
  float stdev=0;
  for (short i=0;i<5;i++)
  {
    avg+=buffer[i];
  }
  avg/=5.0;
  if (abs(avg-center)<dev) return true;
  else return false;
}

Wow thank you so much I'll be staying up late playing with this! I have a few questions though, I'm assuming the gxfactor, etc are calibration factors foe the particular accelerometer what is the 0.15 coefficient is that just some empirical value to make it work better for your application? (gy_factor*0.15)

It looks like your buffer has 5 elements, do you think I would need something a bit bigger for pothole data like 50 to 100 elements? The sampling rate I'm reading in my books from school should be twice the bandwidth of the sensor, if I have a buffer that is too small, and the sample rate is too high it would effectively make the standard deviation inaccurate because it could toss out "PEAK" data before it is calculated?

Since I'm only interested in vertical acceleration I'd be just using one axis (Z), would I calculate the StDev after every Analogread or wait for it to fill up a whole buffer first then calculate it at the higher sampling rates

also what is within_eb()? Is that a normalized curve

Thanks sorry I'm bothering you :confused: I appreciate your time and contributions

Yeah, the 0.15 is emperical for registering a decent knock on the box.

Yes you may want a larger buffer for running average. The buffer size should be able to hold a complete bump with several oscillations at your data rate, which is probably twice the bandwidth of the sensor, as you mentioned.

Yes you don't need the x or y unless you start doing stunt driving with only left wheels on the road while detecting potholes :smiley:

The within_eb is just calculating if the average is within center +- stdev. It's not normalized so stdev is absolute, not percent.

Good luck to your project!

Where do you have the sensor mounted? Is it on the car body (sprung weight) or on the suspension part (unsprung weight). In my opinion, you'd want to mount the sensor so that it moved with the wheel. Otherwise, vehicle speed and mass will interfere with your measurements. The wheel generally does a good job of tracing out the hole even at some speed. Inertia is your enemy.

I want to build something similar to this.Someone told me you should use gyroscope along with the accelerometer.

So what is the difference between accelerometer and gyroscope? Do I need both of them or accelerometer is enough?

Actually, I want to get the difference between potholes, rough roads, and normal roads.I want to detect whether a vehicle is running or standing also? And will this affect during hill climbing?

Thank you in advance.