NVIDIA Jetson TX2 邊緣盒子運行姿態檢測模型記錄
Jetson TX2
- 系統版本:JetPack 4.6.1 (對應 L4T R32.6.1)
- 架構:aarch64(ARM 64)
- CUDA 版本:10.2
- python 3.6
背景介紹
? 最近在做關于視頻流處理方面項目,接觸到NVIDIA 的邊緣盒子,就嘗試著看在邊緣盒子上能不能運行 yolov8 或 Google 的 mediapipe 。
yolov8 是無法在這個版本的盒子運行(無法使用 cuda,CPU 推理卡的厲害),而且 Jetpack 4.6.1 版本的盒子無法升級 5.xx 版本,yolov8 最低需要 Python 3.8 版本,所以就選擇 mediapipe 。
使用 mediapipe 最好就是使用 docker 來處理,自己在系統安裝環境非常麻煩(CUDA10.2 + aarch64 問題),而且坑特別多根本跳不完的坑。
設置 docker
-
安裝nano(或任何其他文本編輯器):
sudo apt-get install nano -
開放時間
/etc/docker/daemon.json:sudo nano /etc/docker/daemon.json -
編輯自:
json
{ "runtimes": { "nvidia": { "path": "nvidia-container-runtime", "runtimeArgs": [] } } }到:
json
{ "runtimes": { "nvidia": { "path": "nvidia-container-runtime", "runtimeArgs": [] } }, "default-runtime": "nvidia" } -
重啟docker:
sudo systemctl restart docker -
現在需要將 docker 添加用戶到組:
sudo usermod -aG docker $USER
拉鏡像
| Python | OpenCV | mediapipe | 鏡像標簽 | 鏡像文件 |
|---|---|---|---|---|
| 3.6.9 | 4.8.0 | 0.8.5 | l4t32.7.1-py3.6.9-ocv4.8.0-mp0.8.5 | Dockerfile |
docker pull ghcr.io/lanzani/mediapipe:l4t32.7.1-py3.6.9-ocv4.8.0-mp0.8.5
啟動容器
顯示支持
運行容器之前:
export DISPLAY=:0
xhost +
然后運行你的容器。
啟動命令:
sudo docker run -it --rm \
--runtime nvidia \
--network host \
-v /tmp/argus_socket:/tmp/argus_socket \
-v /etc/enctune.conf:/etc/enctune.conf \
-v /etc/nv_tegra_release:/etc/nv_tegra_release \
-v /tmp/nv_jetson_model:/tmp/nv_jetson_model \
-v /tmp/.X11-unix/:/tmp/.X11-unix \
-v /tmp/.docker.xauth:/tmp/.docker.xauth \
-v /home/ubuntu/google-media/data:/opt \
--device /dev/snd \
--device /dev/bus/usb \
-e DISPLAY=:0 \
-e XAUTHORITY=/tmp/.docker.xauth \
ghcr.io/lanzani/mediapipe:l4t32.7.1-py3.6.9-ocv4.8.0-mp0.8.5 \
python3.6 /opt/pcv3.py
參數說明
1?? 基本選項
| 參數 | 作用 | 說明 |
|---|---|---|
sudo |
提權執行 docker | 因為 Docker 默認需要 root 權限,或者用戶在 docker 組中 |
docker run |
啟動容器 | 核心命令 |
-it |
交互式終端 | -i 保持 STDIN 打開,-t 分配偽終端,-t 換成 -d 可后臺運行 |
--rm |
容器退出后自動刪除 | 避免堆積臨時容器,測試使用,沒問題后可刪除這個選擇 |
2?? GPU/NVIDIA 相關
| 參數 | 作用 | 說明 |
|---|---|---|
--runtime nvidia |
使用 NVIDIA Docker runtime | 讓容器訪問 GPU。Jetson 的 L4T 環境通常也需要這個 |
--device /dev/snd |
音頻設備 | 允許容器訪問宿主機的音頻設備,方便 TTS 或播放 |
--device /dev/bus/usb |
USB 設備 | 允許訪問 USB 攝像頭或其他設備 |
3?? 網絡
| 參數 | 作用 | 說明 |
|---|---|---|
--network host |
容器共享宿主機網絡 | 對于訪問本地攝像頭、USB 或 V4L2 流媒體方便 |
4?? 掛載卷(Volume)
Volume 主要是讓容器訪問宿主機文件或設備。
| 參數 | 作用 | 說明 |
|---|---|---|
-v /tmp/argus_socket:/tmp/argus_socket |
Argus 攝像頭 socket | Jetson 的攝像頭 API(Argus)需要這個 socket |
-v /etc/enctune.conf:/etc/enctune.conf |
GPU/編碼配置 | Jetson 的編碼器調優文件 |
-v /etc/nv_tegra_release:/etc/nv_tegra_release |
系統版本信息 | 讓容器識別 Jetson 系統版本 |
-v /tmp/nv_jetson_model:/tmp/nv_jetson_model |
Jetson 硬件信息 | 某些 SDK 需要讀取型號信息 |
-v /tmp/.X11-unix/:/tmp/.X11-unix |
X11 socket | 允許 GUI 程序在宿主機顯示 |
-v /tmp/.docker.xauth:/tmp/.docker.xauth |
X11 認證 | 避免權限問題 |
-v /home/ubuntu/google-media/data:/opt |
數據文件 | 容器內 /opt 可訪問宿主機數據(你寫的 pcv3.py 就在這里) |
5?? 顯示相關環境變量
| 參數 | 作用 | 說明 |
|---|---|---|
-e DISPLAY=:0 |
X11 顯示 | 指定 GUI 輸出到宿主機顯示器 |
-e XAUTHORITY=/tmp/.docker.xauth |
X11 認證 | 配合掛載 .docker.xauth 文件使用 |
6?? 鏡像和命令
| 參數 | 作用 | 說明 |
|---|---|---|
ghcr.io/lanzani/mediapipe:l4t32.7.1-py3.6.9-ocv4.8.0-mp0.8.5 |
Docker 鏡像 | 這個鏡像包含 MediaPipe、OpenCV 4.8、Python3.6.9,適配 Jetson L4T 32.7.1 |
python3.6 /opt/pcv3.py |
容器啟動后執行 | 執行你掛載在 /opt 的 Python 文件 |
? 總結
這個命令做了幾件事情:
- 啟動一個基于 Jetson 的 MediaPipe 鏡像。
- 容器可以訪問 GPU、音頻和 USB 設備。
- 容器共享宿主機網絡和 X11 顯示,可以直接用 GUI。
- 容器自動掛載宿主機文件和配置,運行 Python 腳本
/opt/pcv3.py。
測試代碼
pcv3.py
# -*- coding: utf-8 -*-
import cv2
import sys
import os
import mediapipe as mp
print("pwd:", os.getcwd())
print("mediapipe pwd:", mp.__file__)
# === RTSP 地址(請改成你的攝像頭地址) ===
RTSP_URL = "rtsp://admin:123456@192.168.0.1:554/stream1"
# === 初始化 MediaPipe Pose ===
mp_drawing = mp.solutions.drawing_utils
mp_pose = mp.solutions.pose
# 啟動 RTSP 視頻流
cap = cv2.VideoCapture(RTSP_URL)
if not cap.isOpened():
print("xxxx stop")
exit()
print("start")
# 初始化 Pose 模型(static_image_mode=False 表示視頻流)
with mp_pose.Pose(
static_image_mode=False,
model_complexity=1,
min_detection_confidence=0.5,
min_tracking_confidence=0.5
) as pose:
while True:
success, frame = cap.read()
if not success:
print("dddiu")
break
# 轉換為 RGB(MediaPipe 要求輸入 RGB)
image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 推理
results = pose.process(image_rgb)
# 可視化骨架
if results.pose_landmarks:
mp_drawing.draw_landmarks(
frame,
results.pose_landmarks,
mp_pose.POSE_CONNECTIONS,
landmark_drawing_spec=mp_drawing.DrawingSpec(color=(0, 255, 0), thickness=2, circle_radius=2),
connection_drawing_spec=mp_drawing.DrawingSpec(color=(255, 0, 0), thickness=2)
)
# 顯示畫面
#cv2.imshow('Pose Detection (RTSP)', frame)
# 按 q 退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
開合跳代碼
通過關鍵點,計算開合跳數量
# -*- coding: utf-8 -*-
import cv2
import threading
import queue
import math
import time
import requests
import mediapipe as mp
# ======================
# 配置
# ======================
RTSP_URL = "rtsp://admin:123456@127.0.0.1:554/stream1?transport=tcp"
SERVER_URL = "http://127.0.0.1:8080/upload"
JPEG_QUALITY = 80 # JPEG 編碼質量
QUEUE_MAXSIZE = 1 # 隊列長度 1,保持最新幀
# ======================
# 隊列
# ======================
frame_queue = queue.Queue(maxsize=QUEUE_MAXSIZE)
upload_queue = queue.Queue(maxsize=QUEUE_MAXSIZE)
# 計算兩點之間的歐式距離(用于判斷雙腳分開的程度)
def calc_distance(p1, p2):
return math.sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2)
# ======================
# 上傳函數
# ======================
def upload_jpeg_bytes(image_bytes, server_url):
try:
files = {'image': ('uploaded_image.jpg', image_bytes, 'image/jpeg')}
response = requests.post(server_url, files=files, timeout=2)
response.raise_for_status()
except Exception as e:
print("上傳失敗:", e)
# ======================
# 抓幀線程
# ======================
def frame_grabber():
cap = cv2.VideoCapture(RTSP_URL)
if not cap.isOpened():
print("無法打開 RTSP 流")
return
while True:
success, frame = cap.read()
if not success:
continue
# 丟棄舊幀
if frame_queue.full():
try:
frame_queue.get_nowait()
except queue.Empty:
pass
frame_queue.put(frame)
# ======================
# 上傳線程
# ======================
def uploader():
while True:
try:
frame_bytes = upload_queue.get()
upload_jpeg_bytes(frame_bytes, SERVER_URL)
except Exception as e:
print("上傳線程異常:", e)
# ======================
# 主線程處理
# ======================
def main_loop():
mp_drawing = mp.solutions.drawing_utils
mp_pose = mp.solutions.pose
jumping_jacks = 0 # 開合跳次數計數器
stage = "down" # 動作階段:down=手腳收攏,up=手腳張開
with mp_pose.Pose(
static_image_mode=False,
model_complexity=1,
min_detection_confidence=0.5,
min_tracking_confidence=0.5
) as pose:
while True:
if frame_queue.empty():
time.sleep(0.001)
continue
frame = frame_queue.get()
# 轉 RGB
image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# Mediapipe 推理
results = pose.process(image_rgb)
# 繪制骨架
if results.pose_landmarks:
mp_drawing.draw_landmarks(
frame,
results.pose_landmarks,
mp_pose.POSE_CONNECTIONS,
landmark_drawing_spec=mp_drawing.DrawingSpec(color=(0, 255, 0), thickness=2, circle_radius=2),
connection_drawing_spec=mp_drawing.DrawingSpec(color=(255, 0, 0), thickness=2)
)
# 如果檢測到人體關鍵點
if results.pose_landmarks:
landmarks = results.pose_landmarks.landmark
# 取出關鍵點(左右手腕、鼻子、左右腳踝)
left_wrist = landmarks[15]
right_wrist = landmarks[16]
nose = landmarks[0] # 頭頂附近點
left_ankle = landmarks[27]
right_ankle = landmarks[28]
# ===== 手部邏輯 =====
# 當手腕的 y 坐標小于鼻子時,說明手舉過頭頂
hands_up = left_wrist.y < nose.y and right_wrist.y < nose.y
# ===== 腳部邏輯 =====
# 計算左右腳踝之間的距離
ankle_dist = calc_distance(left_ankle, right_ankle)
print(r"ankle_dis",ankle_dist)
# 如果距離大于閾值,說明雙腳分開
legs_open = ankle_dist > 0.10
# ===== 動作階段判斷 =====
# 如果手舉起且腳分開,認為處于 "up" 狀態
if hands_up and legs_open:
stage = "up"
# 如果手放下且腳收攏,并且之前是 "up",說明完成一次開合跳
elif not hands_up and not legs_open and stage == "up":
stage = "down"
jumping_jacks += 1 # 次數 +1
# 在左上角顯示開合跳次數
cv2.putText(
frame,
f'Jumping Jacks: {jumping_jacks}', # 顯示文本
(20, 50), # 位置 (x,y)
cv2.FONT_HERSHEY_SIMPLEX, # 字體
1.2, # 字號
(0, 255, 0), # 顏色 (綠色)
3 # 粗細
)
# 編碼 JPEG 并放入上傳隊列
_, buffer = cv2.imencode('.jpg', frame, [int(cv2.IMWRITE_JPEG_QUALITY), JPEG_QUALITY])
frame_bytes = buffer.tobytes()
if upload_queue.full():
try:
upload_queue.get_nowait()
except queue.Empty:
pass
upload_queue.put(frame_bytes)
# ======================
# 啟動線程
# ======================
if __name__ == "__main__":
threading.Thread(target=frame_grabber, daemon=True).start()
threading.Thread(target=uploader, daemon=True).start()
main_loop()
注意事項
一、目錄掛載問題
-v /home/ubuntu/google-media/data:/opt \ 將宿主機的測試 python 代碼掛載到容器 opt 目錄下,所以/home/ubuntu/google-media/data這個目錄是自己創建的。
二、Can't find file: mediapipe/modules/pose_detection/pose_detection.tflite
Mediapipe 模型加載問題,沒有找到這個文件,python3.6 /opt/pcv3.py 是在容器的 opt 目錄下執行的,在當前工作目錄下默認是沒有這個 mediapipe/modules/pose_detection/pose_detection.tflite 文件,簡單粗暴解決辦法:
通過 docker exec 容器ID /bin/bash,進入容器創建 /opt/mediapipe/modules/pose_detection/ 從 dist-packages 拷貝一個份
mkdir -p /opt/mediapipe/modules/pose_detection/
cp /usr/local/lib/python3.6/dist-packages/mediapipe/modules/pose_detection/pose_detection.tflite /opt/mediapipe/modules/pose_detection/
啟動日志:
WARNING: Logging before InitGoogleLogging() is written to STDERR
I20251016 06:39:17.998665 24 gl_context_egl.cc:163] Successfully initialized EGL. Major : 1 Minor: 5
I20251016 06:39:18.044948 42 gl_context.cc:331] GL version: 3.2 (OpenGL ES 3.2 NVIDIA 32.6.1)
I20251016 06:39:18.045231 24 gl_context_egl.cc:163] Successfully initialized EGL. Major : 1 Minor: 5
I20251016 06:39:18.072178 43 gl_context.cc:331] GL version: 3.2 (OpenGL ES 3.2 NVIDIA 32.6.1)
W20251016 06:39:18.075790 42 tflite_model_loader.cc:32] Trying to resolve path manually as GetResourceContents failed: ; Can't find file: mediapipe/modules/pose_detection/pose_detection.tflite
INFO: Created TensorFlow Lite delegate for GPU.
三、eglChooseConfig() returned no matching EGL configuration for RGBA8888 D16 ES2 request
AI 回答:
MediaPipe 默認會嘗試用 GPU (OpenGL ES) 模式運行 Pose 模型。
但 Jetson TX2 某些驅動 / EGL 庫 版本(尤其 JetPack 4.6.x)下:
- EGL 配置不匹配
- X server 或 DISPLAY 環境未正確設置
- 無法創建 GLES3 / GLES2 上下文
最終 GPU 初始化失敗,導致 MediaPipe Pose 圖計算圖無法啟動。
實際情況是在啟動 docker 容器時缺少了一些必要參數:
-v /tmp/argus_socket:/tmp/argus_socket \
-v /etc/enctune.conf:/etc/enctune.conf \
-v /etc/nv_tegra_release:/etc/nv_tegra_release \
-v /tmp/nv_jetson_model:/tmp/nv_jetson_model \
-v /tmp/.X11-unix/:/tmp/.X11-unix \
-v /tmp/.docker.xauth:/tmp/.docker.xauth \
--device /dev/snd \
--device /dev/bus/usb \
-e DISPLAY=:0 \
-e XAUTHORITY=/tmp/.docker.xauth \
參考資料
擴展信息
查看版本信息
cat /etc/nv_tegra_release
查看系統架構
uname -m
查看 cuda 信息
nvcc --version
查看 CPU、GPU、內存使用情況
sudo tegrastats
診斷系統 OpenCV 是否支持 cuda,也可以自容器執行
python3.6 -c "import cv2; print('--- System OpenCV Info ---'); print(cv2.getBuildInformation())"
查看 mediapipe 版本信息
python3.6 -c "import mediapipe as mp; print(mp.__version__)"
PyTorch(適用于 JetPack 4.6.1):是一個針對使用 GPU 和 CPU 進行深度學習而優化的張量庫
NVIDIA JetPack? :是 NVIDIA Jetson? 平臺的官方軟件堆棧,為您提供構建 AI 驅動的邊緣應用程序所需的一整套工具和庫。

浙公網安備 33010602011771號