Ultrasonic Sensors

If you’ve been through my simple motor control tutorial you now know how to control the input voltage to DC motors using an Arduino. In a differential drive robot like Colin, this means driving in a (nearly) straight line, a circle, or some other preset path. It would be more interesting if we could make a robot that reacts to its surroundings. For this we need sensors and ultrasonic sonar sensors are a good place to start.

Ultrasonic sensors determine the distance between themselves and the nearest obstacle by emitting an ultrasound pulse and timing how long it takes for that pulse to be reflected off the nearest obstacle and back to the sensor. They are easy to use, accurate, and they can be extremely cheap.

They do have limitations, however. Sound reflects best off of hard, smooth objects, so soft or uneven surfaces are not detected very well. Most kinds of fabric are basically invisible to an ultrasonic sensor. Reflection is another problem. To work properly, the sensor needs an obstacle to reflect its emitted pulse straight back to the sensor. If the sensor isn’t aimed straight at a perpendicular surface, the reflected pulse could miss the sensor entirely. In this case the sensor would not detect the obstacle. The datasheets for most sensors specify an angle within which the sensor will be able to reliably detect obstacles.

HC-SR04 sensor

HC-SR04 sensor

For this tutorial I’ll be using an HC-SR04 sensor. The datasheet for the sensor can be found here. Note that it will only reliably detect obstacles within +/- 15 degrees from perpendicular to the face of the sensor. The HC-SR04 definitely isn’t the best ultrasonic sensor out there. More accurate, longer range sensors exist. You cannot, however, find a cheaper ultrasonic sensor than the HC-SR04. If you shop around on Amazon you can find them for $1-3 apiece.

Before I get into the actual tutorial I should mention I’m making use of Tim Eckel’s NewPing library for Arduino here. It makes using these sensors a complete snap. You should definitely take a look at the tutorial on the Arduino Playground in addition to this one. If you don’t have experience installing or using new libraries in Arduino, take a look at this tutorial.


Single Sensor Example

We’ll start out by wiring up and testing a single HC-SR04 sensor. You’ll want to wire it up as in the diagram below.

Wiring diagram for a single HC-SR04 sensor.

Wiring diagram for a single HC-SR04 sensor.

Note the Trig and Echo pins on the sensor can be connected to different pins on the Arduino. The NewPing library makes it possible to connect the Trig and Echo pins to the same Arduino pin. This is great because it means the HC-SR04 doesn’t occupy as many I/O pins.

Note also there is no power source pictured on the above diagram. For the purposes of this exercise we can power the Arduino through its USB port, so no external power source is required.

Example Sketch

#include <NewPing.h>

#define TRIGGER_PIN 2
#define ECHO_PIN 2
#define MAX_DISTANCE 200 // max distance the sensor will return

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // declare a NewPing object

void setup() {
  Serial.begin(115200);
}

void loop() {
  delay(50);
  int uS = sonar.ping(); 
  Serial.print("Ping: ");
  Serial.print(uS / US_ROUNDTRIP_CM); // convert ping time to distance in cm
  Serial.println("cm");
}

Load the above sketch on your Arduino and open a terminal window. Make sure the baud rate of the window is the same as the baud rate in your Serial.begin();  statement.

When initializing a NewPing object use the following form: NewPing(triggerPin, echoPin, distanceLimit); If you’re using the same pin for the trigger and echo just set triggerPin and echoPin to the same pin.

The example sketch will display the distance from the sensor to the nearest obstacle in centimeters every 50 milliseconds. If the nearest obstacle is beyond the distanceLimit or if the nearest obstacle is not detectable then sonar.Ping() will return 0.

Play around with the single sensor for a bit. Have some fun with it. Eventually start to realize how limited a single sensor is. A robot that can only see directly in front of itself can’t see obstacles at its sides. So if it approaches an obstacle at an angle, the obstacle won’t be detected. So what should we do? Add more sensors, of course!


Three Sensor Example

Now that we’ve got a single sensor up and running it should be a simple matter to add two more sensors. It can be useful to have an array of sensors when we’re trying to detect obstacles around a mobile robot like Colin. Wire the sensors up as in the diagram below.

Wiring diagram for three HC-SR04 sensors.

Wiring diagram for three HC-SR04 sensors.

Example Code

#include <NewPing.h>

// trigger and echo pins for each sensor
#define SONAR1 2
#define SONAR2 3
#define SONAR3 4
#define MAX_DISTANCE 200 // maximum distance for sensors
#define NUM_SONAR 3 // number of sonar sensors

NewPing sonar[NUM_SONAR] = { // array of sonar sensor objects
  NewPing(SONAR1, SONAR1, MAX_DISTANCE),
  NewPing(SONAR2, SONAR2, MAX_DISTANCE),
  NewPing(SONAR3, SONAR3, MAX_DISTANCE)
};

int distance[NUM_SONAR]; // array stores distances for each
                         // sensor in cm

void setup() {
  Serial.begin(115200);
}

void loop() {
  delay(50);
  updateSonar(); // update the distance array
  // print all distances
  Serial.print("Sonar 1: ");
  Serial.print(distance[0]);
  Serial.print("  Sonar 2: ");
  Serial.print(distance[1]);
  Serial.print("  Sonar 3: ");
  Serial.println(distance[2]);
}

// takes a new reading from each sensor and updates the
// distance array
void updateSonar() {
  for (int i = 0; i < NUM_SONAR; i++) {
    distance[i] = sonar[i].ping_cm(); // update distance
    // sonar sensors return 0 if no obstacle is detected
    // change distance to max value if no obstacle is detected
    if (distance[i] == 0)
      distance[i] = MAX_DISTANCE;
  }
}

Note the step in the updateSonar() method that checks if the distance returned by sonar[i].ping_cm() is 0, meaning the nearest obstacle is probably out of range. Technically it’s possible there is an obstacle 0cm away from the sensor, but that is not very likely. If the value returned by sonar[i].ping_cm() is 0, we switch it to MAX_DISTANCE .

Now we can control 3 sonar sensors simultaneously. We can use this in conjunction with our ability to control motors to make a robot that has autonomous behavior! One of the simpler things we can do with these two tools is program an obstacle avoidance routine. If that interests you stay tuned for my next tutorial, because I’m planning to do in on that very topic!