4.8 KiB
4.8 KiB
RK3588 YOLOv8 模型输出格式修复记录
问题现象
使用新转换的 RKNN 模型 (ppe_det_yolov8_ppe11_768_rk3588.rknn) 进行推理时,出现以下异常现象:
- 所有检测框的置信度分数相同 - 均为
0.998047 - 框坐标异常 - 大量框为全图
(0,0,768,768)或包含inf值 - 部分帧无检测 -
valid_count=0,global_max_score=0.000580
错误日志示例
[ai_yolo] raw box i=0 cx=inf cy=inf w=inf h=inf score=0.998047 cls=0
[ai_yolo] raw box i=1 cx=inf cy=inf w=inf h=inf score=0.998047 cls=0
[ai_yolo] det: cls=0 score=0.998047 bbox=(0.000000,0.000000,768.000000,768.000000)
根本原因
1. RKNN 输出配置错误 (src/ai_scheduler.cpp)
原代码存在三个关键问题:
问题 A: 使用 INT8 原始输出
// 错误代码
outputs[i].want_float = 0; // 获取 INT8 量化后的原始数据
当 want_float=0 时,RKNN 返回 INT8 量化数据,但代码错误地将其当作 FP16 解析。
问题 B: 硬编码输出类型
// 错误代码
out.type = RKNN_TENSOR_FLOAT16; // 硬编码为 FP16,与实际数据类型不符
实际获取的是 INT8 数据(type=2),但标记为 FP16(type=1),导致后续解析逻辑错误。
问题 C: 缓冲区大小不足
// 错误代码
uint32_t out_sz = ctx->output_attrs[j].n_elems * sizeof(uint16_t); // 只分配 2字节/元素
当 want_float=1 时,RKNN 返回 FP32(4字节/元素),但缓冲区只分配了 FP16 大小(2字节/元素),导致数据截断。
2. 数值解释
0.998047 是 FP16 值 0x3BFF 转换为 FP32 后的结果:
- FP16:
0x3BFF= 0.99951171875 - 由于所有 class scores 被错误解析为相同的 FP16 值,导致所有检测分数相同
解决方法
修复 1: 启用 FP32 输出 (ai_scheduler.cpp:620)
// 修改前
outputs[i].want_float = 0;
// 修改后
outputs[i].want_float = 1; // 请求 FP32 输出,RKNN 自动完成反量化
修复 2: 正确标记输出类型 (ai_scheduler.cpp:647)
// 修改前
out.type = RKNN_TENSOR_FLOAT16;
// 修改后
out.type = RKNN_TENSOR_FLOAT32; // 与 want_float=1 对应
修复 3: 分配正确大小的缓冲区 (ai_scheduler.cpp:220)
// 修改前
uint32_t out_sz = ctx->output_attrs[j].n_elems * sizeof(uint16_t);
// 修改后
uint32_t out_sz = ctx->output_attrs[j].n_elems * sizeof(float); // 4字节/元素
修复后的效果
正常检测输出
[ALARM][info] detections=[{cls=6 score=0.53 bbox=(129,344,427,407)}]
[ALARM][info] detections=[{cls=6 score=0.43 bbox=(73,316,382,452)}]
[ALARM][info] detections=[{cls=6 score=0.52 bbox=(108,315,299,453)}]
关键改进
| 指标 | 修复前 | 修复后 |
|---|---|---|
| 分数分布 | 固定 0.998047 | 0.43 ~ 0.53 变化 |
| 坐标范围 | 全图或 inf | 合理的 (x,y,w,h) |
| 有效检测 | valid_count=0 或异常 | 稳定的有效检测 |
模型转换注意事项
PC 端导出 ONNX
from ultralytics import YOLO
model = YOLO('best.pt')
model.export(
format='onnx',
imgsz=768,
opset=12,
simplify=True,
dynamic=False,
)
PC 端转换为 RKNN
from rknn.api import RKNN
rknn = RKNN()
rknn.config(
target_platform='rk3588',
mean_values=[[0.0, 0.0, 0.0]],
std_values=[[255.0, 255.0, 255.0]],
)
rknn.load_onnx(
model='best.onnx',
input_size_list=[[1, 3, 768, 768]] # NCHW 格式
)
rknn.build(do_quantization=False) # 先测试 FP16
rknn.export_rknn('ppe_det_yolov8_ppe11_768_rk3588.rknn')
调试技巧
1. 验证模型输出
from rknnlite.api import RKNNLite
import numpy as np
rknn = RKNNLite()
rknn.load_rknn('ppe_det_yolov8_ppe11_768_rk3588.rknn')
rknn.init_runtime()
input_data = np.ones((1, 3, 768, 768), dtype=np.float32) * 128
outputs = rknn.inference(inputs=[input_data])
print(f"Output shape: {outputs[0].shape}")
print(f"Min/Max: {outputs[0].min():.4f} / {outputs[0].max():.4f}")
print(f"First 10 values: {outputs[0].flatten()[:10]}")
2. 检查输出类型
// 在 ai_scheduler.cpp 中添加日志
LogInfo("[ai_scheduler] output[" + std::to_string(i) + "] type=" +
std::to_string(ctx->output_attrs[i].type) +
" qnt_type=" + std::to_string(ctx->output_attrs[i].qnt_type));
相关文件
src/ai_scheduler.cpp- RKNN 推理核心逻辑plugins/ai_yolo/ai_yolo_node.cpp- YOLO 后处理include/ai_scheduler.h- 数据结构定义
参考文档
修复日期: 2026-02-28
修复者: AI Assistant
测试模型: models/ppe_det_yolov8_ppe11_768_rk3588.rknn