目标检测部署(YOLOv5/v7/v8)
1. 概述
1.1 功能简介
目标检测是 AI 的核心任务之一,要在一张图里同时找到物体在哪、是什么。YOLO(You Only Look Once)系列是实时目标检测的代表性算法:与传统两阶段检测器(如 Faster R-CNN)不同,YOLO 把检测建模成单次回归问题,速度和精度兼顾,非常适合嵌入式边缘部署。
RK3588 搭载 6 TOPS 算力的 NPU,支持 INT8/INT16 混合量化(INT8 即 8 位整数精度,是 NPU 跑得最快的格式),单芯片就能做多路视频流的实时目标检测。本文档介绍如何在 RK3588 上部署 YOLOv5、YOLOv7 和 YOLOv8,涵盖模型转换、量化、板端推理以及性能调优。
1.2 应用场景
| 场景 | 说明 | 典型模型 |
|---|---|---|
| 安防监控 | 人员检测、行为识别、入侵告警 | YOLOv5s/YOLOv8n |
| 自动驾驶辅助 | 车辆检测、行人检测、车道线识别 | YOLOv8s/YOLOv7-tiny |
| 工业质检 | 产品缺陷检测、异物检测 | YOLOv5m/YOLOv8s |
| 人流量统计 | 人数统计、密度估计 | YOLOv5s/YOLOv8n |
| 物流分拣 | 包裹识别、条码定位 | YOLOv5n/YOLOv8n |
1.3 工作原理
视频流输入 → 帧预处理 → RKNN NPU 推理 → NMS 后处理 → 检测框输出
↓
NMS(非极大值抑制)
↓
最终检测结果- 视频流输入:通过 USB 摄像头、MIPI CSI 摄像头或本地视频文件获取图像帧。
- 帧预处理:将原始图像 resize 到模型输入尺寸(通常为 640x640),进行 BGR2RGB 转换。
- RKNN NPU 推理:将预处理后的图像送入 NPU 执行前向推理,利用 RK3588 的三核 NPU 并行加速。
- NMS 后处理:对推理输出进行解码、置信度过滤和非极大值抑制,去除冗余检测框。
- 检测框输出:将最终检测结果绘制在原图上并输出。
2. 硬件准备
2.1 必需硬件
| 设备 | 型号/规格 | 数量 | 说明 |
|---|---|---|---|
| 核心板 | C-3588LQ | 1 | RK3588 主芯片,6 TOPS NPU,6GB/8GB RAM |
| 底板 | SHIMETAPI-M5-R2A | 1 | 提供 USB、以太网、显示等外设接口 |
| USB 摄像头(可选) | UVC 兼容 | 1 | 用于实时视频流测试,支持 720p/1080p |
| 电源适配器 | 12V/3A DC | 1 | 通过底板 DC 接口供电 |
2.2 硬件连接说明
┌──────────────────────┐
│ SHIMETAPI-M5-R2A │
│ 底板 │
│ ┌──────────────┐ │
│ │ C-3588LQ │ │
│ │ 核心板 │ │
│ │ RK3588 NPU │ │
│ └──────────────┘ │
│ USB ── USB 摄像头 │
│ HDMI ── 显示器 │
│ DC ── 12V 电源 │
└──────────────────────┘2.3 NPU 资源说明
RK3588 内置 3 个 NPU 核心,总算力 6 TOPS(INT8)。推理时可选择不同核心组合:
| 核心模式 | 使用核心 | 适用场景 |
|---|---|---|
| NPU_CORE_0 | 核心 0 | 单路推理,低功耗 |
| NPU_CORE_0_1 | 核心 0 + 核心 1 | 双核并行,中等负载 |
| NPU_CORE_0_1_2 | 核心 0 + 核心 1 + 核心 2 | 三核全开,最高性能 |
3. 软件准备
3.0 运行环境(本机)
开工前先看一眼本机环境,对不上就要先查环境:
| 项 | 实际值 |
|---|---|
| 系统 | Debian 12 / Ubuntu 22.04(内核 6.1.141)/ aarch64 |
| NPU 驱动 | rknpu 0.9.8 内建(DRM 方式 /dev/dri/renderD129,无 /dev/rknpu) |
| C 运行时 | /usr/lib/librknnrt.so 2.3.2(出厂预装) |
| 推理链路 | rknn-toolkit-lite2 2.3.2 + NPU 已跑通(分类模型 224×224 fp16 4.62ms/INT8 1.91ms 三核,见 ch09) |
| 转换链路 | 板上装 toolkit2 2.3.2 可转换量化(见 ch03) |
3.1 开发环境依赖
# RKNN Toolkit2(模型转换工具;正确包名后缀无 lite 之分)
pip3 install rknn-toolkit2==2.3.2 -i https://pypi.org/simple
# 板端推理包(正确包名,注意不是 rknn-lite2)
sudo pip3 install --break-system-packages rknn-toolkit-lite2==2.3.2
# Python 依赖(板端 numpy 1.24.2 / Pillow 已预装,opencv 未预装)
pip3 install numpy opencv-python3.2 RKNN 组件说明(本机版本)
| 组件 | 版本 | 运行位置 |
|---|---|---|
| rknn-toolkit2 | 2.3.2 | PC 端 或 RK3588 板上均可运行 |
| rknn-toolkit-lite2 | 2.3.2 | RK3588 板端 |
| librknnrt.so | 2.3.2(出厂预装) | RK3588 板端 C 运行时 |
| rknpu 内核驱动 | 0.9.8(出厂内建) | RK3588 板端,/dev/dri/renderD129 |
3.3 系统镜像要求
- 操作系统:Debian 12 / Ubuntu 22.04 或更新版本(基于 Linux 6.1 内核)
- 内核需包含 RKNN 驱动模块(
rknn.ko)
# 确认 NPU 驱动已加载
lsmod | grep rknn3.4 模型文件准备
先把 YOLO 模型导出成 ONNX,再准备量化用的校准数据列表:
# 安装 ultralytics 工具链并导出 ONNX
pip3 install ultralytics
yolo export model=yolov5s.pt format=onnx
# 校准数据集准备(用于 INT8 量化,从 COCO 验证集选取 100-200 张)
python3 -c "
import os, glob
val_dir = 'coco/val2017'
files = glob.glob(os.path.join(val_dir, '*.jpg'))
with open('coco_samples.txt', 'w') as f:
for p in files[:200]:
f.write(p + '\n')
print(f'Generated calibration list: {min(len(files), 200)} images')
"4. 操作步骤
4.1 YOLOv5 模型转换
from rknn.api import RKNN
rknn = RKNN()
# 配置:YOLO 系列 mean=[0,0,0], std=[255,255,255]
rknn.config(
mean_values=[[0, 0, 0]],
std_values=[[255, 255, 255]],
target_platform='rk3588',
quantized_dtype='asymmetric_quantized-8',
quantized_algorithm='mmse' # MMSE 量化算法,精度更优
)
ret = rknn.load_onnx(model='yolov5s.onnx')
if ret != 0:
print('加载模型失败')
exit(ret)
# dataset 文件每行包含一张校准图像的路径
ret = rknn.build(do_quantization=True, dataset='coco_samples.txt')
if ret != 0:
print('构建模型失败')
exit(ret)
ret = rknn.export_rknn('yolov5s.rknn')
if ret != 0:
print('导出模型失败')
exit(ret)
rknn.release()
print('YOLOv5s 模型转换完成: yolov5s.rknn')脚本跑完打印「模型转换完成」并生成 yolov5s.rknn 即为成功。几个关键参数的含义:
| 参数 | 值 | 说明 |
|---|---|---|
| target_platform | 'rk3588' | 指定 RK3588 目标平台,自动生成最优算子映射 |
| quantized_dtype | 'asymmetric_quantized-8' | INT8 非对称量化,适合 NPU 硬件特性 |
| quantized_algorithm | 'mmse' | 最小均方误差量化算法,比默认算法精度更高 |
4.2 YOLOv8 模型转换
from rknn.api import RKNN
rknn = RKNN()
rknn.config(
mean_values=[[0, 0, 0]],
std_values=[[255, 255, 255]],
target_platform='rk3588',
quantized_dtype='asymmetric_quantized-8'
)
ret = rknn.load_onnx(model='yolov8n.onnx')
if ret != 0:
print('加载模型失败')
exit(ret)
ret = rknn.build(do_quantization=True, dataset='coco_samples.txt')
if ret != 0:
print('构建模型失败')
exit(ret)
ret = rknn.export_rknn('yolov8n.rknn')
if ret != 0:
print('导出模型失败')
exit(ret)
rknn.release()
print('YOLOv8n 模型转换完成: yolov8n.rknn')注意:YOLOv8 使用 anchor-free 架构(不预设锚框,直接回归框的位置),后处理方式与 YOLOv5 不同,详见 4.5 节。
4.3 板端 Python 推理
下面的脚本从摄像头取流、调用 NPU 推理、做 NMS 后处理并实时画框显示,是完整的端到端示例:
from rknnlite.api import RKNNLite
import cv2
import numpy as np
import time
def non_max_suppression(boxes, scores, iou_threshold=0.45, score_threshold=0.5):
"""非极大值抑制(NMS)后处理"""
mask = scores > score_threshold
boxes, scores = boxes[mask], scores[mask]
if len(boxes) == 0:
return [], []
x1, y1, x2, y2 = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
areas = (x2 - x1) * (y2 - y1)
order = scores.argsort()[::-1]
keep = []
while len(order) > 0:
i = order[0]
keep.append(i)
if len(order) == 1:
break
xx1 = np.maximum(x1[i], x1[order[1:]])
yy1 = np.maximum(y1[i], y1[order[1:]])
xx2 = np.minimum(x2[i], x2[order[1:]])
yy2 = np.minimum(y2[i], y2[order[1:]])
inter = np.maximum(0, xx2 - xx1) * np.maximum(0, yy2 - yy1)
iou = inter / (areas[i] + areas[order[1:]] - inter)
order = order[np.where(iou <= iou_threshold)[0] + 1]
return boxes[keep].tolist(), scores[keep].tolist()
def yolov5_postprocess(output, img_shape, input_size=640):
"""YOLOv5 输出后处理:output shape (1, 25200, 85)"""
output = output.reshape(-1, 85)
boxes = output[:, :4]
obj_conf = output[:, 4]
class_scores = output[:, 5:]
mask = obj_conf > 0.25
boxes, obj_conf, class_scores = boxes[mask], obj_conf[mask], class_scores[mask]
if len(boxes) == 0:
return [], [], []
class_ids = np.argmax(class_scores, axis=1)
scores = obj_conf * class_scores[np.arange(len(class_ids)), class_ids]
# xywh -> xyxy -> 缩放回原图
xyxy = np.zeros_like(boxes)
xyxy[:, 0] = boxes[:, 0] - boxes[:, 2] / 2
xyxy[:, 1] = boxes[:, 1] - boxes[:, 3] / 2
xyxy[:, 2] = boxes[:, 0] + boxes[:, 2] / 2
xyxy[:, 3] = boxes[:, 1] + boxes[:, 3] / 2
scale_x, scale_y = img_shape[1] / input_size, img_shape[0] / input_size
xyxy[:, [0, 2]] *= scale_x
xyxy[:, [1, 3]] *= scale_y
return non_max_suppression(xyxy, scores)
def yolov8_postprocess(output, img_shape, input_size=640):
"""YOLOv8 anchor-free 后处理:output shape (1, 84, 8400)"""
output = output.reshape(84, -1).T # -> (8400, 84)
boxes = output[:, :4]
class_scores = output[:, 4:]
class_ids = np.argmax(class_scores, axis=1)
scores = class_scores[np.arange(len(class_ids)), class_ids]
mask = scores > 0.25
boxes, scores = boxes[mask], scores[mask]
if len(boxes) == 0:
return [], []
# cx,cy,w,h -> x1,y1,x2,y2 -> 缩放回原图
xyxy = np.zeros_like(boxes)
xyxy[:, 0] = boxes[:, 0] - boxes[:, 2] / 2
xyxy[:, 1] = boxes[:, 1] - boxes[:, 3] / 2
xyxy[:, 2] = boxes[:, 0] + boxes[:, 2] / 2
xyxy[:, 3] = boxes[:, 1] + boxes[:, 3] / 2
scale_x, scale_y = img_shape[1] / input_size, img_shape[0] / input_size
xyxy[:, [0, 2]] *= scale_x
xyxy[:, [1, 3]] *= scale_y
return non_max_suppression(xyxy, scores)
def run_inference(model_path='yolov5s.rknn', camera_id=0):
"""主推理循环:从摄像头读取视频流,执行目标检测并显示结果"""
rknn = RKNNLite()
ret = rknn.load_rknn(model_path)
if ret != 0:
print(f'加载 RKNN 模型失败: {model_path}')
return
# 使用三核 NPU
ret = rknn.init_runtime(core_mask=RKNNLite.NPU_CORE_0_1_2)
if ret != 0:
print('初始化运行时失败')
return
cap = cv2.VideoCapture(camera_id)
if not cap.isOpened():
print(f'无法打开摄像头 {camera_id}')
return
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
print('开始推理,按 q 退出...')
fps_count = 0
fps_start = time.time()
while True:
ret, frame = cap.read()
if not ret:
break
img = cv2.resize(frame, (640, 640))
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
outputs = rknn.inference(inputs=[img_rgb])
# 根据模型选择后处理
if 'v8' in model_path:
boxes, scores = yolov8_postprocess(outputs[0], frame.shape)
else:
boxes, scores = yolov5_postprocess(outputs[0], frame.shape)
for box, score in zip(boxes, scores):
x1, y1, x2, y2 = [int(c) for c in box]
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.putText(frame, f'{score:.2f}',
(x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX,
0.5, (0, 255, 0), 1)
# FPS 计算
fps_count += 1
elapsed = time.time() - fps_start
if elapsed >= 1.0:
fps = fps_count / elapsed
fps_count = 0
fps_start = time.time()
cv2.putText(frame, f'FPS: {fps:.1f}', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 2)
cv2.imshow('YOLO Detection', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
rknn.release()
if __name__ == '__main__':
run_inference(model_path='yolov5s.rknn', camera_id=0)运行成功的标志:窗口里实时显示带检测框和 FPS 的画面,按 q 退出。
4.4 C/C++ 推理(高性能)
Python 跑通后,追求更高性能可以换 C 版本:
// yolov5_infer.c — 编译: gcc -o yolov5_infer yolov5_infer.c -lrknn_api -lm
#include "rknn_api.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define INPUT_SIZE 640
int main(int argc, char *argv[])
{
rknn_context ctx;
rknn_input inputs[1];
rknn_output outputs[3];
const char *model_path = (argc > 1) ? argv[1] : "yolov5s.rknn";
// 加载模型
FILE *fp = fopen(model_path, "rb");
if (!fp) { fprintf(stderr, "无法打开模型: %s\n", model_path); return -1; }
fseek(fp, 0, SEEK_END);
int model_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
void *model_data = malloc(model_size);
fread(model_data, 1, model_size, fp);
fclose(fp);
// 初始化 RKNN
int ret = rknn_init(&ctx, model_data, model_size, 0, NULL);
free(model_data);
if (ret < 0) { fprintf(stderr, "RKNN 初始化失败: %d\n", ret); return -1; }
// 查询 IO
rknn_input_output_num io_num;
rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));
printf("inputs: %d, outputs: %d\n", io_num.n_input, io_num.n_output);
// 设置输入(image_data 需填充预处理后的图像)
uint8_t *image_data = (uint8_t *)malloc(INPUT_SIZE * INPUT_SIZE * 3);
memset(inputs, 0, sizeof(inputs));
inputs[0].index = 0;
inputs[0].type = RKNN_TENSOR_UINT8;
inputs[0].size = INPUT_SIZE * INPUT_SIZE * 3;
inputs[0].fmt = RKNN_TENSOR_NHWC;
inputs[0].buf = image_data;
rknn_inputs_set(ctx, 1, inputs);
// 推理并计时
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
rknn_run(ctx, NULL);
clock_gettime(CLOCK_MONOTONIC, &end);
double infer_ms = (end.tv_sec - start.tv_sec) * 1000.0 +
(end.tv_nsec - start.tv_nsec) / 1e6;
printf("推理时间: %.2fms (约 %.1f FPS)\n", infer_ms, 1000.0 / infer_ms);
// 获取输出
for (int i = 0; i < io_num.n_output; i++) { outputs[i].index = i; outputs[i].want_float = 1; }
rknn_outputs_get(ctx, io_num.n_output, outputs, NULL);
for (int i = 0; i < io_num.n_output; i++)
printf("输出[%d]: size=%zu bytes\n", i, outputs[i].size);
// 清理
rknn_outputs_release(ctx, io_num.n_output, outputs);
free(image_data);
rknn_destroy(ctx);
return 0;
}编译与运行:
# 交叉编译
aarch64-linux-gnu-gcc -o yolov5_infer yolov5_infer.c -I/path/to/rknn-api/include -L/path/to/rknn-api/lib/Linux/aarch64 -lrknn_api -lm
# 板端运行
export LD_LIBRARY_PATH=/path/to/rknn-api/lib/Linux/aarch64:$LD_LIBRARY_PATH
./yolov5_infer yolov5s.rknn4.5 YOLOv7/v8 后处理差异
不同 YOLO 版本的输出格式不同,后处理不能混用:
YOLOv5: 输出 (1, 25200, 85) — 原始 anchor-based,85 = 5(x,y,w,h,obj) + 80(类别)
YOLOv7: 输出 (1, 25200, 85) — anchor-based(类似 v5)
YOLOv8: 输出 (1, 84, 8400) — anchor-free,84 = 4(cx,cy,w,h) + 80(类别),需 transpose关键差异汇总:
| 对比项 | YOLOv5/v7 | YOLOv8 |
|---|---|---|
| 输出 shape | (1, 25200, 85) | (1, 84, 8400) |
| 锚框机制 | anchor-based | anchor-free |
| 置信度 | obj_conf * class_score | 直接用 class_score |
| 坐标格式 | xywh | cx, cy, w, h |
| 后处理 | 相同的 NMS | 相同的 NMS |
4.6 多路视频同时检测
RK3588 有三个 NPU 核心,可以一路视频绑一个核心,做多路同时检测:
from rknnlite.api import RKNNLite
import cv2
import threading
class DetectorWorker:
def __init__(self, model_path, camera_id, core_mask):
self.model_path, self.camera_id, self.core_mask = model_path, camera_id, core_mask
self.running = False
def start(self):
self.running = True
self.thread = threading.Thread(target=self._run, daemon=True)
self.thread.start()
def _run(self):
rknn = RKNNLite()
rknn.load_rknn(self.model_path)
rknn.init_runtime(core_mask=self.core_mask)
cap = cv2.VideoCapture(self.camera_id)
while self.running:
ret, frame = cap.read()
if not ret: break
img_rgb = cv2.cvtColor(cv2.resize(frame, (640, 640)), cv2.COLOR_BGR2RGB)
outputs = rknn.inference(inputs=[img_rgb])
cap.release()
rknn.release()
# 双路检测:每路各使用 1 个 NPU 核心
worker1 = DetectorWorker('model1.rknn', 0, RKNNLite.NPU_CORE_0)
worker2 = DetectorWorker('model2.rknn', 1, RKNNLite.NPU_CORE_1)
worker1.start()
worker2.start()5. 代码详解
5.1 NMS(非极大值抑制)原理
NMS 是目标检测后处理的核心算法,用来去掉同一个物体上重叠的冗余检测框。
算法流程:
1. 按置信度从高到低排序所有检测框
2. 选取置信度最高的框作为当前基准框
3. 计算基准框与其余所有框的 IoU(交并比)
4. 删除 IoU 超过阈值的框(认为是同一物体的重复检测)
5. 从剩余框中选取下一个最高分框作为新的基准框
6. 重复步骤 3-5 直到所有框处理完毕IoU 计算公式:
IoU = 交集面积 / (框A面积 + 框B面积 - 交集面积)两个阈值参数的调优建议:
| 参数 | 推荐范围 | 说明 |
|---|---|---|
| iou_threshold | 0.40 ~ 0.55 | 过低会误删不同物体的框,过高会保留冗余框 |
| score_threshold | 0.20 ~ 0.50 | 过低会引入大量误检,过高会漏检 |
5.2 多尺度检测
YOLOv5 输出 3 个尺度的特征图,覆盖不同大小的目标:
YOLOv5 输出 3 个尺度:
- 80x80 特征图:检测小目标(stride=8),感受野 8 像素
- 40x40 特征图:检测中目标(stride=16),感受野 16 像素
- 20x20 特征图:检测大目标(stride=32),感受野 32 像素
总计 80x80 + 40x40 + 20x20 = 8400 个锚框(无多尺度 anchor)
使用 3 组 anchor 时: 8400 * 3 = 25200 个锚框5.3 NPU 三核推理性能
RK3588 的三个 NPU 核心可灵活组合使用:
| 模型 | 输入尺寸 | NPU_CORE_0 | NPU_CORE_0_1 | NPU_CORE_0_1_2 |
|---|---|---|---|---|
| YOLOv5n | 640x640 | ~80 FPS | ~140 FPS | ~200 FPS |
| YOLOv5s | 640x640 | ~50 FPS | ~100 FPS | ~150 FPS |
| YOLOv5m | 640x640 | ~25 FPS | ~45 FPS | ~65 FPS |
| YOLOv7-tiny | 640x640 | ~55 FPS | ~105 FPS | ~155 FPS |
| YOLOv8n | 640x640 | ~60 FPS | ~110 FPS | ~160 FPS |
| YOLOv8s | 640x640 | ~30 FPS | ~55 FPS | ~80 FPS |
| YOLOv8m | 640x640 | ~15 FPS | ~28 FPS | ~40 FPS |
以上为 INT8 量化模型在 RK3588 上的典型测试值;实际帧率受图像预处理、后处理、内存带宽等因素影响。
5.4 量化对检测精度的影响
| 量化方式 | 模型大小 | mAP@0.5 | 推理速度 | 推荐场景 |
|---|---|---|---|---|
| FP32(未量化) | ~34 MB | 基准 | 慢(不推荐) | 仅用于精度对比 |
| INT8(默认算法) | ~8.5 MB | -1~2% | 快 | 通用场景 |
| INT8(MMSE 算法) | ~8.5 MB | -0.5~1% | 快 | 精度敏感场景 |
| INT8(KL 散度算法) | ~8.5 MB | -1~1.5% | 快 | 均衡选择 |
校准数据集的建议:使用 100~200 张有代表性的图像,覆盖实际部署场景;避免纯色或重复图像。
6. 常见问题
6.1 检测框偏移
现象:检测框位置与实际物体不匹配。
多数是预处理没对齐,逐项检查:
# 1. 确认预处理与训练一致(YOLO 系列通常 mean=[0,0,0], std=[255,255,255])
rknn.config(mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], ...)
# 2. 检查输入尺寸与模型输入一致
img = cv2.resize(frame, (640, 640))
# 3. 检查后处理中坐标转换和缩放是否正确6.2 漏检严重
现象:很多物体未被检测到。
三个方向尝试:
# 1. 降低 score_threshold
keep_boxes, keep_scores = non_max_suppression(xyxy, scores, score_threshold=0.25)
# 2. 使用 MMSE 量化算法减少精度损失
rknn.config(quantized_algorithm='mmse', ...)
# 3. 增加校准数据多样性(建议 200+ 张多样化图像)6.3 推理帧率低
按顺序排查:
# 1. 确认使用三核 NPU: core_mask=RKNNLite.NPU_CORE_0_1_2
# 2. 降低输入分辨率(640x640 -> 416x416 可提速 2-3 倍)
# 3. 查看 NPU 时钟频率
cat /sys/class/devfreq/fdab0000.npu/cur_freq # devfreq 接口(clk_npu 在本机不存在)
# 4. 确认 NPU 驱动已加载
cat /sys/kernel/debug/rknpu/version6.4 模型转换失败
现象:rknn.load_onnx() 或 rknn.build() 返回错误。
先升级工具链,再看是不是算子不支持:
# 升级 rknn-toolkit2 到最新版本
pip3 install --upgrade rknn-toolkit2
# 常见不支持的算子处理:
# - GatherND -> 改用 Reshape + Gather
# - NonMaxSuppression -> 移至 CPU 后处理(YOLO 已在模型外处理)
# - Slice -> 改用 Split 或 StridedSlice6.5 推理结果为空
按下面的步骤把输入输出都打印出来看:
# 1. 检查输入数据
print("shape:", img_rgb.shape) # 应为 (640, 640, 3)
print("dtype:", img_rgb.dtype) # 应为 uint8
print("range:", img_rgb.min(), img_rgb.max()) # 应为 0-255
# 2. 检查输出数据
outputs = rknn.inference(inputs=[img_rgb])
for i, out in enumerate(outputs):
print(f"输出[{i}] shape: {out.shape}, range: [{out.min():.4f}, {out.max():.4f}]")
# 3. 尝试不量化运行对比
rknn.build(do_quantization=False) # FP16 推理7. 相关文档
| 文档 | 说明 |
|---|---|
| ch01-npu-architecture-and-performance.md | RK3588 NPU 架构、三核设计与性能指标 |
| 03_模型转换与量化.md | RKNN 模型转换流程、量化算法对比与最佳实践 |
| 04_分类模型部署(LeNet-ResNet).md | 图像分类模型的完整部署流程参考 |
| ch09-npu-performance-and-debugging.md | NPU 推理性能调优方法与调试技巧 |
