Raspberry Pi 筆記(15):超音波測距離

看到機器小車上大多會裝上一個超音波測距模組,看起來像是兩個眼睛,引發我的好奇,如何能利用超音波來測距離呢?於是網購買了幾個套件,利用上班空閒之餘來研究一下這幾個模組的特性。以下是超音波模組含溫度補償功能的US-100。
上一篇提到智能小車除了用鍵盤控制方向外,還可以透過距離偵測感應轉彎,避免碰撞前方物體,本篇就來瞭解超音波測距的一些功能及測試結果。



[基礎知識:超音波測距]

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

距離=(音波發射與接收時間差 * 聲音速度(340M/S))/2;

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

我在網路上購買的是US-100這個型號,根據規格文件,這個模組最遠可測得2公分~4.5公尺, 輸入電壓大約 2.4V ~ 5.5V,其偵測廣度大約是15度。這個模組具有溫度感測,距離值已經溫度調校,無需再根據環境溫度對超音波聲速進行校正。



在這個模組背面有一個Jumper,來控制兩種模式:
  • open:將Jumper拔起來,此時為GPIO 模式(電位觸發模式)
  • short:將Jumper 套上,此時設定為UART串列通訊模式

US-100有五個pin腳,說明如下:
  • VCC:接電源(範圍2.4V~5.5V)。
  • Trig/TX:UART 模式下,接外部電路UART 的TX 端;為GPIO模式時,為訊號發送端 Trigger。
  • Echo/RX:UART 模式下,接外部電路UART 的RX 端;為GPIO模式時,為訊號接收端 Echo 。
  • GND:接地,沒有使用。
  • GND:電路接地。

工作方式:
透過Trig接腳發出一個10us(10^-6 秒)以上的高電位,此時等待Echo高電平輸出,一旦有輸出就可以開始計時,此時模組會發送8個40khz的方波,並開始自動檢測是否有返回信號;當偵測到反射訊號時,此時Echo接腳變為低電位。計算持續高電位的時間,也就是這次測距的時間。如果加上迴圈,就可以一直偵測前方移動物體的距離。


[材料]

• Raspberry Pi 主板 x1
• US-100超音波模組 x1
• 連接線  x4

[線路連接與電路圖](圖片以HC-SR04替代)

• US-100 VCC接pin2(+5V),GND接pin6(GND)
• US-100 Trig/TX接Pin16(GPIO23),Echo/RX接Pin18(GPIO24)


[程式碼]

import RPi.GPIO as GPIO
import time
trigger_pin = 23
echo_pin = 24

GPIO.setmode(GPIO.BCM)
GPIO.setup(trigger_pin, GPIO.OUT)
GPIO.setup(echo_pin, GPIO.IN)

def send_trigger_pulse():
    GPIO.output(trigger_pin, True)
    time.sleep(0.001)
    GPIO.output(trigger_pin, False)

def wait_for_echo(value, timeout):
    count = timeout
    while GPIO.input(echo_pin) != value and count > 0:
        count = count - 1

def get_distance():
    send_trigger_pulse()
    wait_for_echo(True, 5000)
    start = time.time()
    wait_for_echo(False, 5000)
    finish = time.time()
    pulse_len = finish - start
    distance_cm = pulse_len * 340 *100 /2
    distance_in = distance_cm / 2.5
    return (distance_cm, distance_in)

while True:
    print("cm=%f\tinches=%f" % get_distance())
    time.sleep(1)

[測試結果]

我在超音波偵測頭前放一把尺,然後在刻度位置放置一個物體,程式回報測得的數據跟尺的刻度非常接近,誤差不到一公分,以物體感應來說,算是相當準確。

[參考資料]

  • Arduino 超音波測距機設計與製作(作者:曹永忠、許智誠、蔡英德)
  • Raspberry Pi Cookbook

14 留言

  1. 5000是等待timeout的時間,也就是等待不超過5秒鐘。

    回覆刪除
  2. 老師好:我用一個超音波測距做5次不同測量,python要如何把這5個數據傳送到另一個.py程式做進一步計算,學生愚鈍查很久沒找到,請求老師解惑,謝謝

    回覆刪除
    回覆
    1. 你好, 因為要量測5次再交由另一個.py處理,建議寫成檔案的方式,再由另一個.py讀入檔案進行計算處理。Python 的檔案讀寫不複雜,你可以參考一下f=open(檔名, 模式)、f.readlines() 等指令。

      刪除
  3. 老師你好,小弟這邊有個問題,我打算做一個自動判斷是否已滿的垃圾桶,如果滿了會自動發信到特定信箱。
    那方法是打算使用超音波感測器裝在垃圾桶8成的高度,如果連續10秒偵測到物體(如果不設延遲的秒數會導致每丟一次垃圾 超音波就偵測到一次,因而發信),就代表垃圾已經到達8成滿,可以寄信給特定信箱了。
    小弟是新手希望老師能稍微解惑一下...
    第一個問題是,我要如果要做到那個延遲10秒才判定是True(True表示垃圾桶已滿),可以怎麼做呢?? 偵測到的每一秒就count++一次,count=10就變成True嗎?

    第二個問題是,樹莓派有那種判斷式為True就自動發信的function嗎??

    如果可以的話希望老師能給新手一點指導

    回覆刪除
    回覆
    1. 你好,
      第一個問題可以用Count解決,如你提的方法,自己判斷次數後,觸發Mail發送。
      第二個問題是你在樹莓派使用什麼語言呢?如果是用Ptyhon, 有段Python發送郵件的程式可提供參考:https://docs.python.org/2/library/email-examples.html,其他語言可找一下相關的範例,應該很多。


      刪除
    2. 是python沒錯,了解了,非常謝謝指導!

      刪除
  4. 您好 如果樹莓派 不管有沒有接上HC-SR04
    執行程式碼 都會一直跑出距離是正常的嗎??

    回覆刪除
    回覆
    1. 是的, 我程式沒有判斷讀取HC-SR04的邏輯,一直出現距離是正常的。改天來改一下程式,判斷讀取值再呈現比較好。

      刪除
    2. 作者已經移除這則留言。

      刪除
    3. 真的嗎~~期待您的新程式碼 因為最近剛好作業做到HC-SR04
      參考到您的想說怎麼會一直出現距離~

      刪除
  5. 老師您好 請問如果超音波感測超過他可偵測距離後就不再繼續回傳數值,就算再給他感測近處的物體他也不回傳數值,請問要怎麼解決才好。

    回覆刪除
  6. import RPi.GPIO as GPIO
    import time

    TRIG=23
    ECHO=24
    dir=0
    limiDist=20

    GPIO.setmode(GPIO.BCM)
    GPIO.setup(TRIG,GPIO.OUT)
    GPIO.setup(ECHO,GPIO.IN)
    GPIO.setup(2,GPIO.OUT)
    GPIO.setup(3,GPIO.OUT)
    GPIO.setup(4,GPIO.OUT)
    GPIO.setup(13,GPIO.OUT)
    GPIO.setup(19,GPIO.OUT)
    GPIO.setup(26,GPIO.OUT)

    def test_distance():
    GPIO.output(TRIG,GPIO.HIGH)
    time.sleep(0.07)
    GPIO.output(TRIG,GPIO.LOW)
    while GPIO.input(ECHO)==0:
    start=time.time()
    while GPIO.input(ECHO)==1:
    end=time.time()
    distance=(end-start)*17150
    return distance
    def forward():
    GPIO.output(2,True)
    GPIO.output(3,True)
    GPIO.output(4,False)
    GPIO.output(13,True)
    GPIO.output(26,True)
    GPIO.output(19,False)
    def backward():
    GPIO.output(2,True)
    GPIO.output(3,False)
    GPIO.output(4,True)
    GPIO.output(13,True)
    GPIO.output(26,False)
    GPIO.output(19,True)
    def turn():
    GPIO.output(2,True)
    GPIO.output(3,False)
    GPIO.output(4,False)
    GPIO.output(13,True)
    GPIO.output(26,False)
    GPIO.output(19,True)
    def stop():
    GPIO.output(2,True)
    GPIO.output(3,False)
    GPIO.output(4,False)
    GPIO.output(13,True)
    GPIO.output(19,False)
    GPIO.output(26,False)
    while True:
    print("{:.1f}".format(test_distance()))
    try:
    dist=test_distance()
    except Exception:
    dist=400
    if dist>limiDist:
    if dir !=0:
    stop()
    dir=0
    time.sleep(0.5)
    forward()
    elif dist<limiDist:
    if dir !=0:
    stop()
    dir=1
    time.sleep(0.5)
    turn()
    else:
    if dir !=1:
    stop()
    dir=1
    time.sleep(0.5)
    turn()
    time.sleep(1)
    這是我的程式碼

    回覆刪除
  7. 請問 程式打進去了要怎麼執行

    回覆刪除

張貼留言

較新的 較舊