Pothole data smoothing and detection of accelerometer

I wanted to start a separate thread concentrating on smoothing filtering and detecting bumps and potholes with an Arduino and my Accelerometer. I’m using an Analog Accelerometer set to 50HZ bandwidth sampling at 100HZ. I’m plotting data with MegunoLink (GREAT tool BTW). My delemma is that the accelerometer readings are noisy as hell, especially in a car with the engine running and road noise. What would be the best way to filter out the noise? I’m sampling at 100HZ I’m not sure how to implement at filter that wont affect the sampling rate… Also, I’m not sure if 100HZ is enough to sample at, it seems to catch the bump events pretty well though (graphs posted).

Accelerometer is a KXPS5-3157

I converted the accelerometer output to Gforce, should I be using Gforce, or should I use raw voltage or it doesn’t matter for potholes application

Here is my code so far:

/* ***********************************************************************************************************
    Send data to MegunoLink for plotting. 
    
    Read analog Z-axis acceloremeter data , bandwidth set to 50HZ by 
    external capacitors per the datasheet
    
    According to Nyquist we need to sample the accelerometer at least twice the bandwidth 
    
    Set Serial UART to 115200 to handle more than 100HZ sampling rate to plot to Meguno
    
    115200 baud means 115200 bit-times/second, and there are 10 bit-times per byte transmitted on the UART 
    (start bit, 8 data bits, and stop bit). 
    
    So 115200 / 10 = 11520 bytes / second = 11.520 bytes / millisecond. 
    
   ************************************************************************************************************/

#include <GraphSeries.h>

GraphSeries g_aGraphs[] = {"Z"}; //Z acceleration graph label for Meguno

// GLOBALS

long previousMillis = 0;
long interval = 10; // interval in milliseconds (10ms => 100Hz)
int data = 0;

//CALIBRATION DATA FOR ACCELEROMETER
float one_G = 647.0; // OFFSET OF 1G Z axis
float neg_G = 372.0; // OFFSET OF -1G Z axis
// Our ZERO G Reference should be in the middle of these two readings
float mZ = (one_G + neg_G) / 2.0; // ZERO_G REFERENCE FOR Z AXIS
// Estimate Z axis specific sensitivity difference of 2G between readings
float senZ = (one_G - neg_G) / 2.0;
float sensitivity = 440.0; // FROM DATASHEET TYPICAL SENSIVITIY 440mV/G

void setup()
{
  // The data is sent via the serial port. Initialize it.
  Serial.begin(115200);
  analogReference(EXTERNAL); // ACCELEROMETER IS 3.3VOLT
}


void loop()
{
  
  ReadAccelerometer();
  
}

void ReadAccelerometer()
{
   unsigned long currentMillis = millis();
   if((currentMillis - previousMillis) > interval) {
      previousMillis = currentMillis;

      // Read values from the ADC converter and send them out the serial port.
      data = analogRead(2); // READ ANALOG PIN 2 100uS
      
      //float GForceG = ((float)data - mZ) / senZ; // Convert ADC value to G force with gravity
      float GForce = ((float)data - (one_G)) / senZ; // ZERO BASE WITHOUT GRAVITY
 
      
      
      
      g_aGraphs[0].SendData(GForce); // SEND Z AXIS G FORCE
    }
}

pothole at 25 mph

pothole at 25 mph zoomed

I have two graphs at approximately 25 MPH the pothole event is obvious, then there was a subtle bump afterwards The other graph is the same data, but zoomed in slightly

I’m not sure how to detect the actual pothole event, if I could use FFT, standard Deviation threshold, running average?

Any advice, suggestions, input, wisdom is greatly appreciated!

I'd try a simple threshold first. More importantly though I'd want to collect a lot more data of driving in different conditions and speeds to see where that threshold lies and whether the method is viable.

It appears the accelerometer I'm using has an internal LPF of 1000HZ this means it will reject signals 1000HZ or greater right?

It looks like I need a better signal to noise ratio, I'm confused though, the accelerometer has an internal 1000HZ LPF and you can change the bandwidth with external capcitor, is bandwidth the same as changing the LPF to 50HZ or lower acceptance?

I want to accept signals very low probably 1 to 10HZ would I have to design a separate filter and connect that to the Z axis output?

The datasheet explaining the bandwidth is here

http://kanga.gerbilator.org/Sensors/Accelerometers/AN009%20Getting%20Started%20with%20the%20KXPS5.pdf

*UPDATE I just realized the Accelerometer has built in 32K resistors so Ok the Accelerometer has 32k resistors built in that I didn't see before haha that makes it easy, so I can design a single pole RC filter if I want a 10HZ cutoff C = 1 / [2πRf] C = 1 / (2π * 32000 * 10HZ) = 497nF capacitor? This will be better than the 50HZ I currently have since the frequency of a pothole or bump on a cars suspension is very low

I'm upping the sampling rate since it seems like the pothole event is a very fast and large impulse to 1000 samples a second

In my set up, gravity is always working on the accelerometer and I subtracted its value with an offset to "zero" the accelerometer, however I just discovered if you're driving on slopes and hilly terrain gravity will be split between two axises.... Is there a way to compensate for the uneven terrain to correct the G offset of driving up a hill?

I adjusted the sampling rate and lowered the LPF on the accelerometer to 15HZ. I have obtained better data, I see the negative and postive peaks in a pothole, it seems to oscillate and dampen with time making a "beat" type pattern. I wonder how the pattern could be programmatic ally found?

Data looks to be expected with the matlab model, Large negative -G spike falling edge then damping oscillating G's with a +G spike out of the pothole and suspension going to equilibrium I only model with a 2DOF quarter car model though to compare. Usually one wheel hits a pothole in a discernible sample time

I would eliminate all noise this way as a first try... and get max samples from the pothole bump (hires)

#define POTHOLE_THRESHOLD  0.5

void loop()
{
  float data = analogRead(2); // read as often as possible

  float GForce = (data - (one_G)) * senZreciproke;  // senZreciproke ==  2.0 /(one_G - neg_G);   // multiplication is faster than division
  
  if (abs(GForce) > POTHOLE_THRESHOLD)
  {
    // pothole alarm !! blink a led or ..
    g_aGraphs[0].SendData(GForce);   // always send pothole data as often as possible.
  }
  else GForce = 0; // noise -> remove it

  if((currentMillis - previousMillis) > interval) 
  {
    previousMillis = currentMillis;
    g_aGraphs[0].SendData(GForce);   // and once per interval
  }
}