Arduino Obstacle Avoidance Robot

 

by Eileen A.

(with a little help from Cody)


Last March Break, I read a book that changed my life. I ran out and got an Arduino immediately and the Raspberry Pi was not far behind. After a few months of experimenting, and watching YouTube videos of kids building Obstacle Avoidance Robots (OAR), we thought it looked like a good first project. We call it Bobby OAR. An OAR is an autonomous robot that rolls around and avoids obstacles by using sensor input. Then, based on programming, finds an alternate path forward, avoiding obstacles along the way. We had most of the components from various kits, so only a chassis and wheels needed to be ordered. Or was there something else?

Parts

  • Parts Arduino clone (Queale)
  • Ultrasonic sensor
  • Servo motor
  • jumper cables
  • h-bridge
  • dc battery pack & batteries
  • 9V battery clip & battery
  • mini breadboard
  • chassis (BCRobotics)
  • wheels
  • dc motors with brackets and hardware
  • various stand-offs*
  • nuts and bolts*
  • furniture repair hardware* (Canadian Tire)
  • swivel wheel
  • closet bracket
  • hot glue
  • double sided tape and electrical tape

The assembly of the chassis was mostly done by trial and error and required some imagination, digging through our small parts containers in the garage, shopping at Canadian Tire and finally breaking out the Dremel tool. As this was our first project we did have to get a few basic supplies.*

The robot has three main subsystems: the wheels run by two dc motors, the ultrasonic sensor and the servo motor.

1. DC Motors:

the motors and brackets
The dc motors and wheels were attached to the bottom part of the chassis and needed to be connected via an h-bridge to the Arduino. The following video provides a good explanation of what each pin on the h-bridge does. It also explains how to connect the h-bridge to the Arduino and to a separate power supply.

https://www.youtube.com/watch?v=5nDaHJqruq0 (Arduino Tutorial #7, 18:33 min)

The following schematic shows how to connect one motor to one side of the h-bridge. The other side of the h-bridge is wired in the same way, in order to run the second motor.

hbridge

/* DC Motor Test: The following code will make the robot move forward for 4 sec, stop for 1 sec, turn left for 0.4 sec, stop for 1 sec, turn right for 0.4 sec, stop for one sec, go backward for 2 sec, stop for 1 sec, and repeat. */ 

const int MotorR1 = 6; //forward right motor 
const int MotorR2 = 7; // backward right motor 

const int MotorL1 = 4; // forward left motor 
const int MotorL2 = 5; // backward left motor 
void setup() {  
  pinMode(MotorR1, OUTPUT);  
  pinMode(MotorR2, OUTPUT);  
  pinMode(MotorL1, OUTPUT);  
  pinMode(MotorL2, OUTPUT);  

  digitalWrite(MotorR1, LOW);  
  digitalWrite(MotorR2, LOW);  
  digitalWrite(MotorL1, LOW);  
  digitalWrite(MotorL2, LOW); 
} 

void loop() {  
  //go forward  
  digitalWrite(MotorR1, HIGH);  
  digitalWrite(MotorR2, LOW);  
  digitalWrite(MotorL1, HIGH);  
  digitalWrite(MotorL2, LOW);  
  delay(4000); 

 //stop  
  digitalWrite(MotorR1, LOW);  
  digitalWrite(MotorR2, LOW);  
  digitalWrite(MotorL1, LOW);  
  digitalWrite(MotorL2, LOW);  
  delay(1000);  

 //turn left  
  digitalWrite(MotorR1, HIGH);  
  digitalWrite(MotorR2, LOW);  
  digitalWrite(MotorL1, LOW);  
  digitalWrite(MotorL2, HIGH);  
  delay(400);  

 //stop  
  digitalWrite(MotorR1, LOW);  
  digitalWrite(MotorR2, LOW);
  digitalWrite(MotorL1, LOW);  
  digitalWrite(MotorL2, LOW);  
  delay(1000);  
 //turn right  
  digitalWrite(MotorR1, LOW);  
  digitalWrite(MotorR2, HIGH);  
  digitalWrite(MotorL1, HIGH);  
  digitalWrite(MotorL2, LOW);  
  delay(400);  

 //stop  
  digitalWrite(MotorR1, LOW);  
  digitalWrite(MotorR2, LOW);  
  digitalWrite(MotorL1, LOW);  
  digitalWrite(MotorL2, LOW);  
  delay(1000);  

 //go backwards  
  digitalWrite(MotorR1, LOW);  
  digitalWrite(MotorR2, HIGH);  
  digitalWrite(MotorL1, LOW);  
  digitalWrite(MotorL2, HIGH);  
  delay(2000);  

 //stop  
  digitalWrite(MotorR1, LOW);  
  digitalWrite(MotorR2, LOW);  
  digitalWrite(MotorL1, LOW);  
  digitalWrite(MotorL2, LOW);  
 delay(1000); 
} 

2. Ultrasonic Sensor (Ping)

Ultrasonic Sensor Once the motors were working, it was time to add the second sub-system to our robot. The ultrasonic sensor is used to detect obstacles in the robots path. It does this by creating a sound pulse of 40 kHz and then listens to hear the echo. If an echo is detected the travel time of the sound wave in air is divided by 2 and converted into centimetres. The value returned can then be viewed on the serial monitor or sent to the Arduino.

This editor disallows angle brackets in the html tag pre. Don’t forget your ping library… #include

// ----------------------------------------------------
// Example NewPing library sketch that does a ping about 20 times per second. 
// ----------------------------------------------------

#include NewPing.h  // Angle brackets required. 
#define TRIGGER_PIN  12  // Arduino pin tied to trigger pin on the ultrasonic sensor. 
#define ECHO_PIN     11  // Arduino pin tied to echo pin on the ultrasonic sensor. 
#define MAX_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm. 

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance. 

void setup() {  
  Serial.begin(9600); // Open serial monitor at 115200 baud to see ping results. 
} 

void loop() {  
delay(50);           // Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings.  
Serial.print("Ping: ");  
Serial.print(sonar.ping_cm()); // Send ping, get distance in cm and print result (0 = outside set distance range)  
Serial.println("cm"); 
} 
  

3. Servo Motor

We then decided to attach the ultrasonic sensor to a servo motor to increase the area in front and to both sides of the robot, which could be checked for obstacles. We attached a closet bracket to the chassis and then used electrical tape to attach the servo to the bracket. The ultrasonic sensor was then hot-glued to the servo horn.

attach pinger to servo horn

Below is the code that we used to test that the servo would move the ultrasonic sensor from the 90 degree to: 180, 135, 45, and 0 positions. Once again, a library had to be included at the beginning of the code to run the servo motor.

#include Servo.h  //Angle brackets required.

Servo head; 

void setup() {  
head.attach(9);  
head.write(90);  
delay(1000); 
} 

void loop() {  
head.write(90);  
delay(1000);  
head.write(45);  
delay(1000);  
head.write(0);  
delay(1000);  
head.write(45);  
delay(1000);  
head.write(90);  
delay(1000);  
head.write(135);  
delay(1000);  
head.write(180);  
delay(1000);  
head.write(135);  
delay(1000); 
}

Finally, it was time to pull all the code together. This proved to be the most challenging part of the project in many respects. We found some code which seemed to accomplish most of the functions we were interested in, but also, it had functions we didn’t want. Finding the perfect copy and paste solution wasn’t happening for us. We started by removing some code, and then adding other code that would be required, and testing, and repeating. For 2 months we remained unhappy with the results. Finally, last weekend, we found code from Jaidyn Edwards. We tweaked the ifs and elses, subtracted and added functions for motors, and the pinger’s servo, and Yeah! The code below meets our OAR objectives as of March 6, 2016.

#include Servo.h     //Angle brackets required.
#include NewPing.h   //Angle brackets required.

// Added Right DC Motor
const int motorR1 = 6;
const int motorR2 = 7;

// Added Left DC Motor
const int motorL1 = 4;
const int motorL2 = 5;

// Removed Servo leftServo;       //Create Left Servo object
// Removed Servo rightServo;      //Create Right Servo object

#define TRIGGER_PIN  12   //Trigger pin of Ultrasonic sensor connected to pin 6
#define ECHO_PIN     11   //Echo pin of Ultrasonic sensor connected to pin 7
#define MAX_DISTANCE 400 //The maximum distance we want the sensor to look for is 1m

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);  //Create Ultrasonic sensor object
Servo head;

unsigned int time;            //Variable to store how long it takes for the ultrasonic wave to come back
int distance;                 //Variable to store the distance calculated from the sensor
int triggerDistance = 20;     //The distance we want the robot to look for a new path
int fDistance;                //Variable to store the distance in front of the robot
int lDistance;                //Variable to store the distance on the left side of the robot
int rDistance;                //Variable to store the distance on the right side of the robot

void setup() {
/* Removed
 * { 
  leftServo.attach(9);        //Left servo connected to pin 9
  leftServo.write(90);        //Write the neutral position to that servo
  rightServo.attach(8);       //Right servo connected to pin 8
  rightServo.write(90);       //Write the neutral position to that servo
} */
// Added Servo functionality
  head.attach(9);
  head.write(90);

// Added motor initialization  
  pinMode(motorR1, OUTPUT);
  pinMode(motorR2, OUTPUT);
  pinMode(motorL1, OUTPUT);
  pinMode(motorL2, OUTPUT);
  
// Added motor initialization
  digitalWrite(motorR1, LOW);
  digitalWrite(motorR2, LOW);
  digitalWrite(motorL1, LOW);
  digitalWrite(motorL2, LOW);
}

void loop()
{
  scan();                                //Get the distance retrieved
  fDistance = distance;                  //Set that distance to the front distance
  if(fDistance < triggerDistance){       //If there is something closer than 30cm in front of us
    moveBackward();                      // motor Move Backward for a second
    delay(50); 
    moveStop();
    head.write(0);                       // Added Servo turn Right
    scan();
    delay(500);                          // Take a reading
    rDistance = distance;                // Store that to the distance on the right side
    head.write(180);                     // Added Servo Turn left
    scan(); 
    delay(500);                          // Take a reading
    lDistance = distance;                // Store that to the distance on the left side
    head.write(90);                      // Added return servo to 90 degree position
    if(lDistance < rDistance){           // If the distance on the left is smaller than that of the right
      moveRight();                       // Move right for a quarter of a second
      delay(250);
      moveForward();                     //Then move forward
    }
    else {
      moveLeft();                       // Added the motor Move left
      delay(250);
      moveForward();                     //If the left side is larger than the right side move forward
    }
  }
  else{
    moveForward();                       //If there is nothing infront of the robot move forward
  }
}

void scan(){
  time = sonar.ping();                  //Send out a ping and store the time it took for it to come back in the time variable
  distance = time / US_ROUNDTRIP_CM;    //Convert that time into a distance
  if(distance == 0){                    //If no ping was recieved
    distance = 100;                     //Set the distance to max
  }
  delay(10);
}

/* Removed
 * void moveBackward(){
  rightServo.write(180);
  leftServo.write(0); 
}*/

/*void moveForward(){
  rightServo.write(0);
  leftServo.write(180);
}*/

/*void moveRight(){
  rightServo.write(0);
  leftServo.write(0);  
}*/

/*void moveLeft(){
  rightServo.write(180);
  leftServo.write(180);
}*/

/*void moveStop(){
  rightServo.write(90);
  leftServo.write(95); 
}*/

// Added motors go forward
void moveForward(){
  digitalWrite(motorR1, HIGH);
  digitalWrite(motorR2, LOW);
  digitalWrite(motorL1, HIGH);
  digitalWrite(motorL2, LOW);
  }

// Added motors go backward
void moveBackward(){
  digitalWrite(motorR1, LOW);
  digitalWrite(motorR2, HIGH);
  digitalWrite(motorL1, LOW);
  digitalWrite(motorL2, HIGH);
  }

// Added motors stop
void moveStop(){
  digitalWrite(motorR1, LOW);
  digitalWrite(motorR2, LOW);
  digitalWrite(motorL1, LOW);
  digitalWrite(motorL2, LOW);
  }

// Added motors left turn
void moveLeft(){ 
  digitalWrite(motorR1, HIGH);
  digitalWrite(motorR2, LOW);
  digitalWrite(motorL1, LOW);
  digitalWrite(motorL2, HIGH);
  }

// Added motors right turn 
void moveRight(){
  digitalWrite(motorR1, LOW);
  digitalWrite(motorR2, HIGH);
  digitalWrite(motorL1, HIGH);
  digitalWrite(motorL2, LOW);
  }

Here is some video, taken during one of our tests in January.