1. 项目概述
在嵌入式设备上部署YOLOv5目标检测模型一直是个挑战,特别是针对Rockchip系列芯片。最近我在RK3576开发板上成功部署了自定义训练的YOLOv5手势识别模型,整个过程踩了不少坑,也积累了一些实战经验。本文将详细记录从数据准备到最终板端部署的全流程,重点分享RK平台特有的优化点和避坑指南。
1.1 为什么选择RK定制版YOLOv5
Rockchip官方提供的YOLOv5版本(github.com/airockchip/yolov5)针对自家芯片做了深度优化,主要体现在三个方面:
-
计算图优化:重构了Focus和SPPF模块的计算方式,在保持相同推理结果的前提下提升了约15%的推理速度。实测在RK3576上,640x640输入的处理时间从78ms降至66ms。
-
后处理剥离:将原本集成在模型中的后处理操作(NMS等)移出,改为在代码中实现。这样做有两个好处:
- 更友好的量化效果:模型量化时不会受到后处理操作的影响
- 灵活性更高:可以根据实际需求调整后处理参数
-
激活函数替换:默认使用ReLU替代SiLU,这对RKNPU的兼容性更好。不过需要注意,这会导致模型精度有轻微下降(约1-2% mAP),但对嵌入式场景影响不大。
重要提示:使用标准YOLOv5训练的模型无法直接在RKNPU上运行,必须使用这个定制版本重新训练!
2. 数据准备与标注
2.1 数据集结构设计
手势识别项目采用标准YOLO格式的数据集结构:
code复制dataset/
├── images/
│ ├── train/ # 训练集图片
│ └── val/ # 验证集图片
└── labels/
├── train/ # 训练集标注
└── val/ # 验证集标注
数据量建议:
- 每类至少200张图片(我的测试只用了40张/类,实际效果会打折扣)
- 训练集:验证集 ≈ 8:2
- 确保每个类别在验证集中都有代表样本
2.2 标注工具配置
推荐使用conda创建独立Python环境:
bash复制conda create -n labelimg python=3.9
conda activate labelimg
pip install labelimg -i https://pypi.tuna.tsinghua.edu.cn/simple
标注时关键设置:
- View → Auto Save Mode(自动保存)
- 格式选择YOLO(生成.txt标注文件)
- 图片和标注文件要严格同名(如img001.jpg对应img001.txt)
标注技巧:
- 对于手势这类小目标,建议放大到至少200x200像素再标注
- 复杂手势可以标注多个bounding box(如"比心"手势可以同时标注左手和右手)
- 避免标注被严重遮挡或残缺的手势
2.3 配置文件准备
data.yaml示例:
yaml复制train: /path/to/dataset/images/train
val: /path/to/dataset/images/val
nc: 3 # 类别数
names: ['like', 'ok', 'yeah'] # 必须与标注时使用的类别名完全一致
常见问题:
- 路径建议使用绝对路径
- 类别名不要用中文,容易引发编码问题
- 文件编码必须为UTF-8(Windows记事本保存时注意)
3. 模型训练
3.1 训练环境搭建
建议使用官方Docker镜像避免环境问题:
bash复制docker pull ultralytics/yolov5:latest
关键依赖版本:
- torch==1.10.0
- torchvision==0.11.1
- 其他依赖见requirements.txt
3.2 训练参数解析
基础训练命令:
bash复制python train.py \
--weights yolov5s.pt \
--data data.yaml \
--img 640 \
--batch 8 \
--epochs 300 \
--cache ram
参数优化建议:
- batch_size:根据显存调整(RK3576建议8-16)
- img_size:必须是32的倍数(640是性价比最高的尺寸)
- --cache ram:将数据集缓存到内存,提升训练速度
- 添加--hyp data/hyps/hyp.scratch-low.yaml 使用更激进的超参数
训练监控:
- 本地访问 http://localhost:6006 查看TensorBoard
- 重点关注val/obj_loss和val/cls_loss的变化趋势
3.3 模型微调技巧
-
数据增强策略:
- 手势识别建议启用mosaic(默认开启)
- 适当降低hsv_h参数(色调变化不宜过大)
- 增加scale参数(模拟不同距离的手势)
-
早停机制:
bash复制--patience 50 # 连续50轮验证集损失未下降则停止 -
迁移学习:
bash复制--weights yolov5s.pt --freeze 10 # 冻结前10层
4. 模型转换与量化
4.1 PT → ONNX转换
关键命令:
bash复制python export.py \
--rknpu \ # 启用RKNPU优化
--weight best.pt \
--img 640 \
--simplify
常见错误处理:
- "Unsupported ONNX opset version":
bash复制--opset 12 # 指定opset版本 - 输出节点报错:
检查models/yolo.py中Detect类的forward实现
4.2 ONNX → RKNN转换
4.2.1 环境准备
使用官方Docker镜像:
bash复制docker build -t rknn-toolkit2 -f docker/Dockerfile .
docker run -it --privileged rknn-toolkit2
4.2.2 量化数据集准备
修改coco_subset_20.txt指向自己的数据集:
code复制/path/to/your/images/img001.jpg
/path/to/your/images/img002.jpg
...
量化技巧:
- 量化图片数量建议100-200张
- 覆盖所有类别和不同场景
- 图片尺寸应与训练尺寸一致(640x640)
4.2.3 转换执行
bash复制python convert.py \
--onnx_model ../model/best.onnx \
--target_platform rk3576 \
--dataset ../datasets/your_dataset.txt \
--output_dir ./output
关键参数解析:
- --quantize True:启用量化(默认开启)
- --quantized_dtype asymmetric_quantized-8:8位量化
- --optimization_level 3:最高优化等级
5. 板端部署与测试
5.1 代码适配修改
必须修改的两个文件:
-
postprocess.h:
cpp复制#define OBJ_CLASS_NUM 3 // 改为你的类别数 -
coco_80_labels_list.txt:
code复制like ok yeah
5.2 交叉编译
编译命令示例:
bash复制./build-android.sh \
-t rk3576 \
-a arm64-v8a \
-d yolov5 \
--ndk ~/android-ndk-r19c
NDK版本注意:
- 官方推荐r19c
- r18b也可用但可能有兼容性问题
- 不要使用r21+,存在ABI兼容问题
5.3 板端测试
部署流程:
bash复制adb push install/rknn_yolov5_demo_android /data
adb shell
cd /data/rknn_yolov5_demo_android
export LD_LIBRARY_PATH=./lib
./rknn_yolov5_demo model/yolov5.rknn test.jpg
性能优化技巧:
- 启用NPU硬件加速:
cpp复制rknn_set_core_mask(ctx, RKNN_NPU_CORE_0); - 内存优化:
cpp复制rknn_set_internal_mem(ctx, true);
6. 常见问题排查
6.1 模型转换问题
-
量化失败:
- 检查量化图片路径是否正确
- 确认图片格式为JPEG或PNG
- 尝试减少量化图片数量
-
精度下降严重:
- 在convert.py中添加--do_quantization False先测试非量化模型
- 调整量化策略(改为dynamic_quantization)
6.2 板端运行问题
-
段错误(Segmentation Fault):
bash复制adb logcat | grep rknn # 查看NPU日志常见原因:
- 内存不足(减小输入尺寸)
- 模型与芯片不匹配(确认target_platform正确)
-
检测框错乱:
- 检查postprocess.h中的OBJ_CLASS_NUM
- 确认labels.txt与训练时一致
6.3 性能调优
-
帧率提升方案:
- 将img_size从640降至416(需重新训练)
- 使用--batch 1进行流式推理
- 启用多核NPU(RK3576支持双核)
-
内存优化:
cpp复制rknn_set_internal_mem(ctx, true); // 使用内部内存 rknn_set_shared_mem(ctx, true); // 共享内存
7. 进阶优化方向
-
模型剪枝:
bash复制python prune.py \ --weights best.pt \ --percent 0.3 # 剪枝30% -
INT8量化训练:
在train.py中添加:bash复制
--quantize \ --calib_iter 100 -
多模型集成:
cpp复制// 在板端同时加载多个rknn模型 rknn_init(&ctx1, model_path1, 0); rknn_init(&ctx2, model_path2, 0); -
动态输入支持:
修改export.py:python复制torch.onnx.export( ... dynamic_axes={'images': {0: 'batch'}, 'output': {0: 'batch'}} )
这个项目最关键的收获是:一定要使用芯片原厂提供的定制化算法版本。我最初尝试用原生YOLOv5训练模型,在PC端测试完美,但转到RK平台就各种问题。后来改用RK优化版后,不仅顺利部署,性能还提升了30%。另外建议在模型设计阶段就考虑芯片的量化特性,比如避免使用SiLU这类对量化不友好的激活函数。
