PawAI
PawAI

姿勢辨識開發歷程總整理

專案開發紀錄:基於視覺的即時人體動作感知模組 (Real-time Pose & Action Detection)

1. 專案目標與背景

本模組旨在透過一般 WebCam 攝影機,即時擷取人體的骨架關鍵點(Landmarks),並透過幾何運算與時間序列過濾,辨識出使用者的「靜態姿勢」與「動態動作」。

此感知模組為前端輸入節點,當成功捕捉到穩定動作後,未來預計將狀態打包成 JSON 格式,透過 ROS2 傳遞給後端的系統或決策大腦(如 VLM/VLA 模型),賦予機器人或系統視覺理解能力。


2. 使用技術棧 (Tech Stack)

  • 語言: Python 3.9+
  • 環境管理: Conda (Virtual Environment), pip
  • 核心套件:
    • opencv-python: 負責存取硬體攝影機、影像色彩轉換、視窗縮放控制與 UI 畫面繪製。
    • mediapipe (v0.10.14): Google 開源的高效能跨平台機器學習框架,負責提取 33 個人體骨架 3D 關鍵點。
    • numpy (v1.26.4): 負責底層關鍵點座標的矩陣與幾何向量運算。
  • 標準函式庫: time (時間濾波), math (歐氏距離與三角函數計算), collections.deque (時間平滑滑動視窗)。

3. 目前功能與限制

A. 支援姿勢與狀態映射 (Pose Emoji Mapping)

系統目前定義並分類以下 8 種行為狀態:

  • standing: 🧍 (站立)
  • sitting: 🪑 (坐下)
  • crouching: 🏋️ (蹲下)
  • bending: 🙇 (彎腰)
  • hands_on_hips: 🦸 (手插腰)
  • kneeling_one_knee: 🧎 (單膝跪地)
  • fallen: ⚠️ (跌倒)
  • unknown: 🧍 (未知)

B. 模組穩定度評估

  • 🟢 高穩定度狀態:standing (站立)、crouching (蹲下)、sitting (坐下)、bending( 彎腰)、fallen (跌倒)。此類動作幾何特徵鮮明,判定率高。
  • 🟡 待優化/不穩定狀態:hands_on_hips (手插腰)、kneeling_one_knee (單膝跪地)。易受正面視角壓縮或特殊衣物遮擋干擾,仍有小幅波動風險。

C. 現階段系統限制

  1. 視角依賴性:受限於 2D 影像投影,身體轉向側面(Side Profile) 時,關節間的幾何角度與深度落差最為清晰,系統會比較容易且精準地偵測到動作。
  2. 時間特徵延遲:為了過濾環境雜訊與極短暫的晃動,系統設有時間平滑與防抖機制,使用者必須維持該姿勢數秒(數幀)以上,系統才會正式確認並變更當前狀態。
  3. 風險控管機制(跌倒安全鎖):目前因為自然活動中(如深度彎腰、突發大動作)引發「跌倒 (Fallen)」狀態的誤觸頻率仍然偏高。為避免造成後端系統的警報疲勞,目前偵測到跌倒後先不觸發後續的自動警示反應,僅於畫面上做視覺 log 紀錄,待後續結合大腦(VLM)進行多模態複驗。

4. 開發歷程與踩坑紀錄 (Troubleshooting)

問題一:套件已安裝卻出現 ModuleNotFoundError

  • 原因: 終端機(執行環境)與 VS Code 右下角的 Python 解譯器(Interpreter)不一致。套件安裝在 Conda 的虛擬環境中,但執行時卻使用了全域 (base) 的 Python。
  • 解法: 確保在終端機執行 conda activate pose_test,並確認指令行前方有出現 (pose_test) 標籤;若使用 PowerShell 無法啟動,則切換至 Command Prompt (cmd)。

問題二:numpy.core.multiarray failed to import (底層 C 語言報錯)

  • 原因: 依賴衝突 (Dependency Conflict)。最新的 NumPy 2.x 版本進行了底層 C API 大改版,導致舊版或預設版本的 OpenCV 與 MediaPipe 底層 C++ 引擎無法相容。
  • 解法: 將 NumPy 強制降級回 1.x 的最後穩定版本:pip install numpy==1.26.4。

問題三:Windows 系統下攝影機卡死或程式無聲崩潰 (Silent Crash)

  • 原因 1 (套件衝突): 環境中同時存在 opencv-python 與 opencv-contrib-python。兩者在呼叫 import cv2 時產生 Namespace 衝突,導致系統崩潰且不輸出任何報錯。
    • 解法: pip uninstall opencv-python opencv-contrib-python -y,然後僅透過安裝 mediapipe 讓其自動拉取正確的相依套件。
  • 原因 2 (MediaPipe 版本): MediaPipe 版本太新 (如 0.10.32+),其預設依賴與降級後的 NumPy 1.x 產生二次衝突。
    • 解法: 將 MediaPipe 降級至相容版本:pip install mediapipe==0.10.14。
  • 原因 3 (Windows 相機 API): OpenCV 預設呼叫相機的模式在部分 Windows 電腦上會因為硬體抽象層等待而 Hang 住。
    • 解法: 在宣告時強制加上 DirectShow 參數:cv2.VideoCapture(0, cv2.CAP_DSHOW)。

問題四:正面視角投影壓縮,導致「插腰 (Hands on hips)」頻繁落入「站立 (Standing)」

  • 原因: 舊版邏輯依賴手肘(Elbow)在 X 軸方向的外移距離來判斷插腰。當使用者正對鏡頭時,3D 空間的深度在 2D 畫面上被嚴重壓縮,導致 X 軸特徵極不明顯而觸發失敗。
  • 解法: 放棄 X 軸手肘位移判斷。改用 「手腕高度接近髖部 Y 座標」 結合 「雙手肘彎曲角度限制(elbow_angle < 145°)」。由於自然垂手時手肘接近打直(≈ 160–180°),加入手肘彎曲角能精準區分垂手與插腰。

問題五:單膝跪地(Kneeling on one knee)與插腰、蹲姿發生嚴重「搶判斷」

  • 原因 1: 做單膝跪姿時,使用者雙手會自然靠近髖部或大腿,導致插腰(Hands on hips)的幾何條件先被觸發,且原本插腰優先順序排在跪姿前面,導致誤判。
  • 原因 2: 跪地側的腳踝(Ankle)極易因為小腿重疊、地面阻擋或褲子而產生遮擋(Occlusion),導致 MediaPipe 座標盲猜或產生雜訊,造成原本只靠兩膝 Y 軸高低差的邏輯在「蹲下」時也會誤觸。
  • 解法:
    1. 調整優先權:將 kneeling_one_knee 的判斷順序調至 hands_on_hips 之前。
    2. 全局蹲姿預判:在進入這兩個狀態判斷前,先計算 is_crouching_likely(髖角 < 145 且 膝角 < 130)。只要判定極可能是蹲姿,就直接跳過跪地與插腰的檢查。
    3. 多條件聯防:針對跪地腿引入三選一觸發機制:
      • 跪地側腳踝可見度(visibility)低於 0.45(代表被遮擋)。
      • 腳背躺平貼地(腳踝 Y 座標與膝蓋 Y 座標高度差小於 0.08)。
      • 該側膝角明顯彎曲(< 140°),且與站立腳的膝角差距大於 25°(徹底排除蹲姿)。

5. 核心邏輯與演算法設計

本專案採用「啟發式規則 (Heuristic Rules)」進行多階層姿勢判定,具體架構與排除邏輯如下:

A. 幾何特徵計算

  • 三點夾角 (angle_between): 利用 NumPy 進行向量點積與範數運算,計算關節彎曲度(如手肘、膝蓋、髖部)。
  • 軀幹傾角 (trunk_angle_deg): 計算左右肩膀中點與左右髖部中點連線,與絕對垂直線(Vector [0, -1])的夾角,用以判斷前傾或跌倒。

B. 姿勢分類器狀態機優先順序

程式在每幀(Frame)會依序掃描以下條件,一旦滿足即跳出:

  1. Fallen (跌倒): 軀幹傾角大於 60°(接近水平)。
  2. Kneeling on one knee (單膝跪地):
    • 前置排除:非蹲姿。
    • 特徵:兩膝 Y 軸高低差 > 0.07,站立腳挺直(> 130°),跪地側滿足「腳踝被遮擋」、「腳背貼地」或「膝角極度彎曲」之一,且兩腳膝角有顯著代數差(> 25°)。
  3. Hands on hips (雙手叉腰):
    • 前置排除:非蹲姿。
    • 特徵:雙手腕 Y 軸逼近同側髖部(落差 < 0.10),手腕 X 軸縮在肩膀與髖部合理框內,雙手肘皆有明顯彎曲(< 145°),雙膝打直(> 145°,排除坐姿手放腿上)。
  4. Standing (站立): 髖角 > 155°、膝角 > 155° 且軀幹直立(傾角 < 30°)。
  5. Sitting (坐姿): 臀部 Y 軸高度明顯高於踝部(落差 > 0.10),臀膝高度相近(落差 < 0.12)或膝蓋高於臀部(矮凳),軀幹直立且膝角彎曲(< 145°)。
  6. Crouching (蹲姿): 髖角 < 145° 且膝角 < 125°。
  7. Bending (彎腰): 軀幹前傾(傾角 > 30°),但雙膝保持相對挺直(> 130°),明確排除蹲姿。

C. 時間序列平滑處理 (Smoothing)

  • 滑動多數決 (Sliding Window Majority Vote): 為了防止影像環境雜訊導致狀態高頻閃爍(Flickering),使用 collections.deque(maxlen=5) 儲存最近 5 幀的原始辨識結果,並取出現次數最多(Mode)的狀態作為最終輸出的穩定狀態。

On this page