HarmonyOS鸿蒙Next中类似华为AR测量的功能如何实现,有没有相关案例可以参考

HarmonyOS鸿蒙Next中类似华为AR测量的功能如何实现,有没有相关案例可以参考 【问题描述】:通过AR测量两点之间的距离,两个点的位置坐标上放置一个图片标记一下点的位置,两个点之间用直线连接,在直线的中间显示两个点的距离长度,现在主要卡点在于怎么在AR中绘制

【问题现象】:就类似于华为AR测量的那种效果一样

9 回复

尊敬的开发者,您好,您可以使用AR测量组件-鸿蒙组件-华为生态市场实现此功能,

约束与限制

环境

  • DevEco Studio版本:DevEco Studio 6.0.0 Release及以上
  • HarmonyOS SDK版本:HarmonyOS 6.0.0 Release SDK及以上
  • HarmonyOS版本:HarmonyOS 6.0.0(20)及以上

权限

  • 相机权限:ohos.permission.CAMERA
  • 陀螺仪传感器权限:ohos.permission.GYROSCOPE
  • 加速度传感器权限:ohos.permission.ACCELEROMETER

限制

  • 本组件中的AR测量功能不支持模拟器
  • 本组件使用的AR Engine能力具有一定技术局限性,具体请参见AR Engine功能技术局限性

注意:需要在ar_measure/src/main/ets/viewmodel/ARMeasureVM.ets路径下找到load(idStr: string)方法,替换为如下代码:

load(idStr: string) {
    try {
      this.interval = setInterval(() => {
        arEngine.update(idStr);
        this.volume = arEngine.getVolume(idStr);
        UtilLog.info(0xFF00, 'ARMeasure', `ARMeasure result is ${this.volume}`);
        if (this.semanticDenseMode === MeasureType.VOLUME) {
          if (this.volume) {
            let data = this.volume.split(':')[1];
            if (data?.length > 1) {
              this.hasMeasured = true;
              UtilLog.info(0xFF00, 'ARMeasure', `Volume Measure success with data ${data}`);
              this.handleFormatVolume(data);
            }
          }
        } else {
          // 实际为空间数据
          if (this.volume) {
            let data = this.volume.split(':')[1];
            if (data?.length > 1) {
              this.hasMeasured = true;
              UtilLog.info(0xFF00, 'ARMeasure', `Space Measure success with data ${data}`);
              this.handleFormatSpace(data);
            }
          }
        }
      }, ENGINE_UPDATE_INTERVAL);
    } catch (e) {
      UtilLog.error(0xFF00, 'ARMeasure', `Load XComponent failed ${JSON.stringify(e)}`);
    }
  }

更多关于HarmonyOS鸿蒙Next中类似华为AR测量的功能如何实现,有没有相关案例可以参考的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


加油,

cke_129.png

生态市场搜索: AR测量组件 ,需要登录。

这个组件提供了识别空间立方体和嵌入式立方体空间的长、宽、高的能力,可用于测量立方体体积和嵌入式空间的大小。

或者直接在 DevEco Studio 里查找:View -> Tool Windows -> Partner Component

cke_7986.png

  • 本组件中的AR测量功能不支持模拟器
  • 本组件使用的AR Engine能力具有一定技术局限性,具体请参见AR Engine功能技术局限性。

其他三方实现: ar_measure

AR Engine有个测试深度的demo,可以同理实现,跑了一下,挺精准的。

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arengine-get-depth

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}`);
    }
  }
}

可以实现,核心能力就是用 AR Engine 的 平面检测 + HitTest + Anchor + 3D 渲染

你现在卡的"怎么在 AR 里绘制",本质上不是画普通 ArkUI 组件,而是要在 AR 世界坐标系里放 3D 节点。AR Engine 提供运动跟踪、环境跟踪和命中检测,适合做这种测距场景。

实现思路基本就是 4 步:

1、点击屏幕做 HitTest,拿真实世界坐标

用户点击屏幕

→ 调用 hitTest

→ 获取命中的平面点

→ 创建 Anchor

tap -> hitTest -> ARHitResult -> createAnchor()

两个点击得到:

  • pointA
  • pointB

2、在两个 Anchor 放图片 Marker

每个 Anchor 挂一个小 billboard(图片精灵 / 小圆点模型)

作用就是华为 AR 测量里那个两个端点。


3、计算两点 3D 距离

拿 Anchor 世界坐标:

distance =
sqrt(
 (x2-x1)^2 +
 (y2-y1)^2 +
 (z2-z1)^2
)

AR Engine 的空间坐标单位本身就是米。


4、绘制线段 + 中间显示距离

这是你当前卡点。

一般有两种做法:

方案A(推荐)3D Cylinder / Thin Cube 拉伸

在 A、B 中点放一个细长模型:

  • 长度 = distance
  • 朝向 lookAt(B)

这样就是一条空间直线。

中点再挂一个 Text Sprite 显示:

"1.27m"

方案B(简单)2D Overlay 投影

把 3D 点投影到屏幕:

worldToScreen(point)

然后用 ArkUI Canvas 画线。

开发简单,但跟真实空间贴合感差。


如果想做到接近"华为 AR 测量"的效果,建议参考这个架构:

ARSession

Plane Tracking

HitTest

AnchorA / AnchorB

Marker Node

Line Node

Distance Label Node

官方可以重点看:

  • 环境跟踪(Plane Tracking)
  • 命中检测(HitTest)
  • Anchor 生命周期
  • Node 渲染示例

你要的不是"ArkUI 绘制",而是:

在 AR Scene 里创建可渲染节点,并绑定到 Anchor。

一句话:

类似华为 AR 测量完全能做,关键是用 HitTest 获取两点 Anchor,然后用 3D 细长模型(不是普通 Canvas Line)连接两点,中点挂文字节点显示距离。

官方文档思路的最小示例(基于 AR Engine 的 hitTest + Anchor),类似这样:

import { ar } from '@kit.ARKit';

@State anchorList: ar.Anchor[] = [];

onTap(x: number, y: number) {
  let hitResults = this.arSession.hitTest(x, y);
  if (!hitResults || hitResults.length === 0) {
    return;
  }

  let anchor = hitResults[0].createAnchor();
  this.anchorList.push(anchor);

  // 放两个点后计算距离
  if (this.anchorList.length === 2) {
    this.measureDistance(
      this.anchorList[0],
      this.anchorList[1]
    );
  }
}

measureDistance(a: ar.Anchor, b: ar.Anchor) {
  let poseA = a.getPose();
  let poseB = b.getPose();

  let dx = poseA.tx - poseB.tx;
  let dy = poseA.ty - poseB.ty;
  let dz = poseA.tz - poseB.tz;

  let distance = Math.sqrt(dx * dx + dy * dy + dz * dz);

  console.info(`distance = ${distance}m`);

  // 点位 Marker
  this.addMarker(a);
  this.addMarker(b);

  // 连线
  this.drawLine(a, b);

  // 中间显示距离
  this.showDistanceLabel(a, b, distance);
}

绘制部分官方示例一般是通过给 Anchor 挂 Node:

addMarker(anchor: ar.Anchor) {
  let node = this.scene.createNode();
  node.setAnchor(anchor);
  node.setRenderable(this.markerTexture);
}

drawLine(start: ar.Anchor, end: ar.Anchor) {
  let lineNode = this.scene.createNode();
  lineNode.setRenderable(this.lineModel);
  lineNode.lookAt(end.getPose());
}

文档里重点参考这几个示例章节:

  • ARSession 创建
  • HitTest 命中检测
  • Anchor 创建与绑定
  • Node 渲染 / 3D模型挂载

你的需求其实就是把官方这些例子串起来:

点击 → hitTest → createAnchor → 挂 marker → 算距离 → 挂 line node → 挂 text node

所以卡点"怎么绘制",对应文档里要找的不是 Canvas,而是 Scene / Node / Renderable 示例代码。

在 HarmonyOS NEXT 中,AR 测量功能可通过 @ohos.multimodalawareness.ar@kit.ArKit 提供的 AR Engine 实现。官方 Sample 仓库包含 ARMeasureDistanceMeasurement 案例,支持点对点测距、面积测量等。参考 arkui_ace_engine 下 AR 相关示例代码即能快速上手。

在HarmonyOS Next中实现类似AR测量的功能,主要依赖AR Engine服务(@kit.AREngineKit)。核心思路是:

  1. 开启世界跟踪AR会话,通过hitTest获取真实世界平面上的触摸点。
  2. 在两个触摸点位置创建ARAnchor,并附加图片标记(如ImageNode)。
  3. 计算两点世界坐标的距离,在中点创建TextNode显示长度。
  4. 通过动态生成细长立方体模型(或ModelNode)作为连线,根据两点位置计算旋转和缩放,使连线贴合两点。

关键代码片段:

// 1. 初始化AR会话
arSession = new ARSession(context, { type: ARSessionType.WORLD_TRACKING });
arSession.start();

// 2. 触摸处理,获取平面点
this.xComponent.onTouch((event: TouchEvent) => {
  let hitResults = arSession.hitTest(event.touches[0].x, event.touches[0].y);
  if (hitResults.length > 0) {
    let pose = hitResults[0].pose; // 命中点的位姿
    this.addPointMarker(pose);
  }
});

// 3. 添加图片标记
addPointMarker(pose: ARPose) {
  let anchor = arSession.createAnchor(pose);
  let imageNode = this.arScene.createNode('imageNode');
  imageNode.addComponent(ImageComponent, { image: 'marker.png', width: 0.05 });
  imageNode.position = { x: pose.tx, y: pose.ty, z: pose.tz };
  anchor.addChild(imageNode);
  this.points.push({ anchor, pose });
  if (this.points.length === 2) {
    this.drawLineAndText(); // 两点确定后绘制
  }
}

// 4. 绘制连线与距离文字
drawLineAndText() {
  let p1 = this.points[0].pose, p2 = this.points[1].pose;
  let distance = Math.sqrt((p2.tx-p1.tx)**2 + (p2.ty-p1.ty)**2 + (p2.tz-p1.tz)**2);
  let midPoint = { x: (p1.tx+p2.tx)/2, y: (p1.ty+p2.ty)/2, z: (p1.tz+p2.tz)/2 };
  // 放置距离文字
  let textNode = this.arScene.createNode('textNode');
  textNode.addComponent(TextComponent, { text: distance.toFixed(2)+'m', fontSize: 0.02 });
  textNode.position = midPoint;
  this.arScene.root.addChild(textNode);
  // 绘制细长立方体作为连线(计算方向与缩放)
  let lineNode = this.arScene.createNode('line');
  lineNode.addComponent(CubeComponent, { width: 0.005, height: 0.005, depth: distance });
  lineNode.position = midPoint;
  // 旋转至两点方向
  let dir = new Vector3(p2.tx-p1.tx, p2.ty-p1.ty, p2.tz-p1.tz).normalize();
  let quat = Quaternion.lookRotation(dir, Vector3.Up);
  lineNode.rotation = quat;
  this.arScene.root.addChild(lineNode);
}

相关案例:HarmonyOS官方提供了AR Engine Sample,内含基础平面检测与放置物体的示例,可在此基础上扩展测量功能。在DevEco Studio中通过File > New > Import Sample搜索“AR”即可获取,案例名如AREngineSample。其中展示了触摸命中、创建锚点、加载3D模型等能力,参照其架构加入线段生成与文字节点即可实现类似华为AR测量的效果。

回到顶部