news 2026/4/17 20:59:11

YOLOv5模型在Jetson设备上的TensorRT部署

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv5模型在Jetson设备上的TensorRT部署

YOLOv5模型在Jetson设备上的TensorRT部署

在边缘计算场景中,如何让一个训练好的目标检测模型真正“落地”运行,是每个AI工程师绕不开的课题。尤其是在NVIDIA Jetson Nano、Xavier NX、Orin这类资源受限但又需要实时响应的嵌入式平台上,性能与功耗的平衡变得异常关键。

YOLO(You Only Look Once)系列模型因其速度快、精度高,早已成为工业界首选的目标检测方案。虽然YOLOv5并非Ultralytics官方正式命名版本(实为社区广泛采用的实现),但它凭借简洁的代码结构、高效的训练流程和出色的推理表现,迅速成为实际项目中最常用的框架之一。

更令人振奋的是,如今我们已拥有成熟的工具链支持:从 PyTorch 模型导出,到 TensorRT 引擎编译,再到 Jetson 上 C++/Python 的端到端调用,整个部署路径已经非常清晰。本文将以YOLOv5 在 Jetson 系列设备上通过 TensorRT 加速推理为主线,手把手带你完成从模型导出到实时视频流处理的全流程,并结合当前流行的预装开发镜像进行适配说明。


环境准备:硬件选型与系统配置

要跑通这套部署流程,首先得有一块能打的“板子”。

推荐硬件平台

  • Jetson Nano (4GB):适合入门验证,可运行小尺寸YOLOv5s模型
  • Jetson Xavier NX:算力更强,支持多路摄像头或更高帧率
  • Jetson Orin:旗舰级性能,轻松驾驭YOLOv5l/x甚至更大变体

其他必备外设:

  • 至少32GB TF卡(建议64GB以上以避免空间不足)
  • 5V/4A直流电源(Nano必须跳线供电才能稳定运行)
  • 散热风扇或金属散热片(防止过热降频导致性能骤降)
  • USB无线网卡(Nano无内置Wi-Fi)
  • 显示器 + 键盘鼠标(用于初始系统配置)

⚠️ 特别提醒:不要试图在虚拟机里完成最终部署!CUDA 和 TensorRT 对底层驱动高度依赖,跨平台模拟极易出现兼容性问题,浪费大量调试时间。

软件环境要求

主流 Jetson 开发基于JetPack SDK,它集成了 L4T(Linux for Tegra)、CUDA、cuDNN、TensorRT 等核心组件。推荐使用以下版本组合:

组件推荐版本
JetPack5.1 或 5.0.2
CUDA12.x / 11.4
cuDNN9.x
TensorRT8.6+
OpenCV4.5+
Python3.8 ~ 3.10

你可以通过以下命令查看当前系统的 L4T 版本信息:

sudo apt install lsb-release cat /etc/nv_tegra_release

确认 JetPack 版本后,再选择对应的依赖库版本,避免因版本错配导致构建失败。


模型导出:生成可用于 TensorRT 的中间文件

YOLOv5 提供了export.py工具,可以直接将.pt权重转换为 ONNX、TorchScript 或原生的TensorRT Engine (.engine)文件。这里介绍两种常用方式。

方法一:直接导出.engine文件(推荐)

如果你是在具备完整 CUDA/TensorRT 环境的主机上操作(比如带 GPU 的 x86 PC),可以一步到位生成优化后的引擎文件。

进入 YOLOv5 项目目录:

cd /root/ultralytics/yolov5

执行导出命令:

yolo export model=best.pt format=engine imgsz=640 device=0

参数说明:

  • model: 训练好的权重路径,如runs/train/exp/weights/best.pt
  • format=engine: 输出格式为 TensorRT 引擎
  • imgsz=640: 输入图像尺寸,需为 32 的倍数
  • device=0: 使用 GPU 编译引擎

成功后会生成best.engine文件,该文件已完成量化、层融合等优化,可直接拷贝至 Jetson 设备加载。

✅ 优势:无需手动构建插件,省去复杂编译步骤
❌ 缺点:必须在同一架构(aarch64)且有 GPU 的环境下运行,否则无法生成有效引擎


方法二:生成.wts文件(跨平台通用方案)

若你在 x86 主机训练模型,却要在 aarch64 架构的 Jetson 上部署,则应采用生成.wts文件 + 在 Jetson 上本地 build engine的方式。

步骤 1:生成.wts权重文件

下载 tensorrtx 仓库中的 YOLOv5 支持代码:

git clone https://github.com/wang-xinyu/tensorrtx.git

复制gen_wts.py到你的 YOLOv5 目录下:

cp tensorrtx/yolov5/gen_wts.py yolov5/ cd yolov5 python3 gen_wts.py -w best.pt

这会在当前目录生成best.wts文件,其中包含所有层名与对应权重,便于后续在 Jetson 上重建网络并插入参数。

然后将其传输到 Jetson 设备:

scp best.wts user@jetson-ip:/home/user/tensorrtx/yolov5/

Jetson 端部署:构建 TensorRT 引擎

现在我们切换到 Jetson 设备,开始真正的“本地化”构建过程。

安装必要依赖

确保已安装基础库和 Python 包:

sudo apt update sudo apt install python3-pip libopencv-dev python3-opencv libssl-dev pip3 install pycuda numpy

验证 TensorRT 是否可用:

import tensorrt as trt print(trt.__version__)

如果没有报错,说明环境正常。


编译 YOLOv5-TensorRT 示例程序

进入tensorrtx/yolov5目录:

cd ~/tensorrtx/yolov5
修改类别数量与输入尺寸

打开yololayer.h文件,调整以下宏定义:

#define CLASS_NUM 80 // 根据你的数据集类别数修改 #define INPUT_W 640 // 与训练时一致 #define INPUT_H 640

例如,如果你训练的是自定义四分类模型(人、车、狗、猫),则改为:

#define CLASS_NUM 4

⚠️ 务必保持与训练配置完全一致,否则输出解析将出错!

构建工程
mkdir build && cd build cmake .. make -j$(nproc)

如果提示找不到libmyplugins.so,请先执行一次-s参数来生成 engine 和插件库:

sudo ./yolov5 -s ../best.wts best.engine s

该命令含义如下:

# sudo ./yolov5 -s [.wts] [.engine] [model_type] # 示例: sudo ./yolov5 -s ../best.wts best.engine s

支持的模型类型包括:s,m,l,x,s6,m6,l6,x6,也可使用自定义宽度/深度因子:

sudo ./yolov5 -s ../custom.wts custom.engine c 0.33 0.50

其中c表示 custom,0.33是 depth_multiple,0.50是 width_multiple。

构建成功后你会得到两个关键产物:

  • best.engine:序列化的 TensorRT 推理引擎
  • libmyplugins.so:自定义插件库(含 YOLO 层实现)

这两个文件将在后续推理中被同时加载。


推理测试:C++ 与 Python 双模式调用

C++ 测试图片推理

将待测图片放入samples/文件夹:

cp ../data/images/bus.jpg samples/

运行推理:

sudo ./yolov5 -d best.engine ../samples

结果图片将保存为output*.jpg。若没有检测框输出,请检查:

  • CONF_THRESHNMS_THRESH是否设置过高(默认 0.5 和 0.4)
  • 类别数是否匹配
  • 图片路径是否正确

Python 调用(更适合集成应用)

为了方便后续接入 ROS、Flask API 或摄像头流,推荐使用 Python 调用引擎。

编辑yolov5_trt.py文件,进行以下关键修改:

(1)禁用 Torch 相关模块

注释掉对torchtorchvision的引用,避免在 Jetson 上额外安装这些重型包:

# import torch # import torchvision
(2)统一输入尺寸

确保与训练和导出时一致:

INPUT_W = 640 INPUT_H = 640
(3)替换 NMS 实现为 NumPy 版本

删除对torchvision.ops.nms的调用,改用纯 NumPy 实现的非极大值抑制函数:

def nms(self, boxes, scores, iou_threshold=0.4): x1 = boxes[:, 0] y1 = boxes[:, 1] x2 = boxes[:, 2] y2 = boxes[:, 3] areas = (x2 - x1 + 1) * (y2 - y1 + 1) order = scores.argsort()[::-1] keep = [] while order.size > 0: i = order[0] keep.append(i) xx1 = np.maximum(x1[i], x1[order[1:]]) yy1 = np.maximum(y1[i], y1[order[1:]]) xx2 = np.minimum(x2[i], x2[order[1:]]) yy2 = np.minimum(y2[i], y2[order[1:]]) w = np.maximum(0.0, xx2 - xx1 + 1) h = np.maximum(0.0, yy2 - yy1 + 1) inter = w * h ovr = inter / (areas[i] + areas[order[1:]] - inter) inds = np.where(ovr <= iou_threshold)[0] order = order[inds + 1] return keep

并在post_process中调用它:

indices = self.nms(boxes, scores, IOU_THRESHOLD) result_boxes = boxes[indices] result_scores = scores[indices] result_classid = classid[indices]
(4)设置自定义类别标签
CLASSES = ['person', 'bicycle', 'car', 'motorcycle'] # 替换为你自己的类别

完整 Python 推理脚本示例

import cv2 import numpy as np import time import ctypes import tensorrt as trt import pycuda.driver as cuda import pycuda.autoinit # 加载自定义插件库 PLUGIN_LIBRARY = "libmyplugins.so" ctypes.CDLL(PLUGIN_LIBRARY) # 参数设置 ENGINE_PATH = "best.engine" IMAGE_PATH = "samples/bus.jpg" INPUT_W, INPUT_H = 640, 640 CONF_THRESH = 0.4 IOU_THRESHOLD = 0.5 CLASSES = ['person', 'bicycle', 'car', 'motorcycle'] class YOLOv5TRT: def __init__(self, engine_path): self.logger = trt.Logger(trt.Logger.INFO) with open(engine_path, "rb") as f: runtime = trt.Runtime(self.logger) self.engine = runtime.deserialize_cuda_engine(f.read()) self.context = self.engine.create_execution_context() self.stream = cuda.Stream() # 分配内存缓冲区 self.host_inputs = [] self.cuda_inputs = [] self.host_outputs = [] self.cuda_outputs = [] for binding in self.engine: size = trt.volume(self.engine.get_binding_shape(binding)) * self.engine.max_batch_size dtype = trt.nptype(self.engine.get_binding_dtype(binding)) host_mem = cuda.pagelocked_empty(size, dtype) cuda_mem = cuda.mem_alloc(host_mem.nbytes) if self.engine.binding_is_input(binding): self.host_inputs.append(host_mem) self.cuda_inputs.append(cuda_mem) else: self.host_outputs.append(host_mem) self.cuda_outputs.append(cuda_mem) def infer(self, image): # 预处理:BGR → RGB → resize → transpose → normalize input_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) input_image = cv2.resize(input_image, (INPUT_W, INPUT_H)) input_image = np.transpose(input_image, (2, 0, 1)).astype(np.float32) / 255.0 input_image = np.ascontiguousarray(input_image) np.copyto(self.host_inputs[0], input_image.ravel()) # Host to Device cuda.memcpy_htod_async(self.cuda_inputs[0], self.host_inputs[0], self.stream) # 执行异步推理 self.context.execute_async_v2( bindings=[int(d) for d in self.cuda_inputs + self.cuda_outputs], stream_handle=self.stream.handle ) # Device to Host cuda.memcpy_dtoh_async(self.host_outputs[0], self.cuda_outputs[0], self.stream) self.stream.synchronize() output = self.host_outputs[0] num_dets = int(output[0]) det_data = output[1:].reshape(-1, 6)[:num_dets] boxes = det_data[:, :4] scores = det_data[:, 4] class_ids = det_data[:, 5].astype(int) # 置信度过滤 conf_mask = scores > CONF_THRESH boxes = boxes[conf_mask] scores = scores[conf_mask] class_ids = class_ids[conf_mask] # 坐标还原到原始图像尺度 scale_x = image.shape[1] / INPUT_W scale_y = image.shape[0] / INPUT_H boxes[:, 0::2] *= scale_x boxes[:, 1::2] *= scale_y # NMS 后处理 indices = self.nms(boxes, scores, IOU_THRESHOLD) return boxes[indices], scores[indices], class_ids[indices] @staticmethod def nms(boxes, scores, threshold): x1 = boxes[:, 0]; y1 = boxes[:, 1]; x2 = boxes[:, 2]; y2 = boxes[:, 3] areas = (x2 - x1 + 1) * (y2 - y1 + 1) order = scores.argsort()[::-1] keep = [] while order.size > 0: i = order[0] keep.append(i) xx1 = np.maximum(x1[i], x1[order[1:]]) yy1 = np.maximum(y1[i], y1[order[1:]]) xx2 = np.minimum(x2[i], x2[order[1:]]) yy2 = np.minimum(y2[i], y2[order[1:]]) w = np.maximum(0.0, xx2 - xx1 + 1) h = np.maximum(0.0, yy2 - yy1 + 1) inter = w * h ovr = inter / (areas[i] + areas[order[1:]] - inter) inds = np.where(ovr <= threshold)[0] order = order[inds + 1] return keep # 绘图函数 def draw_boxes(img, boxes, scores, class_ids, class_names): for i in range(len(boxes)): x1, y1, x2, y2 = map(int, boxes[i]) label = f"{class_names[class_ids[i]]}: {scores[i]:.2f}" color = (0, 255, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) return img # 主函数 if __name__ == "__main__": detector = YOLOv5TRT(ENGINE_PATH) image = cv2.imread(IMAGE_PATH) start = time.time() boxes, scores, class_ids = detector.infer(image) print(f"Inference time: {(time.time() - start)*1000:.2f} ms") result_img = draw_boxes(image, boxes, scores, class_ids, CLASSES) cv2.imwrite("result_detection.jpg", result_img) print("Detection result saved to result_detection.jpg")

运行后即可看到带有检测框的结果图。


视频与摄像头实时推理

只需稍作扩展,就能实现视频或摄像头实时检测。

视频文件推理

def detect_video(video_path): cap = cv2.VideoCapture(video_path) out = cv2.VideoWriter( "output.avi", cv2.VideoWriter_fourcc(*"MJPG"), 30, (int(cap.get(3)), int(cap.get(4))) ) while cap.isOpened(): ret, frame = cap.read() if not ret: break boxes, scores, class_ids = detector.infer(frame) result_frame = draw_boxes(frame, boxes, scores, class_ids, CLASSES) out.write(result_frame) cv2.imshow("YOLOv5-TensorRT", result_frame) if cv2.waitKey(1) == ord('q'): break cap.release() out.release() cv2.destroyAllWindows()

CSI 摄像头支持(Jetson 原生)

对于 Jetson 原生 CSI 摄像头(如 Raspberry Pi Camera V2),可通过jetcam库直接访问:

from jetcam.csi_camera import CSICamera camera = CSICamera(width=640, height=480, capture_fps=30) while True: frame = camera.read() boxes, scores, class_ids = detector.infer(frame) result_frame = draw_boxes(frame, boxes, scores, class_ids, CLASSES) cv2.imshow("CSI Camera", result_frame) if cv2.waitKey(1) == ord('q'): break cv2.destroyAllWindows()

这种方式延迟低、稳定性好,非常适合做边缘 AI 产品原型。


关于 YOLO-V8 镜像的特别说明

尽管本文聚焦于 YOLOv5,但现实中很多开发者使用的是预装好的YOLO-V8 开发镜像—— 这类镜像通常基于 Docker 或 SD 卡烧录形式分发,特点包括:

  • 预装 PyTorch、Ultralytics、OpenCV、TensorRT
  • 提供 Jupyter Notebook 开发界面
  • 内置ultralytics项目目录
  • 支持一键训练与导出

你可以在其中直接运行 YOLOv5 的训练与导出任务:

cd /root/ultralytics yolo task=detect mode=train model=yolov5s.yaml data=coco.yaml epochs=100 imgsz=640 yolo export model=runs/detect/train/weights/best.pt format=engine

再将生成的.engine.wts文件拷出,用于 Jetson 部署。

这类镜像大大降低了环境配置门槛,尤其适合新手快速上手。不过要注意其内部 Python 环境可能与标准 JetPack 存在差异,在部署时仍需验证 TensorRT 兼容性。


整个流程走下来,你会发现虽然涉及不少底层细节——比如插件编译、内存管理、NMS 实现等——但一旦打通,就能在 Jetson 上实现10~30 FPS 的实时检测能力,足以支撑大多数智能安防、机器人导航、工业质检等边缘 AI 场景。

更重要的是,这种“训练-导出-部署”的标准化路径,也为后续升级到 YOLOv8、打包成 Flask API 或集成进 ROS 节点打下了坚实基础。下一步,不妨尝试把模型封装成 REST 接口,或是加入跟踪算法实现多目标追踪,你会发现,真正的 AI 落地之旅才刚刚开始。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 5:33:51

AI搜索引擎品牌提及指南:用数据驱动策略,让品牌被AI主动推荐

“为什么ChatGPT不提我们的品牌&#xff1f;AI搜索结果里根本看不到我们的身影&#xff1f;”——这是如今SEO从业者和品牌方最常面临的灵魂拷问。在AI成为信息获取核心渠道的时代&#xff0c;品牌能否被AI搜索引擎提及&#xff0c;直接关系到线上曝光与商业转化。但想要获得AI…

作者头像 李华
网站建设 2026/4/15 18:27:58

Windows 10下Miniconda搭建YOLOv5与LabelImg

Windows 10下Miniconda搭建YOLOv5与LabelImg 在目标检测项目的开发过程中&#xff0c;最让人头疼的往往不是模型调参或数据标注&#xff0c;而是环境配置——明明代码写得没问题&#xff0c;运行时却报出“DLL加载失败”、“torch版本不兼容”或者“pip install 卡死不动”。尤…

作者头像 李华
网站建设 2026/4/16 14:14:49

基于串口服务器的Modbus通讯优化实践

基于串口服务器的Modbus通讯优化实践 在某大型化工厂的中央控制室里&#xff0c;一次突如其来的通信中断导致整条生产线停摆。排查整整花了6小时——问题源头竟是一台距离主控柜80米远、接线松动的温度变送器。这种“一点故障&#xff0c;全线瘫痪”的窘境&#xff0c;在采用传…

作者头像 李华
网站建设 2026/4/13 21:49:38

使用tf.image.resize_bilinear进行图像双线性插值缩放

使用 tf.image.resize_bilinear 实现高质量图像缩放 在深度学习的视觉任务中&#xff0c;图像预处理是不可忽视的一环。无论是训练分类模型前对输入图片进行归一化&#xff0c;还是在语义分割或姿态估计中保持空间结构一致性&#xff0c;图像缩放的质量直接影响最终模型的表现。…

作者头像 李华
网站建设 2026/4/11 22:54:00

dropClust:高效处理大规模单细胞RNA聚类

dropClust&#xff1a;高效处理大规模单细胞RNA聚类 在单细胞测序技术飞速发展的今天&#xff0c;研究者们已经能够以前所未有的分辨率解析复杂组织的细胞异质性。然而&#xff0c;随着数据规模的指数级增长——动辄数万甚至数十万个细胞、数万个基因——传统的分析流程开始显…

作者头像 李华