1. 开局:先搞明白你要搭的是一套什么东西
先说结论:在 Ubuntu 22.04 上给 YOLO 配 TensorRT 推理环境,本质上是完成一条“模型文件格式转换 + 运行时环境安装”的链路,前后涉及的东西比想象中多,但每一步都有章可循。
我见过太多人卡在这个任务上,不是因为某个步骤多难,而是因为版本搭配出了问题。TensorRT 本身只是一个推理加速库,但它依赖 CUDA、cuDNN,而 CUDA 又依赖显卡驱动,这一串依赖关系只要有一个环节版本对不上,后面就是无尽的报错。
所以这篇文章,我按自己实际走过的路径,从版本选型、环境安装、模型转换、推理验证到常见报错,完整过一遍。目标很明确:让你照着做,能在 Ubuntu 22.04 上把 YOLO 的 TensorRT 推理跑起来,而且跑得稳。
这套环境适合谁?适合手里已经有了训练好的 YOLO 权重,想在服务器或边缘设备上用 TensorRT 做加速推理的开发者。无论你是做工业视觉、安防巡检还是毕业设计,只要涉及 YOLO 模型的线上部署,这篇文章都适用。
2. 版本选型:为什么这一步决定了你后面会不会崩溃
2.1 这套环境的“食物链”关系
很多人上来就装 TensorRT,结果装完一跑就报错,最后发现是 CUDA 版本不对。先把这个依赖关系刻在脑子里:
显卡驱动 → CUDA → cuDNN → TensorRT
显卡驱动是地基,CUDA 是建立在驱动之上的计算平台,cuDNN 是 CUDA 上的深度神经网络加速库,TensorRT 则是在这之上做推理优化的高级库。每一层都对下一层有版本要求。
2.2 Ubuntu 22.04 上的稳定版本组合
根据我实际测试,Ubuntu 22.04 上最稳的一套组合是:
| 组件 | 推荐版本 | 备注 |
|---|---|---|
| 显卡驱动 | 535.x 或 545.x | 建议用nvidia-driver-535,稳定 |
| CUDA | 12.2 或 12.3 | TensorRT 8.6 GA 对应 CUDA 12.0 和 12.2 |
| cuDNN | 8.9.x | 匹配 CUDA 12.x |
| TensorRT | 8.6 GA 或 10.x | 推荐 8.6 GA,生态兼容性最成熟 |
| Python | 3.10 | Ubuntu 22.04 自带 |
| PyTorch | 2.1 及以上 | 用于导出 ONNX |
这里有个很容易踩的坑:TensorRT 8.6 和 TensorRT 10.x 的 API 有差异。网上很多教程用的是 8.x 的写法,但如果你装的是 10.x,代码可能直接报错。我的建议是:跟着本文用 TensorRT 8.6 GA,这个版本的资料最多,坑最少。
2.3 为什么我不建议盲目追新版本
我理解大家都想装最新版,但深度学习部署环境的逻辑是“能用就不动”。TensorRT 10.x 虽然性能更好,但它的 Python API 做了一些调整,很多旧代码直接跑不通。更重要的是,TensorRT 10 对 ONNX 的算子支持更严格,某些 YOLO 版本导出的模型可能会遇到算子不兼容的问题。
实测下来,TensorRT 8.6 GA 配合 YOLOv5 和 YOLOv8 的导出流程是最顺滑的,这也是目前生产环境中使用最广泛的组合。
3. 安装动作分解:驱动、CUDA、cuDNN 一步步来
3.1 第一步:确认显卡驱动是否已就绪
在开始之前,先检查系统里有没有 NVIDIA 驱动:
nvidia-smi如果这个命令能正常输出显卡信息,说明驱动已装好。如果提示command not found,说明没装驱动,先安装:
sudo apt update sudo apt install nvidia-driver-535 sudo reboot注意:安装驱动后必须重启,否则
nvidia-smi依然不可用。
3.2 第二步:安装 CUDA 12.2
CUDA 的安装方式有两种:runfile 和 deb 包。对于开发环境,我推荐用 runfile 方式,因为可控性更强,不会自动覆盖系统里已有的驱动。
从 NVIDIA 官网下载 CUDA 12.2 runfile 安装包后:
wget https://developer.download.nvidia.com/compute/cuda/12.2.0/local_installers/cuda_12.2.0_535.54.03_linux.run sudo sh cuda_12.2.0_535.54.03_linux.run安装时注意:不要勾选 Driver,只安装 CUDA Toolkit 即可。如果勾选了 Driver,安装程序会尝试覆盖驱动版本,容易出问题。
装完之后配置环境变量:
echo 'export PATH=/usr/local/cuda-12.2/bin:$PATH' >> ~/.bashrc echo 'export LD_LIBRARY_PATH=/usr/local/cuda-12.2/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc source ~/.bashrc验证:
nvcc -V能输出版本信息就说明 CUDA 安装成功。这里有个细节:nvcc和nvidia-smi显示的 CUDA 版本可能不一样,nvidia-smi显示的是驱动支持的 CUDA 最高版本,而nvcc显示的是当前安装的 Toolkit 版本,两者不一样是正常的,不用慌张。
3.3 第三步:安装 cuDNN 8.9
cuDNN 需要从 NVIDIA 官网下载,注意选择与 CUDA 12.2 匹配的版本。下载完成后,解压并进行文件拷贝:
tar -xzvf cudnn-linux-x86_64-8.9.7.29_cuda12-archive.tar.xz sudo cp cudnn-linux-x86_64-8.9.7.29_cuda12-archive/include/cudnn*.h /usr/local/cuda-12.2/include/ sudo cp cudnn-linux-x86_64-8.9.7.29_cuda12-archive/lib/libcudnn* /usr/local/cuda-12.2/lib64/ sudo chmod a+r /usr/local/cuda-12.2/include/cudnn*.h /usr/local/cuda-12.2/lib64/libcudnn*3.4 小结:装完这三件套怎么验证
装完驱动、CUDA、cuDNN 之后,先别急着装 TensorRT,花两分钟做一个完整性检查:
# 检查驱动 nvidia-smi # 检查 CUDA Toolkit nvcc -V # 检查 cuDNN 是否被 CUDA 识别 ldconfig -p | grep cudnn如果ldconfig能搜到libcudnn.so相关输出,说明 cuDNN 已被系统识别。这个检查很关键,因为 TensorRT 安装后运行时会依赖 cuDNN,如果这里没弄好,后面 TensorRT 初始化就会报错。
4. TensorRT 安装:tar 包和 pip 两种方式怎么选
4.1 两种安装方式对比
TensorRT 的安装有两种主流方式,我这里把它们的区别和适用场景说清楚:
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| tar 包 | 自由控制版本、包含全部工具 | 需要手动配置环境变量 | 生产环境、需要 trtexec 工具的 |
| pip 安装 | 简单快捷、自动处理依赖 | 版本可能和 CUDA 不匹配 | 快速体验、开发调试 |
4.2 方式一:tar 包安装 TensorRT 8.6 GA
从 NVIDIA 官网下载 TensorRT 8.6 GA 的 tar 包,注意选择匹配 CUDA 12.x 的版本:
tar -xzvf TensorRT-8.6.1.6.Linux.x86_64-gnu.cuda-12.0.tar.gz解压后将 TensorRT 移动到合适位置,并配置环境变量:
sudo mv TensorRT-8.6.1.6 /opt/tensorrt echo 'export LD_LIBRARY_PATH=/opt/tensorrt/lib:$LD_LIBRARY_PATH' >> ~/.bashrc echo 'export PATH=/opt/tensorrt/bin:$PATH' >> ~/.bashrc source ~/.bashrc安装 Python 接口:
cd /opt/tensorrt/python pip install tensorrt-8.6.1.6-cp310-none-linux_x86_64.whl验证安装是否成功:
python3 -c "import tensorrt; print(tensorrt.__version__)"能输出8.6.1.6就说明 Python 接口装好了。对于使用 Python 做推理的场景,还需要安装 pycuda:
pip install pycuda4.3 方式二:pip 安装 TensorRT
如果你只是想做快速实验,pip 方式确实简单:
pip install tensorrt但我强烈建议你检查一下版本是否与 CUDA 匹配:
python3 -c "import tensorrt; print(tensorrt.__version__)"如果装出来的版本是 10.x,请卸载后安装指定版本 8.6:
pip uninstall tensorrt pip install tensorrt==8.6.1注意:pip 安装的 TensorRT 默认不包含
trtexec工具。如果你需要这个工具做模型转换和性能测试,强烈推荐用 tar 包方式安装。
5. 核心环节:YOLO 模型导出与 TensorRT engine 生成
5.1 从 PyTorch 权重导出 ONNX
TensorRT 不能直接读取 PyTorch 的.pt权重文件,需要先经过 ONNX 这个中间格式。以 YOLOv5 为例:
# 克隆 YOLOv5 仓库(若已有则跳过) git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt # 导出 ONNX python export.py --weights yolov5s.pt --include onnx --opset 12这里有个关键参数--opset。为什么要设为 12?TensorRT 8.6 对 ONNX opset 12 的支持最完善,更高的 opset 虽然也能转,但有概率遇到不支持的算子。
YOLOv8 的导出略有不同:
pip install ultralytics yolo export model=yolov8s.pt format=onnx opset=12导出后可以用onnxruntime验证一下 ONNX 模型是否能正常推理,确保模型文件本身没问题:
pip install onnxruntime-gpu python3 -c " import onnxruntime as ort sess = ort.InferenceSession('yolov5s.onnx', providers=['CUDAExecutionProvider']) print('ONNX 模型验证通过') "5.2 用 trtexec 生成 engine 文件
这是最推荐的方式,也是我实际使用最多的路径。trtexec 是 TensorRT 自带的命令行工具,用法简单,还能顺手做个性能测试:
/opt/tensorrt/bin/trtexec \ --onnx=yolov5s.onnx \ --saveEngine=yolov5s.engine \ --fp16 \ --workspace=4096参数解释:
--fp16:启用半精度推理,速度会有明显提升。只要你的显卡支持 FP16(绝大多数 NVIDIA 显卡都支持),建议开启。--workspace=4096:设置最大工作空间为 4GB,单位为 MB。设置太小可能导致 engine 生成失败。
如果模型比较大,生成时间会很长,这时候耐心等待即可。生成完成后,建议顺便用--loadEngine模式测一下性能:
/opt/tensorrt/bin/trtexec --loadEngine=yolov5s.engine --fp16这里注意到一个细节:--fp16在加载 engine 时也要带上,否则 trtexec 可能用 FP32 模式去加载,速度结果会失真。
5.3 用 Python API 生成 engine:动态 batch 场景的选择
trtexec 的方便之处是快速,但如果你想在 Python 代码里直接控制 engine 的生成逻辑,或者需要支持动态输入尺寸,就需要用 Python API。下面这段代码我实测可用:
import tensorrt as trt logger = trt.Logger(trt.Logger.WARNING) builder = trt.Builder(logger) network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)) parser = trt.OnnxParser(network, logger) with open("yolov5s.onnx", "rb") as f: parser.parse(f.read()) config = builder.create_builder_config() config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 4 << 20) config.set_flag(trt.BuilderFlag.FP16) serialized_engine = builder.build_serialized_network(network, config) with open("yolov5s.engine", "wb") as f: f.write(serialized_engine) print("engine 生成完成")注意4 << 20这行,这是设置 workspace 大小的写法,单位是字节。我把这里写成 4GB,和你用 trtexec 时的--workspace=4096等价。
5.4 静态 engine 与动态 batch 的选择建议
我必须在这里提醒一下:默认生成的 engine 是固定 batch size 的。如果你的业务场景 batch size 永远是 1,直接用默认就好。但如果你的推理请求会动态变化 batch,那生成 engine 时需要在 ONNX 导出阶段就指定动态维度。
YOLOv5 导出动态 batch 的完整命令是:
python export.py --weights yolov5s.pt --include onnx --opset 12 --dynamic然后再用 trtexec 转换时加上:
--minShapes=images:1x3x640x640 \ --optShapes=images:8x3x640x640 \ --maxShapes=images:16x3x640x640但我的经验是:除非确定需要动态 batch,否则就用静态 batch。动态 batch 的 engine 在某些显卡上会出现性能回退,而且显存占用会明显变大。很多线上推理服务通过请求排队 + 固定 batch 方式就能解决问题,没必要吃动态 batch 的苦。
6. 推理代码落地:从 engine 到检测结果的完整链路
6.1 推理脚本的基本框架
engine 文件生成后,需要自己写推理脚本。这里我提供一个经过验证的框架,它包含加载 engine、预处理、推理、后处理四个环节。
import numpy as np import cv2 import tensorrt as trt import pycuda.driver as cuda import pycuda.autoinit class YOLO_TRT: def __init__(self, engine_path): self.logger = trt.Logger(trt.Logger.WARNING) with open(engine_path, "rb") as f: self.runtime = trt.Runtime(self.logger) self.engine = self.runtime.deserialize_cuda_engine(f.read()) self.context = self.engine.create_execution_context() # 分配输入输出缓冲区 self.inputs = [] self.outputs = [] self.allocations = [] for i in range(self.engine.num_io_tensors): name = self.engine.get_tensor_name(i) mode = self.engine.get_tensor_mode(name) shape = self.engine.get_tensor_shape(name) size = trt.volume(shape) dtype = trt.nptype(self.engine.get_tensor_dtype(name)) cuda_alloc = cuda.mem_alloc(size * dtype.itemsize) self.allocations.append(cuda_alloc) if mode == trt.TensorMode.INPUT: self.inputs.append({"name": name, "shape": shape, "dtype": dtype, "alloc": cuda_alloc}) else: self.outputs.append({"name": name, "shape": shape, "dtype": dtype, "alloc": cuda_alloc}) def infer(self, img): # 预处理:resize + 归一化 + HWC to CHW img_resized = cv2.resize(img, (640, 640)) img_rgb = cv2.cvtColor(img_resized, cv2.COLOR_BGR2RGB) img_norm = img_rgb.astype(np.float32) / 255.0 img_chw = np.transpose(img_norm, (2, 0, 1)) img_input = np.ascontiguousarray(img_chw).ravel() # 拷贝输入数据到显存 cuda.memcpy_htod(self.inputs[0]["alloc"], img_input) # 执行推理 self.context.execute_v2(self.allocations) # 取回输出数据 output = np.empty(trt.volume(self.outputs[0]["shape"]), dtype=np.float32) cuda.memcpy_dtoh(output, self.outputs[0]["alloc"]) output = output.reshape(self.outputs[0]["shape"]) return output这个框架在 YOLOv5 和 YOLOv8 上都能用,区别只在于后处理的解析方式不同。
6.2 后处理:从原始输出到检测框
YOLOv5 的 TensorRT 输出是一个[batch, 25200, 85]的数组。其中 25200 是三个尺度特征图上的 anchor 总数,85 是xywh + objectness + 80 个类别概率。后处理要做的是过滤低置信度的框,再用 NMS 去重:
def postprocess(output, conf_thres=0.25, iou_thres=0.45): # output shape: (1, 25200, 85) preds = output[0] # (25200, 85) # 过滤掉 objectness 低于阈值的框 obj_conf = preds[:, 4:5] mask = obj_conf >= conf_thres preds = preds[mask.flatten()] if len(preds) == 0: return [] boxes = preds[:, :4] class_conf = preds[:, 5:] * preds[:, 4:5] class_id = np.argmax(class_conf, axis=1) max_conf = np.max(class_conf, axis=1) # 转换坐标格式:xywh to xyxy boxes[:, 0] -= boxes[:, 2] / 2 boxes[:, 1] -= boxes[:, 3] / 2 boxes[:, 2] += boxes[:, 0] boxes[:, 3] += boxes[:, 1] # NMS 过滤重叠框 indices = cv2.dnn.NMSBoxes(boxes.tolist(), max_conf.tolist(), score_threshold=conf_thres, nms_threshold=iou_thres) result = [] for i in indices: idx = i[0] if isinstance(i, np.ndarray) else i result.append({ "box": boxes[idx].astype(int), "conf": float(max_conf[idx]), "class_id": int(class_id[idx]) }) return result这是我简化后的版本,实际项目中建议把 NMS 逻辑封装好,方便批量测试。
6.3 预处理细节:输入尺寸和归一化不能想当然
YOLO 模型训练时用的是 640x640 输入,推理时必须保持一致。如果你用到的是其他输入尺寸,比如 416 或 1280,需要把模型导出和推理脚本里的尺寸改成一致,否则检测精度会明显下降。
另外,很多人的模型是 PyTorch 里用 BGR 顺序训练的,有些又是 RGB。保险的做法是看训练时代码的数据增强输入的是哪种通道顺序。我踩过一次:YOLOv5 用 BGR 训练,YOLOv8 用 RGB 训练,如果你都用同一个预处理代码,其中一个必然精度崩塌。
7. 常见问题与排查手册:我踩过的坑都在这
7.1 高频错误速查表
| 错误现象 | 原因 | 解决方法 |
|---|---|---|
ImportError: libnvinfer.so.8: cannot open shared object file | TensorRT 的库路径没配置 | export LD_LIBRARY_PATH=/opt/tensorrt/lib:$LD_LIBRARY_PATH |
[TensorRT] ERROR: Failed to load ONNX file | ONNX 导出不完整或模型损坏 | 重新导出 ONNX,并用 onnxruntime 验证 |
AttributeError: module 'tensorrt' has no attribute 'BuilderFlag' | TensorRT 版本过旧或 API 版本差异 | 确认 TensorRT 版本是 8.x,不要用 7.x |
CUDA error: no kernel image is available for execution on the device | CUDA 版本和显卡驱动不匹配 | 升级驱动到 535 以上 |
Assertion failed: shapes.dot is not valid | 输入尺寸和 engine 期望尺寸不一致 | 检查预处理代码里的 resize 尺寸 |
pycuda._driver.LogicError: cuMemAlloc failed: out of memory | 显存不足 | 减小 batch size 或降低输入分辨率 |
7.2 最容易翻车的三个坑(重点看)
坑一:CUDA 装了多个版本导致环境变量混乱
很多人电脑上可能同时存在 CUDA 11.x 和 12.x,环境变量指向了旧的版本。检查方式:
echo $LD_LIBRARY_PATH ls -l /usr/local/cuda确保/usr/local/cuda的软链接指向的是你想用的 CUDA 12.2。如果没有软链接或者指向错误,手动修改:
sudo rm /usr/local/cuda sudo ln -s /usr/local/cuda-12.2 /usr/local/cuda坑二:trtexec 能转 engine,但 Python 端 import tensorrt 失败
这个现象特别迷惑人,因为 trtexec 在命令行下工作正常,说明库文件是存在的,但 Python 却导入不了。最常见的原因是:Python 环境中安装了多个版本的 TensorRT 或者 CUDA 相关的库冲突。
解决方式很简单:确保 Python 用的是同一套环境变量,建议在启动脚本前显式指定:
export LD_LIBRARY_PATH=/opt/tensorrt/lib:/usr/local/cuda-12.2/lib64:$LD_LIBRARY_PATH PYTHONPATH=/opt/tensorrt/python:$PYTHONPATH python3 your_script.py坑三:FP16 模式下检测精度骤降
开启--fp16后推理速度确实快了,但有时候会出现检测框偏移、置信度普遍变低的情况。这个问题的根源是 conv 层的精度敏感度,某些 YOLO 版本的分支结构对 FP16 不友好。
我的经验是:如果开启 FP16 后精度下降明显,可以在生成 engine 时排除掉那几个层的 FP16 优化:
/opt/tensorrt/bin/trtexec \ --onnx=yolov5s.onnx \ --saveEngine=yolov5s_fp16.engine \ --fp16 \ --layerPrecision=conv_0=FP32实在搞不定就不要强上 FP16,FP32 的推理速度在 TensorRT 的加持下也已经比原生 PyTorch 快不少了。
7.3 推理速度上不去的几个检查点
如果你的 TensorRT 推理耗时没有明显优于 PyTorch 的 CUDA 推理,先别急着怀疑 TensorRT 没用,按下面顺序自查:
- 确认 PyTorch 用的是半精度(AMP)推理?如果对比的基准本身就是 FP16,那 TensorRT 的优势会被缩小。
- 确认用了正确的 batch 大小?单帧推理与 batch=32 的吞吐量差距可能超过 10 倍。
- 确认预处理耗时被排除在计时之外?很多人测的是“预处理 + 推理 + 后处理”的完整管线耗时,而 PyTorch 对比时只计了纯模型推理时间,这是不公平的对比。
- 确认显卡处于持续性能模式?运行
nvidia-smi -pm 1开启持久模式,否则 GPU 会频繁降频。
8. 其他可行路径:用开箱即用的方案
如果你嫌手动流程太长,还有一条路:YOLO 官方社区自带了 TensorRT 的默认配置,简化了很多流程。
YOLOv5 自带 TensorRT 推理脚本:
cd yolov5 python detect.py --weights yolov5s.pt --device 0 --include tensorrt它在首次运行时会自动完成 PyTorch → ONNX → TensorRT 的转换流程,不需要手动执行 trtexec。不过这个方式有个局限:它用的是 YOLOv5 仓库内置的推理代码,如果你想自定义后处理逻辑,还是要回到手动方案。
YOLOv8 类似,ultralytics库集成了一键导出 TensorRT engine 的能力:
from ultralytics import YOLO model = YOLO("yolov8s.pt") model.export(format="engine", half=True)实测这种方式的效率很高,但它生成的 engine 可能在 TensorRT API 版本升级后失效,需要重新生成。对于快速验证和原型开发,这个方案很合适;对于生产环境,我依然推荐手动流程,因为可控性最强。
9. 最后几个建议
TensorRT 环境配置这件事,说难也难,说简单也简单。难的是版本匹配的坑太多,简单的是只要按照正确的顺序安装,每一步都是机械操作。
我个人实际部署过几轮之后,最大的体会是:遇到报错不要慌,先把版本对应关系理清楚。在群里看到过太多人报一个错就换一个版本重装,结果越装越乱。正确做法是:出现报错时,先确认报错涉及的是哪一层依赖,然后针对性地修复,不要整个环境从头再来。
还有一点:环境变量是隐形杀手。如果各种奇怪的报错层出不穷,优先检查LD_LIBRARY_PATH和PYTHONPATH。很多时候,问题不是缺库,而是系统找到了不该找的库。
最后,无论你最终选了哪种安装方案,强烈建议把安装过程中用到的包名和版本号记录成一个文本文件,放在项目目录里。下次换机器或者给同事同步环境的时候,这份记录能帮你省掉大量排查时间。另外,我习惯在生成完 engine 后,把对应的 ONNX 文件和转换参数一起存档,这样即使 engine 文件在系统升级后失效,也能快速重建。
希望这篇文章能帮你顺利跑通 YOLO 的 TensorRT 部署。如果你在配置过程中遇到了本文没有覆盖到的坑,欢迎在评论区留言交流,我看到了会尽量帮你分析。