Categories
Sensors

Ultrasonic Sensor for the Jetbot

The Nvidia Jetbot offers an excellent opportunity to explore the Nvidia Jetson Nano, do some nice 3D printing and learn AI at the same time.

I managed to get the Jetbot to recognize persons and follow them along using AI in no time, but I ran into difficulty trying the Jetbot to learn about it’s environment. I could not get the Jetbot to learn the difference between the floor and the wall no matter what I tried. That took away a bit of the fun for me and thus I decided to add an ‘old fashioned’ ultrasonic sensor. I attached it to the front of the Jetbot, using a 3D printed interface piece and combined it with a Adafruit M0 feather which I stuck to the bottom and interfaced to the Jetson Nano via I2C.

In this blog I will explain how to do this yourself so you may try out if this is useful for you. The end result for me looks like this:

Ultrasonic sensor for Jetbot
Ultrasonic sensor for Jetbot

For this project you will need following:

  • A complete and functioning Jetbot. If you want to build one yourself (which I hightly recommend), full instructions can be found on the Nvidia Jetbot Github,
  • The ultrasonic sensor (I use a HY-SRF05, but I guess a HC-SR04 would do as well),
  • The 3D printed front. You will need to print the front for attaching the ultrasonic sensor to the Jetbot yourself. You can download the STL for this part from Thingiverse.
  • A small microcontroller (Adafruit Feather M0) to control the ultrasonic sensor in realtime, avoid timing (scheduling issues) with the Jetson Nano,
  • A set of short female and male headers to piggy back the Adafruit DC Motor FeatherWing of the Jetbot,
  • Two 10K resistors to convert the response from the sensor from 5V to a safe 2.5V,
  • A few microscrews to fix the sensor and the 3D printed front,
  • Some multicolored (jumper) wires.

Start with printing of the front and soldering on the short female headers to the Feather M0. The female headers should be soldered facing upwards (i.e. the headers and components are on the same side). Next, we’ll wire up the ultrasonic sensor to the Feather and run a test to check proper function of the sensor and the feather. Note that the SRF05 sensor requires a 5V voltage and the Feather runs on 3.3V. While the Trig signal can be 3V or 5V, the ‘return’ Echo signal is 5V logic. As the Feather can only handle 3.3V, use the two resistors as a divider to convert the 5V logic level to a safe 2.5V. Be careful with wiring up to prevent damage to the Feather.

  • Connect the SRF05 GND to a GND pin of the Feather M0,
  • Connect the second GND pin of the Feather M0 to a Jetson Nano GND pin,
  • Connect the SRF05 VCC to the 5V power pin of the Jetson Nano,
  • Connect the SRF04 TRIG pin to pin 11 of the Feather M0,
  • Connect the SRF04 ECHO via a divider bridge (using the two resistors) to pin 12 of the Feather M0.

If done correctly, your test assembly should resemble something like the breadboard lay-out as shown.

Breadboard lay-out for ultrasonic sensor and feather M0 testing

Start up the Jetson Nano so that 5V is provided to the sensor, hook up a USB cable between the feather and your computer and upload the source below using the Arduino IDE.

/*
Ultrasonic sensor for Jetbot
----------------------------
Trigger on port 11
Respons on port 12
5V to 3.3V response conversion with 2.2K and 4.7K resistors
I2C slave sender on port 8
----------------------------
This sketch is based on the Ping))) Sensor sketch created by David A. Mellis on 3 Nov 2008, modified by Tom Igoe on 30 Aug 2011 and modified by Ron in 2020 to 
include I2C communications.
----------------------------
The original source of Ping))) can be found on:
  http://www.arduino.cc/en/Tutorial/Ping
This example code is in the public domain.
*/

#include <Wire.h>

// this constant won't change. 
// It's the pin number of the sensor's output:
const int pingPin = 11;
const int distPin = 12;
long cm;

void setup() {
  // initialize serial communication:
  Serial.begin(9600);
  pinMode(pingPin, OUTPUT);
  pinMode(distPin, INPUT);
  Wire.begin(8);
  Wire.onRequest(requestEvent);
}

void loop() {
  // establish variables for duration of the ping, 
  // and the distance result in inches and centimeters:
  long duration, inches;
  // The PING))) is triggered by a HIGH pulse of 2 
  // or more microseconds.
  // Give a short LOW pulse beforehand to ensure a clean 
  // HIGH pulse:
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);
  // A HIGH pulse whose duration is the time (in 
  // microseconds) from the sending of the ping
  // to the reception of its echo off of an object.
  duration = pulseIn(distPin, HIGH);
  // convert the time into a distance
  // inches = microsecondsToInches(duration);
  cm = microsecondsToCentimeters(duration);
  // Print distance to serial monitor for testing
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();
  delay(100);
}

// I2C function that executes whenever data is requested 
// by master. This function is registered as an event, 
// see setup()
void requestEvent() {
  byte buf[4];
  buf[0] = (byte) cm;
  buf[1] = (byte) cm>>8;
  buf[2] = (byte) cm>>16;
  buf[3] = (byte) cm>>24;
  Wire.write(buf, 4); // respond with message of 4 bytes 
                      // (cm is long type)
  Serial.println("I2C request");
}

long microsecondsToInches(long microseconds) {
  // According to Parallax's datasheet for the PING))), 
  // there are 73.746
  // microseconds per inch (i.e. sound travels at 1130 
  // feet per second).
  // This gives the distance travelled by the ping, 
  // outbound and return,
  // so we divide by 2 to get the distance of the 
  // obstacle.
  // See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
  return microseconds / 74 / 2;
}

long microsecondsToCentimeters(long microseconds) {
  // The speed of sound is 340 m/s or 29 microseconds 
  // per centimeter.
  // The ping travels out and back, so to find the 
  // distance of the object we take half of the distance 
  // travelled.
  return microseconds / 29 / 2;
}

When uploading is completed, open the Arduino Monitor. Hold your hand in front of the sensor and the distance between the sensor and your hand should be displayed in the monitor. Move your hand closer and further away from the ultrasonic sensor and note the changing readout on the monitor. Once you have this functioning, we’re ready for the next step; interfacing with the Jetson Nano using I2C.

Switch off power on both Jetson Nano and the Feather M0. Now temporarily disconnect the I2C connection to the motor driver from pins 3 and 5 of the Jetson Nano (remember their positions!) and make an I2C connection between the Jetson Nano and the Feather M0. You do this by connecting the SCL pin of the Feather M0 with the SCL pin of the Jetson Nano (pin 5) and connecting the SDA pin of the Feather M0 with the SDA pin of the Jetson Nano (pin 3). Now power up again and once the Jetbot software has loaded, connect to the JupyterLab interface on your computer and create a new python session. Copy and paste the following short Python test script to JupyterLab:

import smbus
import time

bus = smbus.SMBus(1)
address = 8 # I2C address for Ultrasonic bus

def distance():
    """Returns distance measured by ultrasonics in cm"""
    return bus.read_byte_data(address, 0)

while True:
    print(distance(), " cm")
    time.sleep(1.0)

Start the script by pressing shift-enter in JupyterLab and you should see the result of a distance measurement every second. Try holding and moving your hand at several distances from the ultrasonic sensor and note that the reported distance changes in the JupyterLab output. If everything works as described, you now have a working distance measurement system communicating with the Jetbot over I2C.

All we need to do now is to integrate the ultrasonic sensor and the Feather M0 microcontroller with the Jetbot. Start with unscrewing the DC motor driver Feather Wing from the Jetbot and disconnecting the jumper wires from the Feather M0 microcontroller. Desolder the 2-pin and 3-pin headers on the motor driver, move these headers to the inner rows of the DC motor driver board same pins and resolder. Solder an additional 2-pin header to pins 11 and 12 of the DC motor driver board using the inner rows, and solder a 1-pin header to the second GND of the DC motor driver board. Next, solder the new short male headers strips to the bottom of the DC motor driver board. Position the M0 Feather on the location of the motor driver and secure in place using the micro screws that where used to hold the motor driver in position.

Fasten the ultrasonic sensor to the 3D printed front using two micro screws. Connect the jumper wires to the sensor if required and then fasten the 3D printed front to the Jetbot using two micro screws in the top holes of the 3D printed front.

Now fix the motor driver Feather Wing in place on top of the M0 Feather by inserting the short male headers into the short female headers and re-attach the jumper wires in their original positions. Connect the ECHO and TRIG pins of the ultrasonic sensor to the 2-pin header (pins 11 and 12) of the motor driver Feather Wing, and connect the GND pin of the ultrasonic sensor to the second GND single header pin on the motor driver. Route the 5V jumper wire from the sensor to the 5V pin of the Jetson Nano. Reconnect the I2C wires you disconnected from the Jetson Nano earlier. Once finished, it should resemble the picture below. Note that I had to bend the 2-pin and 3-pin headers 90 degrees to allow sufficient ground clearance. Tidy up the jumper wires as good as you can and check whether it resembles (or looks better!) than the picture below. Note the I2C wires at bottom right of the Feather Wing, the TRIG and ECHO wires of the sensor in the middle right and the GND wire of the sensor at the bottom left.

Jetbot motordriver
Jetbot motor driver underneath Jetbot.

Check all your wiring one last time and power up the Jetbot, open the JupyterLab environment on your computer and try following Python Script to prove the integration has been successful:

from jetbot import Robot
import smbus
import time

robot = Robot()
bus = smbus.SMBus(1)
address = 8 # I2C address for Ultrasonic bus

def distance():
    """Returns distance measured by ultrasonics in cm"""
    return bus.read_byte_data(address, 0)

while True:
    if (distance() < 20):
       robot.backward(0.4)
       time.sleep(0.6)
       robot.left(0.4)
       time.sleep(0.5)
    else:
       robot.forward(0.4)

If all’s well, your Jetbot should now be cruising along the floor and once it encounters a wall it will back up a little bit, make a left turn and will then continue it’s journey. You will probably note that this will only work correctly when the Jetbot is more or less perpendicular to the wall. When the Jetbot is moving at shallow angles (almost in parallel) with the wall, the ultrasonic beams will bounce of the wall away from the Jetbot and the Jetbot will not pick up a return signal from the sensor. But for me, this solution works better for moving around the room than trying to get the AI system to recognize my walls. I find it also a more efficient method (in terms of computing power and power consumption) for basic navigation in a room. I hope you enjoyed my blog and perhaps even managed to replicate my little experient as well. I would love to have your feedback and thoughts on this subject.

4 replies on “Ultrasonic Sensor for the Jetbot”

Thanks for doing this. Just one question: Neither the HC-SR04 or the HYSRF05 will fit the mount (too wide) so which sensor did you use?

A quick trip to tinkercad will fix the problems so its not a big deal but I was wondering.

Hi Carl, thank you for visiting my blog and letting me know about this issue. I used the SRF05 simular to this one with holes spaced at 15 mm vertical and 38.1 mm horizontal. It was an old SRF05 I had laying around so perhaps the dimensions have been changed to metric dimensions in the meantime. I did a quick check on the internet and a lot of the SRF05’s I found now have the holes spaced at 15 mm x 40 mm. I will add a modified mount to Thingiverse. Thanks for pointing this out!

Absolutely love this blog, good job. I just wanted to know could I use an Arduino Mega 2560 instead of the Adafruit Feather M0? It also has an I2C that I’m guessing could be used to interface with the Jetson Nano (or even through USB). I’m wondering what would need to be changed?

Hi Abu, you could use an Arduino Mega 2560 instead of the Adafruit Feather M0. As the Arduino Mega 2560 runs on a 5V voltage, you would need to power it directly from the 5V pin on the Jetson Nano, taking care not to connect it to the Adafruit motor driver that operates at 3.3V. As the Ultrasonic sensor already uses 5V, you may ommit the two resistors that I used to lower the signal voltage from the sensor from 5V to 2.5V. You also may need to find a good alternative location for the Arduino Mega that is somewhat larger than the Feather. Good luck, Ron!

Leave a Reply

Your email address will not be published. Required fields are marked *