.. include:: /index.rst
:start-after: start_hello_message
:end-before: end_hello_message
.. _py_fun_count:
4.6 カウント装置
=======================
**はじめに**
このプロジェクトでは、PIR モーションセンサーと 4 桁 7 セグメントディスプレイを使用して、人の通過数をカウントするシステムを作成します。PIR センサーが動きを検出するたびに、表示されるカウント値が 1 ずつ増加します。このプロジェクトは、廊下や入口の通行量を監視する用途に適しており、GPIO プログラミングと表示制御を実践的に学ぶことができます。
----------------------------------------------
**必要なもの**
このプロジェクトに必要なコンポーネントは以下のとおりです。
.. list-table::
:widths: 30 20
:header-rows: 1
* - COMPONENT INTRODUCTION
- PURCHASE LINK
* - :ref:`cpn_breadboard`
- |link_breadboard_buy|
* - :ref:`cpn_wires`
- |link_wires_buy|
* - :ref:`cpn_resistor`
- |link_resistor_buy|
* - :ref:`cpn_pir`
- |link_pir_buy|
* - :ref:`cpn_4_digit`
- \-
* - :ref:`cpn_74hc595`
- |link_74hc595_buy|
* - :ref:`cpn_fusion_hat`
- \-
* - Raspberry Pi
- \-
----------------------------------------------
**回路図**
以下はこのプロジェクトの回路図です。
.. image:: img/fzz/4.1.4_sch.png
:width: 100%
:align: center
----------------------------------------------
**配線図**
以下の配線図に従って、各コンポーネントを正しく接続してください。
.. image:: img/fzz/4.1.4_bb.png
:width: 80%
:align: center
.. note::
最適な感度と検出距離を得るために、PIR モジュール上の 2 つの可変抵抗を調整してください。最良の結果を得るには、両方を反時計回りいっぱいまで回してください。
.. image:: ../python/img/PIR_TTE.png
:width: 300
:align: center
----------------------------------------------
**サンプルの実行**
このチュートリアルで使用するすべてのサンプルコードは ``ai-lab-kit`` ディレクトリに含まれています。
以下の手順に従ってサンプルを実行してください。
.. raw:: html
.. code-block:: shell
cd ~/ai-lab-kit/python/
sudo python3 4.6_CountingDevice.py
この Python スクリプトは、PIR モーションセンサーと 74HC595 シフトレジスタで駆動する 7 セグメントディスプレイを組み合わせたものです。このスクリプトは次のように動作します。
1. **動きの検出**: GPIO22 に接続された PIR モーションセンサーが動きを検出します。動きを 1 回検出するごとにカウンターが 1 増加します。
2. **数字の表示**:
- 現在のカウント値が 4 桁の 7 セグメントディスプレイに表示されます。
- 動きが検出されるたびに表示が更新され、数値が増加します。
3. **継続的な監視**: スクリプトは動きを継続的に監視し、表示をリアルタイムで更新します。
4. **安全な終了**: ``Ctrl+C`` を押すと、すべてのピンがオフになり、スクリプトは安全に終了します。
----------------------------------------------
**コード**
以下は、このプロジェクトで使用する Python スクリプトです。
.. raw:: html
.. code-block:: python
#!/usr/bin/env python3
from fusion_hat.pin import Pin, Mode, Pull
# Initialize PIR motion sensor on GPIO 22 (input with pull-down)
pir = Pin(22, mode=Mode.IN, pull=Pull.DOWN)
# Define GPIO pins for the 74HC595 shift register
SDI = Pin(17, mode=Mode.OUT) # Serial Data Input
RCLK = Pin(4, mode=Mode.OUT) # Register Clock (latch)
SRCLK = Pin(27, mode=Mode.OUT) # Shift Register Clock
# Define GPIO pins used to select one of the 4 digits on the 7-segment display
placePin = [Pin(pin, mode=Mode.OUT) for pin in (23, 24, 25, 12)]
# Segment code table for digits 0–9 (common-cathode 7-segment display)
number = (0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90)
# Counter to display the number of detections
counter = 0
def clearDisplay():
"""Clear the 7-segment display by shifting out 'all off' bits."""
for _ in range(8):
SDI.high() # Send a high bit
SRCLK.high() # Clock it in
SRCLK.low()
RCLK.high() # Latch output
RCLK.low()
def hc595_shift(data):
"""Shift a full byte of data into the 74HC595 to control the segments."""
for i in range(8):
SDI.value(0x80 & (data << i)) # Output next bit of the data
SRCLK.high() # Pulse shift clock
SRCLK.low()
RCLK.high() # Latch data to display
RCLK.low()
def pickDigit(digit):
"""Enable one specific digit (0–3) on the multiplexed display."""
for pin in placePin:
pin.low() # Disable all digits
placePin[digit].high() # Activate the selected digit
def display():
"""Display the current 4-digit counter value on the 7-segment display."""
global counter
# Display ones place
clearDisplay()
pickDigit(3)
hc595_shift(number[counter % 10])
# Display tens place
clearDisplay()
pickDigit(2)
hc595_shift(number[counter % 100 // 10])
# Display hundreds place
clearDisplay()
pickDigit(1)
hc595_shift(number[counter % 1000 // 100])
# Display thousands place
clearDisplay()
pickDigit(0)
hc595_shift(number[counter % 10000 // 1000])
def loop():
"""Main loop: continuously update display and detect PIR motion transitions."""
global counter
currentState = 0
lastState = 0
while True:
display() # Refresh 7-segment display
currentState = 1 if pir.value() == 1 else 0 # Read PIR output
# Detect rising edge: motion detected now, but not in last cycle
if currentState == 1 and lastState == 0:
counter += 1 # Increase the count
lastState = currentState # Save state for next loop
try:
loop() # Start main loop
except KeyboardInterrupt:
# Clean up GPIO pins when exiting
SDI.low()
SRCLK.low()
RCLK.low()
pass
----------------------------------------------
**コードの解説**
1. **動きの検出:**
PIR センサーが動きを検出すると、その信号によってカウンターが増加します。
2. **表示の更新:**
``hc595_shift`` 関数と ``pickDigit`` 関数が連携して 7 セグメントディスプレイを制御し、カウンター値をリアルタイムで更新します。
3. **メインループ:**
``loop`` 関数は PIR センサーを継続的に監視し、動きが検出されると表示を更新します。
4. **割り込み処理:**
スクリプトは中断時にすべての GPIO ピンを安全にオフにし、予期しない動作を防ぎます。
----------------------------------------------
**トラブルシューティング**
1. **ディスプレイが動作しない**:
- **原因**: 7 セグメントディスプレイまたはシフトレジスタの配線が正しくありません。
- **対処方法**:
- GPIO ピンとシフトレジスタの SDI、SRCLK、RCLK ピンの接続を確認してください。
- すべての ``placePin`` の接続が対応する桁ピンと一致していることを確認してください。
2. **モーションセンサーが反応しない**:
- **原因**: PIR センサーの配線ミス、または周囲環境による干渉。
- **対処方法**:
- PIR センサーが GPIO22、電源、GND に正しく接続されていることを確認してください。
- 必要に応じて、PIR センサー上の感度および遅延時間調整用の可変抵抗を調整してください。
3. **カウンターが増加しない**:
- **原因**: モーションセンサーの状態変化が正しく検出されていません。
- **対処方法**:
- ``pir.value()`` の値が正しく更新されているか確認してください。
- ``loop()`` 関数にデバッグ用の ``print`` を追加して、状態遷移を確認してください。
4. **表示がちらつく**:
- **原因**: 表示更新のタイミングが適切でない、または遅延が不足しています。
- **対処方法**: ``display()`` 関数に小さな待機時間を追加して表示を安定させてください。
.. code-block:: python
import time
time.sleep(0.01)
----------------------------------------------
**拡張アイデア**
1. **双方向カウンター**: もう 1 つ PIR センサーを追加して反対方向の動きを検出し、カウンターを減算できるようにします。
.. code-block:: python
pir2 = Pin(16, Pin.IN, pull= Pin.PULL_DOWN)
if pir2.value()==1:
counter -= 1
2. **しきい値アラート**: カウンターがあらかじめ設定した値を超えたときに、LED やブザーなどで警告を出します。
.. code-block:: python
from fusion_hat import Buzzer
buzzer = Buzzer(Pin(22))
if counter > 50:
buzzer.on()
else:
buzzer.off()
3. **データログ記録**: カウンター値とタイムスタンプをファイルへ保存して分析できます。
.. code-block:: python
with open("motion_log.txt", "a") as log_file:
log_file.write(f"{time.time():.3f}, Counter: {counter}\n")
4. **タイマーによるリセット**: 一定時間動きがない場合にカウンターをリセットします。
.. code-block:: python
last_motion_time = time.time()
if time.time() - last_motion_time > 300: # 5 minutes
counter = 0
5. **イベント連動の動作**: カウンターが特定の値に達したときに、家電の制御など特定の動作を実行します。
----------------------------------------------
**まとめ**
このプロジェクトでは、PIR センサーと 7 セグメントディスプレイを使用した動き検出と表示制御の基本を学びます。より高度な IoT やデータ可視化プロジェクトへ発展させるための、実践的な基礎となります。