Monday, 6 June 2011

Sharp Infra Red Sensor Timing Tests

This is a difficult one to write up and has been abit of a journey to get this far. I am using the 3.3V as the ADC reference voltage, my equation for distance (see last post) and a capacitor across the sensor power rails. What got me thinking about the sweep timings is this webpage. I have learnt alot doing this,

  1. Don't trust your servo, you need to find what values to send to the servo to get the desired angle.
  2. When using integers the sign flips when the value goes above 32,767 (see this), which cause problem if you try to average more than about 32 readings. This can be fixed by using longs, but I just stuck with integers.
  3. The timing for the Sharp infra red sensor is a maximum of around 47ms (see here).
  4. There is a limited amount of RAM on the Arduino and I hit the limit. I found that if you try to assign more memory than is available on the Arduino it fails to run. If you try to access memory that hasn't been assign the Arduino will stop running at that line.
I tried alot of different delays to allow for the servo to move to the next reading position, I tried offsetting the results from the sweep back to zero and finally the number of ADC reads I averaged. I read the source code for the Hyper Squirrel robot by John Palmisano of Society of Robots, which can be found here. I liked the idea of setting a resolution and total sweep angle, so I added this to my code. For all my tests I used a 180 degree sweep with a resolution of 90.

So a bad sweep looks like this,


The two lines correspond to the left-bound sweep and the right-bound sweep. This test used a delay of 10ms for the servo to move and the I took 6 readings of the sensor.

My final good sweep looks like this,


For this test I used a delay of 10*(180.0/resolution), so as the resolution reduces the delay increases. I added a 47ms delay to allow the sensor to update before I took 32 readings of the sensor. Finally I offset the readings on the right-bound sweep by one in the array, this means that there is no reading at 0 degrees.

The code I used to do my tests is given below.

#include <Servo.h>

#define resolution 90 //larger number means more accurate, but also more ram used
#define totalangle 180 //total angle that it turns 
#define offset 1       //result offset for second sweep

#define scanservocentre 79 //the scanner servo centre location
#define scanservomin 3 //the pwm value to give an angle of 0 degrees
#define scanservomax 152 //the pwm value to give an angle of 180 degrees

Servo scanner;          // create servo object to control the sweeping servo 
int distance1[resolution+offset+1];    // store the distance from the infra red sensor
int distance2[resolution+offset+1];    // store the distance from the infra red sensor

int dt=10*(180.0/resolution);  //wait time for servo to move
int n=32;   //number of readings of the sensor 32 is the max before the value in the int goes negative

void setup()
{
  scanner.attach(6);    // attaches the servo on pin 6 to the servo object 

  Serial.begin(115200); // sets the speed of the serial connection to the computer

  scanner.write(mapscanner(90-totalangle/2));    // sets the scanner servo to the start of sweep

  analogReference(EXTERNAL);  // Set the ADC reference voltage to external (read last post before doing this line)

}

// This runs continuously on the arduino
void loop() {

  // 10 second countdown to start
  for(int i=10;i>0;i--) {
    Serial.println(i);  // print to the computer when the USB cable is connected
    delay(1000);        // 1 second delay
  }

  //Scan left and right
  scan();

}


void scan() { // 180 degree scan in front of the car

  Serial.println("Scanning...");

  int reading;       // used for taking the averages
  float dist;        // used for calculating the distance in cm

  //read in first direction  
  for(int i=0;i<resolution+1;i++) {
    scanner.write(mapscanner(i*(totalangle/resolution)));  //set the servo position
    //Serial.println(scanner.read()); //for debugging the code
    delay(dt);           //wait until done

    delay(47); //wait 47ms for the sensor to refresh
 
    reading=0;                // needs to start at 0 and then n readings will be added to it
    for(int i=0;i<n;i++) {
      reading+=analogRead(0); // read from analog input 0
      delay(1);                          // wait one millisecond
    }
    reading/=n;           // finally average the n reading 
    dist=12214.16*pow(reading,-1.08);  // this calculates the distance 
    if(dist<10) { // the infra red sensor is unreliable below 10 cm so set it to 0cm
      dist=0;
    }
    if(dist>80) { // the infra red sensor is unreliable above 80 cm so set it to 80cm
      dist=80;
    }
    distance1[i]=(int)dist;  // put the value dist in to the distance array which contains the actual distances in cm
  }

  delay(dt); //a slight pause before starting the sweep back

  //reading in the second direction  
  for(int i=0;i<resolution+1;i++) {
    scanner.write(mapscanner(totalangle-(i*(totalangle/resolution))));  //set the servo position
    //Serial.println(scanner.read()); //for debugging the code
    delay(dt);           //wait until done

    delay(47); //wait 47ms for the sensor to refresh
 
    reading=0;                // needs to start at 0 and then n readings will be added to it
    for(int i=0;i<n;i++) {
      reading+=analogRead(0); // read from analog input 0
      delay(1);                          // wait one millisecond
    }
    reading/=n;           // finally average the n reading 
    dist=12214.16*pow(reading,-1.08);  // this calculates the distance 
    if(dist<10) { // the infra red sensor is unreliable below 10 cm so set it to 0cm
      dist=0;
    }
    if(dist>80) { // the infra red sensor is unreliable above 80 cm so set it to 80cm
      dist=80;
    }
    distance2[resolution+offset-i]=(int)dist;  // put the value dist in to the distance array which contains the actual distances in cm
  }

  //print out the distances for each sweep direction
  for(int i=0;i<resolution+1;i++) {
    Serial.print(distance1[i]);
    Serial.print(", ");
    Serial.println(distance2[i]);  
  }

}

//map the desired angle to the value required to move the servo to that angle
int mapscanner(int angle) {
  //Serial.println(angle);  //for debugging the code
  float u=angle/180.0;
  //Serial.println(u);  //for debugging the code
  return(scanservomin+(u*(scanservomax-scanservomin)));
}

I think this is right and will give an accurate sweep. Next I will modify my car code and see if these improvements help.

EDIT: As with the last post I am using 3.3V as the reference not 3V.

1 comment:

  1. Nice blog could you please "follow" my blog I'm "following" your blog!:)

    ReplyDelete