当前位置: 首页 > news >正文

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

参考资料

  1. 安装nano(或任何其他文本编辑器):

    sudo apt-get install nano
    
  2. 开放时间/etc/docker/daemon.json

    sudo nano /etc/docker/daemon.json
    
  3. 编辑自:

    json

    {"runtimes": {"nvidia": {"path": "nvidia-container-runtime","runtimeArgs": []}}
    }
    

    到:

    json

    {"runtimes": {"nvidia": {"path": "nvidia-container-runtime","runtimeArgs": []}}, "default-runtime": "nvidia"
    }
    
  4. 重启docker:

    sudo systemctl restart docker
    
  5. 现在需要将 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 文件

✅ 总结

这个命令做了几件事情:

  1. 启动一个基于 Jetson 的 MediaPipe 镜像。
  2. 容器可以访问 GPU、音频和 USB 设备。
  3. 容器共享宿主机网络和 X11 显示,可以直接用 GUI。
  4. 容器自动挂载宿主机文件和配置,运行 Python 脚本 /opt/pcv3.py

测试代码

pcv3.py

# -*- coding: utf-8 -*-
import cv2
import sys
import os
import mediapipe as mpprint("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'):breakcap.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 流")returnwhile True:success, frame = cap.read()if not success:continue# 丢弃旧帧if frame_queue.full():try:frame_queue.get_nowait()except queue.Empty:passframe_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_utilsmp_pose = mp.solutions.posejumping_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)continueframe = frame_queue.get()# 转 RGBimage_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:passupload_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 \

参考资料

  1. Jetson torch 下载中心
  2. Jetson 开发文档
  3. 手动安装 Google MediaPipe Github
  4. Google MediaPipe

扩展信息

查看版本信息

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 驱动的边缘应用程序所需的一整套工具和库。

http://www.hskmm.com/?act=detail&tid=32767

相关文章:

  • 2025年电源适配器厂家权威推荐榜:笔记本适配器/工业电源/充电器厂家实力与市场口碑深度解析
  • 止损,补仓价格的合理设置
  • 鸿蒙应用开发从入门到实战(二十四):一文搞懂ArkUI网格布局
  • Go-Sciter入门系列-环境搭建
  • ChromeOS融合Android技术栈,打造更智能的Chromebook体验
  • Docker 部署 PHP 全手册
  • 2025 年最新推荐包装印刷制造厂家排行榜:聚焦设备、团队与服务的优质品牌精选定制/设计/优质/品质包装印刷厂家推荐
  • 2025 年国内发电机厂家最新推荐排行榜:涵盖多功率型号,助力精准选购优质发电机品牌指南
  • 作业二
  • VSCode 使用默认profile打开文件
  • 2025 年最新推荐国际物流服务公司权威排行榜:聚焦海运快递跨境专线,助力商家选优质物流伙伴
  • 2025年国内高温隧道炉知名品牌推荐,固化炉,回流焊炉,连续式烘干线厂家供货商!
  • 【数据结构】不带表头节点的双向链表的基本操作 - 实践
  • VSCode Java 单元测试没有运行按钮
  • 2025 北京宽带安装公司最新推荐榜:专业口碑双优服务商汇总,企业家庭装机必看指南
  • 分块
  • Qt实现UVC摄像头捕获
  • 2025年10月17日信息公布:太阳能路灯厂家最新推荐榜~覆盖乡村户外、单臂双臂、农村及5-8米LED款,精选优质路灯企业
  • 基于Java+Springboot+Vue开发的新闻管理系统源码+运行步骤
  • Linux 环境变量与软件安装
  • 2025最新超详细 VMware 17虚拟机下载安装教程(附安装包下载)保姆级详细步骤
  • 网络分析与数据可视化工具Gephi 0.9.2 下载安装教程全流程(Gephi图文安装教程)
  • 2025 涂料供应厂家最新推荐榜:权威品牌测评 + 选购指南,家装工程选品必看
  • 2025 年中走丝线切割源头厂家最新推荐排行榜发布,解读优质厂家技术亮点与选择攻略伺服/高效/自动中走丝线切割厂家推荐
  • 2024浙江省省赛决赛wp
  • 【解决办法】pytorch OSError: [WinError 1114] 动态链接库(DLL)初始化例程失败”
  • 23省赛初赛
  • 2025年苏州保洁服务公司最新权威推荐榜:专业家政与深度清洁口碑优选,覆盖日常保洁、开荒保洁、深度清洁及企业保洁服务
  • 2025 年快速退火炉优质厂家最新推荐榜单:真空 / 半导体 / 晶圆 / 高温 / 桌面 / 半自动 / 全自动 / 芯片 / 硅片 / RTP 设备企业权威评选
  • 2025 年油罐厂家最新推荐榜:sf 双层 / 加油站 / 化工 / 不锈钢 / 地埋 / 卧式 / 立式油罐优质厂商权威盘点,帮您避开选择误区精准选品