整套系统 = 一台 Jetson + 一个 USB 摄像头 + 一份约 1900 行的 Python 代码 + 四个下载来的开源模型。搭建分五个阶段,装完后是一个开机自启的服务,局域网里用浏览器打开就能用。脚本和代码都在 ~/Documents/AI/work/jetson-person-demo/。
| 项目 | 要求 |
|---|---|
| 设备 | Jetson Orin NX 16GB,已刷 JetPack 6.2.x(Ubuntu 22.04,Python 3.10),NVMe 启动,能上网 |
| 摄像头 | 任意支持 1080p30 MJPEG 的 USB 摄像头(用的是绿联 CM797) |
| 开发机 | 一台能 SSH 到设备的电脑(用的是 Mac),装有 rsync |
| 网络 | 设备直连国外源很慢,全部用国内镜像和 GitHub 加速前缀 https://gh-proxy.com/ |
| 权限 | 设备上的用户要能免密 sudo |
一条流水线,单进程多线程,每一帧顺序做六件事:
| 文件 | 职责 |
|---|---|
config.py | 所有可调参数:摄像头、模型路径、阈值、温控、网页端口 |
capture.py | 摄像头/视频文件读取线程 |
common.py | 不依赖模型的纯逻辑:估头框、身份投票、手势平滑、双手校验(可在任何电脑上单元测试) |
detectors.py | 人体姿态模型、头部检测模型的封装 |
faces.py | InsightFace 封装 + 人脸库(data/faces/db.json + 缩略图) |
gestures.py | 姿态规则手势 + HaGRID 手部手势模型 |
draw.py | 画框、骨架、中文标签 |
pipeline.py | 主循环、跟踪状态、事件流、温控、信号源切换 |
web.py static/ | FastAPI 接口与网页界面 |
main.py run.sh export_engines.py | 入口、启动脚本、TensorRT 引擎导出 |
ssh-keygen -t ed25519 -N "" -f ~/.ssh/jetson_orin_nx
ssh-copy-id -i ~/.ssh/jetson_orin_nx.pub -p <端口> <用户>@<设备地址> # 这一步要输一次设备密码
在 ~/.ssh/config 里加别名,后面全部用 ssh jetson:
Host jetson
HostName <设备地址>
User linux
Port <端口>
IdentityFile ~/.ssh/jetson_orin_nx
同步代码,以后每次改完代码都是这一条:
rsync -az --delete --exclude __pycache__ ./app/ jetson:~/demo/app/
如果你就坐在 Jetson 前面(接了显示器键盘),第 2 步整个跳过,代码这样拿:
mkdir -p ~/demo && cd ~/demo
wget https://jetson.weorgcloud.com/jetson-demo-src.zip && unzip -o jetson-demo-src.zip # 得到 app/ scripts/ docs/
bash scripts/setup_jetson.sh 2>&1 | tee setup.log # 后面全自动
没网的话用 U 盘把 app/ 和 scripts/ 拷到 ~/demo/ 下也一样。以后改代码直接在设备上改 ~/demo/app/ 里的文件(nano 或 VS Code 都行),改完 sudo systemctl restart person-demo 生效。
把 scripts/setup_jetson.sh 拷到设备后执行,它按下面五个阶段走,每个阶段可以单独重跑:
scp scripts/setup_jetson.sh jetson:~/demo/
ssh jetson 'bash ~/demo/setup_jetson.sh 2>&1 | tee ~/demo/setup.log'
setup_phase1d / 2 / 2b / 3.sh,也在 scripts/ 里)合并整理的,没有在一台全新机器上从头复跑过。遇到差异以分阶段脚本为准。出厂系统只有 CUDA,没有 cuDNN 和 TensorRT。
repo.download.nvidia.com 改成 .cn 镜像,并新增 /etc/apt/preferences.d/nvidia-repo-pin-cn(Pin: origin "*.nvidia.cn",优先级 600)。不补这条,apt 会选中 Ubuntu 仓库里版本号更高的旧 CUDA 包,报依赖冲突。.cn 镜像单连接只有 100KB/s:先用 apt-get install --print-uris 列出全部 deb 地址,用 aria2c -x 16 多线程预取(1.5GB 几分钟),拷进 /var/cache/apt/archives/,再 apt-get install -y nvidia-jetpack。v4l-utils curl ffmpeg python3-venv python3-dev build-essential cmake libopenblas-dev fonts-noto-cjk(最后一个是中文字体,画面标签要用)。| 文件 | 来源 |
|---|---|
yolo26n-pose.pt | github.com/ultralytics/assets/releases/download/v8.4.0/ |
buffalo_l.zip → 解压到 models/insightface/models/buffalo_l/ | github.com/deepinsight/insightface/releases/download/v0.7/ |
heads/head_abcfsa_yolov8n.pt | raw.githubusercontent.com/Abcfsa/YOLOv8_head_detector/main/nano.pt |
gestures/YOLOv10n_gestures.pt | rndml-team-cv.obs.ru-moscow-1.hc.sbercloud.ru/datasets/hagrid_v2/models/(设备直连可下) |
torch-2.10.0 torchvision-0.25.0 onnxruntime_gpu-1.23.0(cp310 aarch64 轮子) | github.com/ultralytics/assets/releases/download/v0.0.0/ |
为什么不用 PyPI:PyPI 上的 torch 是 CPU 版;Jetson 专用轮子站 pypi.jetson-ai-lab.io 从国内只有 80KB/s。
python3 -m venv --system-site-packages ~/demo/venv # 继承系统包:才有 apt 装的 TensorRT 绑定和带硬解的 OpenCV
source ~/demo/venv/bin/activate
pip install --no-deps torch-2.10.0*.whl torchvision-0.25.0*.whl onnxruntime_gpu-1.23.0*.whl
pip install "numpy<2" filelock networkx jinja2 fsspec packaging protobuf flatbuffers coloredlogs pillow
pip install -U "sympy>=1.13.3" "typing_extensions>=4.10" # 系统旧 sympy 会让 torch 无法 import
# cuDSS(torch 2.10 依赖):aria2 从 developer.download.nvidia.cn 拿 deb,dpkg -i 后 apt install cudss
pip install --no-deps ultralytics # 不带依赖,否则拉 CPU 版 torch 和无硬解 opencv
pip install matplotlib pyyaml requests scipy tqdm psutil py-cpuinfo ultralytics-thop polars pandas seaborn lap onnx onnxslim
pip install cython && pip install insightface fastapi "uvicorn[standard]" python-multipart aiofiles
pip uninstall -y onnxruntime opencv-python-headless opencv-python # insightface 顺带装的,会盖掉 GPU 版和系统 OpenCV
pip install --no-deps onnxruntime_gpu-1.23.0*.whl
验证(都要为真):torch.cuda.is_available();cv2 的 build 信息里 GStreamer YES;onnxruntime.get_available_providers() 含 CUDA 和 TensorRT;import tensorrt 成功。
最终版本:torch 2.10.0 / torchvision 0.25.0 / onnxruntime-gpu 1.23.0 / TensorRT 10.3 / cuDNN 9.3 / OpenCV 4.8(系统)/ ultralytics 8.4.138 / insightface 1.0.1 / numpy 1.26.4。
cd ~/demo/app && source ~/demo/venv/bin/activate
python export_engines.py ~/demo/models/yolo26n-pose.pt ~/demo/models/heads/head_abcfsa_yolov8n.pt ~/demo/models/gestures/YOLOv10n_gestures.pt
引擎只能在目标机、同版本 TensorRT 上生成;换 JetPack 要重做。代码里 resolve_model() 会自动优先使用同名 .engine。
person-demo.service:run.sh --source /dev/video0 --port 8080,开机自启,失败自动重启。thermal-log.service:每 30 秒把温度、风扇、GPU 频率记到 ~/demo/thermal.log。/etc/nvfancontrol.conf 里 FAN_DEFAULT_PROFILE 改 cool。nvpmodel -m 0(MAXN SUPER)并在服务里加 ExecStartPre=+/usr/bin/jetson_clocks 锁频,可稳 30 帧;这台散热差,用 nvpmodel -m 3(25W,要重启)且不锁频,约 20~23 帧。config.py):90°C 降低识别频率,97°C 暂停识别,92°C 以下恢复;硬件自身 99°C 强制降频、104°C 断电。v4l2-ctl -d /dev/video0 --list-formats-ext | head # 支持的分辨率/帧率
v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=MJPG --stream-mmap --stream-count=120 # 原始帧率,应接近 30
# 硬解通路(程序里用的就是这条管线)
gst-launch-1.0 v4l2src device=/dev/video0 num-buffers=120 ! "image/jpeg,width=1920,height=1080,framerate=30/1" ! jpegparse ! nvv4l2decoder mjpeg=1 ! nvvidconv ! "video/x-raw,format=BGRx" ! fpsdisplaysink video-sink=fakesink text-overlay=false sync=false -v 2>&1 | grep -oE "average: [0-9.]+" | tail -1
实测:这只摄像头 nvjpegdec 只有 11 帧,nvv4l2decoder 32 帧,CPU 解码 30 帧占 1 核。程序开不了硬解时自动回退到 V4L2 + CPU 解码。
sudo systemctl status person-demo # 状态
journalctl -u person-demo -f # 实时日志
curl -s localhost:8080/api/status | python3 -m json.tool # 帧率、各阶段耗时、温度、原始手势输出
curl -s -X POST -H "Content-Type: application/json" -d '{"kind":"demo"}' localhost:8080/api/source # 切到内置演示视频,camera 切回
curl -s -F "name=张三" -F "files=@photo.jpg" localhost:8080/api/register # 命令行注册人脸
网页:http://<设备局域网 IP>:8080。改代码:开发机改 → rsync → sudo systemctl restart person-demo。
本机单元测试(不需要设备):python3 scripts/test_common.py,覆盖头框估算、身份确认、手势平滑、双手校验。
| 现象 | 原因 | 处理 |
|---|---|---|
| apt 装 nvidia-jetpack 报依赖冲突 | 换 .cn 镜像后自带的优先级规则失效 | 补 *.nvidia.cn 的 pin |
| 下载几十 KB/s | 国外源单连接限速 | GitHub 走 gh-proxy;deb 和 cuDSS 用 aria2 多线程 |
| torch 无法 import(equal_valued) | venv 继承的系统 sympy 太旧 | 在 venv 里升级 sympy |
| 人脸跑在 CPU、摄像头硬解开不了 | insightface 顺带装了 CPU 版 onnxruntime 和 headless OpenCV | 卸掉,重装 GPU 轮子 |
| 只有 7 帧 | 给 TensorRT 模型传了 half=True,Ultralytics 每帧重建推理器 | 不传 half |
| 只有 19 帧 | 没锁频,动态调频拖慢 | jetson_clocks(散热允许时) |
| 硬断电后摄像头打不开(USB -110) | 摄像头卡死 | 拔插摄像头 |
| 远程 heredoc 里跑 ffmpeg 后面命令消失 | ffmpeg 吃掉了标准输入 | ffmpeg -nostdin |
| 网页新旧脚本混用 | 浏览器缓存 | 脚本地址带版本号,静态文件 no-cache |
YOLO(AGPL-3.0)、InsightFace 预训练模型(仅非商用)、HaGRID(CC BY-SA 4.0)都只适合内部 demo;产品化要换可商用模型或购买授权。人脸数据只存设备本地,需符合《人脸识别技术应用安全管理办法》。