跳轉到內容

維基少年:樹莓派/樹莓派 Python GPIO Zero 距離感測器

來自華夏公益教科書

教程作者:喬納森·蒂格 - 公共領域
2017 年 1 月 28 日 - www.cotswoldjam.org

本教程將介紹使用流行的 HC-SR04 超聲波距離感測器、樹莓派和gpiozero庫來測量距離。

你的包中包含以下元件

  • ×4 個 M-F 跳線(公針到母座)
  • ×1 個 R1 680Ω(藍灰棕)電阻
  • ×1 個 R2 470Ω(黃紫棕)電阻
  • ×1 個迷你麵包板
  • ×1 個 HC-SR04 超聲波感測器

使用麵包板

[編輯 | 編輯原始碼]

麵包板用於原型設計和測試電子元件,無需焊接。

每個孔都使用一種符號系統進行標識,該系統使用水平方向上的數字和垂直方向上的字母來標識面包板上的孔。

使用樹莓派的 GPIO 引腳

[編輯 | 編輯原始碼]

樹莓派有兩種方法對通用輸入輸出 (GPIO) 引腳進行標記

  • 板號 – 從左下角開始,從 1 開始,向上和向右排列,一直到 40。
  • BCM 號碼(博通號碼) – 是樹莓派處理器看到的引腳連線方式。

步驟 0:確保你的樹莓派已關閉,並且電源線已斷開。

步驟 1:取一根跳線,將公針端插入麵包板上的B1,母座端插入樹莓派上的引腳 6 (GND)

步驟 2:取一根跳線,將公針端插入麵包板上的B3,母座端插入樹莓派上的引腳 3 (GPIO 20)。這是樹莓派用於觸發距離測量儀進行測量的聯結器。

步驟 3:取 R1 680Ω(藍灰棕)電阻 - 帶藍色色線的那個,將引腳插入麵包板上的E1I2 孔中 - 電阻的方向無關緊要。

步驟 4:取另一個電阻,R2 470Ω(黃紫棕) - 帶黃色色線的那個,將引腳插入麵包板上的D2H2 孔中 - 方向無關緊要。

步驟 5:輕輕地將 R1 電阻彎離 R2,這樣E1D2 處的引腳不會接觸到(如果接觸也不會損壞,只是無法工作)。

步驟 6:取一根跳線,將公針端插入麵包板上的J2,母座端插入樹莓派上的引腳 1 (GPIO 21)。這是樹莓派用於監聽測量結果的聯結器。

步驟 7:取最後一根跳線,將公針端插入麵包板上的B4,母座端插入樹莓派上的引腳 2 (5V)

步驟 8:最後,插入 HC-SR04。它有 4 個引腳,需要插入A1A2A3A4。感測器背面,所有黑色 IC 朝向麵包板,兩個銀色感測器指向遠離麵包板的方向。

啟動你的樹莓派。從桌面選單中選擇程式設計 - Python 3 (IDLE)。然後使用檔案,新建檔案建立一個新程式。

輸入以下程式,然後使用檔案,儲存將程式儲存為任意名稱(不要忘記在末尾加上 .py)在~/python/distance 資料夾中,然後使用執行選單,執行模組執行它。

#!/usr/bin/python

from gpiozero import DistanceSensor
from time import sleep

sensor = DistanceSensor(echo=21, trigger=20, max_distance=2.0)

while True:

   distance = sensor.distance * 100
   print("Distance : %.1f" % distance)
   sleep(1)

程式的功能

#!/usr/bin/python

第一行是註釋(以 # 開頭),告訴作業系統程式是用什麼語言編寫的。由於你將從 Python 本身執行程式,因此這行是多餘的(但這是良好的做法)。

from gpiozero import DistanceSensor
from time import sleep

from 行告訴計算機學習新東西。計算機可以從其他人編寫的程式中學習;我們稱這些其他程式為“庫”。我們的程式需要來自gpiozero 庫的DistanceSensor 函式,該函式將用於與感測器進行通訊,以及來自時間庫的sleep 函式,以便我們插入暫停。函式只是位於其他地方的一些程式碼,我們可以使用它來執行某些操作。

sensor = DistanceSensor(echo=21, trigger=20, max_distance=2.0)

然後我們使用匯入的DistanceSensor 函式建立一個名為sensor 的物件。我們傳遞的引數表明,用於觸發感測器的trigger 將在 GPIO 引腳 20 上;用於監聽回覆的echo 將在 GPIO 引腳 21 上;應返回的最大距離為 2.0 米。

while True:

while True: 告訴程式在迴圈中永遠執行。

   distance = sensor.distance * 100
   print("Distance : %.1f" % distance)
   sleep(1)

然後我們透過呼叫sensor.distance 獲取當前感測器讀數,將讀數乘以 100(因為返回值是毫米);僅用小數點後一位輸出值,最後休眠一秒,然後while True: 使我們再次進行迴圈。

其他說明

[編輯 | 編輯原始碼]

這些說明是主教程的補充,解釋了使用兩個電阻製作“分壓器”以及一個直接控制感測器的示例程式。

分壓器

[編輯 | 編輯原始碼]

樹莓派是一款 3.3V 裝置。本教程中使用的感測器,HC-SR04,是一款 5V 裝置。這意味著感測器需要 5V 電源,並且,對於樹莓派來說至關重要的是,感測器返回給樹莓派的結果的電壓等級也是 5V。將感測器輸出直接連線到樹莓派幾乎肯定會損壞樹莓派。

這種情況在現實世界中很常見,你想將兩個裝置連線在一起,但它們的電壓不同。有幾種方法可以解決這個問題,但最簡單的方法之一,也是這裡使用的方法,是使用兩個電阻製作一個“分壓器”,將電壓從 5V 降至 3.3V 的安全值以下。

兩個電阻串聯放置在電源電壓和零電壓之間,兩個電阻之間點的電壓會根據兩個電阻的值按比例降低。

Vout =VinR1/R1 + R2

所以,將 5V 作為 Vin 和我們使用的兩個電阻的值代入,R1 = 680Ω 和 R2 = 470Ω,我們得到

Vout =Vin680/470 + 680 = 2.95V

這個 2.95V 可以安全地連線到 Pi。

一個直接與 HC-SR04 互動的程式

[編輯 | 編輯原始碼]

在本教程中,我們沒有直接與 HC-SR04 互動,而是將所有操作都留給了 GPIO Zero 庫的 DistanceSensor 功能。這樣做很方便,但有時瞭解確切的操作過程以及如何直接控制裝置很有用。

~/python/distance 資料夾中,你會找到一個名為 rpigpio_DistanceSensor.py 的檔案,它展示瞭如何直接觸發和讀取 HC-SR04 感測器,並使用 RPi.GPIO 庫。

以下是觸發 HC-SR04 進行測量並獲取結果的程式碼,所有操作都包含在一個名為 measure() 的函式中。

def measure():
   # This function measures a distance
   GPIO.output(GPIO_TRIGGER, True)
   time.sleep(0.00001)
   GPIO.output(GPIO_TRIGGER, False)
 
   start = time.time()
   while GPIO.input(GPIO_ECHO)==0:
      start = time.time()
    
   while GPIO.input(GPIO_ECHO)==1:
      stop = time.time()
 
   elapsed = stop-start
   distance = (elapsed * 34300)/2
    
   return distance

這段程式碼是做什麼的?

[編輯 | 編輯原始碼]

GPIO_TRIGGER 設定為 True,休眠 10 微秒,然後將 GPIO_TRIGGER 設定為 False,這會觸發感測器進行讀數。

然後,程式碼將一個名為 start 的變數設定為當前時間,並進入一個緊密迴圈,只要 GPIO_ECHO 為 0(= 低),它就會在每次迴圈中重置 start 變數。

GPIO_ECHO 為 1(= 高)時,第一次迴圈將停止。此時,start 包含 Echo 引腳變高的時間。

現在,程式碼進入一個緊密迴圈,只要 GPIO_ECHO 為高,它就會在每次迴圈中設定 stop 變數。

最後,當 GPIO_ECHO 再次變低時,迴圈退出。此時,start 包含 Echo 變高的的時間,stop 包含 Echo 再次變低的時間。

現在只需進行一些數學運算即可計算出 Echo 引腳變高的持續時間,將其乘以聲速(34300)併除以 2(因為感測器發出的聲音是出去再回來的),我們就可以得到以釐米(cm)為單位的 distance 值。

Distancesensor-gpiozero.pdf

[編輯 | 編輯原始碼]

本教程的原始 PDF 檔案在維基共享資源:Distancesensor-gpiozero.pdf

Distancesensor-gpiozero-additional.pdf

[編輯 | 編輯原始碼]

本教程的原始 PDF 檔案在維基共享資源:Distancesensor-gpiozero-additional.pdf

rpigpio_DistanceSensor.py

[編輯 | 編輯原始碼]
#!/usr/bin/python

import time
import RPi.GPIO as GPIO

# -----------------------
# Define measure function
# -----------------------
def measure():
  # This function measures a distance
  GPIO.output(GPIO_TRIGGER, True)
  time.sleep(0.00001)
  GPIO.output(GPIO_TRIGGER, False)
  start = time.time()

  while GPIO.input(GPIO_ECHO)==0:
    start = time.time()

  while GPIO.input(GPIO_ECHO)==1:
    stop = time.time()

  elapsed = stop-start
  distance = (elapsed * 34300)/2

  return distance

# -----------------------
# Main Script
# -----------------------

# Use BCM GPIO references instead of physical pin numbers
GPIO.setmode(GPIO.BCM)

# Define GPIO to use on Pi
GPIO_TRIGGER = 20
GPIO_ECHO    = 21

# Set trigger as output and echo as input
GPIO.setup(GPIO_TRIGGER,GPIO.OUT)  # Trigger
GPIO.setup(GPIO_ECHO,GPIO.IN)      # Echo

# Set trigger to False (Low)
GPIO.output(GPIO_TRIGGER, False)

# Wrap main content in a try block so we can
# catch the user pressing CTRL-C and call the
# GPIO cleanup function.
try:
  while True:
    distance = measure()
    print("Distance : %.1f" % distance)
    time.sleep(1)

except KeyboardInterrupt:
  # User pressed CTRL-C, reset GPIO settings
  GPIO.cleanup()

gpiozero_DistanceSensor.py

[編輯 | 編輯原始碼]
#!/usr/bin/python

# -----------------------
# Import required Python libraries
# -----------------------
from gpiozero import DistanceSensor
from time import sleep

# -----------------------
# Main Script
# -----------------------

sensor = DistanceSensor(echo=21, trigger=20, max_distance=2.0)

while True:

  distance = sensor.distance * 100
  print("Distance : %.1f" % distance)
  sleep(1)
華夏公益教科書