Ultrasonic RVR+/RVR
Make Your RVR+/RVR Ultrasonic 🦸♂️
This project turns RVR+/RVR into an autonomously driving robot that avoids collisions with walls and other objects 💥. In this tutorial, we'll attach HC-SR04 sensors to the front of the RVR+/RVR that will send out waves and time how long the waves take to return (after bouncing off of an object in front of the RVR+/RVR). When the time to return dips below a certain point, we'll turn the RVR+/RVR to avoid the obstacle.
If you haven't already connected your Raspberry Pi to your RVR+/RVR, go ahead and check out our helpful docs on how to get up and running before attempting to follow along with this example (doing so will get most of the hardware setup completed already, as well 😉).
This intermediate tutorial should take about 30 min to complete (if you've already set up your Raspberry Pi and connected it to the RVR+/RVR).
Hardware Setup
Project Materials
In addition to the materials necessary to connect the Raspberry Pi to the RVR, we'll need:
A short USB-to-Micro USB cable (to allow the RVR+/RVR to provide the Raspberry Pi with power from its USB port)
One RVR+/RVR
Two HC-SR04 sensors
Two 330 Ohm resistors (most wattage ratings should be sufficient)
Two 470 Ohm resistors (most wattage ratings should be sufficient)
Two male-male breadboard cables
Five female-female breadboard cables
Ten female-male breadboard cables
Wiring Diagram
Note: The HC-SR04 ultrasonic range finder is very simple to use, however the signal it outputs needs to be converted from 5V to 3.3V so as not to damage the Raspberry Pi ⚡ Hence, the need for resistors.
The code is already configured to use the pins specified in the diagram. However, if you'd like to use other pins, please be sure to update the pin assignments in the pin declarations 👩💻.
right_trigger = 20
right_echo = 21
left_trigger = 23
left_echo = 24
For a complete reference of the pin layout on a Raspberry Pi 3, see here.
Mounting Suggestion (your design can be much prettier 🌈)
You'll want to mount the Ultrasonic Range Finders on either side of the front of your RVR+/RVR (without them touching the treads, we wouldn't want them going flying! 🛸). You'll then just need to find a secure (enough) way to mount both your Raspberry Pi and your breadboard on top of your RVR+/RVR 🎢. Here is what we came up with:
Software Setup
If you've gone through our Raspberry Pi Set Up Docs, you will have already downloaded/cloned the needed code for this example, in addition to installed the dependencies necessary for your Raspberry Pi to communicate with your RVR+/RVR. Because the Raspberry Pi examples we have set up rely on files located throughout the file structure (not just the file we walk through below), we recommend that you use the version of the file included in those you downloaded/cloned from our GitHub, rather than trying to write the file from scratch as we walk through it together below (with that said, feel free to play with/add to the version you downloaded; we always love to see how creative our users are 🥰).
You can find the file for this example at /projects/ultrasonic_rvr/ultrasonic_rvr.py, in the file structure you downloaded; we'll repeat this later as we talk about how to actually run the code, but figured you might like to have the file in front of you as you follow along here...
Let's get started with our imports. Most of these should be pretty familiar from other examples. Line 3 of the code below is what allows us to pull from elsewhere in the file structure. It references files that are two layers above (../../), as well as those within folders at that level, and appends the absolute path of the directory with the name after from in the imports on lines 7-9:
import sys
import os
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../../')))
import RPi.GPIO as GPIO
import asyncio
from sphero_sdk import SpheroRvrAsync
from sphero_sdk import SerialAsyncDal
import time
Next, we'll initialize our global variables, including a specific asynchronous loop 🔄 and defining rvr 🤖 so that we can use it to call commands off of, later on. We have the ever-familiar setup of creating an asynchronous loop and a rvr object, but we have a few newer things here, as well. The triggers and echos are going to be the pins we'll use and we let the Raspberry Pi know about that by feeding it through GPIO:
loop = asyncio.get_event_loop()
rvr = SpheroRvrAsync(
dal=SerialAsyncDal(
loop
)
)
GPIO.setmode(GPIO.BCM)
right_trigger = 20
right_echo = 21
left_trigger = 23
left_echo = 24
GPIO.setup(left_trigger, GPIO.OUT)
GPIO.setup(left_echo, GPIO.IN)
GPIO.setup(right_trigger, GPIO.OUT)
GPIO.setup(right_echo, GPIO.IN)
Next, we'll create functions for how close something is to the RVR+/RVR on either side, based on the time elapsed between an output from the ultrasonic range finders and an input to the ultrasonic range finders 🔊:
def distance_left():
GPIO.output(left_trigger, True)
time.sleep(0.00001)
GPIO.output(left_trigger, False)
start_time = time.time()
stop_time = time.time()
while GPIO.input(left_echo) == 0:
start_time = time.time()
while GPIO.input(left_echo) == 1:
stop_time = time.time()
time_elapsed = stop_time - start_time
distance = (time_elapsed * 34300) / 2
return distance
def distance_right():
GPIO.output(right_trigger, True)
time.sleep(0.00001)
GPIO.output(right_trigger, False)
start_time = time.time()
stop_time = time.time()
while GPIO.input(right_echo) == 0:
start_time = time.time()
while GPIO.input(right_echo) == 1:
stop_time = time.time()
time_elapsed = stop_time - start_time
distance = (time_elapsed * 34300) / 2
return distance
Last, but certainly not least, we'll set up our main function, which is the heart and soul of our program 💓, letting it know when to execute which functions. We start by waking the RVR+/RVR so that we can ask it to do mechanical things; if the RVR+/RVR is in soft sleep, we can still ask it for information about itself, but we can't ask it to do anything. We then use reset_yaw to tell the RVR+/RVR that whatever direction it is pointing when it wakes up should be considered 0° (forward).
This code also controls the turns that happen when either side detects that something is too close. If the distance on a side gets below 35, we use the raw_motors method to turn the RVR+/RVR to avoid the obstacle and also print an alert to notify the user that we are doing so. Finally, if both sensors are happy, we just drive straight ahead using the drive_with_heading method:
async def main():
await rvr.wake()
await rvr.reset_yaw()
await asyncio.sleep(.5)
while True:
dist_r = distance_right()
dist_l = distance_left()
await asyncio.sleep(.05)
print('Measurements are {0} cm right and {1} cm left'.format(dist_r, dist_l))
if dist_r < 35:
while dist_r < 35:
await rvr.raw_motors(2,255,1,255)
dist_r = distance_right()
await asyncio.sleep(.05)
print('turning right')
await rvr.reset_yaw()
elif dist_l < 35:
while dist_l < 35:
await rvr.raw_motors(1,255,2,255)
dist_l = distance_left()
await asyncio.sleep(.05)
print('turning left')
await rvr.reset_yaw()
elif dist_l >= 35 and dist_r >= 35:
await rvr.drive_with_heading(90,0,0)
try:
loop.run_until_complete(
asyncio.gather(
main()
)
)
except KeyboardInterrupt:
print('Program ended by KeyboardInterrupt')
GPIO.cleanup()
Running the Code!
To run the code, you simply need to go to your terminal, cd into the correct directory (/ultrasonic_rvr) and type:
ultrasonic_rvr.py
but...
If you've taken our advice and decided to use SSH to access your fancy, wireless RVR+/RVR 💁, you'll just need to take a few extra steps to create the connection between your RVR+/RVR and the machine you are SSHing into it with before running the above command.
If you haven't recently grabbed your IP address, you'll need to do that with a monitor/mouse/keyboard attatched to your Raspberry Pi by jumping into the terminal on your Raspberry Pi and typing:
hostname -I
Write down the IP address it spits out and let's jump over to the machine you'll be using to control your RVR+/RVR!
On your remote machine (which must be on the same Wifi network you set your Raspberry Pi to connect to), you'll also want to jump into the terminal, where you'll use the IP address you just grabbed and type:
ssh pi@ipaddress
If it is successful in finding your Raspberry Pi using its IP address (ie if you are on the correct network and typed the correct IP address (and your Raspberry Pi is powered on and has been for at least 15 seconds)), you'll be prompted to type in the password to your Raspberry Pi. As you are doing so, you won't get an indication that the terminal is registering your keystrokes, but that is just to make you feel extra safe, so dangerous hackers can't see how long your password is and steal it 🦹.
If all went to plan with that, you should get a success message and be ready to cd into your /ultrasonic_rvr folder and run:
python3 ultrasonic_rvr.py
Once this is running (ie the command above didn't result in an error), your RVR+/RVR should start doing its thing; stand back and be amazed! Whenever you are ready to stop it, pressing any key should do the trick.
Once you are finished playing, you'll want to make sure you properly shut down your Raspberry Pi. Once you've stopped your RVR+/RVR, you'll want to use Crtl + C to fully kill the program and then you can turn off your Raspberry Pi by running:
sudo shutdown now
Now What?
Try to improve your Ultrasonic RVR+/RVR! Here are some ideas to get you started:
Add a speaker to set off an alarm or to simply announce the RVR's next move when the RVR+/RVR encounters an obstacle.
Have the LEDs on the RVR+/RVR change colors for ranges of how close it is to an obstacle.