简介面向在 Win10 系统上使用 TensorRT 加速部署 YOLOv5 目标检测的开发者这份 PDF 文档系统整理了完整的软件版本与依赖库清单涵盖 CUDA 10.2、cuDNN 7.6.5、VS2019、OpenCV 3.4.0、Anaconda3、CMake 3.19.4、TensorRT 7.0.0.11 以及 YOLOv5-V4.0 源码针对环境版本匹配繁琐、配置过程容易踩坑的问题给出明确指引。资源仅含 1 个 PDF 文件体积 169KB内容为软件下载链接、百度网盘提取码和目录索引并关联配套图文教程适合从零开始搭建部署环境、希望快速上手的小白工程师。目前已有 3492 人学习方案经过多人实践验证可作为项目落地的参考样板。文档详细梳理了从依赖安装、CMake 配置工程、VS2019 编译源码到 TensorRT 解析权重、生成优化推理引擎的整体流程覆盖环境准备到模型优化的关键环节同时提供已预配置好的百度网盘软件包下载后即可按教程逐步操作避开 CUDA/cuDNN 与显卡的兼容性坑快速跑通 GPU 加速的 YOLOv5 目标检测。1. 把YOLOv5从训练机拖到TensorRT部署为什么第一步是VS2019很多同学把YOLOv5训练完拿到best.pt就以为大功告成结果一上生产就傻眼Python推理太慢CPU顶不住显卡利用率死活上不去。换TensorRT之后同一个模型在Windows加NVIDIA显卡上能跑到毫秒级。但TensorRT的C API在Windows上编译绕不开Visual Studio 2019——至少官方示例工程和多数现成方案都是基于它做的。这篇笔记就沿着“环境准备 → pt转engine → VS2019编译 → 避坑 → 提速”这条路径把全套软件下载、版本搭配和编译参数都过一遍。适合手里有YOLOv5模型、想在Windows上做C部署、又被VS2019和TensorRT版本折腾过的人。新手能照步骤走熟手可以直接跳到第5章看翻车点。2. 软件版本矩阵YOLOv5、TensorRT与VS2019怎么选才不打架部署的第一步不是写代码是装环境。我见过太多人把这步搞成玄学CUDA装错版本TensorRT解析ONNX报一堆Unsupported operator最后怀疑人生。其实版本对齐有迹可循核心就一句话TensorRT版本绑定CUDA版本CUDA版本绑定VS版本和显卡驱动YOLOv5只是中间的过客。2.1 先定CUDA和cuDNNTensorRT的前提条件TensorRT不是独立运行的库它所有算子都跑在CUDA上面。所以先定CUDA版本。以当前主流组合为例TensorRT 8.5.3对应CUDA 11.8TensorRT 8.6 GA对应CUDA 12.0。大多数部署项目里YOLOv5用的是PyTorch 1.12或1.13这两个PyTorch版本官方预编译包绑定的是CUDA 11.6/11.7/11.8。所以我一般推荐这套组合组件推荐版本备注显卡驱动525.xx及以上太老装不上CUDA ToolkitCUDA Toolkit11.8兼容PyTorch 1.12/1.13cuDNN8.9.x解压后把bin目录加入PATHTensorRT8.5.3.1 for CUDA 11.8最稳网上教程最多VS2019Community 16.x安装MSVC v142 Windows 10 SDK这套组合经历过大量落地验证。你去看网上能搜到的“tensorrt安装教程”大部分也是基于这个版本写的。上CUDA 12.x也行但YOLOv5里一些不常见的plugin算子可能没适配转engine时会报Unsupported operator排查起来非常耗时间。装CUDA Toolkit时注意安装包会自带驱动检测如果你电脑已经有驱动就取消驱动安装选项只装Toolkit和Integration。装完后打开命令行验证nvcc -V能看到Cuda compilation tools, release 11.8就是成功。nvcc在C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.8\bin需要把这个路径加到系统PATH里。提示不要装两套CUDA。多个项目共用一台机器时先装高版本再装低版本很容易把cudart64.dll覆盖乱。建议直接一台机器只留一个CUDA版本。2.2 VS2019安装时只勾这三个核心组件VS2019在部署链路里作用有两个给CUDA/TensorRT提供MSVC编译器再编译你的C推理工程。很多人问“vs2019安装教程”其实关键不在安装步骤而在组件选择。我在Visual Studio Installer里只勾这几项工作负载使用C的桌面开发单个组件MSVC v142 - VS 2019 C x64/x86生成工具单个组件Windows 10 SDK选最新版为什么要单独提MSVC v142因为TensorRT的.lib文件是用v142工具集编译的你用VS2022的v143去链接大概率报RuntimeLibrary mismatch。更烦的是CUDA Toolkit在安装时会自动检测VS集成如果机器上只有VS2022没有VS2019CUDA安装界面里Visual Studio Integration会打红叉后面编译TensorRT示例工程时直接失败。注意VS2019 Community是免费的在官网Visual Studio下载页能找到旧版本。不要在第三方网站下“绿色版”“整合版”MSVC编译器被篡改后链接报错你根本没法排查。另外CUDA安装时会自动往VS2019里加属性表。装完CUDA再装VS2019也行CUDA安装包里有cuda_installer支持补装Integration但手动补装偶发失效。我的习惯是先装VS2019再装CUDA顺序别反。2.3 TensorRT的下载与解压不用安装但要记住路径TensorRT下载下来是个tar压缩包没有安装向导解压到任意目录就是全部文件。解压后目录长这样TensorRT-8.5.3.1/ ├── bin/ ├── include/ ├── lib/ ├── samples/ └── targets/它的运行依赖一堆.dll比如nvinfer.dll、nvonnxparser.dll。所以解压后必须把bin目录和lib目录都加进系统PATH。Windows下具体操作此电脑→属性→高级系统设置→环境变量→在“系统变量”里新建TENSORRT_PATH指向解压根目录然后把%TENSORRT_PATH%\lib加进PATH。下载地址在NVIDIA官网TensorRT页面需要登录NVIDIA开发者账号免费。下载时注意选对平台比如TensorRT 8.5.3.1 for Windows 10 x86_64。文件名里带CUDA 11.8字样的才是对应的。我不推荐下最新的9.x因为YOLOv5的旧导出文件在9.x上偶尔会报AttributeError没有必要冒险。2.4 用trtexec验证环境一条命令跑通ONNX环境装完先别急着写代码。TensorRT自带一个命令行工具trtexec在bin目录下。用它验证TensorRT能不能正常调用CUDA和显卡trtexec --onnxmodel.onnx --saveEnginemodel.engine --workspace2048 --fp16如果这条命令能正常输出Engine built in xxx ms且最后有[07/xx/xxxx]格式的时间戳日志说明CUDA、TensorRT、显卡驱动全通了。如果报错先看是不是缺少cudart64_*.dll这个文件在CUDA的bin目录里通常是把CUDA的bin没加进PATH导致的。还有个常见现象是trtexec直接闪退在命令行里运行能看到错误信息双击运行反而看不到所以务必用cmd启动。注意--workspace参数在TensorRT 8.x里是--workspace单位是MB在更早版本是--workspaceSize。你从网上抄的命令如果报“unknown option”先检查版本。环境装到这里你已经成功一半了。接下来做正事把PyTorch的best.pt转成TensorRT能吃的engine文件。3. 把YOLOv5的best.pt变成engine导出ONNX与TensorRT转换YOLOv5训练好的权重是PyTorch格式TensorRT不能直接读。标准链路是pt → onnx → engine。中间这个ONNX是桥梁也是最容易出幺蛾子的地方。很多朋友在“pt文件转换tensorrt”这一步卡住要么ONNX里有TensorRT不支持的算子要么动态shape理解错了。这一章我把两种转换路径都写清楚。3.1 用YOLOv5官方export.py导出ONNXYOLOv5仓库自带export.py这是最可靠的导出方式。先激活训练时的Python环境然后执行python export.py --weights best.pt --include onnx --opset 12 --dynamic这条命令的逻辑加载best.pt权重把检测头合并成一个输出batch, 25200, 85再导出成ONNX。参数说明--weights best.pt训练出的权重路径--include onnx只导出ONNX避免顺带导出torchscript等其他格式浪费时间--opset 12ONNX算子集版本。TensorRT 8.x对opset 12支持最完整opset 13也能用但有些旧版plugin不兼容--dynamic让输入维度变成动态batch。这里有个坑如果你不加--dynamic导出的是固定1x3x640x640后面TensorRT转engine时就只能批大小为1想改成8就得重新导出。我一般习惯加--dynamic给转engine时留选择余地。导出后会在同目录生成best.onnx。可以用onnxruntime快速验证输出shape是否正常import onnxruntime as ort import numpy as np sess ort.InferenceSession(best.onnx) x np.random.rand(1, 3, 640, 640).astype(np.float32) out sess.run(None, {sess.get_inputs()[0].name: x}) print([o.shape for o in out])这里有个细节YOLOv5 v6.0之后导出默认会把三个检测头80x80、40x40、20x20cat成一个输出shape是(1, 25200, 85)。如果你看到输出是三个独立的(1, 80, 80, 85)说明导出脚本版本较老或加了--grid之类的参数。这直接影响后面写后处理务必先搞清楚输出格式。3.2 用trtexec转换engine固定shape还是动态shapeONNX拿到手直接用命令行转engine是最省事的trtexec --onnxbest.onnx --saveEnginebest.engine --fp16 --workspace4096如果导出时带了动态batch直接这样转会报错因为trtexec不知道输入shape。需要显式指定trtexec --onnxbest.onnx --saveEnginebest.engine --fp16 \ --minShapesimages:1x3x640x640 \ --optShapesimages:1x3x640x640 \ --maxShapesimages:8x3x640x640参数说明images是ONNX输入张量的名字在YOLOv5导出文件里通常叫images。minShapes、optShapes、maxShapes分别代表运行时输入的最小、最优、最大维度。前两个数字是batch和通道后两个是宽高。optShapes是性能调优用的运行时会按这个shape选择最快的内核。如果你的摄像头场景固定batch1就全写1x3x640x640没必要用动态batch动态加载会让engine体积更大开头还有一次额外reshape开销。--fp16是个关键开关。只要显卡是GTX 10系以上开FP16能让推理速度提升40%到80%。代价是精度略降但YOLOv5的检测头在大多数场景下影响不大。如果你看到输出框抖动或者置信度明显异常可以换回FP32试一下有时候不是精度问题而是个别层溢出。3.3 用Python API批量转换适合要反复调参的场景命令行只能生成engine但如果你想做INT8量化或者想控制每层的精度模式得用TensorRT的Python API。下面这段代码实现了和trtexec同样的事但可以自由加配置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(best.onnx, rb) as f: parser.parse(f.read()) config builder.create_builder_config() config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 4 * (1 20)) config.builder_optimization_level 3 if builder.platform_has_fast_fp16: config.set_flag(trt.BuilderFlag.FP16) serialized builder.build_serialized_network(network, config) with open(best.engine, wb) as f: f.write(serialized)代码逻辑分四步创建Builder、解析ONNX、配置引擎参数、序列化保存。参数说明EXPLICIT_BATCH标志必须设置否则动态batch不被允许set_memory_pool_limit是TensorRT 8.5的写法老教程里用的builder.max_workspace_size在8.5已经废弃报AttributeError别奇怪builder_optimization_level是0到3的优化等级数字越大转换时间越长对应运行时性能越好。部署时用3测试时用2就够了platform_has_fast_fp16用来探测硬件是否支持快速FP16计算A卡没有这个能力装NVIDIA驱动后才会为真这段代码跑完best.engine就到手了。用trtexec加载它比加载ONNX更快因为模型已经优化过trtexec --loadEnginebest.engine如果每秒推理次数Throughput显示正常说明转换成功。接下来才是重头戏在VS2019里把C推理程序编译出来。4. 在VS2019里编译TensorRT C推理程序从空工程到能出框engine文件有了现在要用C把它加载起来然后接摄像头或图片输出检测框。这一步是“YOLOv5实战TensorRT部署”里最有门槛的部分因为VS2019工程配置牵扯到包含目录、库目录、动态库搜索路径稍不留神就链接失败。4.1 创建工程并配置属性表打开VS2019新建一个C空项目平台选x64别选Win32。然后在项目上右键→属性做四件事C/C → 常规 → 附加包含目录填入TensorRT的include目录和CUDA的include目录链接器 → 常规 → 附加库目录填入TensorRT的lib目录和CUDA的lib\x64目录链接器 → 输入 → 附加依赖项填入nvinfer.lib;nvonnxparser.lib;nvparsers.lib;cudart.lib调试 → 环境填入PATH$(CUDA_PATH)\bin;$(TENSORRT_PATH)\lib;$(PATH)第四步经常被忽略但特别重要VS调试时不会自动加载DLL搜索路径不配置就报“找不到nvinfer.dll”。$(CUDA_PATH)和$(TENSORRT_PATH)是系统环境变量如果没定义也可以写绝对路径但换机器就要改。我习惯把这两个变量设置成用户级别环境变量这样工程里用宏引用换机器只要改环境变量不用改工程文件。还有一个坑TensorRT的库只有x64版本所以工程平台必须选x64。如果VS默认生成的解决方案平台是Win32链接时一定报LNK1104: cannot open file nvinfer.lib因为它在x86目录里找不到。改成x64后VS会自动寻找x64子目录下的lib。另外OpenCV如果要用也要下载x64版本库目录指到opencv\build\x64\vc15\lib这个vc15对应VS2019vc14对应VS2015别混。4.2 一个最小可用的TensorRT推理类我不推荐直接在main.cpp里堆代码最好封装一个类。头文件这样起#pragma once #include string #include vector #include opencv2/opencv.hpp class YoloTRT { public: explicit YoloTRT(const std::string engine_path); ~YoloTRT(); std::vectorstd::vectorfloat infer(const cv::Mat img); private: void* engine_ nullptr; void* context_ nullptr; void* buffers_[2] {}; float* output_ nullptr; int input_h_ 640; int input_w_ 640; int output_size_ 25200 * 85; };实现文件里最关键的是加载engine和绑定bufferYoloTRT::YoloTRT(const std::string engine_path) { std::ifstream file(engine_path, std::ios::binary); file.seekg(0, std::ios::end); size_t size file.tellg(); file.seekg(0, std::ios::beg); std::vectorchar data(size); file.read(data.data(), size); trt::Logger logger; auto runtime trt::createInferRuntime(logger); engine_ runtime-deserializeCudaEngine(data.data(), size); context_ engine_-createExecutionContext(); // 分配输入输出显存 cudaMalloc(buffers_[0], 1 * 3 * 640 * 640 * sizeof(float)); cudaMalloc(buffers_[1], output_size_ * sizeof(float)); output_ new float[output_size_]; }代码逻辑读入engine文件反序列化生成ICudaEngine然后创建IExecutionContext——后者才是执行推理的对象。注意buffers_数组存的是显存地址output_是CPU端接收结果用的内存。这里如果输出shape不对output_size_写死了会影响后处理最好从engine里动态读取auto dims engine_-getBindingDimensions(1); output_size_ 1; for (int i 0; i dims.nbDims; i) output_size_ * dims.d[i];推理时先做预处理把OpenCV的cv::Mat缩放到640x640BGR转RGB再归一化到0~1然后拷贝到输入显存调用enqueueV2让它跑。这一步耗时主要在缩放和归一化后面第6章会讲怎么优化。4.3 编译期的常见LNK错误对照我在办公室帮同事排错最常遇到这几个编译错误列成对照表方便自查错误信息原因解决LNK2019 unresolved external symbol createInferRuntime没有链接nvinfer.lib在附加依赖项里补上LNK2038 mismatch detected for RuntimeLibraryVS运行库不一致项目属性→代码生成→运行库Debug用/MTdRelease用/MTLNK1104 cannot open file nvinfer.lib库目录不对或平台选了Win32确认附加库目录指向TensorRT的lib平台改x64LNK2001 unresolved external symbol public: __cdecl ...TensorRT头文件和库版本不匹配检查你是不是把TensorRT 7的头文件和8.5的lib混用了这些错误看着吓人实际上都是配置问题。我建议先把一个只加载engine不推理的空工程编译通过再逐步加推理代码缩小出错范围。还有Release模式下记得把优化选项打开默认的/Od会导致推理性能下降不少。第4章到这里你已经能在VS2019里跑通一个TensorRT推理程序。但跑通不代表跑对接下来这章是血泪经验合集专门讲那些一踩一个准的坑。5. 部署避坑YOLOv5TensorRT路上常见的5个翻车点“pt文件转换tensorrt”看着简单实际部署时每个环节都可能翻车。下面这5个坑是我在实际项目里逐个踩过、填平后又亲眼看着别人踩过的每条都按现象、原因、解决写清楚。5.1 导出ONNX时报Unsupported operator: NonMaxSuppression现象用export.py导出ONNX时后面的TensorRT转换阶段报不支持的算子集中在NMS相关节点。原因YOLOv5某些版本在导出ONNX时默认带上完整的检测头其中包括NMS部分而TensorRT的OnnxParser对动态NMS支持有限尤其在opset 13之后。我一看报错就明白这是导出时没去掉NMS。解决导出时加--no-nms参数让ONNX只输出原始预测张量。NMS后处理放到C侧用OpenCV或自写代码实现。如果你不想手写可以直接用TensorRT的EfficientNMSplugin但需要自己写plugin配置复杂度高。我更建议在C里用CPU NMS反正检测框才25200个排序和抑制的开销在640x640输入下可以接受。5.2 engine运行时提示input dims must be bound现象程序编译通过但运行到context_-enqueueV2()时崩溃控制台输出Assertion failed: input dims must be bound。原因ONNX是动态shapeengine也是动态的但没有在推理前给输入绑定实际尺寸。TensorRT需要知道当前batch的输入形状才能算出中间层张量。解决在每次推理前显式设置输入绑定维度auto input_dims engine_-getBindingDimensions(0); input_dims.d[0] batch_size; input_dims.d[2] 640; input_dims.d[3] 640; context_-setBindingDimensions(0, input_dims);同时确认输出buffer大小和动态shape的最大值匹配否则会越界写坏显存报错比较随机。5.3 推理输出全是nan或黑白噪点现象engine加载成功推理输出数组里有大量nan画到图像上就是一片白点。原因我遇到两次。第一次是FP16模式下YOLOv5检测头最后的sigmoid输出接近饱和区FP16精度不够导致梯度溢出推理结果异常。第二次是输入数据归一化搞错了——训练时是按0~1归一化的我写代码时忘了除255直接喂了0~255的图像导致网络输出完全乱套。解决先关掉FP16用FP32跑一遍看结果是否正常。如果正常再开FP16并在TensorRT配置里给检测头那几层设成FP32精度。另一种解决是在归一化时用cv::divide配合cv::Scalar(255.0)不要用float(img)隐式类型转换后者慢还容易出错。5.4 VS2019编译时报找不到MSVC编译器现象新建工程后写了几行代码编译时VS突然报“找不到Intel Fortran编译器”或“No version of MSVC compiler”一看是VS Installer里少装了组件。原因这个坑最常见于从公司镜像装的VS2019只装了.NET负载没装“使用C的桌面开发”导致C编译组件缺失CUDA集成也用的是空壳。解决打开Visual Studio Installer选择修改勾选“使用C的桌面开发”强制更新。装完后确认VS安装目录下有VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64\cl.exe。没有这个文件所有C工程都没法编译。注意如果是Vue或其他非C项目这个问题不会暴露所以很多人装了VS却不知道缺组件。5.5 TensorRT库能转engine但C程序加载时报Could not load library nvinfer.dll现象trtexec在命令行能正常用但自己编译的exe一运行就报找不到nvinfer.dll或者是nvonnxparser.dll。原因trtexec在安装TensorRT时绑定了系统PATH但你的VS工程环境变量没配置或配置的路径不对。还有一种情况是拷了dll到exe目录但拷了32位版本x64程序加载不了。解决确认dll位数。在TensorRT的lib目录里dll是x64的如果你从系统目录里拷的可能是SysWOW64里的32位货。正确做法是在VS调试环境变量里写上TensorRT的lib目录而不是手动拷dll。发布时打包用dumpbin /dependents查看exe依赖了哪些dll然后把这些dll一并放到exe同目录。6. 让推理再快一步后处理中的CUDA提速与精度验证TensorRT把模型算完输出的还是未解码的原始预测——YOLOv5的输出是25200x85的矩阵包含每个anchor的中心坐标、宽高、置信度和80类物体概率。后处理要做的是解码、过滤、NMS这一步如果放在CPU单线程上可能消耗2到3毫秒甚至比TensorRT的模型推理还慢。所以部署工程里后处理优化和价值验证同样重要。我的做法是把解码放到CUDA核函数里。一个简单的并行思路给每个anchor分配一个线程计算目标框、置信度、类别然后通过thrust::sort_by_key按置信度排序最后在CPU上做NMS。排序放在GPU上做能省下几十毫秒。但这里有个边界——如果检测框种类不多、并发量小CPU NMS反而更稳。我一般先用CPU版本跑通再考虑GPU加速别一上来就上复杂度。另一个实用技巧是使用多输入流。TensorRT的IExecutionContext默认是同步执行如果你开了多个流可以用cudaMemcpyAsync实现输入数据和输出结果的异步拷贝。具体说把摄像头帧采集、预处理、模型推理分别放到不同CUDA流上解码和画框放到另一个线程这样CPU和GPU能重叠工作整体帧率能提升20%左右。但注意enqueueV2在8.5之后被标记为废弃建议用enqueueV3配合setTensorAddress写法更现代化。最后说下精度验证。部署完别直接上生产我习惯写个小脚本同一张图片分别跑PyTorch模型和TensorRT engine把两者输出的检测框和置信度存成JSON再算mAP差值。FP16和FP32的mAP差值小于0.5%就是正常范围超过1%就要检查是不是某个层溢出。这一步做过了你才知道开FP16有没有隐患。我踩过最痛的一次就是贪图FP16提速上线后发现某个类别漏检率暴增回查才知道是检测头越界从那以后精度对比就成了固定流程。希望这篇笔记能帮你在YOLOv5到TensorRT的路上少走几次弯路。本文还有配套的精品资源点击获取