YOLO11n 在 RK3588S 上跑通 4K 单路:从训练到 INT8,再到 30 FPS 实流
写在前面
最近在 AlertGateway 项目里做了一轮 YOLO11 迁移。
这次没有把目标简单定义成“把模型文件换成 YOLO11”,而是按一条完整链路逐项验证:
冻结数据集
↓
YOLO11s / YOLO11n 训练与 final test
↓
ONNX 输出契约确认
↓
RKNN 全 INT8 转换
↓
RK3588S NPU benchmark
↓
4K H.264 硬解 → RGA → RKNN → MPP → RTMP
最终得到的结论比较明确:YOLO11n 在这批 4K 桌面物品数据上没有牺牲最终 mAP50,板端
NPU 耗时却明显低于 YOLO11s,并且已经通过单路 4K 完整实流验证。
不过它目前仍然只是候选模型,不是生产模型切换公告。生产配置没有替换,模型也没有进入
常驻模型目录。
一、先把这次实验的边界固定下来
训练数据来自已经冻结的 4K 照片目录:
/home/rambos/datasets/alertgateway_4k_photo_20260718/
训练和验证使用:
train_val_frozen/
├── train: 408 张图片,494 个框
└── val: 82 张图片,103 个框
类别固定为六类:
cell phone, cup, keyboard, mouse, laptop, book
另外准备了一套不参与训练和调参的 final test:
final_test_annotation_20260719/
├── 166 张图片
└── 221 个人工标注框
final test 的类别框数量如下:
| 类别 | 框数量 |
|---|---|
| cell phone | 58 |
| cup | 26 |
| keyboard | 32 |
| mouse | 28 |
| laptop | 25 |
| book | 52 |
这套测试集有几个硬约束:
- 不参与训练;
- 不参与候选模型选择;
- 不参与阈值、结构和输入尺寸调参;
- 与 train、val 和 keyboard challenge 没有精确重复图片;
- 候选固定后只执行一次有效评估。
这样做是为了避免一边看 test 分数,一边继续调整训练方案,最后得到的只是对测试图片的
反复适应,而不是相对可信的候选比较。
当然,这批数据主要来自室内 4K 桌面物品场景。它能够说明模型在当前业务画面上的表现,
不能直接推导出模型对所有背景、光照、距离和遮挡条件都同样有效。
二、为什么先测 YOLO11s
第一步使用 Ultralytics v8.4.0/yolo11s.pt 作为官方 YOLO11s 初始权重,训练条件为:
| 项目 | 设置 |
|---|---|
| 初始权重 | 官方 YOLO11s 权重 |
| epoch | 20 |
| 输入 | 640×640 |
| batch | 4 |
| 类别 | 六类桌面物品 |
验证集结果:
| Precision | Recall | mAP50 | mAP50-95 |
|---|---|---|---|
| 80.2% | 78.5% | 82.7% | 59.2% |
固定候选后,在 untouched final test 上只执行一次评估:
| Precision | Recall | mAP50 | mAP50-95 |
|---|---|---|---|
| 82.5% | 75.8% | 85.52% | 53.69% |
精度结果可以接受,但上板速度并不理想。
在同一块 RK3588S、同一 core=012、SRAM 和 performance governor 条件下,YOLO11s 的
4K 实流统计为:
| 阶段 | 平均耗时 |
|---|---|
| CPU | 11.485ms |
| NPU | 34.380ms |
| 后处理 | 0.470ms |
| 总计 | 46.392ms |
对比旧的 YOLOv8 4K 模型,YOLO11s 的 NPU 耗时从约 26.7ms 上升到了约 34.4ms。继续优化
几行 C++ NMS 代码并不能解决这个问题,因为后处理只占很小一部分,主要开销发生在 RKNN
图内部。
算子 profiling 还观察到几个比较明显的热点:
exSDPAttention:约 2.36ms;- 检测头
exSoftmax13:约 2.06ms; - CPU
Transpose:约 0.74ms。
所以这次没有先对 YOLO11s 的 Attention 结构做大改,而是先训练更小的 YOLO11n,用它回答
一个更实际的问题:模型规模降低后,能不能保住当前数据集上的精度,同时把板端延迟降下来。
三、YOLO11n 的训练过程
YOLO11n 使用同一套 train、val 和 final test 划分,训练条件如下:
| 项目 | 设置 |
|---|---|
| 初始权重 | 官方 YOLO11n 权重 |
| epoch | 20 |
| 输入 | 640×640 |
| batch | 4 |
| 训练设备 | CUDA:0 |
| 类别 | 六类桌面物品 |
训练环境中有一个容易被忽略的细节:默认沙箱没有 /dev/nvidia*,在沙箱内第一次使用
--device 0 时,PyTorch 报告 CUDA 不可用。
随后单独核验训练环境,确认实际可用的 CUDA 环境为:
torch 2.4.0+cu121
CUDA available: True
GPU: NVIDIA GeForce GTX 1650 SUPER
因此 YOLO11n 使用独立的 CUDA 训练目录重新训练,没有复用此前中断的 CPU 训练目录,也
没有覆盖仓库中的旧模型。
训练产物位于:
/tmp/alertgateway_yolo11_4k/runs/yolo11n_4k_photos_20e_cuda/weights/best.pt
验证集结果:
| Precision | Recall | mAP50 | mAP50-95 |
|---|---|---|---|
| 75.2% | 79.6% | 80.4% | 61.3% |
在冻结 final test 上的唯一有效评估结果:
| Precision | Recall | mAP50 | mAP50-95 |
|---|---|---|---|
| 77.5% | 77.3% | 86.05% | 54.35% |
六类 AP50 为:
| 类别 | AP50 |
|---|---|
| cell phone | 73.25% |
| cup | 81.57% |
| keyboard | 81.13% |
| mouse | 96.11% |
| laptop | 86.91% |
| book | 97.37% |
把 YOLO11s 和 YOLO11n 放到同一张 final test 表里:
| 模型 | Precision | Recall | mAP50 | mAP50-95 |
|---|---|---|---|---|
| YOLO11s | 82.5% | 75.8% | 85.52% | 53.69% |
| YOLO11n | 77.5% | 77.3% | 86.05% | 54.35% |
这里不能简单下结论说“YOLO11n 比 YOLO11s 更准”。n 的 Precision 更低,只是在这套冻结
测试集上 Recall、mAP50 和 mAP50-95 没有下降。它成为首选候选的主要理由,是在精度没有
明显退化的情况下,模型规模和板端延迟都更低。
四、YOLO11 的输出为什么需要单独处理
YOLO11 导出的标准输出是:
[1, 10, 8400]
如果直接把 box 和 class 合并输出交给 INT8 转换器,会遇到量化尺度不匹配的问题:
- box 坐标大约在几百像素范围;
- class 概率在 0 到 1 之间;
- 两者共用一个量化 scale 时,类别概率容易损失有效分辨率。
因此转换时保留两个独立输出节点:
box: /model.23/Mul_2_output_0 [1, 4, 8400]
class: /model.23/Sigmoid_output_0 [1, 6, 8400]
项目中新增了 yolo11_decoded 输出布局,负责:
- 分别查询 box 和 class 两个 RKNN 输出;
- 使用项目的六类类别表,而不是默认 COCO 80 类;
- 使用和 640×640 预处理一致的 letterbox 反算;
- 执行置信度过滤和 NMS;
- 保留旧的
decoded和rockchip_dfl路径,不破坏已有模型契约。
这一步的重点不是把后处理写得更复杂,而是先把模型输出的数值范围和 C++ 后处理的输入
契约固定下来。模型能转换出来,不代表输出解释一定正确;输出形状、scale、类别数量和
最终框坐标都需要单独验证。
五、INT8 RKNN 转换结果
YOLO11n 的 INT8 RKNN 产物为:
/tmp/alertgateway_yolo11_4k/yolo11n_4k_int8.rknn
转换结果:
| 项目 | 结果 |
|---|---|
| 文件大小 | 4,793,255 bytes |
| 输入 | 1×640×640×3 NHWC INT8 |
| box 输出 | 1×4×8400 INT8 |
| class 输出 | 1×6×8400 INT8 |
| box/class scale | 2.53664 / 0.00390128 |
| float/fp16 fallback | 无 |
转换工具确认所有层均为 INT8,没有因为某个算子不支持而回退到 float 或 fp16。
本轮使用独立的候选配置:
config/config_4k_yolo11n_candidate.json
它的输出仍然使用旧的单路固定地址:
rtmp://192.168.0.168/live/alertgateway
这也是为什么本轮可以在不触碰默认生产配置的情况下完成候选验证。
六、板端 benchmark:速度提升来自模型本身
在同一块板上使用 core=012、SRAM、50 次 warmup 和 300 次测量:
rknn_benchmark yolo11n_4k_int8.rknn \
--warmup 50 --runs 300 --core 012 --sram \
--output-mode float --postprocess yolo11 --no-perf-query
结果如下:
| 阶段 | YOLO11n |
|---|---|
| inputs_set | 0.174ms |
| rknn_run_wall | 19.779ms |
| outputs_get | 0.197ms |
| YOLO11 后处理 | 0.195ms |
| run + get + postprocess | 20.171ms |
三种模型放在一起看更直观:
| 模型 | NPU | 完整推理/实流总计 | RKNN 大小 |
|---|---|---|---|
| YOLOv8 4K | 约 26.67ms | 约 38.72ms | — |
| YOLO11s | 34.380ms | 46.392ms | 约 11.8MB |
| YOLO11n | 20.699ms | 32.186ms | 约 4.8MB |
YOLO11n 相比 YOLO11s 的 NPU 耗时下降约 39.8%,相比旧 YOLOv8 4K 模型也更快。
这里的收益来自模型图本身变小,而不是单纯把 infer_every_n_frames 调大。离线 benchmark
测试的是一次真实 NPU 调用的耗时;它说明单次推理确实变快了。
七、把模型放回 4K 完整链路
离线图片和单次 RKNN 调用通过后,才进行完整实流验证。
输入为:
rtmp://192.168.0.168/live/testsrc2
视频条件:
- 3840×2160;
- H.264;
- 30 FPS;
h264_rkmpp硬件解码;- 输出 3840×2160 H.264 Main;
- MPP 编码目标码率约 18 Mbps。
完整链路为:
SRS 4K H.264 输入
↓
h264_rkmpp 硬解 / NV12
↓
RGA 预处理与 640×640 letterbox
↓
RKNN NPU / YOLO11n INT8
↓
检测结果、跟踪和叠框
↓
MPP H.264 编码
↓
固定单路 RTMP 输出
使用 tools/test/run_4k_full_inference_on_board.sh 完成 60 秒隔离验证。脚本只上传本轮
候选二进制、配置和模型到临时目录,SRS 由外部手动运行,脚本没有启动、停止或重启 SRS。
板端记录了 400 条推理样本:
| 阶段 | 平均耗时 |
|---|---|
| CPU | 10.9918ms |
| copy | 0.0563ms |
| NPU | 20.6989ms |
| cnv/预处理 | 0.4385ms |
| total | 32.1855ms |
稳定窗口的链路结果:
- 输入流和结果流均成功变为 active;
- 输入、编码和输出约 30 FPS;
- 结果流约 18 Mbps;
- SRS 确认输出为 3840×2160 H.264 Main;
enc_drop=0、put_fail=0、out_drop=0、write_fail=0;- 没有持续队列积压;
- MQTT 连接正常;
- 没有发现解码、RGA、RKNN、MPP、RTMP 或 MQTT 错误。
这次测试里还出现了两个容易误读的日志现象。
第一,infer_drop 约为 24~31/10 秒。当前候选设置了 infer_every_n_frames=4,推理队列
使用 latest-frame mailbox,新的帧会替换尚未执行的旧帧。因此这个数字表示推理队列覆盖了
旧帧,不是编码帧丢失,更不是 RTMP 输出失败。
第二,测试发布端停止时 FFmpeg 可能打印:
Failed to update header with correct duration/filesize
这是 FLV 收尾时的提示,不是 AlertGateway 运行期间的解码、编码或推流故障。
八、YOLO11s 和 YOLO11n 的板端对比
| 指标 | YOLO11s | YOLO11n | n 相对 s |
|---|---|---|---|
| 4K 实流 CPU | 11.485ms | 10.992ms | 略降 |
| 4K 实流 NPU | 34.380ms | 20.699ms | 下降约 39.8% |
| 4K 实流总耗时 | 46.392ms | 32.186ms | 下降约 30.6% |
| RKNN 大小 | 约 11.8MB | 约 4.8MB | 明显降低 |
YOLO11n 去掉的不是检测功能,而是减少了 YOLO11 网络的宽度和深度。两者仍然使用同一套
六类输出协议、同一套 yolo11_decoded C++ 后处理和同样的 4K 单路输出链路。
九、这次实验真正验证了什么
这轮工作最后通过的是一条完整的证据链:
数据集划分固定
↓
final test 与训练数据隔离
↓
YOLO11n 精度没有明显退化
↓
ONNX 输出契约清楚
↓
RKNN 全 INT8 转换通过
↓
离线 NPU benchmark 变快
↓
4K 硬解、RGA、RKNN、MPP、RTMP 单路实流通过
其中任何一环没有通过,都不能仅凭模型文件已经生成就宣布迁移成功。
尤其是这三个结论不能混为一谈:
- final test 的 mAP50 只能说明这套冻结测试集上的候选表现;
- 离线 benchmark 只能说明一次模型调用的耗时;
- 4K 实流通过才说明模型被放回真实媒体链路后,没有破坏解码、编码和推流。
总结
在当前这套冻结的 4K 桌面物品数据和 RK3588S 测试环境下,YOLO11n 已经达到单路候选门槛:
- final test mAP50 为 86.05%;
- final test mAP50-95 为 54.35%;
- RKNN 模型约 4.8MB;
- NPU 平均耗时约 20.7ms;
- 单次 run + get + postprocess 约 20.171ms;
- 4K 单路完整实流约 30 FPS;
- 输出约 18 Mbps;
- 编码和推流失败计数为 0。
github(experiment/yolo-v11 分支): https://github.com/johnjiamzhong-project/AlertGateway

浙公网安备 33010602011771号