Sonar range finder: HC-SR04
The HC-SR04 is a simple range finder with an advertised capability of measuring from 2 cm to 400 cm (~13 feet.) It costs less than $2. A microprocessor like the Arduino, Raspberry Pi (any model), or ESP8266 can be used to control it. The device sends out a pulse of ultrasonic sound and measures the time for the echo to be received. In the picture below, the sound is transmitted from one of the two turrets and is echo is received by the other. Since the speed of sound is known, the distance can be calculated. The device specifications are here. The RCWL-1601 and US-100 are newer devices that are similar.
One problem is that the HC-SR04 runs on 5 Volts DC and its output signal voltage is also 5v. While this is suitable for most Arduino models, newer microprocessors like the Raspberry Pi and ESP models use 3.3v. Applying 5v to a 3.3v rated input will damage them. A voltage divider is needed to step down the 5v to 3.3v. An example of a voltage divider on a Raspberry Pi Zero is shown below. The Pi has a convenient 5v output to power the range finder.
1. If you take multiple readings, are they the same?
o Take the average to get better estimates
2. Eliminate bogus readings
o Use Standard deviation to discard noisy readings
3. Accuracy: measured vs. actual distance.
o Plot measured reading vs. actual distance
o Plot error vs. distance
o Determine limits of accuracy: Max distance, minimum
4. Can the accuracy be improved ?
o toilet paper roll core
5. Properties of reflecting materials
o Find stealth materials!!
o Angle the reflection to avoid detection
6. Other factors that affect accuracy
o Size of target
o Temperature
o Cross wind
o Moving targets
7. SONAR theory
o What is Ultrasonic sound?
o Sonar in nature: Bats, Dolphins, Whales
o Do Submarines say Marco/Polo?
o How is RADAR different?
8. Understand the code
o Derive the ‘Speed of sound’ constant
o Use different pins on the Pi
o Add a web server to make the range finder mobile
9. Understand Voltage dividers
10. Range finder applications
o Car parking in garage
o Avoiding obstacles in a robot
o Measuring the level of water in doggie bowl
o Use multiple range finders to determine target location
'''
Driver for distance sensor HC-SR04. Tested on a Raspberry Pi
Speed of sound = 1234.8 Km/hour = 1234.8/3600 Km/sec = 1234.8/3600*10^5 cm/sec
Divide by 2 for round trip
'''
import RPi.GPIO as GPIO
import time
TRIG = 23 # Pin 16, GPIO port 23 for trigger
ECHO = 24 # Pin 18, GPIO port 24 for echo response
# Tech doc recommends allowing time for echo to complete after each trigger
MinIdleTime = 0.06 # 60 ms, min time between measurement cycles
class Ranger:
def __init__(self):
GPIO.setmode(GPIO.BCM) # GPIO pin numbering method "Broadcom SOC channel"
GPIO.setup(TRIG, GPIO.OUT)
GPIO.output(TRIG, False) # reset
GPIO.setup(ECHO, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
self.lastTrigger = time.time() - MinIdleTime # effective time of last trigger
# get one distance reading
def get(self):
if GPIO.input(ECHO) != 0: # 'Echo' pin is already high
self.__init__() # attempt to fix it
return -1 # sensor fault, check for loose connections
getStart = time.time() # start time for this reading
idleTime = getStart - self.lastTrigger # time between trigger applications
if idleTime < MinIdleTime: # too fast?
time.sleep(idleTime)
getStart = time.time() # update start time, getStart += idleTime
self.lastTrigger = getStart # time of this trigger (now)
GPIO.output(TRIG, True) # trigger, start new request
time.sleep(0.00002) # trigger duration 20uS, recommended value is 10uS
GPIO.output(TRIG, False) # turn off trigger
while True: # wait for pulse start
pulseStart = time.time()
if pulseStart-getStart > 0.02: # avoid infinite loop
return -2 # error, took too long, sensor fault
if GPIO.input(ECHO) != 0:
break
while True: # wait for pulse end
pulseEnd = time.time()
pulseDuration = pulseEnd - pulseStart
distance = pulseDuration * 17150 # based on 1234.8 kmph
if distance > 500: # > 500 cm?
return -3 # error, too far to be accurate
if GPIO.input(ECHO) == 0:
return distance # in cm
# get many distance readings
def getSet(self, size=100):
return [self.get() for _ in range(size)]
# remove outliers (2 sigma), return average reading
def getAverage(self, size=100):
import numpy as np
distances = self.getSet(size)
while True:
nValues = len(distances)
mean = np.mean(distances)
maxDeviation = np.std(distances) * 2 # max permitted error is 2 standard deviations
distances = [d for d in distances if d >= mean-maxDeviation and d <= mean+maxDeviation]
if len(distances) == nValues:
return mean # no outliers were found
if __name__ == '__main__':
print("Starting...")
ranger = Ranger()
for _ in range(1200): # 20 mins
distance = ranger.get()
if distance < 0:
print("Sensor error: %s" % distance)
else:
print("Distance: %.2f cm (%.2f in)" % (distance, distance/2.54))
time.sleep(1)