从训练到部署:YOLOv5自定义模型在RK3588上的端到端实时摄像头应用
1. 项目背景与核心需求
在工业检测、安防监控等场景中,实时目标检测的需求日益增长。YOLOv5作为当前最流行的轻量级检测模型之一,配合RK3588芯片强大的NPU算力(6TOPS),能够实现高效边缘计算。但在实际落地时,开发者常遇到三大挑战:
- 模型兼容性问题:PyTorch训练的.pt模型无法直接在嵌入式平台运行
- 计算资源限制:需要优化模型结构以适应芯片内存和算力
- 实时性要求:摄像头视频流处理需保持30FPS以上帧率
我曾在一个安全帽检测项目中,将YOLOv5s模型压缩到仅3MB大小,在RK3588上实现了42FPS的实时推理性能。下面将完整还原从数据准备到部署的全流程。
2. 模型训练与优化
2.1 数据准备技巧
自定义数据集建议采用以下结构:
dataset/ ├── images/ │ ├── train/ # 训练图片 │ └── val/ # 验证图片 └── labels/ ├── train/ # YOLO格式标注文件 └── val/标注文件示例(每行表示一个目标):
0 0.5 0.5 0.2 0.3 # 类别ID 中心x 中心y 宽度 高度实测发现,当训练数据少于1000张时,建议:
- 使用Mosaic数据增强
- 冻结backbone层训练
- 启用自动锚框计算
2.2 关键训练参数
修改data/hyps/hyp.scratch-low.yaml:
lr0: 0.01 # 初始学习率 lrf: 0.1 # 最终学习率系数 momentum: 0.9 weight_decay: 0.0005 fl_gamma: 0.0 # 是否启用Focal Loss hsv_h: 0.015 # 色调增强幅度 hsv_s: 0.7 # 饱和度增强 hsv_v: 0.4 # 明度增强启动训练命令:
python train.py --img 640 --batch 16 --epochs 100 --data custom.yaml --weights yolov5s.pt --hyp hyp.scratch-low.yaml3. 模型转换关键步骤
3.1 PyTorch转ONNX
需要修改两处关键代码:
- 修改
models/yolo.py中的forward函数:
def forward(self, x): z = [] # 原输出处理逻辑 for i in range(self.nl): x[i] = self.m[i](x[i]) # 只保留基础卷积处理 return x # 直接返回特征图- 修改
export.py中的输出形状处理:
shape = tuple((y[0] if isinstance(y, tuple) else y)) # 简化输出处理转换命令:
python export.py --weights best.pt --img 640 --batch 1 --include onnx --opset 12验证ONNX模型时,用Netron工具检查输出节点应为3个特征图(如1x255x80x80)。
3.2 ONNX转RKNN
在Ubuntu18.04环境下配置转换环境:
conda create -n rknn python=3.6 conda activate rknn pip install rknn_toolkit2-1.4.0-cp36-cp36m-linux_x86_64.whl转换脚本关键配置:
rknn.config( mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], target_platform='rk3588', quantized_dtype='asymmetric_quantized-8' # 启用INT8量化 )实测发现,开启量化可使模型体积减小4倍,推理速度提升2.3倍。
4. RK3588端部署实战
4.1 环境准备
开发板需安装:
sudo apt update sudo apt install libopencv-dev python3-opencv pip install rknn_toolkit_lite2-1.4.0-cp37-cp37m-linux_aarch64.whl4.2 摄像头实时推理
修改推理代码关键参数:
# 摄像头设备号查询 v4l2-ctl --list-devices # 输出示例:/dev/video11 # Python推理核心逻辑 cap = cv2.VideoCapture(11) # 对应设备号 while True: ret, frame = cap.read() inputs = preprocess(frame) # 尺寸归一化/归一化 outputs = rknn.inference(inputs) detections = postprocess(outputs) # NMS处理 draw_results(frame, detections)优化技巧:
- 使用双缓冲减少内存拷贝
- 开启NPU硬件加速:
rknn_set_core_mask(ctx, RKNN_NPU_CORE_0); # 指定NPU核心5. 性能优化方案
5.1 模型层面
- 通道剪枝:
python prune.py --weights best.pt --percent 0.3 # 剪枝30%通道实测可使模型体积减少40%,速度提升35%
- 量化校准:
rknn.build(do_quantization=True, dataset='./calib_images.txt')5.2 工程层面
- 内存池优化:
rknn_init(&ctx, model_path, 0, RKNN_FLAG_MEM_ALLOC_OUTSIDE);- 多线程处理:
from threading import Thread class CameraThread(Thread): def run(self): while True: # 图像采集与推理分离 self.queue.put(frame)在工业质检场景实测中,经过优化的系统可实现:
- 延迟:<50ms
- 功耗:<3W
- 持续运行温度:<65℃
6. 常见问题解决
- 模型输出异常:
- 现象:检测框位置偏移
- 解决方案:检查ONNX转换时的
--opset版本,建议12或19
- NPU利用率低:
cat /sys/kernel/debug/rknpu/load # 查看NPU负载- 若负载<70%,需检查是否启用INT8量化
- 内存泄漏:
rknn.release() # 每次推理后必须释放资源最近在部署人脸识别系统时,发现RK3588的NPU对ReLU激活函数优化更好,将原模型中的SiLU替换为ReLU后,帧率从38FPS提升到45FPS。这提醒我们在模型设计阶段就需要考虑硬件特性。
