The HC-SR04 ultrasound range finder on Raspberry Pi

Introduction

The HC-SR04 can be used to measure distance and detect obstacles in robotic projects. However, the reported range is not always accurate, small objects are mostly not detected at all and sometimes false positives occur (a short range is reported when the way ahead is free). That said, we’ll give it a try to report ranges that, for instance, to prevent a Pi Rover from running into walls. Furthermore, I’ll undertake an attempt to eliminate bogus values.

Connecting the sensor

The HC-SR04 has 4 pins. Power (5 V DC) is supplied over the VCC and GND pins. A pulse is initiated on the Trig pin, which takes 3.3 V. The result is measured on the Echo pin, which delivers 5 V. So to use it with Raspberry Pi GPIO we need bring it down to around 3.3 V. We’ll use a potential divider for that.

A potential divider divides the potential over two serial resistors, as illustrated here:

The relationship between the input and output voltage is:

So with R1 = 220 Ω and R2 = 330 Ω we have an output of 3 V. That’s good enough. The GPIO pin needs a voltage of at least 2.5 V to read positive.

We’ll connect the trigger to GPIO22 (physical pin 15) and the echo over the potential divider to GPIO4 (physical pin 7).

Collecting data

You might expect that the delta time between the trigger and the echo reflect the distance measured. This is not quite the case. The HC-SR04 expects a pulse of at least 10 μs as a trigger to fire a burst of 8 ultrasound pulses (40 kHz). The distance corresponds to the length of the echo pulse. We multiply this value by the speed of sound in cm to find the total range in cm. Dividing by two gives the distance to the object.

Let’s collect some data in Python. We’ll use numpy for some calculations (sudo pip3 install numpy) and WiringPi to access GPIO.

Note that a pulse in our program results in 8 ultrasound pulses in the HC-SR04. The sleep time between the (software) pulses will interfere with the measurement if it is too short, especially for longer ranges. This does not need to bother us much. First, the sensor is inaccurate above 4 m anyway. Second, 4 m and 20 m essentially mean the same thing to the Pi Rover, namely: the way ahead is free.

Now the range is clearly about 240 cm, but with the ultrasound scattering all over the place we get some values that are obviously not correct. So averaging the data does not appear to make much sense. We need to eliminate the outliers first.

Finding the outliers

Skip to the next section if you don’t want to experiment with this. I’m doing this in PyCharm on a Linux PC and I needed to compile a lot of libraries (and even install a Fortran compiler) to get SciPy working. You probably don’t want to do this on a Raspberry Pi.

This results in the following graph, with the outliers marked red and the useful values nicely clustered.

Here are two more examples to give you an idea of the possible dispersion.

Filtering the data

So what we need to do is pass our data through the mad_based_outlier function to filter out the rubbish. In this final example the Pi Rover sends a burst of 5 pulses (again, 5 x 8 = 40 ultrasound pulses), measures the results, cleans up the data and pauses 200 ms before repeating the cycle. Experiment with these values to obtain optimal results. When an obstacle is detected the Pi Rover reports this and stops its engines.