Introduction
The PING or its cheaper clone the HC-SR04 are often used in robotics as a means of obstacle detection. For some time now I have been meaning to put together a means of visualising what the sensor is detecting. This is useful in diagnosing the performance of your robot as it moves around its environment.
The Hardware
To read the HC-SR04 data and control the pan servo I used an Arduino Uno variant (the DFRobot Romeo BLE) that I already had. This is programable via Bluetooth but this isn't necessary, any vanilla uno will do. My setup also included a tilt servo, but this isn't used currently.
I 3D printed a mount for the Arduino which also provides a base for the servos and ultrasonic sensor. The Arduino sketch is very straight forward. It pans the servo from 10 to 170 degrees, with 90 degrees being straight ahead, and sends the current angle and distance to any obstacle (called the range) out on the serial port every 1 degree travelled. This code is reproduced below. The Servo and NewPing libraries do most of the heavy lifting.
/********************** @file Sonar_Visualisation.ino @brief Create visual representation of a sonar sweep using Processing. @author David Such Code: David Such Version: 1.0 Last edited: 04/11/18 **********************/ #include < Servo.h >
#include < NewPing.h > // DEFINITIONS #define MAX_DISTANCE 30
#define MAX_ANGLE 80
#define ANGLE_STEP 1 // PIN CONNECTIONS const byte TRIG_PIN = 2; const byte ECHO_PIN = 3; const byte H_SERVO = 9, V_SERVO = 10; const byte LED_PIN = 13; // GLOBALS int angle = 0; int dir = 1; // CREATE CLASS INSTANCES Servo hServo, vServo; NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DISTANCE); // METHODS void centre(Servo servo, int offset) { digitalWrite(LED_PIN, !digitalRead(LED_PIN)); servo.write(90 + offset); delay(15); digitalWrite(LED_PIN, !digitalRead(LED_PIN)); } void sweep(Servo servo, int min, int max) { int pos = 0; min = constrain(min, 0, 180); max = constrain(max, min, 180); digitalWrite(LED_PIN, !digitalRead(LED_PIN)); for (pos = min; pos <= max; pos += 1) { servo.write(pos); delay(15); } digitalWrite(LED_PIN, !digitalRead(LED_PIN)); for (pos = max; pos >= min; pos -= 1) { servo.write(pos); delay(15); } } void sendSerialPacket(int angle, int distance) { Serial.print(angle); Serial.print(","); Serial.println(distance); } // MAIN void setup() { Serial.begin(115200); pinMode(H_SERVO, OUTPUT); pinMode(V_SERVO, OUTPUT); pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, HIGH); hServo.attach(H_SERVO); vServo.attach(V_SERVO); centre(hServo, 0); sweep(vServo, 45, 90); centre(vServo, 5); } void loop() { delay(40); unsigned int ping_distance_cm = sonar.ping_cm(); ping_distance_cm = constrain(ping_distance_cm, 0, MAX_DISTANCE); sendSerialPacket(angle, ping_distance_cm); hServo.write(angle + MAX_ANGLE); if (angle >= MAX_ANGLE || angle <= -MAX_ANGLE) { dir = -dir; } angle += (dir * ANGLE_STEP); }
Processing 3
Processing is a language and IDE designed for visual display. The language is VERY similar to that used for programming the Arduino and is a c variant. It is perfect for displaying data from the Arduino and this is what we used for our sonar display.
Processing is available for free and there are versions for Windows, MAC and Linux. It also comes as standard on Raspbian and so we used a Raspberry Pi to run our processing sketch and display the output. The same sketches will work on what ever OS you are using, you will just need to change the name of the USB port.
One thing you normally need to consider when connecting serial data is what voltage levels are being used. For example the Raspberry Pi uses 3.3V logic on its I/O and the UNO uses 5V. Connecting these directly could damage the Raspberry Pi. By using the USB ports, voltage conversion is handled by the boards and we don't have to worry about it.
So to get the serial data from the UNO to the Raspberry Pi we just connect the appropriate USB cable between the two boards.
The Raspberry Pi also comes with the Arduino IDE so you can even program the UNO using this if you want, using the same USB cable. Upload the Arduino code first. You can then use this data to debug your processing sketches.
Sonar Displays
I wrote 3 Processing sketches to display the data in different ways. The first is based on the design done by Tony Zhang at hackster.io, I liked his pseudo radar display and wanted to emulate it. Note that I have significantly modified his sketch as it seems to be unnecessarily complicated and includes a bunch of unused code for some reason. You can download the Sonar Display Processing Sketch. Note that all 3 of the sketches use the integer point class which you can also download from the Reefwing Gist.
The second display is my attempt at a waterfall display, similar to that used on submarines to display sonar data. It turned out more like a depth sounder display, but I like the use of perlin noise to represent the outer limit of the sonar range. Download the Depth Display Processing Sketch.
The third display is a combination of the first two displays, which I called the Range Display Processing Sketch.