1. 项目概述
最近在RV1126B嵌入式平台上完成了YOLOv8模型的部署工作,这是一次典型的边缘计算场景下的AI模型落地实践。RV1126B作为一款面向AI应用的嵌入式处理器,其NPU(神经网络处理单元)能够高效运行经过优化的神经网络模型。本文将详细记录从模型转换到板端部署的全流程技术细节。
在工业质检、智能安防等实时性要求高的场景中,将YOLO这类目标检测模型部署到边缘设备已成为刚需。相比云端部署,边缘部署具有低延迟、高隐私性、不依赖网络等优势。而RV1126B这类专用AI芯片的出现,使得在资源受限的嵌入式设备上运行复杂模型成为可能。
2. 环境准备与验证
2.1 RKNN工具链安装确认
RKNN是Rockchip提供的神经网络推理框架,支持将主流框架训练的模型转换为可在Rockchip NPU上高效运行的格式。环境确认是部署工作的第一步:
bash复制# 激活Python虚拟环境
conda activate rknn_env
# 验证RKNN API能否正常导入
python -c "from rknn.api import RKNN"
若上述命令无报错,则说明RKNN工具链安装正确。建议使用Python3.6-3.8版本,这是官方测试最充分的版本范围。环境变量需要正确设置,特别是LD_LIBRARY_PATH应包含RKNN的库文件路径。
2.2 模型准备流程
- 训练阶段:使用Ultralytics YOLOv8官方代码训练得到.pt权重文件
- 导出ONNX:通过官方export.py脚本将.pt转换为.onnx格式
python复制from ultralytics import YOLO model = YOLO('yolov8n.pt') # 加载自定义训练的模型 model.export(format='onnx') # 导出为ONNX格式 - 模型简化:建议使用onnx-simplifier对导出的ONNX进行优化:
bash复制
python -m onnxsim yolov8.onnx yolov8-sim.onnx
注意:导出ONNX时需确保opset_version>=12,YOLOv8需要较新的算子支持。输入输出节点的动态维度需要固定为具体数值,这对后续RKNN转换至关重要。
2.3 代码仓库准备
Rockchip官方提供的rknn_model_zoo仓库包含了丰富的示例代码:
bash复制git clone https://github.com/airockchip/rknn_model_zoo.git
cd rknn_model_zoo/examples/yolov8
仓库中主要包含:
python/convert.py:模型转换脚本python/yolov8.py:推理示例代码model/:存放模型文件的目录datasets/:量化校准数据集存放位置
3. 模型转换详解
3.1 基础转换流程
对于RV1126B平台,RKNN支持FP16和INT8两种精度模式:
bash复制# 进入转换脚本目录
cd /root/rknn_model_zoo/examples/yolov8/python/
# FP16模式转换(无需量化)
python convert.py ../model/yolov8.onnx rv1126b fp ../model/yolov8_fp.rknn
# INT8模式转换(需要量化)
python convert.py ../model/yolov8.onnx rv1126b i8 ../model/yolov8_i8.rknn
关键参数说明:
rv1126b:目标平台型号fp/i8:指定模型精度- 最后一个参数为输出RKNN模型路径
FP16模式转换速度较快,适合快速验证流程;INT8模式需要额外提供校准数据集,但能获得更好的推理性能。
3.2 INT8量化细节
3.2.1 量化算法选择
RKNN支持多种量化算法,通过quantized_algorithm参数指定:
python复制rknn.config(
quantized_algorithm='mmse', # 最小均方误差算法
quantized_method='channel' # 通道级量化
)
常用算法对比:
| 算法类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| mmse | 精度损失小 | 计算量大 | 高精度要求 |
| kl | 速度快 | 对异常值敏感 | 一般场景 |
| aciq | 自适应 | 需要调参 | 数据分布复杂 |
3.2.2 校准数据集准备
校准数据集应具有代表性,通常从训练集中抽取100-200张图片。数据集组织格式:
code复制datasets/
└── coco_subset_20.txt
文件内容为图片路径列表:
code复制/path/to/image1.jpg
/path/to/image2.jpg
...
注意事项:校准图片需与实际应用场景相似,覆盖所有目标类别。图片数量不是越多越好,关键是要有代表性。
3.3 模型转换脚本解析
完整的转换脚本包含以下关键步骤:
-
RKNN对象初始化:
python复制rknn = RKNN(verbose=True) -
模型配置:
python复制rknn.config( mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], target_platform='rv1126b', quantized_algorithm='mmse' )- mean_values/std_values:输入图像的归一化参数
- target_platform:指定目标硬件平台
-
加载ONNX模型:
python复制
ret = rknn.load_onnx(model=model_path) -
模型构建:
python复制
ret = rknn.build( do_quantization=do_quant, dataset=DATASET_PATH ) -
导出RKNN模型:
python复制
ret = rknn.export_rknn(output_path) -
资源释放:
python复制
rknn.release()
4. 板端推理实现
4.1 模型加载与初始化
RV1126B上使用RKNNLite接口进行推理:
python复制from rknnlite.api import RKNNLite
rknn = RKNNLite()
ret = rknn.load_rknn('yolov8_fp.rknn')
ret = rknn.init_runtime()
初始化时可指定核心绑定策略:
python复制ret = rknn.init_runtime(
core_mask=RKNNLite.NPU_CORE_0 # 指定使用NPU核心0
)
4.2 图像预处理
YOLOv8的预处理包含以下步骤:
-
LetterBox处理:保持长宽比进行resize并填充
python复制def letter_box(image, target_size): ih, iw = image.shape[:2] h, w = target_size scale = min(w/iw, h/ih) nw, nh = int(iw*scale), int(ih*scale) resized = cv2.resize(image, (nw, nh)) new_image = np.full((h,w,3), 114, dtype=np.uint8) dw, dh = (w - nw) // 2, (h - nh) // 2 new_image[dh:dh+nh, dw:dw+nw] = resized return new_image, (scale, dw, dh) -
颜色空间转换:BGR→RGB
python复制
rgb_image = cv2.cvtColor(letter_box_img, cv2.COLOR_BGR2RGB) -
维度扩展:添加batch维度
python复制input_data = np.expand_dims(rgb_image, axis=0)
4.3 推理执行
python复制outputs = rknn.inference(inputs=[input_data])
RV1126B的典型推理性能:
| 模型精度 | 输入尺寸 | 推理时间(ms) | 内存占用(MB) |
|---|---|---|---|
| FP16 | 640x640 | 45 | 120 |
| INT8 | 640x640 | 28 | 90 |
实测技巧:连续推理时,保持RKNNLite对象持久化可避免重复加载模型的开销。
4.4 后处理实现
YOLOv8的输出解析包含以下步骤:
-
输出解析:
python复制output = outputs[0][0] # 形状为[84,8400] -
维度重组:
python复制# 前4维是bbox坐标(cx,cy,w,h) bbox = output[:4, :].transpose(1, 0) # 后80维是类别概率 scores = output[4:, :].transpose(1, 0) -
坐标转换:
python复制# 中心坐标→角点坐标 bbox[:, 0] = bbox[:, 0] - bbox[:, 2] / 2 # x1 bbox[:, 1] = bbox[:, 1] - bbox[:, 3] / 2 # y1 bbox[:, 2] = bbox[:, 0] + bbox[:, 2] # x2 bbox[:, 3] = bbox[:, 1] + bbox[:, 3] # y2 -
分数计算:
python复制class_ids = np.argmax(scores, axis=1) confidences = np.max(scores, axis=1) -
NMS过滤:
python复制indices = cv2.dnn.NMSBoxes( bbox.tolist(), confidences.tolist(), score_threshold=0.25, nms_threshold=0.45 ) -
坐标反变换:
python复制# 将letterbox坐标转换回原图坐标 detections = [] for i in indices: box = bbox[i] x1 = (box[0] - dw) / scale y1 = (box[1] - dh) / scale x2 = (box[2] - dw) / scale y2 = (box[3] - dh) / scale detections.append([x1, y1, x2, y2, class_ids[i], confidences[i]])
5. 常见问题与解决方案
5.1 模型转换问题
问题1:ONNX导入失败,提示"Unsupported OP type"
- 检查ONNX opset版本,建议使用opset=12
- 确保使用了onnx-simplifier进行优化
- 尝试更新RKNN Toolkit版本
问题2:INT8量化后精度大幅下降
- 检查校准数据集是否具有代表性
- 尝试调整量化算法:
python复制rknn.config(quantized_algorithm='kl') - 增加校准图片数量(建议100-200张)
5.2 推理执行问题
问题1:推理结果全为0
- 检查输入数据预处理是否正确
- 确认mean/std值与训练时一致
- 验证颜色空间转换(BGR→RGB)
- 检查模型输入输出节点名称是否匹配
问题2:推理速度远低于预期
- 确认NPU核心是否正常初始化
python复制rknn.init_runtime(core_mask=RKNNLite.NPU_CORE_0_1_2) # 使用多核 - 检查输入数据是否为连续内存:
python复制
input_data = np.ascontiguousarray(input_data)
5.3 精度调优技巧
-
FP16模式调试:
- 先在FP16模式下验证流程正确性
- 对比ONNX和RKNN的输出差异
-
INT8量化策略:
- 分阶段量化:先量化部分层,逐步扩大范围
- 使用QAT(量化感知训练)提升量化效果
-
后处理优化:
- 调整NMS阈值:
python复制cv2.dnn.NMSBoxes(..., nms_threshold=0.6) - 对特定类别设置不同的置信度阈值
- 调整NMS阈值:
6. 性能优化实践
6.1 内存优化策略
-
共享内存池:
python复制rknn.init_runtime( mem_type='shared', alloc_mem_from_linux=1 ) -
输入输出内存复用:
python复制rknn.inference( inputs=[input_data], data_format=['nhwc'], inputs_pass_through=[0] )
6.2 多线程处理
python复制from threading import Thread
class InferThread(Thread):
def __init__(self, rknn, input_queue, output_queue):
super().__init__()
self.rknn = rknn
self.input_queue = input_queue
self.output_queue = output_queue
def run(self):
while True:
img = self.input_queue.get()
outputs = self.rknn.inference([img])
self.output_queue.put(outputs)
# 创建多个推理线程
for i in range(4):
thread = InferThread(rknn, input_queue, output_queue)
thread.start()
6.3 功耗控制
RV1126B支持动态频率调整:
bash复制# 查看当前频率
cat /sys/class/devfreq/devfreq0/cur_freq
# 设置频率(单位:Hz)
echo 1000000000 > /sys/class/devfreq/devfreq0/userspace/set_freq
典型功耗对比:
| 频率(GHz) | 推理时间(ms) | 功耗(W) |
|---|---|---|
| 1.0 | 45 | 2.1 |
| 0.8 | 56 | 1.6 |
| 0.5 | 92 | 1.1 |
在实际部署中,需要根据实时性要求和散热条件选择合适的运行频率。
