Ultralytics RKNNBackend 详解:在瑞芯微 NPU 上运行 YOLO 推理(.rknn 模型)
2026/9/8 20:01:06 网站建设 项目流程

Ultralytics RKNNBackend 详解:在瑞芯微 NPU 上运行 YOLO 推理(.rknn 模型)

【免费下载链接】ultralyticsUltralytics YOLO26, YOLO11, YOLOv8 — object detection, instance segmentation, semantic segmentation, image classification, pose estimation, object tracking项目地址: https://gitcode.com/GitHub_Trending/ul/ultralytics

本文围绕 Ultralytics 仓库中 RKNNBackend 的 API 参考展开,逐方法解读RKNNBackendload_modelforward实现,并结合 ultralytics/nn/backends/rknn.py、ultralytics/nn/backends/base.py 与 ultralytics/nn/autobackend.py 的源码,说明.rknn模型如何在 Rockchip(RK3588、RK3566 等)NPU 上完成加载、推理与元数据恢复,以及 INT8 量化模型的坐标还原细节,帮助你在 Rockchip 边缘设备上正确部署和调试 YOLO 推理链路。

RKNNBackend 类概览

RKNNBackend定义在 ultralytics/nn/backends/rknn.py 中,类文档字符串对其职责的表述是:

Rockchip RKNN inference backend for Rockchip NPU hardware. Loads and runs inference with RKNN models (.rknn files) using the RKNN-Toolkit-Lite2 runtime. Only supported on Rockchip devices with NPU hardware (e.g., RK3588, RK3566).

由此可以提炼出三条关键事实:

  1. 它只负责运行已导出的.rknn模型,不负责模型转换(转换由rknn-toolkit2在 PC 侧完成,导出流程见 docs/en/integrations/rockchip-rknn.md);
  2. 运行依赖RKNN-Toolkit-Lite2运行时(rknnlite.api.RKNNLite),而非桌面端的rknn-toolkit2
  3. 只能在带 NPU 的 Rockchip 设备上运行(如 RK3588、RK3566),在 x86 PC 上会直接抛异常。

类结构上,它继承自 BaseBackend,后者是一个抽象基类,统一约定了所有推理后端必须实现的两个方法:

  • load_model(weight):从权重文件或模型目录加载推理运行时;
  • forward(im):对输入张量执行一次前向推理。

BaseBackend.__init__会初始化一组公共属性(stride=32namestaskbatch=1channels=3end2end=Falsemetadata等),并在构造末尾自动调用load_model(weight),因此只要AutoBackend能路由到RKNNBackend,加载过程就会自动发生,无需用户显式调用。

运行环境前提:Rockchip 设备检测

load_model的第一步是环境自检(rknn.py L32-L33):

if not is_rockchip(): raise OSError("RKNN inference is only supported on Rockchip devices.")

is_rockchip()实现于 ultralytics/utils/checks.py L1140-L1156,其判定逻辑是:

  • 仅在Linux + ARM64环境下尝试检测(LINUX and ARM64);
  • 读取设备树节点/proc/device-tree/compatible,取最后一个逗号分隔的 SoC 标识,去掉空字符后取-之前的主型号;
  • 判断该主型号是否落在白名单RKNN_CHIPS内。

白名单定义在 ultralytics/utils/init.py L84-L98:

RKNN_CHIPS = frozenset( { "rk3588", "rk3576", "rk3566", "rk3568", "rk3562", "rv1103", "rv1106", "rv1103b", "rv1106b", "rk2118", "rv1126b", } ) # Rockchip processors available for export

也就是说,RKNNBackend支持的设备覆盖 RK35 系列主流芯片以及 RV11 系列工业级芯片;不在这份集合中的 Rockchip 芯片(哪怕有 NPU)会在设备检测阶段被拒绝,这是一个硬性的能力边界。

load_model:模型定位、运行时初始化与元数据恢复

通过设备检查后,load_model(rknn.py L22-L52)依次完成四步:

def load_model(self, weight: str | Path) -> None: if not is_rockchip(): raise OSError("RKNN inference is only supported on Rockchip devices.") LOGGER.info(f"Loading {weight} for RKNN inference...") check_requirements("rknn-toolkit-lite2") from rknnlite.api import RKNNLite w = Path(weight) if not w.is_file(): w = next(w.rglob("*.rknn")) self.model = RKNNLite() ret = self.model.load_rknn(str(w)) if ret != 0: raise RuntimeError(f"Failed to load RKNN model: {ret}") ret = self.model.init_runtime() if ret != 0: raise RuntimeError(f"Failed to init RKNN runtime: {ret}") self.apply_metadata(self.read_metadata(w))

1. 依赖检查与延迟导入。check_requirements("rknn-toolkit-lite2")会在运行时自动校验(必要时安装)rknn-toolkit-lite2包;from rknnlite.api import RKNNLite是延迟导入,保证未安装该包的 PC 环境导入 Ultralytics 本身不会失败。

2. 权重路径解析:文件即目录皆可。入参weight既可以是.rknn文件路径,也可以是包含模型的目录。当传入的是目录(非文件)时,next(w.rglob("*.rknn"))会在目录树中递归找到第一个.rknn文件。这正好对接 Ultralytics 的导出产物约定:RKNN 导出的命名约定是yolo26n_rknn_model/目录(见 ultralytics/nn/autobackend.py L120 的格式表RKNN | *_rknn_model/),目录内同时包含<模型名>-<平台>.rknn与导出时写入的metadata.yaml

3. 两步初始化并严格校验返回码。RKNN Lite 的load_rknn()(解析模型)与init_runtime()(初始化目标硬件运行时)各自返回状态码,任何一步非零都会抛出带状态码的RuntimeError,便于在设备上定位是“模型文件不合法”还是“NPU 运行时不可用”(例如设备缺少 RKNN 驱动时后者会失败)。

4. 元数据恢复。最后调用self.apply_metadata(self.read_metadata(w))。由于.rknn是二进制格式,BaseBackend.read_metadata(base.py L152-L192)走的是旁路 sidecar 文件分支:它在模型所在目录(或其上级目录)寻找metadata.yaml并解析。该文件由导出侧写入,参见 ultralytics/utils/export/rknn.py L82-L84:

if metadata: YAML.save(output_dir / "metadata.yaml", metadata)

forward:输入格式约束与 NPU 推理

forward(rknn.py L54-L81)的文档字符串明确了输入约定:

im (torch.Tensor): Input image tensor inBHWC format, normalized to [0, 1].

方法体先做张量到运行时输入格式的转换:

h, w = im.shape[1:3] im = (im.cpu().numpy() * 255).astype("uint8") im = im if isinstance(im, (list, tuple)) else [im] y = self.model.inference(inputs=im)

要点:

  • BHWC + uint8:RKNN NPU 的推理输入是 HxWxC 的 8 位无符号整数,[0,1]浮点张量乘以 255 后转为uint8;这与 NCHW/FP32 的 PyTorch、ONNX 后端形成鲜明对比。AutoBackend中通过self.nhwc = format in {"coreml", "saved_model", "pb", "edgetpu", "rknn"}(autobackend.py L263)标记了这一点,预测管线的预处理会据此在送入后端前完成通道布局转换;
  • 归一化由导出配置承担:导出侧onnx2rknn固定写入了config = {"mean_values": [[0, 0, 0]], "std_values": [[255, 255, 255]], "target_platform": name}(export/rknn.py L74),即“除以 255”的归一化被烘焙进了 RKNN 模型本身,所以推理端只需喂原始像素值即可,两侧严格互补;
  • 批量输入inputs=im接受列表,RKNNLite.inference可对列表中的多帧做批量推理(批量能力取决于导出时的batch参数)。

INT8 量化模型的坐标还原

forward中最值得关注的是一段针对 INT8 导出的后处理(rknn.py L67-L81):

# INT8 exports use input-relative coordinates so a single per-tensor scale preserves class scores. if ( self.metadata.get("args", {}).get("quantize") == 8 and self.task in {"detect", "segment", "pose", "obb"} and not self.end2end ): kpt_start = 4 + len(self.names) # pose keypoints follow the box (4) and class-score (nc) channels for x in y: if x.ndim == 3: x[:, [0, 2]] *= w x[:, [1, 3]] *= h if self.task == "pose": x[:, kpt_start::3] *= w x[:, kpt_start + 1 :: 3] *= h

这段逻辑的触发条件有三个,且都来自导出时嵌入的元数据(metadata.yamlapply_metadata恢复为实例属性):

  1. args.quantize == 8:模型是 INT8 量化导出的;
  2. 任务属于detect / segment / pose / obb:这些任务的原始输出里包含坐标通道;
  3. not self.end2end:非端到端 NMS 模型。从源码结构看,这与导出侧的限制一致——ultralytics/engine/exporter.py L681-L684 明确对rknn格式禁用end2end分支(“This export format does not support end2end models”),因此该条件在 RKNN 导出中实际恒为 True,保留它是为了与BaseBackend的通用语义对齐。

为什么需要这一步?注释解释了原因:INT8 导出采用“相对输入的坐标”(input-relative coordinates),这样单个 per-tensor 量化 scale 就不会破坏类别分数(class scores)。代价是 NPU 输出的框坐标是归一化到输入尺寸的比例值,forward需要把它们乘回真实像素值:x/y通道乘宽ww/h通道乘高h。对pose任务,关键点紧跟在框(4 通道)与类别分数(nc通道)之后,因此用kpt_start = 4 + len(self.names)定位起点,再按每 3 个通道一组(x, y, conf)分别还原 x 与 y。

这段还原逻辑与导出侧一一对应:导出 INT8 图时,ultralytics/engine/exporter.py L1096-L1104 会在 ONNX 图中插入_NormalizeCoords节点(“Normalize coordinates by input size so RKNN's per-tensor INT8 scale preserves class scores”),两者共同构成“导出端归一化、推理端反归一化”的闭环。

AutoBackend 如何路由到 RKNNBackend

用户在 Ultralytics 中从不直接实例化RKNNBackend,入口是AutoBackend(ultralytics/nn/autobackend.py):

  • 格式识别:AutoBackend_BACKEND_MAP"rknn"映射到RKNNBackend(autobackend.py L167),文件命名约定为*_rknn_model/目录;
  • FP16 不支持:fp16 &= format in {"pt", "torchscript", "onnx", "openvino", "engine", "triton"}(autobackend.py L215),RKNN 不在其中,设备端推理的精度由导出时的quantize决定;
  • 设备回落:非 PyTorch 系格式在 CUDA 不可用时会自动落回cpu设备对象(autobackend.py L218-L224),而 RKNN 的实际执行硬件由RKNNLite.init_runtime()在 NPU 侧完成,device参数更多是管线层面的记账。

与导出侧的衔接:.rknn 目录是怎么来的

虽然本文主体是推理后端,但理解导出侧能完整解释后端的行为约定。导出函数onnx2rknn(ultralytics/utils/export/rknn.py L16-L84)的要点:

  • 目标平台通过name指定(默认"rk3588"),写入target_platform配置;
  • 平台精度限制rv1103 / rv1106 / rv1103b / rv1106b这四个 INT8-only 平台不做量化会直接报错,必须quantize=8(export/rknn.py L44-L48);
  • INT8 校准quantize=8时必须有校准图像列表文件(dataset),build(do_quantization=True, dataset=...)完成后导出<模型名>-<平台>.rknn,并把元数据以metadata.yaml旁路写入同一目录(export/rknn.py L80-L84)——这正是后端read_metadata读取的文件;
  • INT8 仅检测任务:ultralytics/engine/exporter.py L720-L725 规定 RKNN INT8 导出只支持detect任务,其他任务请改用 FP16(quantize=16);
  • 批次扩展rknn_batch_size在加载 batch-1 的 ONNX 后由 RKNN Toolkit 扩展,所以export_rknn中会先截取self.im = self.im[:1](exporter.py L1535)。

导出入口的完整命令示例(含namequantize参数)参见 docs/en/integrations/rockchip-rknn.md。

使用方式与仓库内的验证约束

在 Rockchip 设备上,只要把导出的yolo26n_rknn_model/目录拷贝到板端,Ultralytics 的预测/验证入口即可直接使用,例如:

from ultralytics import YOLO model = YOLO("yolo26n_rknn_model/") # 目录中自动 rglob 定位 .rknn results = model.predict("bus.jpg") # 推理走 RKNN NPU

仓库自身也对 RKNN 运行环境做了硬性约束,可在 ultralytics/utils/benchmarks.py L170-L173 看到:

if export_format == "rknn": assert not isinstance(model, YOLOWorld), "YOLOWorldv2 RKNN exports not supported yet" assert LINUX, "RKNN only supported on Linux" assert not is_rockchip(), "RKNN Inference only supported on Rockchip devices"

这三条 assert 给出了三条适用前提:YOLO-World v2 暂不支持 RKNN 导出;仅 Linux;且推理必须真的在 Rockchip 板卡上执行(该 benchmark 路径特意排除 Rockchip 环境,因为 PC 上既不能跑rknnlite也不应跑)。

适用范围与限制小结

综合文档与源码,RKNNBackend的能力边界可以归纳为:

维度事实(依据)
运行硬件仅 Rockchip 带 NPU 设备,白名单RKNN_CHIPS(utils/init.py L84-L98)
运行系统仅 Linux + ARM64 路径下才会尝试设备检测(checks.py L1140-L1156)
依赖rknn-toolkit-lite2运行时(rknn.py L36-L37)
输入格式BHWC、[0,1]归一化张量,内部转 uint8 像素
精度由导出决定(FP16/INT8),推理端不支持 FP16 开关(autobackend.py L215)
元数据依赖导出目录内的metadata.yamlsidecar(base.py L187-L190)
INT8 后处理detect/segment/pose/obb 的坐标乘回像素值,pose 关键点按kpt_start定位(rknn.py L67-L81)
端到端 NMSRKNN 格式导出时自动禁用end2end(exporter.py L681-L684)

如果你在 Rockchip 板端调试 RKNN 推理遇到问题,排查顺序建议为:先确认 SoC 是否在RKNN_CHIPS白名单内 → 再确认metadata.yaml.rknn同目录(否则tasknames等元数据为空,后处理会退化)→ 最后关注init_runtime()的返回码以区分模型问题与 NPU 驱动/运行时问题。

参考文档:nn.backends.rknn API Reference(本类由 ultralytics/nn/backends/rknn.py 自动生成)。

【免费下载链接】ultralyticsUltralytics YOLO26, YOLO11, YOLOv8 — object detection, instance segmentation, semantic segmentation, image classification, pose estimation, object tracking项目地址: https://gitcode.com/GitHub_Trending/ul/ultralytics

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询