从零到一:在Jetson Nano上实现自定义YOLOv5模型的TensorRT推理与DeepStream集成
1. 环境准备与数据集制作
在Jetson Nano上部署自定义YOLOv5模型前,我们需要先准备好开发环境。我建议使用JetPack 4.6.1作为基础系统,这是目前最稳定的版本。安装完成后,记得执行sudo apt update && sudo apt upgrade更新所有软件包。
数据集制作是项目中最耗时的环节之一。以"鸭子"和"马桶抽"为例,我建议至少准备500张标注图片。使用LabelImg工具标注时,有几点经验分享:
- 标注框要尽量贴近物体边缘
- 对于遮挡物体,只标注可见部分
- 保持标注一致性,避免同一物体在不同图片中有不同标注方式
标注完成后,我们需要将VOC格式转换为YOLO格式。这里有个实用脚本可以帮你自动完成转换:
import xml.etree.ElementTree as ET import os def convert(size, box): dw = 1./size[0] dh = 1./size[1] x = (box[0] + box[1])/2.0 y = (box[2] + box[3])/2.0 w = box[1] - box[0] h = box[3] - box[2] x = x*dw w = w*dw y = y*dh h = h*dh return (x,y,w,h) def convert_annotation(xml_file, txt_file, classes): tree = ET.parse(xml_file) root = tree.getroot() size = root.find('size') w = int(size.find('width').text) h = int(size.find('height').text) with open(txt_file, 'w') as f: for obj in root.iter('object'): cls = obj.find('name').text if cls not in classes: continue cls_id = classes.index(cls) xmlbox = obj.find('bndbox') b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text)) bb = convert((w,h), b) f.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')2. 模型训练与优化
在主机端训练YOLOv5模型时,我强烈建议使用预训练权重。实测发现,使用预训练权重可以提升10-15%的准确率,同时减少约30%的训练时间。训练命令如下:
python train.py --img 640 --batch 16 --epochs 100 --data dataset.yaml --cfg yolov5s.yaml --weights yolov5s.pt --device 0这里有几个关键参数需要注意:
--img 640:输入图像尺寸,Jetson Nano上建议保持640x640--batch 16:根据显存大小调整,RTX 2080Ti可以设置到32--epochs 100:对于小数据集,100-150个epoch通常足够
训练完成后,使用detect.py测试模型效果时,我发现一个实用技巧:添加--augment参数可以启用测试时数据增强,这能更全面地评估模型性能:
python detect.py --weights runs/train/exp/weights/best.pt --source test_images/ --augment3. TensorRT模型转换
将PyTorch模型转换为TensorRT引擎是提升推理速度的关键。我推荐使用tensorrtx项目进行转换,这是目前最稳定的方案。转换过程分为三步:
- 生成.wts中间文件:
python gen_wts.py yolov5s.pt- 在Jetson Nano上编译tensorrtx:
mkdir build cd build cmake .. make- 生成TensorRT引擎:
./yolov5 -s yolov5s.wts yolov5s.engine s这里有个坑要注意:Jetson Nano的内存有限,如果转换大模型(如yolov5x)可能会失败。我的经验是,在Nano上最好使用yolov5s或yolov5n这类轻量模型。
4. DeepStream集成实战
DeepStream是NVIDIA专为视频分析优化的框架。集成YOLOv5模型时,需要修改几个关键文件:
- 修改
nvdsparsebbox_Yolo.cpp中的类别数:
static const int NUM_CLASSES_YOLO = 2; // 修改为你的类别数- 配置
config_infer_primary_yoloV5.txt:
[property] ... model-engine-file=yolov5s.engine num-detected-classes=2 ...- 创建
labels.txt文件:
duck sucker启动DeepStream应用的命令如下:
LD_PRELOAD=./libmyplugins.so deepstream-app -c deepstream_app_config_yoloV5.txt在实际部署中,我发现通过调整DeepStream的流媒体配置可以显著提升性能。例如,将[streammux]部分的width和height设置为实际视频分辨率的1/2,可以减少30%的GPU负载,同时保持不错的识别精度。
5. 性能优化技巧
经过多次实践,我总结出几个提升Jetson Nano推理性能的关键技巧:
- 启用持久模式:这可以减少内核启动开销
sudo nvpmodel -m 0 sudo jetson_clocks- 调整电源模式:设置为MAXN模式可获得最佳性能
sudo nvpmodel -m 0- 使用TensorRT的FP16模式:在生成引擎时添加
-d 16参数:
./yolov5 -s yolov5s.wts yolov5s.engine s -d 16- 优化DeepStream配置:在
deepstream_app_config_yoloV5.txt中:
[streammux] batch-size=1 # Nano上建议设为1实测下来,经过这些优化后,我的"鸭子检测"模型在Jetson Nano上达到了18FPS,完全满足实时检测需求。对于更复杂的场景,可以考虑使用TensorRT的INT8量化,虽然会损失一些精度,但能进一步提升速度。
6. 常见问题解决
在项目实践中,我遇到过几个典型问题及解决方案:
- 内存不足错误:
- 症状:转换大模型时出现"out of memory"
- 解决方案:改用更小的模型,或尝试在主机上转换后传输到Nano
- 类别不匹配:
- 症状:检测结果标签错误
- 解决方案:检查所有配置文件中类别数是否一致,特别是
nvdsparsebbox_Yolo.cpp和config_infer_primary_yoloV5.txt
- 低帧率问题:
- 症状:推理速度远低于预期
- 解决方案:检查是否启用了持久模式,尝试降低输入分辨率
- 模型不加载:
- 症状:DeepStream无法加载TensorRT引擎
- 解决方案:确保生成引擎的TensorRT版本与JetPack中的版本一致
记得每次修改配置后,都要重新编译相关组件。我在一个项目中曾花了3小时debug,最后发现只是忘了重新make。
