HarmonyOS 鸿蒙Next中物体识别问题难题求助

HarmonyOS 鸿蒙Next中物体识别问题难题求助 需求描述: 我在做一个盲人用的环境识别及引导功能,需要结合物体识别与 深度测量数据实现;

问题描述:

在验证 yoloV8n .ms 目标识别+ARengine深度数据 方案;碰到如下问题:

  1. NPU无法运行,CPU能运行,但识别成功率极低(不靠谱);卡壳了

  2. yolov8n.pt 转 yolov8n.onnx 转 yolov8n.ms 经过三天一番折腾,

  3. yolov8n.pt在windows 测试图片 识别成功,并提供位置框;

已验证的两种方案,识别效果太差,希望提供一下解决方案。

方案1 . MindSpore Lite实现图像分类(ArkTS) mobilenetv2 ----已放弃 物体识别准确度太低 ,完全错误;远低于 小艺看世界 的物体识别效果;所用相关例程:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/mindspore-guidelines-based-js物体识别问题难题求助/HarmonyOS_Samples/guide-snippets/tree/master/MindSporeLiteKit/MindSporeLiteArkTSDemo

方案2. 系统自带的 core vision kit 识别类别太少,识别准确度极低,未采用。----已放弃


更多关于HarmonyOS 鸿蒙Next中物体识别问题难题求助的实战教程也可以访问 https://www.itying.com/category-93-b0.html

6 回复

尊敬的开发者,您好,因为有的算子在NPU上目前无法支持,模型中某些算子在NPU上存在兼容性限制,会把整图或者一部分子图回退到CPU上执行,另外模型中某些算子的数据类型在NPU上也存在兼容性限制,而且模型如果是动态shape一些算子在NPU上也会回退。
具体支持的算子列表参考昇思大模型开源社区文档:Lite算子支持
关于您所属参考 MindSpore Lite实现图像分类(ArkTS)物体识别准确度太低,Core Vision Kit识别类别太少,识别准确度极低,麻烦您提供如下信息:
1、麻烦您提供下能复现问题的最小demo(描述下具体实现步骤);
2、版本信息(如:IDE版本信息、手机系统版本信息);
3、麻烦您提供下完整的日志信息;
4、麻烦您详细描述下您所需求识别哪些物品,具体需要哪些物品类别;

更多关于HarmonyOS 鸿蒙Next中物体识别问题难题求助的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


我也用AREngine做App,感觉你可以用AREngine的距离检测(深度估计https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arengine-get-depth-conversion)了做一定的优化,同时结合

环境Mesh识别介绍https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arengine-get-mesh-conversion

做出智能化地提醒。

import { arEngine, ARView, arViewController } from '@kit.AREngine';
import { Node, Scene } from '@kit.ArkGraphics3D';
import { BusinessError } from '@kit.BasicServicesKit';
let centerDistance: number;
let centerConfidence: number;

@Builder
export function ARDepthBuilder(): void {
  ARDepth();
}

@Component
struct ARDepth {
  private delayInterval: number = 33;
  private intervalId: number = -1;
  @State arContext?: arViewController.ARViewContext = undefined;
  @State depthConfidence: number = 0;
  @State depthDistance: string = '0';

  build(): void {
    NavDestination() {
      RelativeContainer() {
        if (this.arContext) {
          ARView({ context: this.arContext })
            .height('100%')
            .width('100%')
            .alignRules({
              center: { anchor: '__container__', align: VerticalAlign.Center },
              middle: { anchor: '__container__', align: HorizontalAlign.Center }
            })

          // 在屏幕上显示中心点、深度估计值及置信度
          Text('●')
            .fontSize(8)
            .fontColor(Color.Red)
            .alignRules({
              center: { anchor: '__container__', align: VerticalAlign.Center },
              middle: { anchor: '__container__', align: HorizontalAlign.Center }
            })

          Column() {
            Text(`${this.depthDistance} | ${this.depthConfidence}`)
              .fontColor(Color.Yellow)
              .fontSize(24)
              .textShadow({
                radius: 10,
                color: Color.Black,
                offsetX: 0,
                offsetY: 0
              })
          }
          .alignItems(HorizontalAlign.Center)
          .margin({ bottom: 10 })
          .alignRules({
            bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
            middle: { anchor: '__container__', align: HorizontalAlign.Center }
          })
        }
      }
    }
    .onAppear(() => {
      this.initARView();
      this.renderDepthMsg();
    })
    .onWillAppear(() => {
      this.stopARView();
    })
    .onShown(() => {
      this.resumeARView();
    })
    .onHidden(() => {
      this.pauseARView();
    })
    .hideTitleBar(true)
    .hideBackButton(true)
    .hideToolBar(true)
  }

  private initARView(): void {
    Scene.load().then((scene: Scene) => {
      let viewContext: arViewController.ARViewContext = new arViewController.ARViewContext();
      viewContext.scene = scene;
      viewContext.callback = new ARViewCallbackImpl();
      viewContext.config = {
        type: arEngine.ARType.WORLD,
        planeFindingMode: arEngine.ARPlaneFindingMode.HORIZONTAL_AND_VERTICAL,
        powerMode: arEngine.ARPowerMode.NORMAL,
        semanticMode: arEngine.ARSemanticMode.NONE,
        poseMode: arEngine.ARPoseMode.GRAVITY,
        depthMode: arEngine.ARDepthMode.AUTOMATIC,
        meshMode: arEngine.ARMeshMode.DISABLED,
        focusMode: arEngine.ARFocusMode.AUTO
      };
      viewContext.init().then(() => {
        this.arContext = viewContext;
        console.info('Succeeded in initializing ARView.');
      }).catch((err: BusinessError) => {
        console.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}`);
      });
    });
  }

  private renderDepthMsg(): void {
    this.intervalId = setInterval(() => {
      if (centerDistance === undefined || centerConfidence === undefined) {
        return;
      }
      this.depthDistance = centerDistance.toFixed(4);
      this.depthConfidence = centerConfidence;
    }, this.delayInterval);
  }

  private stopARView(): void {
    if (!this.arContext) {
      return;
    }
    try {
      clearInterval(this.intervalId);
      this.arContext.destroy();
      centerDistance = 0;
      centerConfidence = 0;
    } catch (error) {
      const err: BusinessError = error as BusinessError;
      console.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`);
    }
  }

  private resumeARView(): void {
    // ...
  }
  private pauseARView(): void {
    // ...
  }
}
class ARViewCallbackImpl extends arViewController.ARViewCallback {
  onAnchorAdd(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void {
    // ...
  }

  onAnchorUpdate(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void {
    // ...
  }

  onFrameUpdate(ctx: arViewController.ARViewContext, sysBootTs: number): void {
    if (!ctx.session) {
      return;
    }

    let session: arEngine.ARSession | undefined = ctx.session;

    try {
      let frame: arEngine.ARFrame = session.getFrame();
      let depthImage: arEngine.ARImage = frame.acquireDepthImage16Bits();
      let confidenceImage: arEngine.ARImage = frame.acquireDepthConfidenceImage();
      let depthPlane: number[] = arrayBufferInt32ToNumber(depthImage.planes[0].buffer);
      let confidencePlane: number[] = arrayBufferInt32ToNumber(confidenceImage.planes[0].buffer);
      const index: number = depthImage.height * depthImage.width / 2 + depthImage.width / 2;

      centerDistance = depthPlane[index] / 1000;
      centerConfidence = confidencePlane[index];
    } catch (error) {
      const err: BusinessError = error as BusinessError;
      console.error(`Failed to acquire depth information. Code is ${err.code}, message is ${err.message}`);
    }
  }
}

从你的描述来看,其实你已经踩中了很多做视觉项目的人都会遇到的坑:

问题不在于 AREngine,也不在于深度数据,而是在“目标检测模型部署”这一层。

先说结论

对于盲人环境识别这种场景:

摄像头
  ↓
目标检测(YOLO)
  ↓
目标框
  ↓
深度测距(AR Engine)
  ↓
语义决策
  ↓
语音播报

其中最关键的是:

目标检测准确率

而不是深度测量。

如果目标识别错了:

把椅子识别成桌子
把门识别成墙
把楼梯识别成地面

深度再准也没有意义。


你目前两个方案为什么效果差

方案1:MobilenetV2 图像分类

这个方案本质上不适合你的需求。

因为:

图像分类
≠
目标检测

MobilenetV2只能回答:

这张图大概率是什么

例如:

桌子 60%
椅子 20%
门 10%

但无法告诉你:

桌子在哪里
距离多少
有几个

对于盲人导航基本没法用。

所以放弃是正确的。


方案2:Core Vision Kit

Core Vision Kit 的定位更偏向:

OCR
条码
文档
基础视觉能力

而不是:

复杂环境目标检测

类别数量有限是正常现象。

它不是 YOLO 的替代品。


关于 YOLOv8 的问题

你说:

Windows:
yolov8n.pt
识别正常

而:

HarmonyOS:
yolov8n.ms
识别极差

这种情况一般只有三个原因。


原因1(概率最高)

后处理错了

很多人:

pt
↓
onnx
↓
ms

转换成功了。

但:

NMS
sigmoid
坐标还原

处理错了。

表现就是:

框乱飞
类别乱跳
置信度异常

看起来像:

识别率极低

实际上模型本身没问题。


原因2

输入预处理不一致

Windows YOLO:

letterbox
RGB
640×640
归一化

而 ArkTS 侧经常变成:

BGR
或者
RGBA

或者:

resize方式不同

结果:

准确率断崖式下降

这是最常见问题之一。


原因3

模型转换损失

虽然 MindSpore Lite 支持:

ONNX
→
MS

并支持麒麟 NPU。

但并不是所有 YOLOv8 算子都能无损转换。

尤其:

Detect Head
动态Shape
部分后处理

经常出问题。


关于 NPU

你说:

CPU能跑
NPU不能跑

最近社区其实已经有人反馈:

CPU正常
NPU加载失败
getInputs为空

类似问题。

所以:

不是你一个人遇到

当前 HarmonyOS + MindSpore Lite + NPU 的生态还不像 Android TensorRT 那么成熟。


我更推荐的路线

如果项目目标是:

盲人环境识别

不要直接上:

YOLOv8n
+
AR深度

建议:

第一步

先把检测跑通

只做:

摄像头
↓
YOLOv8
↓
播报

例如:

前方1米处有椅子
前方有门
右侧有垃圾桶

不要加深度。


第二步

换模型

YOLOv8n 太小。

对于室内复杂场景:

YOLOv8s
YOLOv8m

效果会明显提升。

YOLOv8n 本来就是:

速度优先

不是精度优先。


第三步

自建数据集

你这个场景:

门
楼梯
扶手
电梯
垃圾桶
障碍物

属于特殊场景。

COCO 数据集本身就不够。

最有效的方法是:

自己采集几千张数据
重新训练

很多项目精度提升最大的来源其实是数据,而不是模型。


第四步

再接 AREngine

此时:

YOLO
得到目标框

然后:

框中心点
↓
对应深度图
↓
获取距离

例如:

门
2.3米

楼梯
1.1米

椅子
0.8米

这样架构才合理。


第五步(最终方案)

如果预算允许,我甚至建议:

YOLOv8
+
视觉大模型
+
深度数据

因为你提到:

小艺看世界

它并不是简单的:

Mobilenet

或者:

传统分类模型

而是更接近:

多模态视觉理解

所以你会感觉差距非常大。


针对你目前的情况,我建议先重点排查:

  1. ONNX → MS 转换后的输出是否与 Windows ONNX Runtime 一致;
  2. ArkTS 侧图片预处理(RGB/BGR、归一化、letterbox)是否完全一致;
  3. 后处理(NMS、坐标映射)是否正确;
  4. NPU 是否是模型兼容问题,而不是代码问题。

如果你能贴出:

  • 使用的 YOLOv8 版本(n/s/m)
  • ONNX 导出命令
  • 转 MS 的 converter 命令
  • ArkTS 推理后的输出 tensor 结构

基本可以进一步判断到底是模型转换问题、预处理问题还是 NPU 兼容问题。

建议优先验证NPU通路:使用官方最简单的NPU模型示例进行测试,确保硬件和基础软件栈正常。

然后再切换或优化模型:尝试转换和部署YOLOv5s或MobileNetV3等经过更多移动端验证的模型,并严格检查预处理流程。

也可以评估端云方案:如果功能允许部分联网,强烈建议集成ML Kit物体识别服务,可快速达到可用精度,将开发重点转向引导逻辑与多传感器融合。

持续迭代数据:收集实际使用场景数据,对本地模型进行持续优化,这是提升精度的根本。

在HarmonyOS Next中,物体识别依赖 @kit.visionKit@kit.mindSporeKit 提供的AI推理能力。常见难点包括:模型格式不兼容(需转换为MSLite格式)、Camera权限未正确声明、或图像预处理尺寸与模型输入不符。请检查 .ets 文件中 ImageAnalyzer 实例化及回调逻辑。

根本原因在于模型转换与部署流程不完全匹配 HarmonyOS 的 NPU 算子集。

直接解决方案:使用官方已验证的 YOLO 示例代码与转换步骤。

不要使用通用的 MindSpore Lite 分类示例,请直接使用华为官方提供的目标检测示例,该示例包含正确的 YOLOv8 模型转换脚本和完整的 ARKTS 推理代码。

  1. 模型转换:按照随示例提供的 convert_yolov8.sh 脚本进行转换,关键参数必须一致(--optimize=ascend_oriented 和对应的量化配置),不能使用默认的 ONNX 转 MS 参数。这是 NPU 无法运行的根本原因。
  2. 推理代码:直接复制示例中的 ARKTS 推理部分,重点在于:
    • 数据预处理:必须与模型训练时的归一化方式(通常 mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])和 RGB 排布完全一致。
    • 后处理:YOLOv8 的后处理(NMS、坐标反算)必须正确实现,示例代码已包含此逻辑。
    • 性能配置:在创建 Model 时,显式指定 deviceType'npu',并设置 enableFloat16true。如果因个别算子不支持导致 NPU 失败,添加 backend'cpu' 的回退配置。

示例获取:在你的 HarmonyOS SDK 目录下搜索 ObjectDetectionYoloV8 或者访问官方代码仓 applications_sample 中的 Media/AI 目录,查找此示例。严格按其中的 README 操作,即可在 NPU 上得到与电脑端一致的检测框和准确率。

替代方案:如果需追求 “小艺看世界” 级的效果,不应直接使用基础模型。应使用 HiAI Foundation 的模型zoo 中适配完成的 YOLOv8s 或更优模型,其推理精度和性能通过 NPU 调优,能提供更快的推理帧率和更高的识别准确度,更适合你的盲人引导场景。

回到顶部