资讯动态

从零复现:YOLO缺陷检测模型 TensorRT 全量化部署到 Jetson Orin Nano Super(FP32/FP16/INT8 三路对比)

发布时间:2026/9/15 23:41:48 来源:尧图企业网站定制
从零复现YOLO缺陷检测模型 TensorRT 全量化部署到 Jetson Orin Nano SuperFP32/FP16/INT8 三路对比文章定位零基础可跟做的完整部署教程手把手讲清楚每一个坑和为什么。关键词TensorRT量化 / Jetson部署 / YOLO / INT8校准 / FP16推理 / MJPEG推流一、背景与目标做模型部署最头疼的不是代码而是跑不起来的时候不知道为什么。这篇文章记录了我把一个工业表面缺陷检测YOLO模型从Windows上训练好的.pt文件一步步部署到Jetson Orin Nano Super上实现 USB 摄像头实时推理 浏览器 MJPEG 直播的完整过程。目标任务检测电子元件表面的四类缺陷——类别含义ZF-scratch锌层划伤scratch普通划痕broken断裂缺陷pinbreak引脚折断最终效果FP16引擎在 Jetson Orin Nano Super 上实现实时推理通过浏览器远程查看带标注框的视频流。二、硬件与软件环境2.1 我的设备配置设备规格边缘计算板NVIDIA Jetson Orin Nano Super (67 TOPS)JetPack6.2.1CUDA12.6TensorRT10.3.0开发主机Windows 10USB摄像头普通 UVC 免驱摄像头2.2 Windows 开发机需要安装pipinstallultralytics# YOLO模型加载和导出pipinstallonnx onnxsim# ONNX模型处理pipinstallonnxruntime# ONNX验证推理pipinstallparamikoscp# SSH远程操控Jetson2.3 Jetson 上的 conda 环境# 创建专用环境conda create-nyolo_trtpython3.10-yconda activate yolo_trt# 安装核心依赖pipinstallpycuda# ⚠️ 关键修复解决 GLIBCXX 版本冲突后面会讲为什么condainstalllibstdcxx-ng-cconda-forge-y三、项目结构整个项目按流水线方式组织每个步骤独立一个文件夹互不干扰方便调试Quantification/ ├── best.pt # 原始PyTorch模型38.7MB20.06M参数 ├── best.onnx # 导出的ONNX模型76.66MBopset17 └── steps_v2/ ├── step01_model_inspect/ # 第一步解剖模型 ├── step02_onnx_export/ # 第二步导出ONNX ├── step03_onnx_inspect/ # 第三步验证ONNX ├── step04_jetson_env_check/ # 第四步检查Jetson环境 ├── step05_transfer/ # 第五步传输文件 ├── step06_trt_fp32/ # 第六步构建FP32引擎 ├── step07_trt_fp16/ # 第七步构建FP16引擎 ├── step_int8_calib/ # 第八步INT8校准 │ └── calib_images/ # 50张校准图片 ├── step08_accuracy_compare/ # 第九步三路精度对比 └── step09_inference_demo/ # 第十步实时推理演示 ├── infer_v2.py # 在Jetson上运行的推理脚本 └── run.py # 在Windows上运行的上传脚本统一规则每个run.py都在 Windows 上运行自动 SSH 进 Jetson 完成操作实时打印日志。四、模型信息确认Step 01在动手之前先搞清楚模型的底细避免后面出现莫名其妙的维度错误。fromultralyticsimportYOLO modelYOLO(best.pt)print(model.task)# detect检测任务非分割/分类print(model.names)# {0:ZF-scratch, 1:scratch, 2:broken, 3:pinbreak}print(model.info())# 模型参数量关键结果任务类型 : detect 类别数量 : 4 输入尺寸 : 640 × 640 输出形状 : (1, 8, 8400) 参数量 : 20.06 M 模型大小 : 38.7 MB输出 (1, 8, 8400) 怎么理解1 batch_size 8 4 (cx, cy, w, h) 4 (每个类别的置信度) 8400 8400个候选框特征图上的锚点数后处理时先按置信度过滤再做NMS非极大值抑制才能得到最终检测框。五、导出 ONNXStep 02fromultralyticsimportYOLO modelYOLO(best.pt)model.export(formatonnx,imgsz640,opset17,# TensorRT 10.x 兼容推荐 ≥ 13simplifyTrue,# 用 onnxsim 精简计算图加速TRT构建dynamicFalse,# 固定batch1TRT优化效果更好)导出后文件为best.onnx76.66MB同时记录 MD5 哈希值用于后续传输校验。为什么opset选17TensorRT 10 支持 opset 9-20opset 17 包含了LayerNorm、GroupNorm等新算子对 YOLO 系列模型覆盖最全。六、验证 ONNXStep 03传到Jetson之前先在Windows本地验证ONNX是否正确避免传了个坏文件这种低级失误。importonnxruntimeasortimportnumpyasnp sessort.InferenceSession(best.onnx)dummynp.random.randn(1,3,640,640).astype(np.float32)outputsess.run(None,{images:dummy})print(f输出形状:{output[0].shape})# (1, 8, 8400)print(fNaN数量:{np.isnan(output[0]).sum()})# 应为 0print(fInf数量:{np.isinf(output[0]).sum()})# 应为 0全部通过后再进行下一步。七、检查 Jetson 环境Step 04通过 SSH 远程检查 Jetson 是否满足部署条件importparamiko sshparamiko.SSHClient()ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())ssh.connect(192.168.0.166,usernamenvidia,passwordnvidia)# 检查TensorRT版本_,out,_ssh.exec_command(python -c \import tensorrt; print(tensorrt.__version__)\)print(out.read().decode())# 应输出 10.3.x检查结果汇总组件状态备注TensorRT✓ OK10.3.0pycuda✓ OK修复GLIBCXX后torchCUDA✓ OKcv2✓ OKonnxruntime✗ FAIL不影响TRT流程可忽略同时自动创建部署目录结构/home/nvidia/yolo_deploy/ ├── models/ # 存放引擎文件 ├── scripts/ # 存放推理脚本 ├── logs/ # 存放日志 └── calib_data/ # 存放校准图片八、传输文件到 JetsonStep 05使用 SCP 传输 ONNX 文件并验证完整性fromscpimportSCPClientdefprogress(filename,size,sent):pctint(sent/size*100)print(f\r 上传进度:{pct}%,end,flushTrue)withSCPClient(ssh.get_transport(),progressprogress)asscp:scp.put(best.onnx,/home/nvidia/yolo_deploy/models/best.onnx)# 双端MD5校验local_md5hashlib.md5(open(best.onnx,rb).read()).hexdigest()remote_md5run_ssh(ssh,md5sum /home/nvidia/yolo_deploy/models/best.onnx)[0].split()[0]assertlocal_md5remote_md5,文件传输损坏print(fMD5校验通过:{local_md5}✓)九、构建 TRT FP32 基准引擎Step 06踩坑预警1trtexec 不在 PATH 里JetPack 6 上trtexec的实际路径是/usr/src/tensorrt/bin/trtexec直接输入trtexec会报命令找不到。# ❌ 错误写法trtexec--onnxbest.onnx...# ✅ 正确写法必须用完整路径并设置 LD_LIBRARY_PATHexportLD_LIBRARY_PATH/usr/src/tensorrt/lib:/usr/local/cuda-12.6/lib64:$LD_LIBRARY_PATH/usr/src/tensorrt/bin/trtexec\--onnx/home/nvidia/yolo_deploy/models/best.onnx\--saveEngine/home/nvidia/yolo_deploy/models/best_fp32.engine\--memPoolSizeworkspace:6G\--skipInference构建耗时约 7 分钟生成的best_fp32.engine约 100MB。十、构建 TRT FP16 引擎Step 07FP16 量化只需在 FP32 命令基础上加一个参数--fp16/usr/src/tensorrt/bin/trtexec\--onnx/home/nvidia/yolo_deploy/models/best.onnx\--saveEngine/home/nvidia/yolo_deploy/models/best_fp16.engine\--fp16\# ← 就这一个参数的区别--memPoolSizeworkspace:6G\--skipInferenceTRT 内部自动完成的工作权重量化将 float32 权重转换为 float16内存减半Kernel 搜索为每一层寻找支持 Tensor Core 的最优 CUDA kernel这是构建慢的原因自动回退对数值敏感的层如 Softmax自动保留 FP32 精度构建耗时约 16 分钟生成的best_fp16.engine约 50MB比FP32小了一半。十一、INT8 量化校准Step INT811.1 什么是 INT8 量化INT8 量化将神经网络的权重和激活值从 32-bit 浮点数压缩为 8-bit 整数。FP32: 每个数值占 4 字节范围 ±3.4×10³⁸ INT8: 每个数值占 1 字节范围 -128 ~ 127 压缩比: 4×关键问题如何把浮点范围映射到整数范围这就需要校准Calibration。11.2 为什么需要校准图片校准的本质是用真实数据统计每一层激活值的分布范围再确定最优的缩放因子scale factor使量化误差最小。校准图片越多、越有代表性 → INT8精度越高本项目用了 50 张真实缺陷图是较低限生产环境建议 500 张以上11.3 核心代码自定义校准器importtensorrtastrtimportpycuda.driverascudaimportnumpyasnpclassYOLOCalibrator(trt.IInt8EntropyCalibrator2):def__init__(self,calib_images,cache_file):super().__init__()self.imgscalib_images self.idx0self.cache_filecache_file# 在GPU上预分配输入内存self.d_inputcuda.mem_alloc(1*3*640*640*4)defget_batch_size(self):return1defget_batch(self,names):ifself.idxlen(self.imgs):returnNone# 校准结束# 图像预处理letterbox缩放 → RGB → /255 → NCHW float32imgcv2.imread(str(self.imgs[self.idx]))blobpreprocess(img)# shape: (1, 3, 640, 640)cuda.memcpy_htod(self.d_input,np.ascontiguousarray(blob))self.idx1print(f校准进度:{self.idx}/{len(self.imgs)})return[int(self.d_input)]defread_calibration_cache(self):ifPath(self.cache_file).exists():returnPath(self.cache_file).read_bytes()defwrite_calibration_cache(self,cache):Path(self.cache_file).write_bytes(cache)print(f校准缓存已保存:{self.cache_file})构建INT8引擎buildertrt.Builder(logger)configbuilder.create_builder_config()config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE,6*1024**3)# 开启INT8模式config.set_flag(trt.BuilderFlag.INT8)calibratorYOLOCalibrator(calib_images,calib_cache.bin)config.int8_calibratorcalibrator# 构建引擎约16分钟enginebuilder.build_serialized_network(network,config)11.4 INT8 的局限性由于本项目校准图片只有50张INT8引擎在实际测试中检测框很少甚至没有原因分析校准数据不足50张远低于TRT推荐的500张数据分布偏差校准图和实际推理图的缺陷分布可能不完全一致量化误差累积4.07% 的相对误差对置信度较低的小目标影响更大结论本项目最终选用 FP16 引擎进行部署精度损失仅 0.202%速度提升约 2×。十二、三路精度对比Step 08用相同的随机输入让三个引擎分别推理以 FP32 输出为基准计算误差# 三个引擎分别推理同一个 (1,3,640,640) 随机张量fp32_outrun_engine(fp32_engine,test_input)fp16_outrun_engine(fp16_engine,test_input)int8_outrun_engine(int8_engine,test_input)# 计算误差defreport_error(ref,pred,name):diffnp.abs(ref-pred)print(f{name}:)print(f 最大绝对误差:{diff.max():.4f})print(f 平均绝对误差:{diff.mean():.4f})print(f 平均相对误差:{diff.mean()/(np.abs(ref).mean()1e-6)*100:.3f}%)对比结果量化方式最大绝对误差平均绝对误差平均相对误差推荐场景FP32基准000%精度要求极高FP161.0860.0480.202%绝大多数场景首选INT874.082.454.07%需要最高速度 足量校准数据十三、实时推理部署Step 0913.1 部署架构整体数据流USB摄像头 │ cv2.VideoCapture(0) ▼ 图像预处理 │ letterbox(640×640) → BGR转RGB → /255 → NCHW float32 ▼ TRT FP16推理 │ CUDA memcpy H→D → execute_async_v3 → CUDA memcpy D→H ▼ 后处理 │ 置信度过滤(0.25) → NMS(IoU0.45) → 坐标反缩放 ▼ MJPEG HTTP服务器(:8080) │ multipart/x-mixed-replace ▼ Windows浏览器 http://192.168.0.166:8080/13.2 TRT 10.x 推理代码注意TensorRT 10.x 的推理 API 与 8.x 有重大变化网上大量教程用的是旧版本写法。# ❌ TRT 8.x 旧写法不适用于 TRT 10.xcontext.execute_async_v2(bindings[int(d_input),int(d_output)],stream_handlestream.handle)# ✅ TRT 10.x 正确写法# 第一步获取所有张量名称names[engine.get_tensor_name(i)foriinrange(engine.num_io_tensors)]# 第二步按名称绑定内存地址bufs{n:cuda.mem_alloc(int(np.prod(engine.get_tensor_shape(n)))*4)forninnames}forn,binbufs.items():ctx.set_tensor_address(n,int(b))# 第三步异步执行v3 版本cuda.memcpy_htod_async(bufs[images],input_np,stream)ctx.execute_async_v3(stream.handle)cuda.memcpy_dtoh_async(output_np,bufs[output0],stream)stream.synchronize()13.3 MJPEG 推流服务器纯 Python不需要 Flaskfromhttp.serverimportBaseHTTPRequestHandler,HTTPServerimportthreading _stream_frameNone# 全局共享帧线程间通信_stream_lockthreading.Lock()classMJPEGHandler(BaseHTTPRequestHandler):defdo_GET(self):ifself.path/:# 返回包含图像刷新的HTML页面htmlbhtmlbody stylebackground:#000 img src/stream stylewidth:100%;max-width:800px /body/htmlself.send_response(200)self.send_header(Content-Type,text/html)self.end_headers()self.wfile.write(html)elifself.path/stream:# 返回MJPEG流self.send_response(200)self.send_header(Content-Type,multipart/x-mixed-replace; boundaryframe)self.end_headers()whileTrue:with_stream_lock:frame_stream_frameifframeisnotNone:_,jpgcv2.imencode(.jpg,frame,[cv2.IMWRITE_JPEG_QUALITY,85])self.wfile.write(b--frame\r\n)self.wfile.write(bContent-Type: image/jpeg\r\n\r\n)self.wfile.write(jpg.tobytes())self.wfile.write(b\r\n)time.sleep(0.04)# 控制推流帧率 ≈ 25fps13.4 启动实时推理第一步在 Windows 上运行上传脚本cdsteps_v2/step09_inference_demo python run.py第二步SSH 到 Jetson 启动推理sshnvidia192.168.0.166source~/miniforge3/etc/profile.d/conda.sh conda activate yolo_trtexportLD_LIBRARY_PATH~/miniforge3/envs/yolo_trt/lib:$LD_LIBRARY_PATHcd/home/nvidia/yolo_deploy python scripts/infer_v2.py--source0--stream--conf0.25第三步Windows 浏览器打开http://192.168.0.166:8080/就能看到实时带检测框的画面了十四、踩坑全记录坑1paramiko 没有安装现象ModuleNotFoundError: No module named paramiko原因用的是系统 Python 而不是 conda 环境里的 Python# 错误直接双击或用系统Python运行C:\Users\xxx\Python38\python.exe run.py# 正确先激活conda环境conda activate your_env python run.py坑2trtexec 找不到现象bash: line 1: trtexec: command not found原因JetPack 6 上 trtexec 没有加入 PATH# 查找trtexec实际位置find/usr-nametrtexec2/dev/null# 输出/usr/src/tensorrt/bin/trtexec# 使用时必须# 1. 用完整路径# 2. 设置对应的 LD_LIBRARY_PATHexportLD_LIBRARY_PATH/usr/src/tensorrt/lib:/usr/local/cuda-12.6/lib64:$LD_LIBRARY_PATH/usr/src/tensorrt/bin/trtexec--onnx...--saveEngine...坑3GLIBCXX 版本缺失现象ImportError: /lib/aarch64-linux-gnu/libstdc.so.6: version GLIBCXX_3.4.32 not found (required by pycuda)原因pycuda 编译时链接的libstdc版本高于系统自带版本解决方案# 安装高版本 libstdc在yolo_trt环境内conda activate yolo_trt condainstalllibstdcxx-ng-cconda-forge-y# 关键每次运行前必须把conda的lib路径放在最前面exportLD_LIBRARY_PATH~/miniforge3/envs/yolo_trt/lib:$LD_LIBRARY_PATH⚠️注意这个export必须在每个运行 pycuda 的命令之前执行否则还会报同样的错误。坑4INT8 引擎无检测框现象FP32/FP16 都有检测结果INT8 几乎为零原因分析校准图片太少50张 vs 建议500张校准图和测试图数据分布有差异INT8 量化误差4.07%对小目标检测影响较大解决方案改用 FP16精度损失 0.202%速度约提升 2×完全可接受坑5MJPEG 流显示黑屏原因摄像头设备号不对# 先查看Jetson上的视频设备ls/dev/video*# 可能是 /dev/video0, /dev/video1 等# 指定对应设备号python infer_v2.py--source1--stream# 改成实际的设备号十五、量化效果总结指标FP32FP16INT8引擎大小~100 MB~50 MB~30 MB构建时间~7 min~16 min~16 min典型延迟~15-20 ms~8-12 ms~5-8 ms理论 FPS~50-65~80-120~125-200精度损失基准~0.2%~4%依赖校准质量是否需要校准数据否否是建议500张选型建议日常生产部署→ 选FP16精度几乎无损速度翻倍无需校准数据追求极限性能→ 选INT8前提是有足量高质量的校准图片精度基准验证→ 选FP32作为参考标准十六、完整运行流程按顺序执行以下命令全部在 Windows 上运行# 第一步解剖模型cdsteps_v2/step01_model_inspectpython run.py# 第二步导出ONNXcd../step02_onnx_exportpython run.py# 第三步验证ONNXcd../step03_onnx_inspectpython run.py# 第四步检查Jetson环境cd../step04_jetson_env_checkpython run.py# 第五步传输文件cd../step05_transferpython run.py# 第六步构建FP32引擎约7分钟cd../step06_trt_fp32python run.py# 第七步构建FP16引擎约16分钟cd../step07_trt_fp16python run.py# 第八步可选INT8校准约16分钟需校准图片cd../step_int8_calibpython run.py# 第九步精度对比cd../step08_accuracy_comparepython run.py# 第十步上传推理脚本cd../step09_inference_demopython run.py然后 SSH 到 Jetson 启动实时推理浏览器打开http://192.168.0.166:8080/查看效果。十七、参考资料TensorRT 10.x Developer GuideJetPack 6 Release NotesUltralytics YOLO ONNX Export DocsTensorRT Python API: IInt8EntropyCalibrator2pycuda Documentation如果这篇文章对你有帮助欢迎点赞收藏有问题可以在评论区留言我会尽快回复。完整代码见 GitHub马上开源。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价