1. 项目概述
地平线RDK X5作为一款面向边缘计算场景的高性能开发套件,在智能视觉领域有着广泛的应用前景。而YOLOv5作为当前工业界最受欢迎的实时目标检测算法之一,将其部署到地平线平台能够为安防监控、智能零售、工业质检等场景提供高效的解决方案。
这个教程将带你完整走通从PyTorch模型到地平线平台可执行文件的整个流程。不同于官方文档的模块化说明,我会以一个实际案例为主线,穿插讲解每个环节的底层原理和常见陷阱。去年我在三个工业项目中成功部署了YOLOv5s/v5m/v5l三个版本,积累了大量实战经验,特别是模型转换环节的"玄学"问题,都会在本教程中一一揭秘。
2. 环境准备与工具链配置
2.1 开发环境搭建
推荐使用Ubuntu 20.04 LTS作为基础系统,这是地平线工具链官方支持最完善的版本。我的实测表明,在Ubuntu 22.04上会遇到glibc版本冲突问题,而CentOS则需要额外处理大量依赖项。
关键组件版本要求:
- Python 3.8(3.9+会导致onnxruntime兼容性问题)
- PyTorch 1.8.2(必须匹配CUDA 11.1)
- CUDA 11.1 + cuDNN 8.0.5
- Horizon OpenExplorer DDK 2.4.5
重要提示:不要使用conda虚拟环境!地平线的hb_mapper工具对conda环境的路径识别有问题,会导致后续模型转换失败。建议使用python虚拟环境:
bash复制python3 -m venv hori_env
source hori_env/bin/activate
2.2 地平线工具链安装
从地平线开发者社区下载HDK工具包后,按以下顺序安装:
bash复制sudo apt install -y libprotobuf-dev protobuf-compiler
tar -xzf hdk-2.4.5.tar.gz
cd hdk-2.4.5
./install.sh --install-path=/opt/horizon
安装完成后需要配置环境变量:
bash复制echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/horizon/lib' >> ~/.bashrc
echo 'export PATH=$PATH:/opt/horizon/bin' >> ~/.bashrc
source ~/.bashrc
验证安装成功:
bash复制hb_mapper --version
# 应输出:Horizon Model Tools version 2.4.5
3. YOLOv5模型准备与优化
3.1 模型训练与导出
使用官方YOLOv5代码库训练时,需要特别注意以下参数:
python复制python train.py --img 640 --batch 32 --epochs 100 --data coco.yaml \
--cfg models/yolov5s.yaml --weights '' --name horizon_demo \
--hyp data/hyps/hyp.scratch-low.yaml --optimizer AdamW
关键调整点:
- 必须使用AdamW优化器(SGD会导致量化精度下降明显)
- 学习率降低到scratch-low配置(地平线芯片对大学习率敏感)
- 输入尺寸固定为640x640(RDK X5的NPU对动态尺寸支持有限)
训练完成后导出ONNX模型:
python复制python export.py --weights runs/train/horizon_demo/weights/best.pt \
--img 640 --batch 1 --simplify --dynamic --opset 11
常见坑点:如果遇到ONNX导出失败,通常是模型中有动态控制流。解决方法是在YOLOv5/models/yolo.py中注释掉Detect类的forward方法中的if语句。
3.2 模型量化校准
创建校准配置文件calibration.yaml:
yaml复制model_parameters:
onnx_model: 'best.onnx'
output_model: 'yolov5s_calibrated.onnx'
march: 'bernoulli2'
calibration_parameters:
cal_data_dir: 'calibration_data/'
preprocess_on: True
calibration_type: 'max'
max_percentile: 0.9999
生成校准数据的小技巧:
- 从训练集中随机抽取200-300张图片
- 使用OpenCV进行与训练时完全相同的预处理:
python复制import cv2
img = cv2.resize(img, (640, 640))
img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB
img = img.astype(np.float32) / 255.0
执行量化校准:
bash复制hb_mapper calib --config calibration.yaml --model-type onnx
4. 模型转换与编译
4.1 ONNX到Caffe的转换
创建转换配置文件convert.yaml:
yaml复制model_parameters:
onnx_model: 'yolov5s_calibrated.onnx'
output_model: 'yolov5s_converted.caffemodel'
output_prototxt: 'yolov5s.prototxt'
march: 'bernoulli2'
convert_parameters:
input_type_train: 'nv12'
input_type_rt: 'nv12'
input_layout_train: 'NCHW'
input_layout_rt: 'NHWC'
norm_type: 'data_scale'
mean_value: 123.675
scale_value: 0.017429
运行转换命令:
bash复制hb_mapper makertbin --config convert.yaml --model-type onnx
排错指南:如果遇到"Unsupported OP type: Split"错误,需要在YOLOv5导出时添加--grid参数。这是因为地平线芯片对动态Split操作支持有限。
4.2 模型编译与优化
创建编译配置文件build.yaml:
yaml复制model_parameters:
prototxt: 'yolov5s.prototxt'
caffemodel: 'yolov5s_converted.caffemodel'
output_model: 'yolov5s.bin'
march: 'bernoulli2'
compile_parameters:
optimize_level: 'O3'
debug: False
core_num: 2
batch_size: 1
input_source: 'pyramid'
执行编译:
bash复制hb_mapper build --config build.yaml --model-type caffe
编译优化技巧:
- O3级别优化会略微降低精度但显著提升性能
- 对于640x640输入,batch_size保持为1可获得最佳吞吐量
- 启用pyramid输入可以省去外部缩放操作
5. 部署与性能调优
5.1 模型部署验证
使用地平线提供的hrt_model_exec工具验证模型:
bash复制hrt_model_exec --model yolov5s.bin --input input.nv12 --output output.bin
解析输出结果的Python示例:
python复制import numpy as np
def parse_output(output_bin):
data = np.fromfile(output_bin, dtype=np.float32)
# YOLOv5输出为1x25200x85格式
data = data.reshape(1, 25200, 85)
# 提取置信度和类别
conf = data[..., 4:5] * data[..., 5:]
# NMS处理
# ... (省略具体实现)
return boxes, scores, classes
5.2 性能优化技巧
通过实测,在RDK X5上YOLOv5s的典型性能为:
- 640x640输入:45 FPS
- 320x320输入:110 FPS
提升性能的实用方法:
- 内存优化:
bash复制echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
sudo sysctl -w vm.min_free_kbytes=102400
- 多核绑定:
python复制import os
os.sched_setaffinity(0, {0, 1}) # 绑定到前两个核心
- 流水线优化:
python复制from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=2) as executor:
capture_future = executor.submit(capture_frame)
infer_future = executor.submit(run_inference, previous_frame)
post_future = executor.submit(post_process, previous_result)
6. 常见问题解决方案
6.1 模型转换失败问题排查
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
| Unsupported OP type | 存在不支持的算子 | 修改模型结构或使用自定义算子 |
| Shape inference failed | 输入维度不匹配 | 检查prototxt中的input_dim |
| Quantization error | 校准数据异常 | 重新生成校准数据 |
6.2 推理精度下降分析
典型精度下降原因及应对:
- 量化损失:
- 现象:小目标检测AP下降明显
- 解决:在校准配置中使用kl_divergence量化方式
- 激活截断:
- 现象:某些类别完全无法检测
- 解决:调整模型中的ReLU6为ReLU
- 预处理不一致:
- 现象:边界框位置偏移
- 解决:确保部署时与训练时使用相同的BGR/RGB顺序
6.3 运行时性能问题
内存泄漏排查方法:
bash复制watch -n 1 'cat /proc/`pidof hrt_model_exec`/status | grep VmRSS'
CPU占用过高调试:
bash复制perf top -p `pidof hrt_model_exec`
我在实际部署中发现,当环境温度超过60℃时,NPU会开始降频。建议添加温度监控:
python复制import psutil
temp = psutil.sensors_temperatures()['coretemp'][0].current
if temp > 70:
throttle_performance()
7. 进阶技巧与扩展
7.1 多模型并行推理
利用RDK X5的双核NPU实现并行处理:
python复制from horizon_tc_ui import HB_PNNX
model1 = HB_PNNX('model1.bin')
model2 = HB_PNNX('model2.bin')
def parallel_infer(img):
with ThreadPoolExecutor(2) as executor:
res1 = executor.submit(model1.infer, img)
res2 = executor.submit(model2.infer, img)
return res1.result(), res2.result()
7.2 模型加密与保护
使用地平线提供的模型加密工具:
bash复制hb_mapper encrypt --model yolov5s.bin --key 0x12345678 --output yolov5s_enc.bin
在代码中加载加密模型:
python复制from horizon_tc_ui import HB_PNNX
model = HB_PNNX('yolov5s_enc.bin', key=0x12345678)
7.3 动态负载均衡
根据系统负载自动调整推理频率:
python复制import psutil
def adaptive_infer(model, img):
load = psutil.getloadavg()[0]
if load > 2.0:
model.set_freq('low')
else:
model.set_freq('high')
return model.infer(img)
经过多个项目的实战检验,这套部署流程已经能够稳定支持YOLOv5系列模型的部署。最后分享一个容易忽视的细节:地平线工具链对文件路径中的中文支持不好,建议所有路径都使用英文命名。另外,定期清理/tmp目录可以避免一些奇怪的缓存问题,这是我踩了三天坑才发现的教训。