Purpose
This tutorial walks through using a set of ultrasonic sensors to develop a surface map (full disclosure: the final graphic mapping is done in R). This tutorial assumes a gridded layout of sensors. We will use four ultrasonic sensors in the example, with assumed modularity (additional notes below). Let us know if you’ve expanded the system, and what additional modifications you made!
Components
- 1 Arduino Uno
- 4 Ultrasonic Sensors (HC-SR04)
- 19 M-M wires
- 1 Breadboard
Overview
The HC-SR04 ultrasonic sensor can detect objects with a distance range of 2-400cm, using ultrasonic transmitters, receiver, and a control circuit. You’ll see in the user manual and on your sensor four pins: power (VCC), ground (GND), echo, and trigger (trig). From the user manual (link here): “to start measurement, Trig of SR04 must receive a pulse of high (5V) for at least 10 [microseconds], this will initiate the sensor will transmit out 8 cycle of ultrasonic burst at 40kHz and wait for the reflected ultrasonic burst. When the sensor detected ultrasonic from receiver, it will set the Echo pin to high (5V) and delay for a period (width) which proportion to distance. To obtain the distance, measure the width (Ton) of Echo pin.”
We will use the HCSR04 library (link here) in our code, which includes functions for calculating distance. This will keep our code clean, and allow us to focus instead on comparing distance readings across sensors.
Hardware Setup
Each sensor requires both a 5V and ground connection. To keep wires clean, we’ll connect all four sensors to the same grounding, power, and trigger pin, but each sensor will need a separate echo pin.
Code
In the first version, we just want to make sure everything is wired correctly, and we’ve imported the library correctly. Let’s run intit_DistanceSensors. This code is an adaptation of code provided as an example with the HCSR04 library.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include HCSR04 hc(12, new int[2]{5, 6}, 2); //initialisation class HCSR04 (trig pin , echo pin, number of sensor) void setup(){ Serial.begin(9600); } void loop() { for (int i = 0; i < 2; i++ ){ int c = i + 1; Serial.print("Sensor "); Serial.print(c); Serial.print(": "); Serial.print( round(hc.dist(i)) ); //return current distance (cm) in serial for sensor 1 to 6 Serial.print("cm"); Serial.print('\n'); } delay(1000); // we suggest to use over 60ms measurement cycle, in order to prevent trigger signal to the echo signal. } |
Once that’s up and running, we can see that there’s a lot of noise in the readings – even if we test it against a flat and unmoving sensor. There’s some variation in the readings – the specs for the ultrasonic sensors gives a range of 0.3cm. To deal with this, let’s add a moving average distance across five readings. Let’s run moveAvg_DistanceSensors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// Libraries #include //Variables float moveAvg [5][4]; // hold five values for each sensor int c = 0; // counter for 5-sample rolling average bool arrayFull = false; // boolean for tracking if 5 samples have been collected float meanVal [4]; //average value to print HCSR04 hc(12, new int[2]{5, 6,7,8}, 4); //initialisation class HCSR04 (trig pin , echo pin, number of sensor) // setup void setup(){ Serial.begin(9600); } // loop void loop() { for (int i = 0; i < 4; i++ ){ if (c 0){c = c + 1;} // increment array after both sensors have recorded a distance } else{ arrayFull = true; // set array full to true because we've collected 5 samples c = 0; // reset counter } if (arrayFull == true){ meanVal[i] = round((moveAvg[0][i] + moveAvg[1][i] + moveAvg[2][i] + moveAvg[3][i] + moveAvg[4][i])/5); } Serial.print("Sensor "); Serial.print(i); Serial.print(": "); Serial.print(round(meanVal[i])); //return curent distance (cm) in serial for sensor 1 to 6 Serial.print("cm"); Serial.print('\n'); } delay(1000); // we suggest to use over 60ms measurement cycle, in order to prevent trigger signal to the echo signal. } |
In our last version of the code for the Arduino, we’re going to clean things up a little to allow for post-processing in R. Here, we’re modifying our outputs so the serial monitor print-out can easily be saved as a csv. Our columns will be: x-axis sensor position; y-axis sensor position; value (distance from sensor); and units (always a good practice!). Let’s run serialToCSV_DistanceSensors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
// Libraries #include //Variables const int sensorCount = 4; // number of ultrasonic sensors float moveAvg [5][sensorCount]; // hold five values for each sensor int c = 0; // counter for 5-sample rolling average bool arrayFull = false; // boolean for tracking if 5 samples have been collected float meanVal [sensorCount]; //average value to print float x_pos[sensorCount]{0, 0, 6, 6}; // x positions of sensors float y_pos[sensorCount]{0, 6, 0, 6}; // y positions of sensors float slopes[1]; // number of slopes // note that you'll need to change the pins based on how many sensors you're using! HCSR04 hc(12, new int[sensorCount]{5, 6,7,8}, sensorCount); //initialisation class HCSR04 (trig pin , echo pin, number of sensor) // setup void setup(){ Serial.begin(9600); Serial.print("x"); Serial.print(","); Serial.print("y"); Serial.print(","); Serial.print("z"); Serial.print(","); Serial.print("units"); Serial.print('\n'); } // loop void loop() { for (int i = 0; i < sensorCount; i++ ){ if (c 0){c = c + 1;} // increment array after both sensors have recorded a distance } else{ arrayFull = true; // set array full to true because we've collected 5 samples c = 0; // reset counter } if (arrayFull == true){ meanVal[i] = round((moveAvg[0][i] + moveAvg[1][i] + moveAvg[2][i] + moveAvg[3][i] + moveAvg[4][i])/5); } Serial.print(x_pos[i]); Serial.print(","); Serial.print(y_pos[i]); Serial.print(","); Serial.print(round(meanVal[i])); //return curent distance (cm) in serial for sensor 1 to 6 Serial.print(","); Serial.print("cm"); Serial.print('\n'); } delay(1000); // we suggest to use over 60ms measurement cycle, in order to prevent trigger signal to the echo signal. } |
Bonus
To plot surfaces in R, you can pull the csv you save from the serial monitor output. We used plotly in our sample!
1 2 |
plot_ly() %>% add_trace(x=fromcsv$x, y=fromcsv$y, z=fromcsv$z, type="mesh3d") |
Sources
- HCRS04 Library: https://github.com/gamegine/HCSR04-ultrasonic-sensor-lib
- Elegoo Super Starter Kit for UNO: Lesson 10 Ultrasonic Sensor Module
- Plotly: https://plotly.com/r/