- Don't trust your servo, you need to find what values to send to the servo to get the desired angle.
- 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.
- The timing for the Sharp infra red sensor is a maximum of around 47ms (see here).
- 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.
Nice blog could you please "follow" my blog I'm "following" your blog!:)
ReplyDeleteI’ve been surfing online more than 5 hours today, yet I never found any interesting article like yours without a doubt. It’s pretty worth enough for me. Thanks... Wltoys RC car
ReplyDelete