filtering noise with arduino coding for Maxsonar sensor
Moderators: adafruit_support_bill, adafruit

filtering noise with arduino coding for Maxsonar sensor

by jakethemice on Tue Feb 02, 2010 2:08 pm

Hi everyone

I would need some help on my coding.

I am using a maxbotix AE0 ultrasonic sensor with analog output. I need to take the reading from 200cm to 20cm(the minimum).
My main problem is that from very close, the sensor is going crazy and indicating me false readings (works fine if you place your hand very close, but if you wear a glove for exemple, the fabrics of the glove creates unwanted reflections).
Then, i go closer, the output indicates 30... then 25... then it goes 200...900.. 800 and so on...
as soon as I go away from it, it goes back to normal reading.

What I want to do is filter this with the coding. I would like to say something like :
if the precedent reading was 25cm and then it goes straight to 300cm, dont take the last reading and indicate 25cm. But if you go away slowly, then it reads normally. So I just want to filter this unwanted behaviour, not to get some crazy readings when I am too close.

I tried to make this code. And I said, if you are between 25 and 35 cm on the previous reading and then the next one jumps above 100cm, dont take it. It works fine when I go close, but as soon as I am going further away than 100 cm it indicates me 25cm. I dont know where I made the mistake, it looks like it never goes to some of my loops.

Code: Select all | TOGGLE FULL SIZE
//analog output on pin1
const int Pin_Analog= 1;

//digital pin3 to make the pulse in order to authorize the sensor to range
int Pin_RX= 3;

//anVolt to receive data from analog pin1,
//cm is the most recent range detected,
//previous_cm the precedent range
long anVolt, cm, previous_cm;


void setup() {
 
  previous_cm=0;
  Serial.begin(9600);
  pinMode(Pin_Analog, INPUT);


}

void loop() {
 
//pulse to open the range reading
    digitalWrite(Pin_Analog, LOW);
    delayMicroseconds(50);
    digitalWrite(Pin_Analog, HIGH);
    delayMicroseconds(50);   
    digitalWrite(Pin_Analog, LOW);
    delayMicroseconds(50);
   
//read the most recent value on the analog output of the maxsonar
    anVolt = analogRead(Pin_Analog);
   
//convert the reading into cm
    cm = (anVolt/2) * 2.54;

//filter, if the previous reading was a close range between 0 to 25 cm and the next reading is something like
//100 (sensor is going crazy), i want it to return cm = 25, otherwise, return the most recent value
    if (0 < previous_cm < 35) {
     
      if ( cm > 100) {
        cm = 25;
        Serial.print(cm);
        Serial.print(" cm compensated");
        Serial.println();
        goto end;
        }
      else {
        Serial.print(cm);
        Serial.print(" cm");
        Serial.println();
        goto end;
        }
    }
    else if (previous_cm >= 35) {
     
      if(cm<200){
        Serial.print(cm);
        Serial.print(" cm");
        goto end;
        }
      else {
        cm=200;
        Serial.print(cm);
        Serial.print(" cm compensated");
        Serial.println();
        goto end; 
        }
    }
    end:
    cmprecedent=cm;
    delay(500);
   
}


here is an exemple of the readings :

33 cm
30 cm
30 cm
35 cm
40 cm
43 cm
38 cm
35 cm
55 cm
55 cm
50 cm
48 cm
40 cm
93 cm
93 cm
99 cm
25 cm compensated
25 cm compensated
25 cm compensated
25 cm compensated
25 cm compensated
25 cm compensated

Does anyone have an idea on whats going wrong ?

Hope someone can help me with this :) Thanks!!

Jake.
jakethemice
 
Posts: 17
Joined: Thu Dec 24, 2009 11:49 am

Re: filtering noise with arduino coding for Maxsonar sensor

by adafruit_support_bill on Tue Feb 02, 2010 2:32 pm

Looks like you are testing for previous_cm but it only gets set at startup.
You set cmprecedent at the end, did you mean to set previous_cm?
User avatar
adafruit_support_bill
 
Posts: 30838
Joined: Sat Feb 07, 2009 10:11 am

Re: filtering noise with arduino coding for Maxsonar sensor

by henk on Tue Feb 02, 2010 2:40 pm

Your code does not provide for an escape. Once you took a measurement below 25 cm you will never get out, because the previous measurement will, from then on, always be 25 cm.
henk
 
Posts: 86
Joined: Tue Nov 17, 2009 6:51 pm
Location: Harrisburg, PA

Re: filtering noise with arduino coding for Maxsonar sensor

by jakethemice on Tue Feb 02, 2010 2:56 pm

yes, no actually, thats a mistake posting the code, but i already tested with

previous_cm=100;

in the variable at the beginning of the code. And this does not work either.

Do you see any mistake in the loops ? When I write that on the paper, it seems that it should work.
jakethemice
 
Posts: 17
Joined: Thu Dec 24, 2009 11:49 am

Re: filtering noise with arduino coding for Maxsonar sensor

by jakethemice on Tue Feb 02, 2010 3:01 pm

yes it seems you are right. thanks Henk,

do you have any idea how I could make this work properly ? Do you think there is a way to do this filtering ?
jakethemice
 
Posts: 17
Joined: Thu Dec 24, 2009 11:49 am

Re: filtering noise with arduino coding for Maxsonar sensor

by adafruit_support_bill on Tue Feb 02, 2010 4:07 pm

This statement is probably not what you intended either:

Code: Select all | TOGGLE FULL SIZE
  if (0 < previous_cm < 35)


This will always be true and you will never execute the 'else' clause.
User avatar
adafruit_support_bill
 
Posts: 30838
Joined: Sat Feb 07, 2009 10:11 am

Re: filtering noise with arduino coding for Maxsonar sensor

by henk on Tue Feb 02, 2010 5:03 pm

jakethemice,

You probably have to set a flag or something that you were in the loop, so that the flag and the last reading will allow you to escape the loop. May be what you want to do is, if you were in the loop, go back and take another reading.
henk
 
Posts: 86
Joined: Tue Nov 17, 2009 6:51 pm
Location: Harrisburg, PA

Re: filtering noise with arduino coding for Maxsonar sensor

by jakethemice on Tue Feb 02, 2010 9:40 pm

a friend helped me on that, and Arduwino you were right, the condition at the beginning was wrong. And its been replaced by (0 < previous_cm && previous_cm< 35)
Works fine now, so here is the code :

Code: Select all | TOGGLE FULL SIZE
//analog output on pin1
    const int Pin_Analog= 1;

    //digital pin3 to make the pulse in order to authorize the sensor to range
    int Pin_RX= 3;

    //anVolt to receive data from analog pin1,
    //cm is the most recent range detected,
    //previous_cm the precedent range
    long anVolt, cm, previous_cm, cmprecedent;


    void setup() {
   
      previous_cm=0;
      Serial.begin(9600);
      pinMode(Pin_Analog, INPUT);



    }

    void loop() {

    //pulse to open the range reading
        digitalWrite(Pin_Analog, LOW);
        delayMicroseconds(50);
        digitalWrite(Pin_Analog, HIGH);
        delayMicroseconds(50); 
        digitalWrite(Pin_Analog, LOW);
        delayMicroseconds(50);
     
    //read the most recent value on the analog output of the maxsonar
        anVolt = analogRead(Pin_Analog);

     
    //convert the reading into cm
        cm = (anVolt/2) * 2.54;
        //cm = anVolt;
       
           /*
            Serial.print(cm);
            Serial.print(" cm original");
            Serial.println();
          */
         
    //filter, if the previous reading was a close range between 0 to 25 cm and the next reading is something like
    //100 (sensor is going crazy), i want it to return cm = 25, otherwise, return the most recent
   
        /* Seb - here I changed the condition, for a multiple condition it needs to have &&(and) or ||(or)
        it allows to compare if the both statement are true
        */
       
        if (0 < previous_cm && previous_cm< 35) {
       
          if ( cm > 60) {
            cm = 25;
            Serial.print(cm);
            Serial.print(" cm compensated (boucle cm>100)");
            Serial.println();
            goto end;
            }
          else {
            Serial.print(cm);
            Serial.print(" cm (boucle cm>100 partie else)");
            Serial.println();
            goto end;
            }
        }
        else if (previous_cm >= 35) {
       
          if(cm<200){
            Serial.print(cm);
            Serial.print(" cm (boucle cm<200)");
            Serial.println();
            goto end;
            }
          else {
            cm=200;
            Serial.print(cm);
            Serial.print(" cm compensated (boucle cm<200 partie else)");
            Serial.println();
            goto end;
            }
        }
        end:

       
        //ici j'ai changer ton attribution de la valeur cm a ta varialble previous_cm pour ne psa qu'elle soi toujours = 0
       
        previous_cm=cm;
        delay(1000);
     
    }


thanks for the helps guys :D
jakethemice
 
Posts: 17
Joined: Thu Dec 24, 2009 11:49 am

Re: filtering noise with arduino coding for Maxsonar sensor

by jakethemice on Thu Feb 04, 2010 2:07 pm

hi,

the code finally doesnt work great. Formally yes. But for the sensor and the problem I described, its not working. Someone talked me about another method used for digital signal calcultation, the sliding average method

http://www.dspguide.com/ch15.htm

On the other side, I sent an email to maxbotix, and it seems that the sensors I have are from an old batch and that their new release is sharper to detect soft target like people. Also I think I ve read somewhere that the EZ0 line is the less able to detect soft target (something related to the gain of the sensor). As soon as I try the new ones I ll post something.

In the meantime, if anyone has already programmed a sliding average filter in arduino, let me know. Thx.

Jake.
jakethemice
 
Posts: 17
Joined: Thu Dec 24, 2009 11:49 am

Re: filtering noise with arduino coding for Maxsonar sensor

by adafruit_support_bill on Thu Feb 04, 2010 3:26 pm

Several algorithms are discussed in this thread: http://forums.adafruit.com/viewtopic.php?f=25&t=13290&hilit=average
User avatar
adafruit_support_bill
 
Posts: 30838
Joined: Sat Feb 07, 2009 10:11 am

Re: filtering noise with arduino coding for Maxsonar sensor

by jakethemice on Thu Feb 04, 2010 5:15 pm

thx for the link !

I tried to make an basic sliding average filter. But still the readings are so huge when false detection (from very close) that I had to had a condition at the end of the code to remove everything above a certain distance. I only need to have the reading from 20 to 300cm lets say. Above 300cm , its false reading... but this assertion is only true because I know that the most distant object is placed at 300cm! otherwise I dont know how I would do it.

here is the code :

Code: Select all | TOGGLE FULL SIZE
 const int Pin_Analog = 1;
 int Pin_RX = 3;


 long anVolt, inches, cm ;

 //analog signal entry and filtered output signal
 int anVolt_input[255];
 int anVolt_output[255];


 void setup() {

  Serial.begin(9600);
 
  //declaration des entrées
  pinMode(Pin_Analog, INPUT);

 }

 void loop() {
 
  //loading the reading samples
  for(int a = 0; a < 100 ; a++)
  {
    //open close the reading
    digitalWrite(Pin_RX, LOW);
    delayMicroseconds(20);
    digitalWrite(Pin_RX, HIGH);
    delayMicroseconds(20);   
    digitalWrite(Pin_RX, LOW);
    delayMicroseconds(20);
   
    //getting value
    anVolt_input[a] = analogRead(Pin_Analog);
 
  } 

 //sliding average filter, the loop will return 51 averaged value
 for (int i=0; i< 50 ; i++){

   anVolt_output[i]=0;
   //sum the value on the range specified, here on 16 samples
   for (int j=0; j< 15; j++){
     anVolt_output[i]=anVolt_output[i]+anVolt_input[i+j];
     }
   //calcul of the sliding average = sum/(number of sample)
   anVolt_output[i]=anVolt_output[i]/16;
   
   //convert the sliding average to cm
   cm=(anVolt_output[i])*1.27;
   
   /*remove false reading when you are too close, below 20cm
   when too close from the sensor, the reading goes really high above 300cm
   in this case my sensor detect the ground at 300cm and when it gives me
   a much higher value it s only when I am too close from the sensor
   so, if value above 350cm, report 20cm...
   a bit shity but cant think of something else
   */

   if (cm>300){
     cm=20;
   }
     
     
   //print the result
   Serial.print(cm);
   Serial.print(" cm ------- ");
   Serial.println();
   delay (10);
   
 }
 
 
}
jakethemice
 
Posts: 17
Joined: Thu Dec 24, 2009 11:49 am

Re: filtering noise with arduino coding for Maxsonar sensor

by jakethemice on Mon Feb 08, 2010 12:29 pm

Hi everybody,

so here is the answer from Bob at Maxbotix. I think he would be ok that I copy paste his answers about what could be the cause of incorrect range readings and also what would be the best way to filter !! So here is the answer :


Causes of Incorrect range readings (with a properly installed and properly powered sensor)
External acoustic noise sources can cause false range readings that are too close. Soft or angled soft targets can be missed occasionally. That is, there must be a return of sufficient amplitude for detection and if enough energy is not returned then the target is missed. This missed target issue can be exacerbated up close, where the returning energy must be detected in the presence of very high energy ring-down, so soft targets, or off axis targets, may not be detected easily.

Filtering in General
For filtering, do not average. Do not average. Do not average. (Did I say, do not average?) We have a lot of internal filtering in our product and if you get back readings that you feel must be filtered, you must not use those readings. Instead of throwing out the incorrect range values, averaging just groups the readings all together and, with our products, will almost always provide incorrect data.

The filtering that works best, is either a Mode filter or a Median filter. (Did I forget to say, "Do not average the readings"?)

Mode Filter
The mode filter would take the largest group of the same readings out of a set of readings. The simplest mode filter is simple as this question. Do the last two readings agree, (with in a certain distance, and the certain distance depends upon how much variation one expects in the actual use)? If so, use the readings, otherwise throw away to oldest reading, and compare the next reading with the most current reading and do this again. Obviously with very noisy data, one might have to do a larger group of readings, but for most this works very well.

Median
The median filter would take the last group of readings and first sort them and then pull out the center reading. Here one might take three or more readings (up to say about 11 for people sensing) and after sorting the readings in order of range, pull out and use only the middle (median) reading.


I chose to made a median filter, here is the code :


Code: Select all | TOGGLE FULL SIZE
const int Pin_Analog = 1;
int Pin_RX = 3;

long cm, w, median ;
long middle, samples;

//analog signal entry and filtered output signal
long reading[255];

void setup() {

  Serial.begin(9600);
 
  //declaration des entrées
  pinMode(Pin_Analog, INPUT);
  pinMode(Pin_RX, OUTPUT);
 
  //number of samples (has to be odd number)
  samples=11;
 
 }

void loop() {
 
 
 
  // fill the array with N number of samples
  for (int k=0; k< samples; k++){
   
    //open close the reading

    digitalWrite(Pin_RX, HIGH);
    delayMicroseconds(20);   
    digitalWrite(Pin_RX, LOW);
    delayMicroseconds(20);
   
    //read the sensor analog output and stock the value in the array
    reading[k]=analogRead(Pin_Analog);

    }




  //sort the value of the array
  for (int i=0; i< samples-1; i++){
    for (int j=i+1; j< samples; j++){
      if (reading[i] > reading[j]){
     
        w = reading[i];
        reading[i] = reading[j];
        reading[j] =w;
        }
      }

  }
 
  //find the middle value of the sample   
  middle=(samples+1)/2;
  median = reading[middle];
 
 
  // place the median value as the output value 
  cm = (median/2)*2.54;


Serial.print(cm);
Serial.print(" cm");
Serial.println();

}

jakethemice
 
Posts: 17
Joined: Thu Dec 24, 2009 11:49 am

Re: filtering noise with arduino coding for Maxsonar sensor

by adafruit on Mon Feb 08, 2010 1:31 pm

this is great info! thanks for following up
User avatar
adafruit
 
Posts: 11711
Joined: Thu Apr 06, 2006 4:21 pm
Location: nyc

Re: filtering noise with arduino coding for Maxsonar sensor

by suj10 on Mon Mar 21, 2011 9:30 pm

Hello,
I understand the merits of median and mode filtering discussed above. I am attempting to use a mode filter, which happens to be the ideal filtering for my application. I am using this article http://en.wikipedia.org/wiki/Mode_(statistics) to get a preliminary code started. According to the link, a sorting function is needed first. The working code below prints an array of 10 samples from the Ultrasonic sensors and then sorts the samples.

Code: Select all | TOGGLE FULL SIZE
int frontUSPin = 8;
int arraysize = 10;  // Size of Array to collect samples
int rangevalue[10] = {0}; Declaring the above Array

void setup() {
pinMode(frontUSPin, INPUT);
Serial.begin(9600); //This opens up a serial connection
}
void loop()
{
for(int i = 0; i < arraysize; i++) // Collect 10 readings and store in array
{
rangevalue[i] =analogRead(frontUSPin);
delay (100); // time delay between collection of each reading withing array
}

isort(rangevalue, arraysize); //sort function
   Serial.print("sorted ");
   printArray(rangevalue, arraysize);
   Serial.println(); // print sorted values
}   //end of loop

//*********************************************************************************
// sort function
void isort(int *a, int n)//  *a is an array pointer function
{for (int i = 1; i < n; ++i)
{int j = a[i];
 int k;
 for (k = i - 1; (k >= 0) && (j < a[k]); k--)
{a[k + 1] = a[k];}
 a[k + 1] = j;}
}
//***********************************************************************************
//function to print array values
void printArray(int *a, int n)
{for (int i = 0; i < n; i++)
  {Serial.print(a[i], DEC);
   Serial.print(' ');}
   Serial.println();
 }


I would like to incorporate bob's explanation in my code.
Mode Filter
The mode filter would take the largest group of the same readings out of a set of readings. The simplest mode filter is simple as this question. Do the last two readings agree, (with in a certain distance, and the certain distance depends upon how much variation one expects in the actual use)? If so, use the readings, otherwise throw away to oldest reading, and compare the next reading with the most current reading and do this again. Obviously with very noisy data, one might have to do a larger group of readings, but for most this works very well.


I would greatly appreciate any help. Thank you!
suj10
 
Posts: 3
Joined: Sun Mar 20, 2011 5:48 pm

Re: filtering noise with arduino coding for Maxsonar sensor

by adafruit_support_bill on Tue Mar 22, 2011 5:56 am

The method described in the Wikipedia article is appropriate for analysis of static data sets. For real-time mode-filtering, the method described by Bob of Maxbotix will work better.

Basically it is this:

Choose a number 'N' which will determine the sample size of your filter.
Collect 'N' readings.

Start Loop:
Take a new reading.
If it agrees with the last 'N' readings (within some threshold) Use it.
Discard the oldest reading,
End Loop:
User avatar
adafruit_support_bill
 
Posts: 30838
Joined: Sat Feb 07, 2009 10:11 am