Super simple noise filtering for Maxbotics LV-EZ1 Sonar

Hey everybody! This is my first post here and it's going to be a long one. We had so much trouble setting up and cleaning the signal of 3 LV-EZ1 sonars that I felt that the right thing to do was share with you our solution. I'll describe the project and the setup and then I'll describe our solution.

So, I'm working on this project in which we want to determine the location of a car in a parking spot. We tried several technologies to measure distance like RF, Magnetometers and stuff. We wanted to achieve a very accurate measurement so we had left the sonars apart, but after failing with all our other options (mainly due to lack of experience) we ended up hooking up 3 LV-EZ1 sonars to an arduino UNO board.

The full experiment consist of the car entering the parking spot and an interface showing the location. So the parking spot was fitted with barriers at the sides and at the front. So we fixed one sonar on the front bumper facing forward, one sonar on the right and one sonar on the left, both facing "outwards".

We had to characterize the sonars because the AN pin of the LV-EZ1 already has an ADC, that then goes to another ADC in the arduino. Therefore, the readings couldn't be directly related to a distance in any unit.To characterize them we drew a "ruler" on the ground, set one sonar on a support and moved a flat piece of board in a linear fashion from 15cm (the indicated minimum level) up to 7m (the max), taking measurements every 30cm. Then we plotted these points in an excel file and did a trend line to get the equation that would allow us to read cm instead of meaningless values.

We were reading pretty cool and accurate measurements, being able to read as accurately as the sensor's resolution allowed us (1inch). But the signal was super noisy, oscillating +-1.25cm (.5inch) and we were having some really nasty noise spikes that were driving our interface crazy.

We tried a low pass filter and it didn't work for the spikes, then we spent 3 days searching for filters implementations that we could use or code but most of them referred to a Kalman filter (awesome but nasty thing) or to complimentary filters. As we are not using accelerometers or other sensors different than sonars, we couldn't do a filter as efficient and simple as a complimentary one. So we kept on looking but we couldn't find anything useful or simple enough for our proyect and coding skills.

Then, after reading so much math and equations and coding, we turned back to the whiteboards and started doing our own thinking (we should have done it waaay earlier), and came with the following implementation that while it's not the best or fastest or cleanest, work perfect for our project and we think that will great for lot's of other simple applications.

The logic was the following:

  • The car is entering a parking spot, so there has to be a max speed
  • We determined after looking at our user research that such speed is less than 30Km/h (pretty fast if approaching a wall!)
  • We translated that speed in Km/h to cm/ms to be able to study the noise (.8cm/ms)
  • The sonar takes a measurement every 50ms, so a car moving at 30Km/h will show a measurement of about 40cm
  • Then we looked at our plotted points and observed that the spikes were always larger than that value

So we take a reading from the sonar and compare it with the previous one. If the absolute value of the difference is greater than our threshold, then we discard the measurement and copy the previous reading as the current. Then we do a low pass filtering in which the second member of the equation is an averaging of the present reading and the past two.

Here's the code:
#define S1 A0 //analog0
float a,b,c,x1, sonar1;

sonar1 = analogRead(S1);

//convert the ADC reading to cm with y = mx+b
//the values of m and b come from characterizing the sonar
float x1 = (sonar1+1.3496732)/.79924503;

//filter the spikes
if(fabs(x1-a)<40) a=x1;

//now we move the other set of 2 values to average
c=b;
b=a;

// perform a 50% lowpass filtering + averaging
filtered = .5filtered + .5(a+b+c)/3;

And I'm attaching a plot of our readings and a zoomed view. The filter has a very small lag and provides us with a very smooth curve despite the spikes. We're very happy with the results.

Hope this helps somebody!

informative and useful

wrote this once - Arduino Playground - RunningMedian - removes spikes quite easily. The noisier the signal the larger the internal array should be. The nice part is that it implicit adjusts the threshold of a spike, it is not hardcoded, it just ignores them (unless they become mainstream :slight_smile:

Note that the price is that samples get reordered/ delayed in time a bit.

Give it a try.