news 2026/8/8 8:16:15

进迭时空 K1 开发板:Qt5 + YOLOv8 + SpacemiT NPU 实时目标检测全记录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
进迭时空 K1 开发板:Qt5 + YOLOv8 + SpacemiT NPU 实时目标检测全记录

一、项目概述

在进迭时空 SpacemiT K1 RISC-V 开发板上,实现了一个完整的Qt5 GUI + 摄像头实时采集 + YOLOv8 目标检测应用,并最终将推理后端从 OpenCV DNN CPU 推理切换到ONNX Runtime + SpacemiT NPU 硬件加速,推理性能获得显著提升。

技术栈

组件技术选型
芯片SpacemiT K1(8 核 RISC-V,融合 AI 架构)
操作系统Buildroot Linux(OpenHarmony 5.0 兼容)
GUI 框架Qt5 Widgets
摄像头采集GStreamer + SpacemiT CSI 驱动(NV12 输出)
图像处理OpenCV 4.8
AI 推理ONNX Runtime 1.18.1 + SpacemiT EP(NPU 加速)
检测模型YOLOv8n(80 类 COCO,640×640 输入)
交叉编译Docker (Ubuntu 24.04) + Buildroot Toolchain

最终效果

  • 摄像头 30fps 采集,每 30 帧执行一次 YOLOv8 推理
  • SpacemiT NPU 硬件加速推理
  • Qt5 Wayland 界面实时显示检测框和类别标签
  • 采集线程(CPU 0-3)与推理线程(CPU 6-7)完全隔离,系统稳定

二、开发环境搭建

2.1 硬件环境

  • 进迭时空 K1 开发板(RISC-V 8 核,2 TOPS 融合 AI 算力)
  • IMX415 CSI 摄像头
  • HDMI/DSI 显示屏

2.2 主机环境

  • Ubuntu 22.04 (x86_64)
  • Docker(用于交叉编译,因为 Buildroot Toolchain 需要 GLIBC 2.38+)
  • Buildroot SDK 2.2

2.3 关键路径

# Buildroot SDK ~/buildroot-sdk-2.2/output/mlk_k1_v2_defconfig/ # 交叉编译器 .../host/bin/riscv64-unknown-linux-gnu-g++ # sysroot(Qt5, OpenCV, ONNX Runtime 均在此) .../host/riscv64-buildroot-linux-gnu/sysroot/ # ONNX Runtime .../sysroot/usr/lib/libonnxruntime.so.1.18.1 .../sysroot/usr/include/onnxruntime_cxx_api.h # SpacemiT NPU EP .../sysroot/usr/lib/libspacemit_ep.so.1.2.3

三、原始代码分析

项目最初的实现使用OpenCV DNN作为推理后端,架构如下:

SpacemiT CSI Camera (NV12) → GStreamer spacemitsrc → NV12 → BGR (OpenCV cvtColor) → NV12 → BGR (OpenCV cvtColor) → cv::dnn::readNetFromONNX("yolov8n.onnx") → cv::dnn::blobFromImage → net.forward() → 后处理 + NMS → 画检测框 → QImage → Qt5 QLabel

原始代码关键片段

// cameraworker.cpp - 原始推理逻辑 cv::dnn::Net net = cv::dnn::readNetFromONNX(modelPath); // 每 90 帧推理一次 if (frameCount % 90 == 0 && !inferRunning) { inferRunning = true; cv::Mat bgrCopy = bgr.clone(); std::thread([this, &net, bgrCopy, pInfer]() { cv::Mat blob = cv::dnn::blobFromImage(letterbox, 1.0/255.0, cv::Size(640, 640), cv::Scalar(), true, false); net.setInput(blob); cv::Mat out = net.forward(); // ... 后处理 }).detach(); }

四、发现的问题

4.1 六个稳定性问题

#问题原因影响
1QObject 跨线程创建子对象信号槽默认 AutoConnection,跨线程时变成 DirectConnectionGUI 崩溃
2检测结果数据竞争推理线程写、GUI 线程读,无互斥保护随机崩溃
3ISP 资源冲突 SIGSEGVOpenCV DNN 与 GStreamer ISP 库冲突段错误
4推理异常未捕获ONNX 模型格式不匹配或内存不足时直接 crash进程退出
5模型路径无校验文件不存在时直接崩溃启动崩溃
6nice 导致推理饿死nice(5) 降低优先级,推理线程长时间无输出功能失效

4.2 性能问题

  • 推理只绑定 CPU 6-7 两个核,且nice(5)主动让出 CPU
  • 每 90 帧才推理一次(约 3 秒),目标移动快时框严重滞后
  • std::thread(...).detach()存在栈变量引用悬空风险
  • SpacemiT NPU 完全未使用,2 TOPS 算力浪费

五、解决方案

5.1 架构重设计

┌─────────────────────────────────────────────────┐ │ Widget (Qt5 GUI, 主线程) │ │ connect → 全部 QueuedConnection │ ├─────────────────────────────────────────────────┤ │ CameraWorker (采集线程, CPU 0-3) │ │ spacemitsrc → NV12 → BGR → emit frameReady │ │ → 每30帧发送给推理线程 │ ├─────────────────────────────────────────────────┤ │ inferenceLoop (推理线程, CPU 6-7) │ │ condition_variable 接收帧 │ │ YoloDetector::detect() → ONNX Runtime + NPU │ │ std::lock_guard<std::mutex> 写结果 │ └─────────────────────────────────────────────────┘

5.2 NPU 加速:找到正确的注册方式

这是整个项目最具挑战的部分。

第一步:发现 EP 库

# 开发板上 ls /usr/lib/libspacemit_ep.so # libspacemit_ep.so → libspacemit_ep.so.1 → libspacemit_ep.so.1.2.3

第二步:尝试 ORT 标准 API 失败

// 方式 1:AppendExecutionProvider(string) — 失败 opts.AppendExecutionProvider("SpaceMITExecutionProvider", {}); // 错误:Unknown provider name. Currently supported values are // 'OPENVINO', 'SNPE', 'XNNPACK', 'QNN', 'WEBNN' and 'AZURE'

第三步:从 .so 导出符号中找到真相

readelf -Ws libspacemit_ep.so | grep "FUNC.*GLOBAL" | grep -v "UND"
SpaceMITSharedProviderInit CreateSpaceMITSessionWrapper GetSpaceMITSharedProviderFactory OrtSessionOptionsSpaceMITEnvInit ← 关键!

第四步:查看官方头文件确认 API

cat spacemit_ort_env_c_api.h
ORT_EXPORT OrtStatus* ORT_API_CALL OrtSessionOptionsSpaceMITEnvInit( OrtSessionOptions* options, _In_reads_(num_keys) const char* const* provider_options_keys, _In_reads_(num_keys) const char* const* provider_options_values, size_t num_keys);

第五步:正确的 NPU 注册代码

bool YoloDetector::trySpacemiTEP(Ort::SessionOptions& opts) { void* handle = dlopen("libspacemit_ep.so", RTLD_NOW | RTLD_LOCAL); if (!handle) return false; // 查找官方 C API using RegFn = OrtStatus*(ORT_API_CALL*)( OrtSessionOptions*, const char* const*, const char* const*, size_t); auto fn = reinterpret_cast<RegFn>( dlsym(handle, "OrtSessionOptionsSpaceMITEnvInit")); if (!fn) { dlclose(handle); return false; } // 注册 NPU EP OrtStatus* status = fn(opts, nullptr, nullptr, 0); if (status == nullptr) { std::cout << "[INFO] NPU EP registered" << std::endl; return true; // 不 dlclose,运行时需要 } // 错误处理 const OrtApi* api = OrtGetApiBase()->GetApi(ORT_API_VERSION); api->ReleaseStatus(status); dlclose(handle); return false; }

5.3 六个崩溃的修复方案

崩溃 1:信号槽线程类型

// 修复前:默认 AutoConnection connect(thread_, &QThread::started, worker_, &CameraWorker::startCamera); // 修复后:显式 QueuedConnection connect(thread_, &QThread::started, worker_, &CameraWorker::startCamera, Qt::QueuedConnection);

崩溃 2:数据竞争

// 修复后:所有读写都加锁 std::mutex detMtx_; // 检测结果互斥锁 std::mutex inferMtx_; // 推理帧传递互斥锁 // 写 { std::lock_guard<std::mutex> lk(detMtx_); lastDets_ = std::move(boxes); } // 读 { std::lock_guard<std::mutex> lk(detMtx_); for (const auto& d : lastDets_) { /* 画框 */ } }

崩溃 3:ISP 资源隔离

// 采集线程绑定 CPU 0-3 cpu_set_t cpuset; for (int i = 0; i < 4; i++) CPU_SET(i, &cpuset); sched_setaffinity(0, sizeof(cpuset), &cpuset); // 推理线程绑定 CPU 6-7 cpu_set_t cs; CPU_SET(6, &cs); CPU_SET(7, &cs); sched_setaffinity(0, sizeof(cs), &cs); cv::setNumThreads(1);

崩溃 4:推理异常捕获

try { auto dets = detector_.detect(frame); // ... } catch (const Ort::Exception& e) { std::cerr << "[ERROR] ORT: " << e.what() << std::endl; } catch (const std::exception& e) { std::cerr << "[ERROR] Inference: " << e.what() << std::endl; }

崩溃 5:模型文件校验

std::ifstream f(modelPath); if (!f.good()) { std::cerr << "[ERROR] Model not found: " << modelPath << std::endl; return false; }

崩溃 6:推理线程饥饿

// 修复前:nice(5) + detach nice(5); std::thread([...](){ ... }).detach(); // 修复后:独立线程 + 条件变量 + 超时 std::thread inferThread_; std::condition_variable inferCv_; inferCv_.wait_for(lk, std::chrono::seconds(2), [this]{ return inferReady_ || !running_; });

六、代码

6.1 项目结构

yolo-qt5-cam/ ├── CMakeLists.txt # 构建配置 ├── Dockerfile # Docker 交叉编译环境 ├── toolchain-k1.cmake # RISC-V 工具链 ├── main.cpp # 入口 ├── widget.h / widget.cpp # Qt5 GUI ├── cameraworker.h / .cpp # 采集 + 推理调度 ├── yolodetector.h / .cpp # ONNX Runtime + SpacemiT NPU ├── models/ │ └── yolov8n.onnx └── build-rv/ └── YoloQt5Cam # RISC-V 二进制

7.2 编译命令

docker run --rm \

-v /home/uisrc/buildroot-sdk-2.2:/home/uisrc/buildroot-sdk-2.2 \

-v /home/uisrc/yolo-qt5-cam:/yolo-qt5-cam \

yolo-qt5-cam:latest \

export PATH=/home/uisrc/buildroot-sdk-2.2/output/mlk_k1_v2_defconfig/host/bin:\$PATH && \

cd /yolo-qt5-cam/build-rv && \

cmake .. -DCMAKE_TOOLCHAIN_FILE=../toolchain-k1.cmake -DCMAKE_BUILD_TYPE=Release && \

make -j\$(nproc)

7.3 部署

scp /home/uisrc/yolo-qt5-cam/build-rv/YoloQt5Cam root@192.168.137.2:/yolo-qt5-cam/build/

八、开发板运行

chmod +x /yolo-qt5-cam/build/YoloQt5Cam

export LD_LIBRARY_PATH=/usr/lib64:/lib

export QT_QPA_PLATFORM=wayland

export XDG_RUNTIME_DIR=/root

export WAYLAND_DISPLAY=wayland-1

/yolo-qt5-cam/build/YoloQt5Cm 2>&1 | \

grep -v "cam_wrn\|cam_not\|cam_inf\|cam_err\|ccic\|goodix\|usb 3-1"

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/8 8:15:43

AI大模型兼容性实战:从协议对齐到系统架构的避坑指南

1. 从“兼容”到“崩溃”&#xff1a;一个被低估的深水区如果你最近在折腾AI智能体&#xff08;Agent&#xff09;&#xff0c;特别是尝试让不同的大模型在你的应用里协同工作&#xff0c;那你大概率已经体会过什么叫“踩坑到崩溃”。这个标题——“AI大模型兼容&#xff0c;踩…

作者头像 李华
网站建设 2026/8/8 8:15:24

DMDIS 单机部署、数据清洗与周期增量同步实践

本文先介绍 DMDIS 的产品定位、主要作用和典型使用场景&#xff0c;再基于已完成的 DMDIS V5.2.0.1 实验环境&#xff0c;说明安装准备、服务验证、数据源配置、数据清洗、增量同步、作业调度和结果核验。 1. DMDIS 概述 1.1 DMDIS 定义 DMDIS 是达梦提供的专业实时数据集成软…

作者头像 李华
网站建设 2026/8/8 8:11:49

Claude 4.8和GPT 5.6哪个好?统一Prompt实测对比

先说结论 Claude 4.8在编程、中文、深度分析三个场景中领先&#xff0c;GPT 5.6在响应速度和格式规范上保持优势。综合差距0.2分&#xff0c;比上一代缩小了约50%。没有绝对的"更好"&#xff0c;只有"更适合"。 最近我在猪猪AI&#xff08;titiai.cn&…

作者头像 李华
网站建设 2026/8/8 8:09:56

知典小读 PRD 公开:一个 AI 名著解读小程序的产品设计思路

从需求到功能清单&#xff0c;我是怎么设计这款小程序的。做产品最怕的不是写代码&#xff0c;而是需求模糊。"我想做个读书小程序"——这句话背后可能有一百种理解。是微信读书那种正版阅读&#xff1f;是得到那种音频解读&#xff1f;还是小红书那种读书笔记社区&a…

作者头像 李华
网站建设 2026/8/8 8:09:51

CentOS7.9 离线部署 K8s v1.24 高可用集群【完整整合手册】

文章目录 CentOS7.9 离线部署 K8s v1.24 高可用集群【完整整合手册】 前置说明 二、离线包完整性校验(所有节点执行) 2.1 目录与文件大小校验 2.2 预生成本地离线YUM源(所有节点) 三、系统统一初始化(所有节点全量执行) 3.1 主机名与hosts解析 3.2 安全与交换分区关闭 3.…

作者头像 李华
网站建设 2026/8/8 8:05:28

Serilog结构化日志在.NET应用中的核心原理与生产实践指南

1. 为什么我们需要Serilog&#xff1a;从日志的“混沌”到“秩序” 如果你写过几年.NET程序&#xff0c;尤其是Web应用&#xff0c;肯定经历过日志的“混沌时期”。打开一个传统的日志文件&#xff0c;里面充斥着各种 Debug 、 Info 、 Error &#xff0c;信息像一团乱麻…

作者头像 李华