2017年3月4日 星期六

樹莓派3 Raspberry Pi 3 - 超音波 (HC-SR04 超聲波測距模組) 測距,並在4位7段數字屏上顯示結果


超音波測距的方式是發射一個電波,當電波遇到物體反射回來,再被測距儀偵測到反射電波,利用來回時間與音波的速度算出距離,計算公式如下:

距離 = (音波發射與接收時間差 * 聲音速度(343m/s)) / 2

聲音的速度,在一般空氣中約為每秒 343m,因來回時間要將距離除以 2,才是單程的距離。實際的聲音速度決定於好幾個環境因素,其中一個是溫度,計算時,需將環境因素考慮在內,才能更精確計算距離。

我在淘寶購買的是型號 HC-SR04 的超聲波測距模組,這個模組最遠可測得 2cm ~ 400cm, 輸入電壓大約 2.4V ~ 5.5V,工作電流大約 15mA,偵測廣度大約是15度。

基本工作原理:

  1. 利用HC-SR04 超聲波測距模組的 TRIG觸發測距,給至少10us的高電平信號;
  2. 模組自動發送 8個 40khz的方波,自動檢測是否有信號返回;
  3. 有信號返回,通過 ECHO輸出一個高電平,高電平持續的時間就是超聲波從發射到返回的時間。

Source: http://coopermaa2nd.blogspot.hk/2012/09/hc-sr04.html

電子零件:

  • 麵包板 x 1
  • GPIO Board x 1
  • 74HC595 x 1
  • HS420361k-A32 共陰 4位 7段數字顯示器 x 1
  • HC-SR04 超聲波測距模組 x 1
  • 220Ω 電阻 x 4
  • 470Ω 電阻 x 1
  • 1kΩ   電阻 x 1
  • 公對公杜邦線 N 條

接駁圖:



值得注意的地方:

HC-SR04 的Trig 為 Output (Pi→HC-SR04),而 Echo 為 Input (HC-SR04→Pi),其工作電壓為 5V,但 Pi 容忍電壓僅為 3.3 V 所以需要將 Echo 接上分壓電路降壓至3.3 V 左右,分壓電路如下圖:



實物連接圖:


Python 程式碼:

#!/usr/bin/python3
import RPi.GPIO as gpio
import time
from datetime import datetime
from threading import Thread

# Set up 7 segments display
seg = [[1,1,1,1,1,1,0],    # 0
       [0,1,1,0,0,0,0],    # 1
       [1,1,0,1,1,0,1],    # 2
       [1,1,1,1,0,0,1],    # 3
       [0,1,1,0,0,1,1],    # 4
       [1,0,1,1,0,1,1],    # 5
       [1,0,1,1,1,1,1],    # 6
       [1,1,1,0,0,0,0],    # 7
       [1,1,1,1,1,1,1],    # 8
       [1,1,1,1,0,1,1],    # 9
      ]

# Set up 74HC595N Shift Registor pins
DS = 16
SHCP = 21
STCP = 20
gpio.setmode(gpio.BCM)
gpio.setup(DS, gpio.OUT)
gpio.setup(SHCP, gpio.OUT)
gpio.setup(STCP, gpio.OUT)

# Setup digit number pins
digits = [26,19,13,12]
for i in range(4):
    gpio.setup(digits[i], gpio.OUT)

# Setup HC-SR04 ultrasonic distance sensor
triggerPin = 23
echoPin = 22
gpio.setup(triggerPin, gpio.OUT)
gpio.setup(echoPin, gpio.IN)

distance = 0
threadStop = False

# Define a get distance thread
def getDistance():
    global distance, threadStop, echoPin, triggerPin
    while not threadStop:
        gpio.output(triggerPin, True)
        time.sleep(0.00001)
        gpio.output(triggerPin, False)

        start = time.time()
        finish = time.time()
        # Check whether the ECHO is LOW
        while gpio.input(echoPin) == 0:
        # Saves the last known time of LOW pulse
            start = time.time()
        #Check whether the ECHO is HIGH
        while gpio.input(echoPin) == 1:
            #Saves the last known time of HIGH pulse
            finish = time.time()

        pulseLength = finish-start
        distance = int(pulseLength*17150)
        time.sleep(1)
    
#  Send Clock signal to 74HC595
def clock():
    gpio.output(STCP, 1)
    gpio.output(STCP, 0)
             
# Send Latch signal TO 74HC595
def latch():
    gpio.output(SHCP, 1)
    gpio.output(SHCP, 0)

def display(digi, number):
    # Turn off all digits
    for i in range(4):
        gpio.output(digits[i], 1)
        
    # Send signals to 74HC595
    clock()
    latch()
    for bit in seg[number]:
        gpio.output(DS, bit)
        latch()
    gpio.output(DS, 0)
    latch()
    clock()
    # Turn on digits
    gpio.output(digits[digi], 0)


# Main program
try:
    t = 0.003       # refresh time for each digit display
    x = 3           # start digit in 4 digits 7 segments display
    dig = [0,0,0,0] # array to hold each digit to be displayed
    
    # Start the getDistance thread to obtain sensor measurement
    d = Thread(target=getDistance, args=())
    d.start()
    # Wait for sensor ready
    time.sleep(0.5)
    while True:
        dist = distance
        print(dist)
        if dist > 2 and dist < 400:
            dig[1] = int(dist/100)
            dig[2] = int(dist/10) % 10
            dig[3] = int(dist % 10)
            if dig[1] > 0:
                x = 1
            elif dig[2] > 0:
                x = 2
            else:
                x = 3
            for i in range(x, 4):
                display(i, dig[i])
                time.sleep(t)
        else:
            for i in range(x, 4):
                display(i, dig[i])
                time.sleep(t)

except KeyboardInterrupt:
    # Terminate the getDistance thread when user Press Ctrl+C
    threadStop = True
    time.sleep(0.5)

finally:
    gpio.cleanup()


備註:

由於 4 位 7 段數字顯示器使用動態更新每個數字,為減少四位數字殘影,我使用了別一條thread 每1秒做超音波測距離,主程式則持續更新數字顯示器。


實驗短片:



沒有留言:

張貼留言