The HC-SR04
The HC - SR04 ultrasonic ranging module provides 2cm - 400cm non-contact
measurement, with ranging accuracy up to 3mm. The module includes ultrasonic transmitters, receiver and control circuitry. The time difference between transmission and reception of ultrasonic signals is calculated. Using the speed of sound and ‘Speed = Distance/Time‘ equation, the distance between the source and target can be easily calculated.
Credit to Vivek and his article on the same subject for the diagrams.
Wiring the HC-SR04 to a Raspberry Pi
The module has 4 pins:
- VCC - 5V Supply
- TRIG - Trigger Pulse Input
- ECHO - Echo Pulse Output
- GND - 0V Ground
Wiring is straight forward with one exception, note that the sensor operates at 5V not the 3.3V of the Raspberry Pi. Connecting the ECHO pulse pin directly to the Raspberry Pi would be a BAD idea and could damage the Pi. We need to use a voltage divider or a logic level converter module to drop the logic level from the HC-SR04 to a maximum of 3.3V. Current draw for the sensor is 15 mA.
As we have a spare logic level converter, we will use that. Connections for the logic converter are shown below.
For the voltage divider option: Vout = Vin x R2/(R1+R2) = 5 x 10000/(4700 + 10000) = 3.4V
Python Class for the HC-SR04 Ultrasonic Sensor
To utilise the HC-SR04:
- Provide a trigger signal to TRIG input, it requires a HIGH signal of at least 10μS duration.
- This enables the module to transmit eight 40KHz ultrasonic bursts.
- If there is an obstacle in-front of the module, it will reflect those ultrasonic waves
- If the signal comes back, the ECHO output of the module will be HIGH for a duration of time taken for sending and receiving ultrasonic signals. The pulse width ranges from 150μS to 25mS depending upon the distance of the obstacle from the sensor and it will be about 38ms if there is no obstacle.
- Obstacle distance = (high level time × velocity of sound (343.21 m/s at sea level and 20°C) / 2
- Allow at least 60 ms between measurements.
Time taken by the pulse is actually for return travel of the ultrasonic signals. Therefore Time is taken as Time/2.
Distance = Speed * Time/2
Speed of sound at sea level = 343.21 m/s or 34321 cm/s
Thus, Distance = 17160.5 * Time (unit cm).
We have included a simple low pass filter function which is equivalent to an exponentially weighted moving average. This is useful for smoothing the distance values returned from the sensor. The higher the value of beta, the greater the smoothing.
#!/usr/bin/python # RS_UltraSonic.py - Ultrasonic Distance Sensor Class for the Raspberry Pi # # 15 March 2017 - 1.0 Original Issue # # Reefwing Software # Simplified BSD Licence - see bottom of file. import RPi.GPIO as GPIO import os, signal from time import sleep, time # Private Attributes __CALIBRATE = "1" __TEST = "2" __FILTER = "3" __QUIT = "q" class UltraSonic(): # Ultrasonic sensor class def __init__(self, TRIG, ECHO, offset = 0.5): # Create a new sensor instance self.TRIG = TRIG self.ECHO = ECHO self.offset = offset # Sensor calibration factor GPIO.setmode(GPIO.BCM) GPIO.setup(self.TRIG, GPIO.OUT) # Set pin as GPIO output GPIO.setup(self.ECHO, GPIO.IN) # Set pin as GPIO input def __str__(self): # Return string representation of sensor return "Ultrasonic Sensor: TRIG - {0}, ECHO - {1}, Offset: {2} cm".format(self.TRIG, self.ECHO, self.offset) def ping(self): # Get distance measurement GPIO.output(self.TRIG, GPIO.LOW) # Set TRIG LOW sleep(0.1) # Min gap between measurements # Create 10 us pulse on TRIG GPIO.output(self.TRIG, GPIO.HIGH) # Set TRIG HIGH sleep(0.00001) # Delay 10 us GPIO.output(self.TRIG, GPIO.LOW) # Set TRIG LOW # Measure return echo pulse duration while GPIO.input(self.ECHO) == GPIO.LOW: # Wait until ECHO is LOW pulse_start = time() # Save pulse start time while GPIO.input(self.ECHO) == GPIO.HIGH: # Wait until ECHO is HIGH pulse_end = time() # Save pulse end time pulse_duration = pulse_end - pulse_start # Distance = 17160.5 * Time (unit cm) at sea level and 20C distance = pulse_duration * 17160.5 # Calculate distance distance = round(distance, 2) # Round to two decimal points if distance > 2 and distance < 400: # Check distance is in sensor range distance = distance + self.offset print("Distance: ", distance," cm") else: distance = 0 print("No obstacle") # Nothing detected by sensor return distance def calibrate(self): # Calibrate sensor distance measurement while True: self.ping() response = input("Enter Offset (q = quit): ") if response == __QUIT: break; sensor.offset = float(response) print(sensor) @staticmethod def low_pass_filter(value, previous_value, beta): # Simple infinite-impulse-response (IIR) single-pole low-pass filter. # ß = discrete-time smoothing parameter (determines smoothness). 0 < ß < 1 # LPF: Y(n) = (1-ß)*Y(n-1) + (ß*X(n))) = Y(n-1) - (ß*(Y(n-1)-X(n))) smooth_value = previous_value - (beta * (previous_value - value)) return smooth_value def main(): sensor = UltraSonic(8, 7) # create a new sensor instance on GPIO pins 7 & 8 print(sensor) def endProcess(signum = None, frame = None): # Called on process termination. if signum is not None: SIGNAL_NAMES_DICT = dict((getattr(signal, n), n) for n in dir(signal) if n.startswith('SIG') and '_' not in n ) print("signal {} received by process with PID {}".format(SIGNAL_NAMES_DICT[signum], os.getpid())) print("\n-- Terminating program --") print("Cleaning up GPIO...") GPIO.cleanup() print("Done.") exit(0) # Assign handler for process exit signal.signal(signal.SIGTERM, endProcess) signal.signal(signal.SIGINT, endProcess) signal.signal(signal.SIGHUP, endProcess) signal.signal(signal.SIGQUIT, endProcess) while True: action = input("\nSelect Action - (1) Calibrate, (2) Test, or (3) Filter: ") if action == __CALIBRATE: sensor.calibrate() elif action == __FILTER: beta = input("Enter Beta 0 < ß < 1 (q = quit): ") filtered_value = 0 if beta == __QUIT: break; while True: filtered_value = sensor.low_pass_filter(sensor.ping(), filtered_value, float(beta)) filtered_value = round(filtered_value, 2) print("Filtered: ", filtered_value, " cm") else: sensor.ping() if __name__ == "__main__": # execute only if run as a script main() ## Copyright (c) 2017, Reefwing Software ## All rights reserved. ## ## Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions are met: ## ## 1. Redistributions of source code must retain the above copyright notice, this ## list of conditions and the following disclaimer. ## 2. Redistributions in binary form must reproduce the above copyright notice, ## this list of conditions and the following disclaimer in the documentation ## and/or other materials provided with the distribution. ## ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ## ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ## WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ## DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ## ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ## (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ## SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
No comments:
Post a Comment