✊🤚✌️ Raspberry Pi 5 + PiCamera2 + MediaPipe 手勢辨識遊戲:「剪刀、石頭、布」

利用 MediaPipe Hands 偵測 21 個手部關鍵點,自行判斷使用者的手勢屬於哪一種。


🧰 一、環境與需求

✅ 硬體

  • Raspberry Pi 5(64-bit OS)

  • Raspberry Pi Camera Module v2/v3(已接上 CSI)

✅ 軟體

  • Python 3.9+

  • OpenCV

  • PiCamera2

  • MediaPipe 0.10.9(可於 ARM 安裝)


🛠️ 二、環境安裝

sudo apt update
sudo apt install -y python3-opencv python3-picamera2 libatlas-base-dev
pip install mediapipe==0.10.9

確認安裝成功:

python3 -c "import mediapipe as mp; print(mp.__version__)"

🧠 三、手勢分類邏輯(簡單版)

我們使用 MediaPipe 偵測手部的 21 個關鍵點(landmarks),然後依據以下規則簡單分類:

手勢 判斷邏輯(簡化)
✊ 石頭 所有手指都彎曲
✋ 布 所有手指打開
✌️ 剪刀 只有食指與中指伸直,其餘彎曲

📸 四、完整程式碼

🔹 檔名:rps_game_picamera2.py

import cv2
import mediapipe as mp
from picamera2 import Picamera2
import time

# 初始化 MediaPipe Hands 與 Drawing 工具
mp_hands = mp.solutions.hands
mp_drawing = mp.solutions.drawing_utils

hands = mp_hands.Hands(static_image_mode=False,
                       max_num_hands=1,
                       min_detection_confidence=0.5,
                       min_tracking_confidence=0.5)

# 初始化 PiCamera2
picam2 = Picamera2()
picam2.preview_configuration.main.size = (640, 480)
picam2.preview_configuration.main.format = "RGB888"
picam2.configure("preview")
picam2.start()
time.sleep(2)

# 判斷手勢的函數(簡單版)
def classify_hand_sign(landmarks):
    finger_states = []

    # 拇指判斷(橫向)
    if landmarks[4].x > landmarks[3].x:
        finger_states.append(1)
    else:
        finger_states.append(0)

    # 其餘手指(縱向)
    for tip_id in [8, 12, 16, 20]:  # 食指、中指、無名指、小指
        if landmarks[tip_id].y < landmarks[tip_id - 2].y:
            finger_states.append(1)
        else:
            finger_states.append(0)

    # 根據 finger_states 決定結果
    if finger_states == [0, 0, 0, 0, 0]:
        return "Rock ✊"
    elif finger_states == [1, 1, 1, 1, 1]:
        return "Paper ✋"
    elif finger_states[1] == 1 and finger_states[2] == 1 and all(f == 0 for i, f in enumerate(finger_states) if i != 1 and i != 2):
        return "Scissors ✌️"
    else:
        return "Unknown"

# 主迴圈
while True:
    frame = picam2.capture_array()
    image = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)

    result = hands.process(image)

    if result.multi_hand_landmarks:
        for hand_landmarks in result.multi_hand_landmarks:
            mp_drawing.draw_landmarks(image, hand_landmarks, mp_hands.HAND_CONNECTIONS)

            # 判斷手勢
            sign = classify_hand_sign(hand_landmarks.landmark)

            # 顯示文字
            cv2.putText(image, sign, (10, 50), cv2.FONT_HERSHEY_SIMPLEX,
                        1.5, (0, 255, 0), 3)

    cv2.imshow("RP5 - Rock Paper Scissors", image)

    if cv2.waitKey(1) & 0xFF == 27:
        break

cv2.destroyAllWindows()
picam2.stop()

✅ 執行方式

python3 rps_game_picamera2.py

👁️ 將你的手伸向攝影機!畫面上會顯示:

  • ✊ Rock

  • ✋ Paper

  • ✌️ Scissors

  • Unknown(辨識不到)


📌 提升辨識準確度建議

方法 建議
環境光源 使用充足但不刺眼的光線
相機高度 設定攝影機與使用者手勢齊平
多角度資料 可增加不同手勢角度的訓練與測試資料
模型改進 改用分類器(KNN/SVM)分類 landmarks 可提升準確率

💡 延伸功能建議

功能 敘述
玩家 vs 電腦 隨機生成電腦手勢,進行對戰
加入倒數計時 用 OpenCV 畫出 3-2-1 計時圖示
配合聲音提醒 使用 pygame.mixer 播放音效
加上 Flask UI 做成網頁版互動平台(可結合 WebSocket)

📚 相關資源


 

文章標籤
全站熱搜
創作者介紹
創作者 liusming 的頭像
liusming

劉老師的跨域創想工坊

liusming 發表在 痞客邦 留言(0) 人氣(113)