Obstacle Avoidance robot
Hi all, in this article I will be teaching you how to make an obstacle avoidance robot. Basically, we will measure how distant are the objects in front of the robot using a distance sensor and make a decision to change the path. You will require following things:-
- Raspberry Pi board
- Ultrasonic distance sensor (HC-SR04)
- Jumper wires
- Chassis, motors, tyres
- Motor control IC (L293d)
I will divide the article into three parts:-
- Measure the distance
- Control the motors
- Object avoidance based on above two
Measuring the distance
As you may recall from my previous article where I discussed GPIO pins and how to read/write data from them. I will build upon that and show you how to measure how far away the objects are from the robot. First let me give you a quick brief about the HC-SR04 distance sensor IC.
HC-SR04 is an ultrasonic (US) distance sensor i.e. it uses US waves to measure the distance. As you can see from the figure, HC-SR04 has 4 pins. The pins Vcc and Gnd are to be connected to +5 volts and Gnd respectively. Trig pin is input pin which is to be made high to send US wave. Echo pin is output pin which goes high for a period of time equal to the time taken for the US wave to return back to the sensor. For more details check this site.
Following is the schematic diagram you will require to read the distance from the distance sensor.
Following is the code-
import RPi.GPIO as GPIO
import time
import numpy as np
import sys# define the GPIO pins for trigger and echo
TRIG = 17 # pin 11
ECHO = 22 # pin 15# define the pins as input/output
GPIO.setup(TRIG, GPIO.OUT)
GPIO.setup(ECHO, GPIO.IN)
Here we are importing the necessary libraries. Also, we have defined and initialized the GPIO pins of our raspberry Pi that we are going to use as trigger and echo.
def get_distance(prev_values):
GPIO.output(TRIG, True)
time.sleep(.0001)
GPIO.output(TRIG, False)
while GPIO.input(ECHO) == False:
start = time.time()
while GPIO.input(ECHO) == True:
end = time.time()
try:
sig_time = end - start
new_distance = sig_time /0.000058
prev_values = prev_values[1:] + [new_distance]
distance = np.mean(prev_values) # taking moving average
print(" Distance is {} cm".format(distance))
return prev_values, distance
except:
new_distance = 10 # considereing new distance is 20cm or more
prev_values = prev_values[1:] + [new_distance]
distance = np.mean(prev_values) # taking moving average
print("object far away")
return prev_values, distance
The function get_distance() return the measured distance from any object. The way this works is that we initially make the TRIG as high for 100 micro seconds. Then measure the amount of time for which the ECHO pin remains high i.e. detect the US wave based on start and end time stamps. Then we measure distance = speed * time. Where, speed is 330 m/s as the speed of US wave. In the next line, instead of taking the distance directly I am taking a moving average of distance based on last 4 values. This makes the distance more robust to sudden fluctuations. Note that we also have to maintain a list of previous values.
Now that you have learnt how to read the distance, let us discuss how to control the motors.
Controlling the Motors
Here we will discuss how to control the motors and in turn control the direction of movement of our robot. But before we jump to controlling of motors, let us also understand why we require L293D IC.
L293D IC — As you are aware of that the motors require a lot of power to operate. But our Raspberry Pi board runs on very small values of current and therefore, will not be able to provide sufficient current/power to the motors. In fact, if we connect our motors directly to the GPIO pins, we may end up damaging our board. This is where L293D Ic comes to our rescue. Basically what it does is that we will be able to control the flow of current in to the motor through the GPIO pins but the current wont come directly from these pins rather it would come from the H-Bridge transistors connected to external source.
For controlling the motors we have to make connection using the schematic diagram shown below. As you can see, we also require an external source at +12 volts that will provide the necessary power to operate the motors.
Before we jump to the code, let us understand how we can control the direction of the robot. As you can see from the schematic diagram above, each motor is controlled by two GPIO pins, where at a time only one of the pin will be high or both are low. This is what happens by changing the input of these pins:-
- Both pin are low- Motor wont move
- First pin is high, second is low - Motor will move in clockwise direction
- Second pin is high, first is low - Motor will move in counter clockwise direction.
Now let us understand how we can control the direction of robot based on the direction of motors:-
- Move forward- Right side motor clockwise, left hand side motor anti-clockwise.
- Move Backward- Right hand side motor anti-clockwise, left hand side motor clockwise.
- Move Right- Right side motor anti-clockwise, left hand side motor also anti-clockwise.
- Move left- Right hand side motor clockwise, left hand side motor also clockwise.
Now that we have understood how to control the direction of robot using direction of motors, let us jump to the code.
#choose the GPIO pins
fwd1 = 23 # pin 16
bwd1 = 24 #pin 18
fwd2 = 16 # pin 36
bwd2 = 20 # pin 38# declare selected pin as output pin
GPIO.setup(fwd1, GPIO.OUT)
GPIO.setup(bwd1, GPIO.OUT)
GPIO.setup(fwd2, GPIO.OUT)
GPIO.setup(bwd2, GPIO.OUT)sleep_time = .2
Here we have just defined the pins that we are going to connect to the motors.
def forward():
GPIO.output(fwd1, GPIO.HIGH)
GPIO.output(bwd1, GPIO.LOW)
GPIO.output(fwd2, GPIO.HIGH)
GPIO.output(bwd2, GPIO.LOW)def backward():
GPIO.output(fwd1, GPIO.LOW)
GPIO.output(bwd1, GPIO.HIGH)
GPIO.output(fwd2, GPIO.LOW)
GPIO.output(bwd2, GPIO.HIGH)def left():
GPIO.output(fwd1, GPIO.HIGH)
GPIO.output(bwd1, GPIO.LOW)
GPIO.output(fwd2, GPIO.LOW)
GPIO.output(bwd2, GPIO.HIGH)def right():
GPIO.output(fwd1, GPIO.LOW)
GPIO.output(bwd1, GPIO.HIGH)
GPIO.output(fwd2, GPIO.HIGH)
GPIO.output(bwd2, GPIO.LOW)def stop():
GPIO.output(fwd1, GPIO.LOW)
GPIO.output(bwd1, GPIO.LOW)
GPIO.output(fwd2, GPIO.LOW)
GPIO.output(bwd2, GPIO.LOW)
The above defined functions, when called, will set the pins in a way that the robot will move in the predefined direction.
Object Avoidance
We have come to the final section of this tutorial where we will learn how to detect an object and change direction of robot accordingly.
if __name__ == "__main__":
try:
prev_values = [0,0,0,0,0]
while True:
prev_values, distance = get_distance(prev_values)
if distance <= 4:
backward()
time.sleep(1)
right()
time.sleep(.5)
else:
forward()
time.sleep(.1)
except KeyboardInterrupt:
stop()
GPIO.cleanup()
sys.exit()
First we declare a list of previous values, where we maintain last 5 distances measured by the sensor. This is required to calculate moving average of the distance. Then, we call the get_distance() function to get the distance. Based on the distance value, we can decide the direction of the robot. For e.g. in our case, when we detect the distance to be less than 4 cm, we move the robot backward and then turn it to right direction. For distances more than 4 cm, we move the robot in forward direction.
Now you know how, based on detecting that an object is close by, you can change the direction of the robot and avoid the obstacles. Go ahead and experiment with what you have just learned and you may come up with some innovative ideas to use it instead of object avoidance.