上一篇介绍了如何调用yolo的模型简单推理,这篇我们来个大招。
YOLO26l INT8 (QNN 2.36 / NPU) 测试说明 —— 犀牛派 X1 (QCS8550)
这篇文档是讲述如何在犀牛派 X1 上使用 QNN 2.36 HTP(NPU) 后端测试 YOLO26l W8A8 量化模型的完整流程,
包括:环境确认、单图基准测试、USB 摄像头实时检测、NPU 后端是否生效的验证方法、以及实测帧率水平。
1. 测试环境
| 项目 | 内容 |
|---|---|
| 主板 | 犀牛派 X1,SoC 为高通 QCS8550 (Kalama) |
| 系统 | Ubuntu 22.04 (AidLux),内核 5.15,Python 3.10 |
| 推理后端 | QNN 2.36 (/usr/local/lib/aidlux/aidlite/qnn236/libQnnHtp.so),HTP V73 |
| Python SDK | pyaidlite 2.3.1 + opencv-python 4.13 + numpy 1.26 (已预装) |
| 模型 | cutoff_yolo26l_qcs8550_w8a8.qnn236.ctx.bin(mms 下载,INT8 W8A8,输入 640x640) |
| 模型输出 | split_xyxy 布局:[1,4,8400](框) + [1,80,8400](类别分数) |
AidLite 的关键配置(camera_test.py 中 load_interpreter()):
config.framework_type = aidlite.FrameworkType.TYPE_QNN # QNN 后端
config.accelerate_type = aidlite.AccelerateType.TYPE_DSP # DSP => HTP(NPU)
config.is_quantify_model = 1 # INT8 量化模型
2. 目录结构
yolo26l_test/
├── README.md # 本文档
├── camera_test.py # 主测试:摄像头实时检测 + NPU 验证 + 帧率统计
├── benchmark_image.py # 单张图片基准测试(官方示例流程)
├── npu_monitor.sh # 从另一个终端验证/观察 NPU 是否在工作
├── yolo.py / utils.py # 模型封装与预处理/后处理(NMS 等)
├── bus.jpg # 官方测试图片
└── models/QCS8550/W8A8/
└── cutoff_yolo26l_qcs8550_w8a8.qnn236.ctx.bin
由于之前写第一篇的时候把代码都贴论坛里面,导致字符太多。超出限制了。所以后期我代码放到github上,链接发布出来给大家分享。
3. 测试步骤
3.1 单图基准测试(先确认模型可用)
cd ~/yolo26l_test
python3 benchmark_image.py --invoke_nums 30
预期输出:bus.jpg 检出 5 个目标(1 个 bus + 4 个 person),标注图保存为 result.jpg,
并打印 30 次 invoke 的耗时统计。
3.2 摄像头实时检测(正式测试)
python3 camera_test.py # 自动探测 USB 摄像头,默认统计 300 帧
python3 camera_test.py --source 0 --show # 指定 /dev/video0 并实时显示画面(需桌面)
python3 camera_test.py --frames 0 # 不限帧数,Ctrl+C 结束并打印统计
python3 camera_test.py --save out.mp4 # 同时保存标注视频
无摄像头时也可以用图片/视频模拟帧源验证全流程:
python3 camera_test.py --source bus.jpg --frames 300
3.3 验证是否真正调用了 NPU
脚本启动时会自动执行 check_npu_backend(),从三个角度验证,全部通过则打印
验证结论: 通过,推理运行在 NPU(HTP) 上:
- HTP 后端库映射:读取
/proc/self/maps,确认进程加载了
libQnnHtp.so和libQnnHtpV73Stub.so(QCS8550 的 NPU 为 HTP V73;
如果只加载了libQnnCpu.so则说明跑在 CPU 上)。 - fastrpc 通道:进程持有
/dev/adsprpc-smd-secure文件句柄,
这是 ARM 侧与 CDSP/NPU 通信的 RPC 通道,只有真正下发到 NPU 才会打开。 - CDSP 服务进程:
cdsprpcd在运行中(NPU 推理的系统侧依赖)。
另开一个终端,可在推理进行中用监控脚本交叉验证:
脚本内容如下:
#!/bin/bash
# npu_monitor.sh —— 验证并观察 NPU(HTP/CDSP) 是否在被使用
#
# 用法:
# ./npu_monitor.sh # 自动寻找 camera_test.py 进程,检查一次
# ./npu_monitor.sh <PID> # 检查指定进程
# ./npu_monitor.sh <PID> 30 # 每 2 秒检查一次,共 30 次
#
# 验证依据(满足即说明推理跑在 NPU 上):
# 1) 进程加载了 libQnnHtp.so 和 libQnnHtpV73Stub.so (QCS8550 对应 HTP V73)
# 2) 进程持有 /dev/adsprpc-smd-secure fd(ARM 与 CDSP/NPU 的 fastrpc 通道)
# 说明: 本平台上 cdsprpcd 的 CPU 占用即使 NPU 满负荷也接近 0(DSP 侧计算不计入),
# 因此 cdsprpcd %CPU 不能作为判据,仅打印供参考。
PID=$1
TIMES=${2:-1}
CLK_TCK=100
if [ -z "$PID" ]; then
for p in $(pgrep -f "camera_test.py"); do
if [ "$(cat /proc/$p/comm 2>/dev/null)" = "python3" ]; then
PID=$p
break
fi
done
if [ -z "$PID" ]; then
echo "未发现正在运行的 camera_test.py,请先启动测试,或用: $0 <PID> [次数]"
exit 1
fi
fi
echo "监控进程 PID=$PID"
echo "=========================================================="
for i in $(seq 1 "$TIMES"); do
if [ ! -d "/proc/$PID" ]; then
echo "[$i] 进程 $PID 已退出"
break
fi
HTP=$(grep -o 'libQnnHtp[^ ]*\.so' "/proc/$PID/maps" 2>/dev/null | sort -u | tr '\n' ' ')
FASTRPC=$(ls -l "/proc/$PID/fd" 2>/dev/null | grep -o 'adsprpc-smd[a-z-]*' | sort -u | tr '\n' ' ')
echo "[$i] $(date +%H:%M:%S)"
echo " HTP 库 : ${HTP:-未加载!}"
echo " fastrpc fd: ${FASTRPC:-未打开!}"
if echo "$HTP" | grep -q "libQnnHtp.so" && echo "$HTP" | grep -q "V73" && [ -n "$FASTRPC" ]; then
echo " 结论 : NPU(HTP V73) 后端生效 ✔"
else
echo " 结论 : 未确认 NPU 后端,请检查模型加载配置 ✘"
fi
# 参考: cdsprpcd CPU(本平台上即使 NPU 工作中也通常保持 0)
CPID=$(pidof cdsprpcd | awk '{print $1}')
if [ -n "$CPID" ]; then
PREV=$(awk '{print $14 + $15}' "/proc/$CPID/stat" 2>/dev/null)
[ "$i" -lt "$TIMES" ] && sleep 2
CUR=$(awk '{print $14 + $15}' "/proc/$CPID/stat" 2>/dev/null)
if [ -n "$PREV" ] && [ -n "$CUR" ]; then
CPU=$(awk "BEGIN {printf \"%.1f\", ($CUR - $PREV) * 100.0 / ($CLK_TCK * 2)}")
echo " (参考) cdsprpcd %CPU: $CPU"
fi
elif [ "$i" -lt "$TIMES" ]; then
sleep 2
fi
done
echo "=========================================================="
chmod +x ./npu_monitor.sh # 给丫执行权限,能够执行。
./npu_monitor.sh # 自动找到 camera_test.py 进程检查一次
./npu_monitor.sh <PID> 30 # 对指定进程每 2 秒检查一次,共 30 次
也可以手动检查(把 <PID> 换成 camera_test.py 的进程号):
grep -o 'libQnnHtp[^ ]*\.so' /proc/<PID>/maps | sort -u # 应有 libQnnHtp.so + libQnnHtpV73Stub.so
ls -l /proc/<PID>/fd | grep adsprpc # 应有 /dev/adsprpc-smd-secure
注意:本平台上
cdsprpcd的 CPU 占用即使 NPU 满负荷也接近 0
(计算发生在 DSP 固件侧,不计入 ARM 进程),不能用它判断 NPU 是否工作。
4. 实测结果(本机实测,供参考)
单图 invoke 基准(benchmark_image.py,30 次):
- 平均 6.59 ms/次,最小 6.51 ms —— 纯推理上限约 150 FPS。
帧源实时模式(camera_test.py --source bus.jpg --frames 300,810x1080 帧,300 帧统计):
| 阶段 | 平均耗时 |
|---|---|
| 预处理 letterbox+归一化 (CPU) | 5.09 ms |
| NPU 推理 invoke | 7.18 ms |
| 后处理 置信度过滤+NMS (CPU) | 1.16 ms |
| 端到端单帧 | 13.43 ms ⇒ 约 74.5 FPS |
USB 摄像头实测(GEMBIRD AX2311 UVC 摄像头,/dev/video2,640x480,300 帧):
| 指标 | 数值 |
|---|---|
| NPU 推理 invoke | 7.9~8.2 ms(上限约 125 FPS) |
| 端到端单帧(预处理+推理+后处理) | 约 14.9 ms ⇒ 约 67 FPS |
| 系统整体帧率 | 约 8.2 FPS(受摄像头取流限制) |
结论:
- YOLO26l INT8 在 QCS8550 NPU 上纯推理约 6.6~8.2 ms(120~150 FPS),
串行流水线端到端约 67~75 FPS,检测管线本身远超实时需求。 - 最终整体帧率取决于摄像头:测试用的这款廉价 UVC 摄像头实际只能输出约
8 FPS(纯cap.read()不含任何处理也是 8.2 FPS,换 320x240 也一样,
驱动也未开放曝光调节),属于摄像头硬件/光线限制,与 NPU 无关。
换用能稳定输出 30 FPS 的摄像头,整体即可达到 30 FPS 满帧。
5. 常见问题
5.1 摄像头打不开 / 提示"未找到可用的 USB 摄像头"
/dev/video0、/dev/video1 是高通相机子系统节点(cam-req-mgr、cam_sync),
不是 USB 摄像头;/dev/video32/33 是视频解码器。UVC 摄像头插入后才会
出现新的 /dev/videoX。排查:
lsusb # 应能看到摄像头设备;若只有 root hub 说明系统没识别到
sudo dmesg -w # 保持运行,然后重新插拔摄像头,观察是否枚举
v4l2-ctl --list-devices # 查看 UVC 设备节点
若 lsusb 看不到设备:换一个 USB 口(优先蓝色 USB3 口)、确认摄像头在其他电脑上
可用、检查转接线/供电。识别到设备后用 v4l2-ctl --list-devices 找到节点编号,
再用 python3 camera_test.py --source <编号> 指定。
5.2 提示模型加载失败
确认模型文件存在且完整(约 26 MB):
ls -l models/QCS8550/W8A8/cutoff_yolo26l_qcs8550_w8a8.qnn236.ctx.bin
md5sum models/QCS8550/W8A8/cutoff_yolo26l_qcs8550_w8a8.qnn236.ctx.bin
5.3 如何对比 CPU 与 NPU 速度
把 camera_test.py 中 load_interpreter() 的
config.accelerate_type 改为 aidlite.AccelerateType.TYPE_CPU 再跑一次,
对比 invoke 耗时即可直观看到 NPU 的加速比(此时 NPU 验证会提示未生效,属预期)。
现在调用后端QNN是真的丝滑,就是自己需要调整一下iou的threshold 来调整NMS最小非近邻的值,来减少框框的数量,不然乱糟糟~
我写了帮助文档进去。可以用-h调用:
可以调大一些参数:
每次执行完成会有统计结果:
我执行的时候保存了视频。
可以后期作为检测结果检查回放。
如果有兴趣复现操作和学习用的朋友可以直接从我的github链接下载完整代码和说明文档。
Github仓库链接: https://github.com/yoyojacky/rhinox1_yolo26l_test










