【开源分享】【RDK X5】三模式视觉:YOLO、双目深度、人体年龄与 SPI 全屏切换

2026-09-11 更新:GS130WI 三模式触屏与局域网演示

现已在原 X5 工程中加入双目模组,SPI 触屏和 PC 浏览器使用同一页面。点击底部按钮切换算法,页面自动清空旧画面并加载新结果,其他已打开的浏览器同步切换。支持开机全屏与记住上次模式。

模式 实现 显示
检测 + 单目深度 双目左路接入原 YOLO26 检测、深度模型 左检测、右相对深度,不能当作米
双目深度 官方 StereoNet,320×256 子码流及对应 CameraInfo 左原图、右深度伪彩;测距标定仍待验收
人体 + 年龄 RDK 官方人体/人脸/头部/手部/关键点模型,加 face_age_detection 保留原人体消息,再按 track_id 合并年龄

PC 端随窗口等比例放大,双目不再上下排列;SPI 480×320 全屏保留三个触控按钮。局域网访问:http://<X5_IP>:8080/。

复现入口:三模式安装与排障 · 实机验收记录。本次代码提交:b13cd2f。

已有 Git checkout 的用户先保存自己的修改,更新后执行 bash scripts/setup_multimode.sh 并重启。首次安装、TROS 依赖、SPI 前置条件和旧 USB 方案恢复命令均在安装说明中;不要直接覆盖自己的目录或配置。

这次解决了什么

  • 相机与图像桥接常驻,切换只重启选定算法进程组,避免三套算法同时占用 BPU。修正切换时重建图像订阅后可能停帧的问题,连续六次切换与重启后复测通过;常规切换约 5–8 秒,冷启动还需等待相机初始化。
  • 人体模式此前 Python 逐字节校验 NV12,单核满载、显示约 1.4fps;改用 ROS 字节数组后,板端显示输出约 8–9fps。
  • 关闭 StereoNet 默认逐帧保存,改用模型尺寸子码流,绕开大图路径重复缩放内参;算法日志约 15fps、延迟约 78–84ms。以上不是 PC 实际呈现 FPS 或 SPI 物理刷新率。
  • 官方年龄节点会过滤掉人体框和关键点,不能拿它代替原人体消息;现在以人体消息为主进行合并。

尚未完成的验收

双目左右图像仍有明显垂直对齐偏差,当前距离读数暂不作测量依据。 已在页面提示,需要核对模组左右路与标定对应关系,并用标定板、已知距离重新验收。能出深度图不等于测距准确。

当前安装的官方人体与年龄模型没有返回性别,不会生成虚构的性别标签。年龄是模型估计,未做准确率评估;全身动作、多人遮挡及实际手指触控命中率仍需现场验收。


原 USB 双模型方案与代码架构(历史版本)

以下保留原 USB 摄像头方案、架构图和性能记录,不能把旧 USB 约 30fps 的结果套用到新的 MIPI / ROS 桥接路径。

大家好,这次把社区里的 S100P 双模型演示方案移植到了 RDK X5,并整理成一套适合学习、二次开发和展会通电展示的开源工程。

当前默认使用 YOLO26s 检测 + YOLO26s 深度,通过 X5 BPU 运行,USB 摄像头采集,浏览器展示检测框、深度伪彩和系统指标。已经在 480×320 SPI 屏上完成全屏显示、开机自动登录、自动运行和进程退出恢复验证。

项目源码:xuguosheng110/rdk-x5-yolo26-detect-depth

先感谢 Max.Ma 的 S100P 原始案例和上游开源项目。本仓库保留了带 MIT 许可证的 S100P 固定版本源码,模型和前后处理参考 D-Robotics 官方 X5 Model Zoo。本文重点分享 X5 实现的代码结构、移植差异和使用方法。

一、先看整体架构

整套程序分为三部分:

部分 代码入口 职责
Python 启动管理 scripts/run.py 读取模型预设、配置摄像头、加进程锁、启动 C++,正常退出时恢复摄像头设置
C++ 图像与推理 cpp/src/main.cpp 采集、检测、深度、合成、JPEG 编码、HTTP 服务
浏览器展示 web/index.html 解析 MJPEG、绘制 canvas、轮询指标、SPI 左右布局

实时推理是 C++ + libdnn。Python 不在逐帧推理循环中,浏览器也不调用模型;模型运行在板端,展示不依赖云端服务。

在 C++ 内部,主线程负责摄像头采集,检测、深度、合成和 JPEG 编码分别使用工作线程。两个模型共享 X5 BPU,各线程内部仍按“预处理 → 推理并等待 → 后处理”的顺序执行。

二、结合代码理解一帧图像

1. 从摄像头到模型

相机采集得到 BGR 图像,通过 FrameHub 发布最新帧、递增序号和发布时间。

  • 检测:等比例缩放、灰色 127 补边到 640×640,转换为 NV12。
  • 深度:等比例缩放、灰色 114 补边到 768×768,转换为 NV12。
  • X5 模型使用 一块连续的 Y+UV 输入内存。代码里的 Y/UV 平面视图不能理解为两个独立输入张量。

两个模型共用 DnnModel 的底层封装:分配张量内存 → 输入 cache clean → hbDNNInfer → hbDNNWaitTaskDone → 释放任务 → 输出 cache invalidate。

2. 两条工作线程怎么写

检测线程的关键代码结构如下,省略了跟踪与统计部分,完整实现见 det_worker 源码:

// 读取比上次更新的相机帧,允许跳过已经过时的帧
if (!hub.latest(seq, frame, 1.0, &captured)) continue;

DetResult r;
r.source_seq = seq;
r.captured = captured;
r.pre_ms = det.Preprocess(frame);
r.bpu_ms = det.Infer(args.prio_det, backend);
std::vector<tracking::SourceDetection> source_dets;
r.post_ms = det.Postprocess(
    frame.cols, frame.rows,
    (args.tracking ? std::min(args.score, args.track_low_thresh) : args.score),
    args.score, args.nms, r.dets, source_dets, r.cls_hist);
// 此处省略可选 person ByteTrack 和统计逻辑
store.set_det(r);

这段代码展示了“最新帧 → 预处理 → 推理 → 后处理 → 发布结果”的主线。深度线程采用相同组织方式,结果写到 ResultStore 的深度槽。

检测输出经过分类/LTRB 解码、逐类别 NMS 和坐标还原;深度输出对已校准 log-depth 取 exp,经分位数归一化生成伪彩与网格。默认 4×3 网格有 5×4 个交点,共 20 个相对读数,0 近、1 远,不能直接当作米。

3. 为什么视频帧率可以高于算法更新速度

FrameHub、ResultStore、CompositeSlot 和 SharedImage 都保存最新状态,没有为每个消费者累积完整视频队列。

例如合成线程已拿到相机第 100 帧,检测可能完成到第 99 帧,深度可能完成到第 97 帧。程序会使用最新可用结果继续显示,因此视频能保持连续,但快速运动时框或深度可能滞后。这是一项明确的延迟与算法更新速度取舍,并非每帧都同步执行完两项模型。

C++ 将原图与检测框、深度伪彩上下拼接,再交给独立线程编码 JPEG。浏览器 ?spi 分支从合成 JPEG 中取出两幅图,缩放为左右两栏。图像流里的 X-Source-Seq 用来统计不同相机源帧,避免用重复绘制冒充视频出帧。

三、X5 和 S100P 哪些地方不能直接照搬

项目 S100P 参考实现 本项目 X5 实现
BPU 模型平台 Nash Bayes-e
模型文件 HBM 官方 X5 NV12 BIN
推理与调度 hbDNNInferV2 / hbUCP hbDNNInfer / hbDNNWaitTaskDone
内存与缓存 hbUCP 相关接口 hbSysAllocCachedMem / hbSysFlushMem
NV12 输入 参考路径使用分离 Y/UV 一块连续 Y+UV,sysMem[0]
显示重点 Web + HDMI/KMS Web + SPI Firefox kiosk
模型选择 原案例的大模型配置 N/N、S/N、S/S 三档;默认双 S
深度单位 上游有尺度系数选项 当前只显示相对近远,拒绝非零 --depth-meters

模型不能只改扩展名就使用。当前 X5 实现要求匹配的输入布局和连续 float32 输出;更换模型必须同时检查 shape、输出顺序、量化形式及解码方式。完整说明见 X5 / S100P 移植差异。

四、X5 实测:分别看视频、算法和屏幕

测试环境:RDK X5 V1.0、RDK OS 3.5.0 / Ubuntu 22.04、内核 6.1.83、DNN 1.24.5 / HBRT 3.15.55,BPU 约 996MHz;USB 摄像头 640×480 MJPEG、临时手动 10ms 曝光,ByteTrack 关闭,SPI Firefox 展示运行。

检测 / 深度 BPU 平均占用 检测更新 深度更新 视频编码
YOLO26n / YOLO26n 约 70% 约 29.5fps 约 19.5fps 约 29.6fps
YOLO26s / YOLO26n 约 82% 约 22.3fps 约 18.7fps 约 29.6fps
YOLO26s / YOLO26s 83.5% 18.30fps 14.55fps 29.58fps

双 S 的最终稳定测量窗口为 64.2 秒,BPU P95 为 91%,有个别 100% 峰值。这些是该设备与场景下的观测结果,不是所有摄像头、散热和场景的最低性能保证。测量与验证摘要。

这里的约 29.6fps 是软件视频输出,不是 SPI 屏物理刷新率。 当前屏幕为 480×320 RGB565、40MHz SPI,全帧传输理论上限为:

40,000,000 / (480 × 320 × 16) ≈ 16.3 帧/秒

实际还要扣除命令传输和驱动开销,因此不能宣称当前 SPI 屏达到了物理全画面 30fps。HDMI 代码路径保留且可编译,但本次没有接 HDMI 屏进行验收。

另已完成 CTest 3/3,以及官方 bus.jpg 单图数值对照:高置信检测匹配 IoU 均大于 0.99999,深度 log 输出余弦相似度约 0.999875。这是实现一致性验证,不等同于数据集精度评估。

五、展会模式:通电自动登录、推理和全屏

启动分两条路径:系统服务启动推理;LightDM 自动登录 sunrise / Xfce,桌面启动项拉起用户级 kiosk 服务。浏览器脚本等待 X11 和本机 /stats.json 就绪后,再用独立 Firefox 配置全屏打开:

http://127.0.0.1:8080/?spi

展会模式关闭屏保与自动熄屏;模型和软件准备好后,展示不依赖外网。推理和浏览器均配置退出后自动重试,页面断流则自行重连。

已经做过实际重启与恢复测试:

  • 重启后无需手动操作,自动进入 480×320 全屏双模型展示。
  • 浏览器主进程 SIGKILL 后,约 16.2 秒恢复全屏。
  • 推理主进程 SIGTERM 后,约 10.4 秒恢复且已采集超过 60 帧。

3 秒是服务的重试间隔,实际恢复还需要模型加载与浏览器初始化。维护时使用 systemctl stop 保持停止,单纯关闭浏览器窗口会被自动拉起。

六、部署和学习入口

**前置条件:**X5 上已有对应 SPI 驱动、面板固件、背光、X11 桌面、LightDM/Xfce 和 Firefox;当前脚本默认 sunrise 用户、UID 1000、DISPLAY :0。安装脚本不安装 SPI 内核驱动,也不覆盖设备树,其他系统配置需要先适配。SPI 前置条件。

在 X5 上以 root 执行;以下用于首次部署到尚不存在的工程目录:

apt-get update
apt-get install -y git
mkdir -p /app
git clone https://github.com/xuguosheng110/rdk-x5-yolo26-detect-depth.git /app/rdk-x5-vision
cd /app/rdk-x5-vision
bash scripts/install.sh

安装过程会下载并 SHA256 校验四个官方模型、构建、执行测试,然后配置推理和桌面自启动。它会设置桌面自动登录和屏幕常亮,同名配置安装前会备份。

调试时可以临时切换组合,结束后恢复常驻服务:

cd /app/rdk-x5-vision
systemctl stop rdk-x5-vision
bash scripts/run.sh --preset balanced --max-seconds 20
systemctl start rdk-x5-vision

模型组合由 config/runtime.json 管理。已有服务若有显式模型参数的覆盖项,应先用 systemctl cat rdk-x5-vision 查看最终启动命令。其他摄像头不支持默认曝光控件时,可尝试 --keep-camera-settings 并复测帧率与图像效果。

局域网面板为 http://<X5_IP>:8080/,指标为 /stats.json。这是无用户认证的局域网演示服务,不应直接暴露到公网。

想学习或修改什么 从哪里开始
模型预设、摄像头和进程管理 config/runtime.json、scripts/run.py
BPU 模型与内存调用 main.cpp 的 DnnModel
检测和深度前后处理 DetectModel、DepthModel
多线程最新帧交换 FrameHub、ResultStore、CompositeSlot、SharedImage
检测框和 HUD DrawYoloBox()、DrawHud()、browser_overlay.cpp
SPI 标题和左右布局 web/index.html 的 spiMode canvas 分支
开机全屏与异常恢复 config/、setup_exhibition.sh、kiosk_session.sh

推荐阅读顺序:模型预设 → run.py → main() → FrameHub 与工作线程 → 模型前后处理 → Web 展示。仓库已提供带具体源码位置的架构讲解和学习与修改指南。

七、源码和参考资料

应用派生代码保留上游 MIT 许可证,官方 Python 对照实现保留 Apache-2.0 许可证;模型权重不打包进 Git,其许可随原提供方。欢迎交流其他摄像头和显示接口上的适配结果,也欢迎结合源码讨论 CPU 前后处理、延迟与显示刷新方面的改进。

1 个赞

大佬请问一下,您的相机用的什么型号?

自己选一个USB的通用sensor就可以了