sábado, 11 de enero de 2014

Arduino - How to improve the realiability of sensors readings

Some days ago, as part of an obstacle avoiding robot project, I was trying to identify nearby objects using an ultrasonic distance sensor in order to turn right or left to avoid them.

In theory, this was a pretty straight forward task... but when I coded the described behavior, the robot started turning when it shouldnt. I looked at the code and everything looked fine, the only possible reason was that the robot thought that there was an object when the was no object.

I connected the arduino to the computer and started printing the distance sensor readings to the Arduino's IDE serial monitor. I was using 20cms as the distance where the robot should stop and turn in order avoid an object. So, I placed an object at 40cms and started watching the readings through the serial monitor, and I noticed that I was getting weird values. For example, I was getting:

39 40 41 3 39 4 42 42 3000 2800 40 41 38 2 40

I was aware that sensors are not accurate things because they are affected by multiple variables (quality of its construction, electrical noise, enviromental changes, etc), I was expecting values near the real value, for example if the real value was 40cm, I was expecting values between 35 and 45 maybe... but I never expected outliers that far from the real value.

Probably the better thing to do is to buy a sensor with the quality needed for the project, and also do everything you can to reduce the electrical noise, however no matter what you do, you wont get exact values on every reading. So, what can you do to improve the realiability of your sensors so they dont mess your program's logic?

I found that getting multiple samples and processing them sustantially improves the accuracy of the sensors readings.

So, every time that we want to measure the distance to an object, we are going to get multiple sample and get only one value from them.

Your first thought might be "lets average the numbers and we'll probably get near to the real value". Well, this is a BAD IDEA.

Lets say that for each measure we use 7 sensor readings and we get:

42 40 3000 41 3 38 43

The average is: 42 + 40 + 3000 + 41 + 3 + 38 + 43 = 3207 / 7 = 458cm (aprox)

A better way of processing the 7 samples is to use the median method. This is a very simple method, where you first sort all the values and then you use the value in the middle position, so half of the values are lower and half of the values are higher.

Lets apply the median method to this set of 7 samples:

First we sort it, getting : 3 38 30 41 42 43 3000

The result of the median method is 41, which is really close to the real value (which was 40).

I applied this method to the obstacle avoiding robot and I had really good results. Its important to notice that even with data filtering, you wont get the exact value, so you need to use "safe thresholds" in your program's logic. For example Im using 20 cms as the value where the robot should stop and turn in order to avoid an object. If there is an object at 19cms and the sensor returns a distance of 21cm, there is no problem, on the next iteration of the loop the robot will be closer to the object and I'll get a distance lower 20cms, so it will stop and turn before hitting the object.

There is an existing library for Arduino that allows us to easily get the median value of a set of samples. You can get it from here.

This is an example of how to use it:

#include 
#include 

//Define the pins for the ultrasonic sensor's echo and trigger pins 
#define DISTANCE_ECHO 4
#define DISTANCE_TRIGGER 7
#define MAX_ULTRASONIC_DISTANCE 200

NewPing sonar(DISTANCE_TRIGGER, DISTANCE_ECHO, MAX_ULTRASONIC_DISTANCE);

void setup()
{
    //Initialize serial communication to send the distance value  to the serial monitor
    Serial.begin(9600);
}

void loop()
{
  int distance_cm;
  distance_cm = getDistanceFromUltrasonicSensor();
  
  Serial.print(distance_cm);
  Serial.print("cm");
  Serial.println();
}

int getDistanceFromUltrasonicSensor(){
  //Create an instance of the RunningMedian class with the number of samples to use
  RunningMedian samples = RunningMedian(7);
  
  //Get the 7 samples
  for(int i= 0; i<5; i++){
      delay(50);
      int pingVal = sonar.ping();
      
      int distance = pingVal / US_ROUNDTRIP_CM;
      if(distance == 0){
        distance = 3000;
      }
     samples.add(distance);
   }   
   //Return the median value
   return samples.getMedian();  
}


Here I applied the median method for an ultrasonic distance sensor, but this applies to any other sensor, for example, temperature sensors, compass sensor, etc.

No hay comentarios:

Publicar un comentario