Quectel Pi M2 上手实战yolo案例

移远 Quectel Pi M2 上手:YOLO 目标检测/分割模型板端部署实战

系列说明:《移远 Quectel Pi M2 从入门到实战》系列。
资料来源:本文所有步骤、参数均来自移远官方文档中心。
标注 【本机实测 2026-10-15】 的内容为作者实测记录(Debian 13,USB 摄像头 1280×720)。



一、YOLO 与本篇的两条部署路线

1. YOLO 是什么

YOLO(You Only Look Once,只看一眼)是一种基于深度卷积神经网络(CNN)的实时目标检测算法,2016 年由 Joseph Redmon 等人提出,开创了"单阶段(one-stage)目标检测"的先河。

传统目标检测算法(如 R-CNN 系列)分两步:先"找候选区域",再"对每个区域分类",速度慢。而 YOLO 的核心思想是:把整张图片一次性送入网络,在一次前向推理中同时输出:

  • 图像中每个目标的类别(如 person、bus、car…)
  • 每个目标的边框位置(bounding box)
  • 每个目标属于该类别的置信度(confidence)

2. 检测 vs 分割:输出差在哪

任务 输出 精度
目标检测(YOLOv8) 类别 + 矩形框 边界是矩形框
实例分割(YOLOv8-seg) 类别 + 矩形框 + 像素掩膜 边界贴合目标轮廓

检测给"框",分割给"抠图"——分割输出的每个目标都带一张像素级掩膜,轮廓精准贴合目标边缘。

3. 两条部署路线怎么选

路线 模型 推理硬件 框架 实测速度 适用
A:目标检测 YOLOv5s(COCO 80 类) NPU(6 TOPS) RKNN 运行时 v2.3.2 单帧约 19 ms(约 50 FPS) 实时检测、项目落地
B:实例分割 yolo11n-seg(COCO 80 类,约 11.7 MB) CPU(模拟推理) ONNX Runtime + OpenCV 单张约 0.5 s 功能验证、算法调试

:pushpin: 什么是"模拟推理(Simulated Inference)":不依赖 NPU/VPU 等专用硬件加速器,直接在 CPU 上用通用推理框架(如 ONNX Runtime)完成神经网络的前向计算。优点是通用性强、无需特定硬件 SDK、部署简单;缺点是算力利用率不如 NPU,速度较慢。官方定位很明确:在真实硬件加速(NPU)方案确定之前,先验证算法与效果。


二、路线 A:单张图片检测(开箱即用)

1. 板载资源:什么都不用下载

M2 板 Debian 系统已预置以下资源,开箱即用:

资源 路径
NPU 推理运行时 /usr/lib/librknnrt.so(v2.3.2)
YOLOv5 检测程序 /usr/bin/rknn_yolov5_demo
YOLOv5s 模型(COCO 80 类) /usr/share/model/yolov5/RK3576/yolov5s-640-640.rknn
类别标签文件 /usr/share/model/yolov5/model/coco_80_labels_list.txt
测试图片 /usr/share/model/yolov5/model/bus.jpg

硬件要求只有两条:Quectel Pi M2 开发板(RK3576,板载 6 TOPS NPU),以及 USB 数据线连接 PC(ADB 调试)。先确认连接:

adb devices          # 确认设备已连接(显示 device 状态)

:light_bulb: 【本机实测 2026-10-15】 除了 ADB,SSH 也能执行本节全部命令:ssh chen@192.168.1.108 登录后直接跑,效果与 ADB 完全一致。

2. 三步跑通

第 1 步:准备测试图片。 板载已有一张 COCO 标准测试图 bus.jpg(公交车 + 行人场景):

adb shell "ls /usr/share/model/yolov5/model/bus.jpg"

第 2 步:执行检测。 注意必须先进入模型目录——demo 需要在此目录下找到 model/coco_80_labels_list.txt:

adb shell "cd /usr/share/model/yolov5 && rknn_yolov5_demo \
  /usr/share/model/yolov5/RK3576/yolov5s-640-640.rknn \
  model/bus.jpg resize /tmp/bus_detected.jpg"

rknn_yolov5_demo 的四个位置参数:

参数 说明
第 1 个 RKNN 模型路径
第 2 个 输入图片路径
第 3 个 缩放方式:resize(拉伸)或 letterbox(等比留边)
第 4 个 输出图片路径

第 3 步:查看结果。

adb shell "ls -la /tmp/bus_detected.jpg"        # 确认检测结果文件生成
adb pull /tmp/bus_detected.jpg ./               # 拉回 PC 查看

3. 运行输出解读

官方文档给出的完整输出(原样引用):

post process config: box_conf_threshold = 0.25, nms_threshold = 0.45
sdk version: 2.3.2 (429f97ae6b@2025-04-09T09:09:27) driver version: 0.9.8
model input height=640, width=640, channel=3
Read model/bus.jpg ...
once run use 24.300000 ms
person @ (209 243 286 510) 0.879723
person @ (479 238 560 526) 0.870588
person @ (109 237 232 534) 0.828112
bus @ (93 129 553 464) 0.700761
person @ (79 353 122 517) 0.307297
save detect result to /tmp/bus_detected.jpg
loop count = 10 , average run  19.313700 ms

几个值得注意的点:

  • 识别出 4 个 person 和 1 个 bus,单帧推理约 19 ms(约 50 FPS)——这是官方文档的结论;
  • once run use 24.300000 ms 是首帧耗时;后面 loop count = 10 , average run 19.313700 ms 是连续 10 次取平均,更能代表稳态性能;
  • 第一行是后处理配置:置信度阈值 box_conf_threshold = 0.25、NMS 阈值 nms_threshold = 0.45;
  • sdk version: 2.3.2 对应板载 librknnrt.so 的版本,driver version: 0.9.8 对应 NPU 驱动版本,与官方资源表一致。

:pushpin: 两组性能数据别混淆:《YOLO板端部署目标检测》实测 demo 单帧约 19 ms(约 50 FPS),包含读图与后处理;《NPU使用指南》中 rknn_benchmark 实测 yolov5s(640×640)平均 17.37 ms / 57.6 FPS,是纯推理基准。两处均为官方数据,只是口径不同。

:white_check_mark: 【本机实测 2026-10-15】 在 M2 板上原样复跑上述命令(NPU 定频 950MHz),输出与官方文档完全一致:person @ (209 243 286 510) 0.879723 等五个目标逐一复现,loop count = 10 , average run 19.433000 ms,与官方 19.31 ms 基本持平。

4. 检测自己的图片

# 1. 把图片推送到板子
adb push my_photo.jpg /tmp/

# 2. 执行检测(注意要在模型目录运行)
adb shell "cd /usr/share/model/yolov5 && rknn_yolov5_demo \
  /usr/share/model/yolov5/RK3576/yolov5s-640-640.rknn \
  /tmp/my_photo.jpg resize /tmp/my_photo_detected.jpg"

# 3. 拉回结果查看
adb pull /tmp/my_photo_detected.jpg ./

:pushpin: 官方提示:若图片不是 640×640,demo 会自动缩放;resize 会拉伸变形,letterbox 保持宽高比(推荐)。


三、路线 A 进阶:摄像头/视频实时检测

视频检测流程:视频解码 → 每帧 NPU 推理 → 画框后实时送显。板端由 MPP 硬解 + NPU 逐帧推理,检测画面直接输出到屏幕,无需抽帧落盘。

这一节给两条实现路线:官方交叉编译 demo(视频文件 + 屏幕送显)与 Python + RKNNLite 免编译方案(USB 摄像头直连 + 屏幕窗口/网页流,本机实测)。

路线 A-1:官方交叉编译 demo(视频文件 + 送显)

前置条件(官方列出三条,缺一不可):

  • 板端 Debian 系统已进入 GNOME 桌面(Wayland 会话已登录)——检测画面要显示在屏幕上;
  • 主机已安装 adb,adb devices 可看到设备;
  • 已获取本 SDK 源码(含 external/rknpu2 与 buildroot 输出,用于编译 demo 与取运行库)。

与单图检测不同,视频 demo 板端没有预置,需要在主机上交叉编译。

第 1 步:主机交叉编译实时检测 demo

# SDK 根目录
export SDK=/path/to/RK3576_Linux6.1_SDK
cd $SDK/external/rknpu2/examples/rknn_yolov5_demo

# 1) 指定交叉工具链与 buildroot sysroot(提供 gstreamer 头文件)
export GCC_COMPILER=$SDK/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu
export PKG_CONFIG_PATH=$SDK/buildroot/output/rockchip_rk3576/host/aarch64-buildroot-linux-gnu/sysroot/usr/lib/pkgconfig
export PKG_CONFIG_SYSROOT_DIR=$SDK/buildroot/output/rockchip_rk3576/host/aarch64-buildroot-linux-gnu/sysroot

# 2) 编译并安装到 install/(带 --display 实时显示的 video demo)
bash build-linux.sh -t rk3576 -a aarch64

# 3) 产物目录 install/rknn_yolov5_demo_Linux/
#      rknn_yolov5_video_demo    实时检测 demo(本流程使用)
#      lib/                      板端运行库:librknnrt.so / librga.so / librockchip_mpp.so / libmk_api.so
#      model/RK3576/yolov5s-640-640.rknn + model/coco_80_labels_list.txt

第 2 步:部署到板端

cd $SDK/external/rknpu2/examples/rknn_yolov5_demo/install/rknn_yolov5_demo_Linux

adb shell "mkdir -p /data/local/tmp/yolo/lib /data/local/tmp/yolo/model /data/local/tmp/yolo/videos"
adb push rknn_yolov5_video_demo /data/local/tmp/yolo/
adb push lib/. /data/local/tmp/yolo/lib/
adb push model/coco_80_labels_list.txt model/RK3576/yolov5s-640-640.rknn /data/local/tmp/yolo/model/
adb shell "chmod +x /data/local/tmp/yolo/rknn_yolov5_video_demo"

第 3 步:准备视频(转 h264 裸流)

demo 只吃 h264 裸流,先用板端 gstreamer 转码(注意:此步不要设 LD_LIBRARY_PATH):

adb push my_video.mp4 /data/local/tmp/yolo/videos/
adb shell "cd /data/local/tmp/yolo/videos && gst-launch-1.0 filesrc location=my_video.mp4 ! qtdemux ! h264parse ! video/x-h264,stream-format=byte-stream ! filesink location=my_video.h264"

第 4 步:运行实时检测

# 自动探测当前图形会话的 Wayland socket(不写死用户名/UID)
adb shell 'cd /data/local/tmp/yolo && \
  export LD_LIBRARY_PATH=$PWD/lib && \
  export XDG_RUNTIME_DIR=$(dirname $(ls /run/user/*/wayland-* | head -1)) \
         WAYLAND_DISPLAY=$(basename $(ls /run/user/*/wayland-* | head -1)) && \
  ./rknn_yolov5_video_demo model/yolov5s-640-640.rknn videos/my_video.h264 264 --display'

官方对参数与行为的说明,归纳如下:

  • 第 1 个参数为 RKNN 模型路径,第 2 个为 h264 视频,264 表示 h264 编码,--display 实时送显(--noshow 关闭显示、--save 另存 out.h264);
  • 检测画面即屏幕实际显示:红框 + 类别名;
  • 需要循环播放时可将命令包在 while true; do ... ; done 中;
  • 若屏幕不出画面,追加 export GST_PLUGIN_PATH=/usr/lib/aarch64-linux-gnu/gstreamer-1.0 再试。

官方文档还附了一段板端实拍演示视频(YOLOv5s NPU 逐帧推理 + 屏幕实时显示,源视频 combined_qbox.mp4,1280×720),可到文档中心查看。

路线 A-2:Python + RKNNLite 直连摄像头(免交叉编译)【本机实测 2026-10-15】

交叉编译路线效果好,但 SDK 体积大、工具链门槛高。如果只是想让 USB 摄像头的实时画面跑上 NPU,用 Python 就够了——本节是作者在板上的完整实测记录,从零到出画面约 20 分钟。

1. 板端环境:绕开 Python 版本坑

板载 Debian 13 自带 Python 3.13,而 RKNN 的板端推理包 rknn-toolkit-lite2 目前(2026-10)PyPI 只发布到 cp312——直接 pip install 会报"找不到匹配版本"。解决办法是用 Miniconda 装一个 Python 3.12 环境,与系统 Python 共存:

# 1) Miniconda(国内建议清华镜像;GitHub 直连在板子上不通)
curl -fsSL -o /tmp/miniconda.sh https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-latest-Linux-aarch64.sh
bash /tmp/miniconda.sh -b -p $HOME/miniconda3

# 2) 建 py3.12 环境(conda 建环境时加 --override-channels 避开需要接受 ToS 的官方源)
$HOME/miniconda3/bin/conda create -y -n yolo --override-channels \
  -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ python=3.12

# 3) 装包(注意版本与板载运行时对齐:librknnrt.so 是 v2.3.2)
$HOME/miniconda3/envs/yolo/bin/pip install -i https://pypi.tuna.tsinghua.edu.cn/simple \
  rknn-toolkit-lite2==2.3.2 opencv-python numpy

:pushpin: rknn-toolkit-lite2 版本要与板载 librknnrt.so 对齐(都是 2.3.2),避免运行时模型版本错配。确认运行时版本:strings /usr/lib/librknnrt.so | grep -m1 "librknnrt version" 或跑一次推理看日志。

2. 实时检测脚本要点

核心流程四十来行就够了,关键步骤:

from rknnlite.api import RKNNLite
import cv2, numpy as np

rknn = RKNNLite()
rknn.load_rknn('/usr/share/model/yolov5/RK3576/yolov5s-640-640.rknn')
rknn.init_runtime()                      # NPU 初始化

cap = cv2.VideoCapture('/dev/video73', cv2.CAP_V4L2)   # USB 摄像头(UVC)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)

while True:
    ok, frame = cap.read()
    small = letterbox(frame, 640)                     # 等比缩放补边到 640×640
    rgb = cv2.cvtColor(small, cv2.COLOR_BGR2RGB)
    outs = rknn.inference(inputs=[np.expand_dims(rgb, 0)])   # uint8 NHWC 直接喂
    dets = post_process(outs)                          # 解码 + NMS,画框送显/推流

最容易踩的坑:输出解码。 这颗 yolov5s RKNN 模型的输出是 3 个 (1, 255, 80/40/20, 80/40/20) 的 float32 张量,正确姿势是:

  • 先 reshape(3, 85, h, w) 再转 (3, h, w, 85)(NCHW → 按锚框分组);

  • 模型图内已经做了 Sigmoid(输出值域就是 [0,1]),后处理直接 x*2-0.5+grid 解码即可,千万别再套一层 Sigmoid——否则置信度全部压到 0.5 左右、框明显偏移,这是最隐蔽的坑;

  • 校验方法:拿板载 bus.jpg 跑一遍,输出应与官方 demo 完全一致(4 person + 1 bus,person @ (209 243 286 510) 0.880)。本机按此校准后逐框对齐官方结果。

3. 两种实时输出:屏幕窗口 + 网页流

  • 屏幕窗口:cv2.imshow()。板子桌面是 Wayland(GNOME),从 SSH 启动的程序要补三个环境变量走 XWayland,否则窗口弹不出来:

    export DISPLAY=:0
    export XAUTHORITY=$(ls /run/user/$(id -u)/.mutter-Xwaylandauth* | head -1)
    export QT_QPA_PLATFORM=xcb
    
  • 网页流:检测帧用 cv2.imencode('.jpg', ...) 编码后以 multipart/x-mixed-replace 推流,PC 浏览器直接打开 http://<板子IP>:8000 就能看,适合演示和录屏。

4. 实测数据

项目 数值 说明
NPU 单帧推理 ~27 ms Python + RKNNLite,含前后处理;纯推理口径见下行对照
端到端帧率 12~15 FPS 720p USB 摄像头抓帧 + 推理 + 画框 + 编码推流全链路
官方 C demo 对照 19.4 ms / ~50 FPS 纯推理(含读图后处理),Python 版差距主要在前后处理
内存占用 常驻 ~500 MB 板载 4GB,GNOME 桌面同时在线无压力

:pushpin: 摄像头选择:UVC USB 摄像头(/dev/video*,OpenCV V4L2 后端直读)最省事;MIPI 摄像头走 RKISP,需要 GStreamer 管道(v4l2src ! video/x-raw,format=NV12 ! videoconvert ! appsink),且 OpenCV 需带 GStreamer 支持。本机实测时 MIPI 座子未插摄像头(dmesg 提示 imx219 ... failed to read chip id),故用 USB 摄像头完成全部实测。


四、路线 B:分割模型 CPU 模拟推理

1. 分割模型原理一分钟

YOLO 分割模型(YOLOv8-seg / YOLO11-seg)有两个输出头:

输出1(检测头): [1, 116, 8400]
                  ├─ 前 4 行:框中心 (cx,cy) 与宽高 (w,h)
                  ├─ 中间 80 行:80 个类别的置信度
                  └─ 后 32 行:每个候选的掩膜系数(mask coefficients)
输出2(原型掩膜):[1, 32, 160, 160]
                  32 张 160×160 的原型掩膜(prototype masks)

掩膜重建公式:

mask = Sigmoid( 掩膜系数(32) × 原型掩膜(32×160×160) )   → 160×160

即用每个目标的 32 个掩膜系数对 32 张原型掩膜做线性组合,再经 Sigmoid 激活与阈值化,得到该目标的二值掩膜,最后缩放到检测框尺寸。

:pushpin: 出处提醒:官方文档"分割效果演示"一节展示的输入图/掩膜效果图,来自 RK3576(Buildroot 系统)板端的 NPU 人像分割 Demo(输入 513×513 原图 → 输出像素级掩膜),用来说明分割输出的形态;而下面部署的 /opt/yolo demo 是 CPU 模拟推理版本。两处出处不同,看图理解效果即可,别当成同一个 demo。

2. 环境准备(M2 板端)

# 设备自带 Python 3,安装 OpenCV 与 numpy
sudo apt update && sudo apt install -y python3-opencv python3-numpy

# 安装 onnxruntime(aarch64)
pip3 install onnxruntime

若 pip 安装 onnxruntime 失败,可在宿主机(x86_64)下载 aarch64 wheel 后推送:
adb push onnxruntime.whl /opt/yolo/ && adb shell "cd /opt/yolo && python3 -m zipfile -e onnxruntime.whl /opt/yolo/ort"

3. 模型下载与目录结构

设备端目录结构:

/opt/yolo/
├── yolov8n-seg.onnx      # 分割模型(yolo11n-seg,约 11.7 MB)
├── yolo_seg.py           # 分割 Demo 脚本
├── ort/                  # 兼容版 onnxruntime(CPU 模拟推理用)
└── test.jpg              # 测试图片

在可联网宿主机下载后推送:

curl -sL -o yolov8n-seg.onnx \
  https://github.com/ultralytics/assets/releases/download/v8.3.0/yolo11n-seg.onnx
adb push yolov8n-seg.onnx /opt/yolo/

:pushpin: 一个容易困惑的命名细节:文件名叫 yolov8n-seg.onnx,实际下载的是 yolo11n-seg(YOLO11 的 nano 分割模型,COCO 80 类,约 11.7 MB)。官方目录注释里写明了这层对应关系,脚本按文件名引用,照抄即可。

4. 运行与性能

adb shell "cd /opt/yolo && python3 yolo_seg.py test.jpg"

输出 result.jpg(原图 + 分割掩膜叠加效果),效果即"分割模型原理"一节所说的像素级抠图。

实测性能(官方数据):

项目 数值
推理方式 CPU(ONNX Runtime)
单张耗时 ~0.5 s
模型 yolo11n-seg(COCO 80 类)

如需加速,官方指路:可将分割模型转换为 RKNN 格式跑 NPU 推理,参考《NPU使用指南》。


五、推理变慢?先给 NPU 定频

《YOLO板端部署目标检测》和《NPU使用指南》两章的常见问题表里,"推理慢"都指向同一个原因:NPU 未定频。官方给出的解决命令(root 权限,跑模型前推荐执行):

# root 权限,NPU 固定最高频率
echo userspace > /sys/class/devfreq/27700000.npu/governor
echo 950000000 > /sys/class/devfreq/27700000.npu/userspace/set_freq

:light_bulb: 【本机实测 2026-10-15】 以 SSH 执行时用 echo "你的密码" | sudo -S sh -c "..." 一条 shell 包住两行;定频在重启后失效,长跑前重设一次即可。定频后 cat /sys/class/devfreq/27700000.npu/cur_freq 应显示 950000000。

作为参照,官方《NPU使用指南》给出了 RK3576 NPU 的 rknn_benchmark 实测成绩:

模型 输入尺寸 平均耗时 FPS 类型
resnet18 224x224 3.50ms 285 图像分类
resnet152 224x224 19.35ms 51.7 图像分类
yolov5s 640x640 17.37ms 57.6 目标检测
yolo11s 640x640 33.24ms 30.1 目标检测

官方结论:yolov5s 的 57.6 FPS > 30 FPS(视频帧率),说明 RK3576 可以实时做视频目标检测。


六、常见问题与换模型

路线 A(NPU 目标检测)常见问题

问题 原因 解决
Segmentation fault 工作目录不对,找不到 ./model/coco_80_labels_list.txt 先 cd /usr/share/model/yolov5 再运行
检测不到目标 置信度阈值过高 修改 postprocess.h 中 BOX_THRESH(默认 0.25)
误检(如猴子识别成 bird) COCO 数据集无 monkey 类 换自定义模型或调阈值
推理慢 NPU 未定频 设置 NPU 最高频率 950MHz(见《NPU使用指南》)

路线 A-2(Python 实时检测)踩坑实录【本机实测】

问题 原因 解决
pip install rknn-toolkit-lite2 找不到版本 板载 Python 3.13,官方轮子只到 cp312 Miniconda 建 Python 3.12 环境
GitHub 下载不通(Miniconda/micromamba 等) 板端访问 GitHub 不稳定 全部改走清华 TUNA 镜像(conda 源 + pip 源)
检测框错乱、置信度全在 0.5 左右 后处理对模型输出重复做 Sigmoid(模型图内已做) 输出 reshape 成 (3,h,w,85) 后直接解码,校准到 bus.jpg 官方结果
SSH 启动后屏幕不弹窗口 Wayland 会话下无 DISPLAY DISPLAY=:0 + XAUTHORITY(mutter-Xwaylandauth)+ QT_QPA_PLATFORM=xcb
NPU 定频命令权限不够 普通用户写不了 devfreq sudo 执行;重启后失效需重设
板子运行中突然重启(NPU 满载时) 供电不足 官方要求 5V/5A 以上适配器,换官方电源与 5A 线

路线 B(分割模拟推理)常见问题

问题 解决
onnxruntime 安装失败 用宿主机下载 aarch64 wheel 推送(见第四节)
ImportError: no module named cv2 sudo apt install python3-opencv
推理慢 CPU 模拟推理正常现象;需要速度请转 RKNN 走 NPU

换自己的模型

如需换其他 YOLO 模型(如自定义训练的模型),官方给出三步:

  1. 用 rknn-toolkit2 将模型转换为 RK3576 版 .rknn(模型转换详细流程见《NPU使用指南》);
  2. 替换模型文件与 labels 文件;
  3. 重新运行检测命令。

写在最后

本篇在 M2 上跑通了三类视觉任务:NPU 目标检测单图(YOLOv5s,一条命令,官方与实测一致:约 19 ms / 约 50 FPS)、摄像头实时检测(官方交叉编译 demo 走 MPP 硬解 + 屏幕送显;以及本机实测的 Python + RKNNLite 免编译方案:NPU 单帧 ~27 ms,端到端 12~15 FPS,屏幕窗口和网页流双输出)和 CPU 模拟推理分割(yolo11n-seg,单张约 0.5 s,适合先验证效果再转 NPU)。三条关键经验:检测命令必须在模型目录下运行;缩放方式优先 letterbox;推理慢先给 NPU 定频到 950 MHz。


参考资料与延伸阅读

1 个赞