HC-SR501 是一種被動紅外線感測模組(PIR,Passive Infrared Sensor),主要用於偵測人體或動物的紅外線輻射變化,從而判斷是否有物體進入感測範圍。它的基本原理如下:
紅外線偵測:模組內有一個紅外線感測元件(Pyroelectric sensor),它能夠偵測到人體所發出的紅外線輻射變化。人體在移動時,輻射的變化會觸發感測器。
透鏡組件:模組上有一個菲涅爾透鏡,可以將廣角範圍內的紅外線聚焦到感測器上,提升偵測距離和範圍。
信號處理:當感測器偵測到紅外線變化時,會產生微弱的電壓變化。模組內部的運算放大器將此信號放大並轉換成高低電平輸出信號。
輸出信號:當偵測到移動時,模組的 OUT 腳位會輸出高電平(3.3V 或 5V),表示有物體移動;沒有偵測到時,輸出低電平(0V)。
VCC:接 Raspberry Pi 的 5V 或 3.3V(建議使用 5V)。
GND:接 Raspberry Pi 的地(GND)。
OUT:接 Raspberry Pi 的 GPIO16(物理引腳號 36)。
以下是用 Python 寫的一個簡單測試程式,來讀取 HC-SR501 的輸出信號,當偵測到移動時,會在終端機中打印出相關訊息:
python
複製程式碼
import RPi.GPIO as GPIO
import time
# 設定 GPIO 模式為 BCM
GPIO.setmode(GPIO.BCM)
# 定義 HC-SR501 的輸出腳位
PIR_PIN = 16
# 設定 GPIO 腳位為輸入模式
GPIO.setup(PIR_PIN, GPIO.IN)
# 設定程式結束時的清理函數
def cleanup():
GPIO.cleanup()
try:
print("PIR 模組初始化中...")
time.sleep(2) # 等待模組穩定
print("準備偵測中(按 Ctrl+C 結束)")
while True:
if GPIO.input(PIR_PIN):
print("偵測到移動!")
else:
print("未偵測到移動")
time.sleep(1) # 每秒偵測一次
except KeyboardInterrupt:
print("程式結束")
finally:
cleanup()
初始化與設定:
使用 GPIO.setmode(GPIO.BCM) 來設定 GPIO 模式。
使用 GPIO.setup(PIR_PIN, GPIO.IN) 將 GPIO16 設定為輸入模式。
偵測迴圈:
程式進入無限迴圈 while True:,每秒檢查一次 HC-SR501 的輸出。
如果感測到移動(GPIO.input(PIR_PIN) 為 True),則打印「偵測到移動!」。
否則打印「未偵測到移動」。
程式結束:
捕捉到 KeyboardInterrupt(如按 Ctrl+C 結束程式),執行 finally 中的 cleanup() 函數來清理 GPIO 設定。
這樣的程式可以用來簡單測試 HC-SR501 模組的偵測功能。
建議在訊號輸出端可以並連一顆LED串上330歐姆電阻,就可以觀測到訊號輸出端的變化,有助於在程式開發時比對功能是否正常。
動態變化觸發:
模組感測的是紅外線輻射的變化,而不是靜態的紅外線值。當人體或動物在感測範圍內移動時,會造成紅外線輻射量的變化,這種變化會被模組內的熱釋電感測器偵測到,然後觸發輸出信號。
靜態不觸發:
如果人體在感測範圍內「靜止不動」,紅外線的輻射強度不會有明顯的變化,模組不會再持續輸出高電平信號,而會回到低電平狀態。因此,靜止的狀態不會持續觸發感測器。
重複觸發模式與單次觸發模式:
HC-SR501 上有一個跳線帽可以設定「重複觸發模式」(Repeat Triggering Mode)和「單次觸發模式」(Single Triggering Mode)。
重複觸發模式 (H):在感測範圍內持續有移動,模組會不斷重複輸出高電平。
單次觸發模式 (L):無論移動是否持續發生,模組在每次感測到移動後只會輸出一次高電平,然後等待設置的時間(Time Delay)結束,再次偵測新移動。
延遲時間設定:
HC-SR501 模組上有一個電位器可以調整延遲時間(Time Delay),範圍通常在幾秒到幾分鐘之間。這個設定決定了模組在偵測到移動後,會保持高電平輸出的時間長短。
靜止不觸發:人體或物體靜止在感測範圍內,模組將不會繼續輸出高電平信號,反而會回到低電平。
移動觸發:當人體或物體開始移動,模組會偵測到紅外線輻射變化,輸出高電平信號。
這樣的設計使得 HC-SR501 非常適合用來偵測有人進入房間或區域,或是觸發某些自動化的動作,但不適合用來判斷物體在感測範圍內的靜止狀態。
如果你需要偵測靜態的存在狀態,可能需要考慮其他技術,例如超聲波感測器、雷達感測器或攝影機圖像辨識。
最後決定的程式版本
可以在「停止計時...」訊息中加入當前的時間(格式為「時:分:秒」),並將偵測的間隔時間從 0.1 秒改為 3 秒。這樣每 3 秒檢查一次移動狀態,並在停止計時時顯示當前的時間。以下是修改過的程式碼:
python
複製程式碼
import RPi.GPIO as GPIO
import time
from datetime import datetime
# 設定 GPIO 模式為 BCM
GPIO.setmode(GPIO.BCM)
# 定義 HC-SR501 的輸出腳位
PIR_PIN = 16
# 設定 GPIO 腳位為輸入模式
GPIO.setup(PIR_PIN, GPIO.IN)
# 設定相關變數
detect_flag = False # 是否偵測到物體
start_time = 0 # 計時開始時間
timeout = 30 # 超時時間,單位:秒
interval = 3 # 偵測間隔時間,單位:秒
# 設定程式結束時的清理函數
def cleanup():
GPIO.cleanup()
try:
print("PIR 模組初始化中...")
time.sleep(2) # 等待模組穩定
print("準備偵測中(按 Ctrl+C 結束)")
while True:
# 讀取當前的 PIR 狀態
current_state = GPIO.input(PIR_PIN)
if current_state:
if not detect_flag:
# 當偵測到移動並且標誌為 False 時,開始計時
detect_flag = True
start_time = time.time()
print("\n偵測到物體移動! 開始計時...", end="\r")
else:
# 偵測到物體移動且計時已開始,更新計時器顯示
elapsed_time = time.time() - start_time
print(f"持續偵測到物體移動! 計時... {elapsed_time:.2f} 秒", end="\r")
else:
# 當偵測到物體停止移動(狀態改變)且計時器已啟動
if detect_flag:
elapsed_time = time.time() - start_time
# 取得當前時間
stop_time = datetime.now().strftime('%H:%M:%S')
print(f"\n30秒內未偵測到移動。持續計時時間為:{elapsed_time:.2f} 秒,停止計時... 停止時間:{stop_time}")
detect_flag = False
start_time = 0
# 每隔3秒檢查一次狀態,更新時間顯示
time.sleep(interval)
except KeyboardInterrupt:
print("\n程式結束")
finally:
cleanup()
加入停止時間顯示:
使用 datetime.now().strftime('%H:%M:%S') 來獲取當前的時間,並格式化為 時:分:秒 的形式。
當計時結束時,顯示「停止計時... 停止時間:時:分:秒」。
偵測間隔修改為 3 秒:
將偵測的間隔時間設置為 interval = 3,並在 time.sleep(interval) 中應用,這樣每 3 秒檢查一次移動狀態。
計時顯示與結束顯示:
當偵測到移動時,會每 3 秒更新一次計時器的時間。
當沒有偵測到移動且超過設定時間時,會顯示計時結果和停止的時間。
這樣修改後,程式應該能夠每 3 秒更新一次計時器,並在停止計時時顯示時間。