zhaoali0709头像

深度学习系列实验(四):嵌入式模块目标检测轻量化部署

一、实验目的

通过本实验,在 NVIDIA Jetson AGX Orin 嵌入式平台上完成目标检测模型的轻量化部署。实验设备已完成 JetPack 基础环境配置,并已安装 Anaconda 与 PyTorch,因此本实验直接从“在 conda 环境中配置 Ultralytics YOLO”开始,依次完成预训练模型准备、TensorRT 引擎构建、性能基准测量,并通过外置 USB 摄像头实现实时人员检测,理解边缘端部署中“精度—速度—端到端实时性”的权衡关系。

1.1 知识目标

  • 理解 Jetson 上 Ultralytics YOLO 的配置要点:aarch64 架构下 onnxruntime-gpu 需要手动获取专用 wheel,PyPI 默认包不包含 Jetson 可用的二进制

  • 掌握 TensorRT 的核心优化原理:层融合、FP16 降低计算量、kernel 针对 Ampere 架构自动调优,以及 engine 的硬件特定性

  • 认识“模型推理延迟”与“端到端流水线 FPS”的区别:摄像头采集、格式转换和串行结构可能成为新的瓶颈

1.2 技能目标

  • 能够在已有 conda + PyTorch 环境中完成 Ultralytics 及 ONNX 相关依赖的配置

  • 能够锁定 MAXN / jetson_clocks,获得可复现的基准数据

  • 能够使用预训练 YOLO 完成 PyTorch 基线推理,并导出 ONNX 与 TensorRT FP16 引擎

  • 能够对比 PyTorch 与 TensorRT 的推理性能,并在数据子集上校验 FP16 精度损失

  • 能够通过 GStreamer 管线读取 USB 摄像头,实现实时人员检测,并分析端到端瓶颈

1.3 素养目标

  • 建立以基线测量为起点的优化习惯:先量化,再优化,再对比

  • 理解硬件特定性(engine 不可跨设备移植)与功耗模式对边缘部署工程的实际约束

二、实验内容

模块一:YOLO 环境配置(基于已有 Anaconda + PyTorch)

  1. 确认 JetPack、CUDA、TensorRT 版本及 aarch64 架构

  2. 在已有的 conda 环境中安装 onnx、aarch64 专用 onnxruntime-gpu 和 ultralytics,处理 numpy 版本冲突

  3. 验证 YOLO 加载与 GPU 可用性,锁定 MAXN 性能模式

模块二:PyTorch 基线推理

  1. 使用 yolov8n.pt 对测试图像推理,测量平均延迟与 FPS

模块三:ONNX 导出与 TensorRT 引擎构建

  1. 导出静态 ONNX(固定 imgsz)

  2. 使用 Ultralytics format='engine' 或 trtexec 构建 FP16 引擎

模块四:TensorRT 推理与性能对比

  1. 单帧推理,验证结果一致性

  2. 测量 TensorRT 延迟与 FPS,计算加速比

  3. 在小型公开数据集上校验 FP16 精度变化

选做模块:外置 USB 摄像头实时人员检测

  1. 朴素 cv2.VideoCapture(0) 路径的端到端 FPS(问题基线)

  2. GStreamer 管线读取,仅保留 person 类别,叠加 FPS 显示

  3. 对比两种读取方式的端到端 FPS,分析瓶颈

三、实验步骤

3.1 环境确认

步骤1:确认 JetPack、TensorRT 与已有 PyTorch

在 Jetson 终端中依次执行:

bash

cat /etc/nv_tegra_release          # JetPack / L4T 版本
dpkg -l | grep 'nvidia-jetpack'    #查看jetpack版本记录,后面要用。
uname -m                            # 应输出 aarch64
dpkg -l | grep nvinfer              # TensorRT 版本

激活你已有的 conda 环境后,确认 PyTorch 状态:

bash

conda activate <你的环境名>
python3 -c "import torch; print('PyTorch:', torch.__version__); print('CUDA:', torch.cuda.is_available()); print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'N/A')"

本实验的前提是 torch.cuda.is_available() 已返回 True。如果在此步骤发现问题,应先回到已有的 JetPack + PyTorch 配置,排除 PyTorch wheel 与 JetPack 版本不匹配的问题,再继续后续步骤。

步骤2:锁定性能模式

bash

sudo nvpmodel -m 0        # MAXN 模式
sudo jetson_clocks        # 锁定 CPU/GPU/EMC 至最高频率
sudo jetson_clocks --show

基准测试前必须锁定频率。否则,动态调频会使“PyTorch vs TensorRT”的对比失去可复现性。

3.2 YOLO 环境配置(基于已有 conda + PyTorch)

步骤3:安装 onnx 与 aarch64 专用 oruntime-gpu

PyPI 上的 onnxruntime-gpu 不包含 Jetson 可用的 aarch64 二进制,必须手动下载安装。以下以 Python 3.8 为例:

bash

# 在已激活的 conda 环境中
pip install onnx

# 安装 aarch64 专用 wheel(cpXX 需与 Python 版本匹配,注意与自己安装的python版本要一致!!!!!!!!)
wget https://nvidia.box.com/shared/static/zostg6agm00fb6t5uisw51qi6kpcuwzd.whl -O onnxruntime_gpu-1.17.0-cp38-cp38-linux_aarch64.whl
pip install onnxruntime_gpu-1.17.0-cp38-cp38-linux_aarch64.whl

【如果网络无法访问,参考下面的链接。如果仍然无法访问,安装CPU版本】

关键冲突:onnxruntime-gpu 安装过程中可能自动将 numpy 升级到最新版本,破坏原有 PyTorch 或 YOLO 环境的兼容性。安装完成后,应立即将 numpy 固定到已知兼容的版本:

bash

pip install numpy==1.23.5

如果 conda 环境中原本使用的 numpy 版本不同(例如 1.26.x),请以你环境中已验证可用的版本为准,不要盲目覆盖。

参考:

Nvidia Orin 安装onnxruntime-gpu,Nvidia Orin上安装onnxruntime-gpu的保姆级避坑指南(附Jetpack版本查询与GPU验证)

步骤4:安装 Ultralytics 并验证

bash

pip install ultralytics

# 再次确认 numpy 未被再次改动
python3 -c "import numpy; print(numpy.__version__)"

验证 YOLO 与 GPU:

bash

python3 -c "
import torch, ultralytics, onnxruntime
print('PyTorch:', torch.__version__, '| CUDA:', torch.cuda.is_available())
print('Ultralytics:', ultralytics.__version__)
print('ORT providers:', onnxruntime.get_available_providers())
"

预期 CUDA: True,且 onnxruntime 包含 CUDAExecutionProvider(如果 ONNX Runtime 仅用于图校验而非推理,此条不是必需)。

步骤5:加载预训练模型(可选择其他模型,也可将前面实验课的代码直接拷贝过来)

python

from ultralytics import YOLO
model = YOLO("yolov8n.pt")   # 首次自动下载,约 3.2M 参数
print("设备:", model.device)

yolov8n.pt 在 COCO 上预训练,包含 person 类别(类别 ID 为 0),足以支撑本实验的全部必做与选做内容。

3.3 PyTorch 基线推理

步骤6:测量单帧基线

准备一张任意包含常见目标的测试图像,创建 baseline_pytorch.py:

python

import time, cv2, numpy as np
from ultralytics import YOLO

model = YOLO("yolov8n.pt")
img = cv2.imread("test_images/sample.jpg")
assert img is not None

for _ in range(10):
    model(img, verbose=False)

times = []
for _ in range(50):
    t0 = time.perf_counter()
    model(img, verbose=False)
    times.append(time.perf_counter() - t0)

avg = np.mean(times) * 1000
print(f"PyTorch 基线 — 平均延迟: {avg:.2f} ms | FPS: {1000/avg:.2f}")

这个数值代表“单帧、图像文件输入、纯推理”的性能。选做部分摄像头端到端 FPS 通常显著低于它,差距本身即是分析对象。

3.4 ONNX 导出与 TensorRT 引擎构建

步骤7:导出 ONNX

python

from ultralytics import YOLO
model = YOLO("yolov8n.pt")
onnx_path = model.export(format="onnx", imgsz=640, opset=12, simplify=True)
print("ONNX:", onnx_path)

固定 imgsz 有利于 TensorRT 进行层融合和内存优化;opset 建议控制在 12–15,过高的 opset 可能在部分 TensorRT 版本中触发算子兼容性问题。

步骤8:构建 TensorRT FP16 引擎

方式 A:Ultralytics 直接导出

python

engine_path = model.export(format="engine", half=True, imgsz=640, workspace=4)
print("Engine:", engine_path)

Ultralytics 对 Jetson 设备的 TensorRT 导出默认 workspace 为 4GB,可避免构建过程中的内存分配失败。

方式 B:trtexec(可观察优化日志,非熟练不建议)

bash

/usr/src/tensorrt/bin/trtexec \
    --onnx=yolov8n.onnx \
    --saveEngine=yolov8n_fp16.engine \
    --fp16 --workspace=4096 \
    --verbose 2>&1 | tee trtexec_build.log

--workspace=4096 单位为 MB,即 4GB。构建过程可能持续数分钟,因为 TensorRT 需要针对 AGX Orin 的 GPU 逐层寻找最优执行策略。

注意!!!硬件特定性:engine 是为当前 JetPack 版本和 GPU 架构编译的。JetPack 5 上构建的 engine 不能直接在 JetPack 6 上加载,AGX Orin 的 engine 也不应直接用于 Orin Nano。

3.5 TensorRT 推理与性能对比

步骤9:TensorRT 单帧推理

python

import time, cv2, numpy as np
from ultralytics import YOLO

model = YOLO("yolov8n_fp16.engine", task="detect")
img = cv2.imread("test_images/sample.jpg")

for _ in range(10): model(img, verbose=False)
times = []
for _ in range(50):
    t0 = time.perf_counter(); model(img, verbose=False)
    times.append(time.perf_counter() - t0)

avg = np.mean(times) * 1000
print(f"TensorRT FP16 — 平均延迟: {avg:.2f} ms | FPS: {1000/avg:.2f}")

填入对比表:

推理后端精度平均延迟 (ms)吞吐量 (FPS)相对加速比
PyTorchFP32实测实测1.00×
TensorRTFP16实测实测实测

作为量级参考,AGX Orin 64GB 在 MAXN 模式下运行 YOLOv8n 的 TensorRT FP16 推理,公开数据可达到约 371 FPS,YOLOv8s 约为 108–128 FPS,具体数值受功耗模式、输入尺寸和 jetson_clocks 状态影响。

步骤10:FP16 精度校验

在小型公开数据集(如 coco128.yaml)上对比:

python

from ultralytics import YOLO

pt_m = YOLO("yolov8n.pt").val(data="coco128.yaml")
trt_m = YOLO("yolov8n_fp16.engine", task="detect").val(data="coco128.yaml")

print("PyTorch mAP@0.5:", pt_m.box.map50)
print("TensorRT FP16 mAP@0.5:", trt_m.box.map50)
print("变化:", trt_m.box.map50 - pt_m.box.map50)

FP16 通常不会造成显著精度损失;若 mAP 变化异常,应优先排查前后处理是否一致,再考虑激活值溢出等数值问题。

3.6 选做:外置 USB 摄像头实时人员检测

步骤11:摄像头测试(程序运行后,摄像头应该能正常使用)

python

import cv2
# 打开默认摄像头
cap = cv2.VideoCapture(0)
if not cap.isOpened():
    print("错误:无法打开摄像头")
    exit()
while True:
    # 读取一帧
    ret, frame = cap.read()
    if not ret:
        print("错误:无法读取画面")
        break
    # 显示画面
    cv2.imshow('USB Camera', frame)
    # 按 'q' 键退出
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
# 释放资源并关闭窗口
cap.release()
cv2.destroyAllWindows()
步骤12:实时人员检测与可视化

COCO 预训练模型 person 的类别 ID 为 0:

python

import cv2, time
from ultralytics import YOLO

model = YOLO("yolov8n_fp16.engine", task="detect")
cap = cv2.VideoCapture(gstreamer_pipeline(), cv2.CAP_GSTREAMER)
if not cap.isOpened():
    raise RuntimeError("GStreamer 管线打开失败")

prev = time.perf_counter()
fps_display = 0.0

while True:
    ret, frame = cap.read()
    if not ret: break

    results = model(frame, conf=0.3, verbose=False)
    boxes = results[0].boxes
    person_count = 0

    if boxes is not None:
        for box in boxes:
            if int(box.cls[0].item()) != 0:
                continue
            person_count += 1
            x1, y1, x2, y2 = map(int, box.xyxy[0].tolist())
            conf = box.conf[0].item()
            cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
            cv2.putText(frame, f"person {conf:.2f}", (x1, max(y1-8, 20)),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)

    now = time.perf_counter()
    inst_fps = 1.0 / (now - prev) if now > prev else 0
    fps_display = 0.9 * fps_display + 0.1 * inst_fps if fps_display else inst_fps
    prev = now

    cv2.putText(frame, f"FPS: {fps_display:.1f}  Persons: {person_count}",
                (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
    cv2.imshow("Jetson Person Detection", frame)
    if cv2.waitKey(1) & 0xFF == ord("q"): break

cap.release()
cv2.destroyAllWindows()

帧率天花板:USB 摄像头的实际采集帧率可能低于标称值(例如标称 30 FPS,实测约 27–28 FPS)。此时端到端 FPS 由采集硬件决定,继续降低推理延迟不再带来收益。记录并讨论这一点是选做部分的关键分析内容。

四、实验验收标准

4.1 必做部分

  • 在已有 conda + PyTorch 环境中成功安装 onnx、aarch64 专用 onnxruntime-gpu 和 ultralytics,并处理好 numpy 版本兼容性

  • 成功锁定 MAXN / jetson_clocks,记录设置前后状态

  • PyTorch 基线脚本输出平均延迟与 FPS

  • ONNX 导出成功,TensorRT FP16 引擎构建成功(记录构建耗时)

  • 给出 PyTorch vs TensorRT 延迟/FPS 对比表与加速比

  • 完成 FP16 精度校验,报告 mAP 变化量

4.2 选做部分

  • 成功使用 GStreamer 管线打开 USB 摄像头

  • 实时人员检测程序正常运行,仅绘制 person 类别,FPS 与人数显示正确

  • 记录朴素读取与 GStreamer 两种方式的端到端 FPS,并给出瓶颈分析

4.3 报告质量

  • 记录 onnxruntime-gpu 安装后 numpy 版本冲突的发现与解决过程

  • 对 TensorRT 加速机理和“单帧推理快 ≠ 端到端实时”有实质性讨论

  • 理解 engine 硬件特定性和功耗模式对部署工程的影响

五、常见问题与解决方案

问题可能原因解决方案
安装 onnxruntime-gpu 后 YOLO/PyTorch 报 numpy 错误onnxruntime 自动将 numpy 升级到不兼容版本安装后立即固定兼容版本,如 pip install numpy==1.23.5
onnxruntime-gpu 安装失败或没有 CUDA ProviderPyPI 包不包含 aarch64 二进制从 Jetson Zoo 或 Ultralytics 资源获取对应 JetPack + Python 版本的 aarch64 wheel
TensorRT 构建报 Unsupported ONNX data typeopset 过高或算子兼容性降低 opset 至 12–15,简化 ONNX 图
trtexec 找不到路径未加入 PATH使用绝对路径 /usr/src/tensorrt/bin/trtexec
engine 构建耗时很长TensorRT 在为目标硬件逐层调优属正常现象;--workspace 给足空间(如 4096 MB)
摄像头实时 FPS 远低于单帧推理 FPSV4L2 拷贝 + CPU 转换 + 串行流水线改用 GStreamer 管线;确认 appsink drop=true 丢弃旧帧
GStreamer 管线打开失败设备节点或格式参数不匹配v4l2-ctl --list-devices 确认 /dev/video*;先用 videoconvert 通用管线验证
检测不到人员置信度阈值过高降低 conf(如 0.25–0.3);确认 yolov8n.pt 已正确加载

六、实验总结与思考

  1. 记录结果:填写 PyTorch vs TensorRT 的性能对比表,以及朴素读取 vs GStreamer 的端到端 FPS 对比。单帧推理的加速比是否直接转化为端到端实时性的同等提升?

  2. 问题思考:

    • 在本实验的 conda 环境中,onnxruntime-gpu 的安装为什么需要手动处理 wheel 和 numpy 版本,而不能直接 pip install onnxruntime-gpu?

    • TensorRT 的加速来自层融合、FP16 降低计算量、kernel 针对 Ampere 架构自动调优。在你观察到的加速比中,哪一部分可能贡献最大?

    • 选做部分中,朴素 cv2.VideoCapture(0) 路径的瓶颈具体发生在哪几个环节?GStreamer 改变了什么,又无法改变什么?

    • 如果 USB 摄像头采集上限约为 27–30 FPS,而 TensorRT 推理仅需数毫秒,继续优化推理延迟还有意义吗?在什么条件下推理仍然是瓶颈?

    • TensorRT engine 的“硬件特定性”对多型号 Jetson 部署意味着什么策略?

  3. 实验报告要求:

    • 提交 PDF 报告,包含:JetPack/依赖版本、YOLO 配置过程中 numpy 冲突的解决记录、性能模式设置、基线/优化后计时输出、对比表、精度校验结果、选做部分的摄像头实测截图与 FPS 数据,以及对思考题的书面回答。

    • 提交代码压缩包(baseline_pytorch.py、infer_tensorrt.py、camera_person_detect.py 及 GStreamer 函数),确保核心脚本可复现。

提交格式:PDF 文档 + 代码压缩包
提交时间:实验完成后一周内
评分标准:YOLO 环境配置(20%) + 基线与模型转换(20%) + 推理与性能对比(20%) + 选做实时人员检测(15%) + 报告与思考(25%)

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/godblesszzc/article/details/169070229

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--