实战:用 AR Engine 做物体测量

你有没有想过用手机测量桌子有多宽、房间有多长?AR 技术可以做到——打开摄像头,点击两个点,手机就告诉你这两点之间的距离。

物体测量整体流程

下面是用AR Engine测量距离的完整流程:

用户打开测量工具

初始化AR Session

启用平面检测

移动手机扫描平面

检测到平面

用户点击第一个点

命中检测hitTest

获取3D坐标

用户点击第二个点

获取第二个3D坐标

计算两点距离

显示测量结果

物体测量的原理

物体测量的核心是平面检测 + 距离计算

  1. 平面检测:AR Engine 检测摄像头画面中的平面(地面、桌面、墙面)
  2. 点击定位:用户在平面上点击两个点
  3. 距离计算:根据两个点的 3D 坐标,计算实际距离

第一步:初始化 AR

import { arEngine } from '@kit.ArkGraphics2D';
import { common } from '@kit.AbilityKit';

导入需要的模块。

interface Point3D {
  x: number;
  y: number;
  z: number;
}

@Entry
@Component
struct MeasureTool {
  @State status: string = '初始化中...';
  @State distance: string = '';
  @State points: Point3D[] = [];

  private arSession: arEngine.ARSession | null = null;
  private planes: arEngine.ARPlane[] = [];

定义 3D 坐标接口和状态变量。

  async aboutToAppear() {
    await this.initAR();
  }

  async initAR() {
    const context: Context = this.getUIContext().getHostContext() as common.UIAbilityContext;

获取上下文。

    // 检查 AR 能力
    let isSupported = arEngine.isSupport(context);
    if (!isSupported) {
      this.status = '设备不支持 AR';
      return;
    }

检查设备是否支持 AR。

    // 创建 AR Session,启用平面检测
    let config: arEngine.ARSessionConfig = {
      planeFindingMode: arEngine.PlaneFindingMode.HORIZONTAL_AND_VERTICAL
    };

    this.arSession = await arEngine.createSession(context, config);

创建 AR Session 时,启用平面检测。HORIZONTAL_AND_VERTICAL 表示同时检测水平面(地面、桌面)和垂直面(墙壁)。

    if (this.arSession == null) {
      this.status = '创建 AR Session 失败';
      return;
    }

    // 启动 AR
    this.arSession.start();
    this.status = '请移动手机扫描平面';

启动 AR。

第二步:检测平面

    // 持续检测平面
    this.startPlaneDetection();
  }

  startPlaneDetection() {
    setInterval(() => {
      if (this.arSession == null) return;

      // 获取所有检测到的平面
      this.planes = this.arSession.getAllTrackables(arEngine.ARPlane);

      if (this.planes.length > 0) {
        this.status = `检测到 ${this.planes.length} 个平面,点击两个点测量距离`;
      }
    }, 500);
  }

每 500ms 检测一次平面。getAllTrackables(arEngine.ARPlane) 获取所有检测到的平面。

第三步:命中检测

  // 用户点击屏幕时调用
  onScreenTap(x: number, y: number) {
    if (this.arSession == null) return;

    // 命中检测:从屏幕坐标转换到 3D 空间
    let hitResult = this.arSession.hitTest(x, y);

hitTest 从屏幕坐标 (x, y) 发射一条射线,检测它和哪个平面相交。返回的是一个数组——可能命中多个平面,我们取第一个。

    if (hitResult.length > 0) {
      let hit = hitResult[0];
      let pose = hit.getHitPose();

      // 获取 3D 坐标
      let point: Point3D = {
        x: pose.tx(),
        y: pose.ty(),
        z: pose.tz()
      };

getHitPose() 返回命中点的位姿(位置 + 旋转)。tx()ty()tz() 分别是 X、Y、Z 坐标,单位是米。

      this.points.push(point);

      if (this.points.length === 1) {
        this.status = '已标记第一个点,请点击第二个点';
      } else if (this.points.length === 2) {
        // 计算距离
        let dist = this.calculateDistance(this.points[0], this.points[1]);
        this.distance = `${(dist * 100).toFixed(1)} 厘米`;
        this.status = '测量完成,点击"重置"继续测量';
      }
    }
  }

把点击的点保存起来。如果已经有两个点,计算距离。

第四步:计算距离

  calculateDistance(p1: Point3D, p2: Point3D): number {
    let dx = p2.x - p1.x;
    let dy = p2.y - p1.y;
    let dz = p2.z - p1.z;
    return Math.sqrt(dx * dx + dy * dy + dz * dz);
  }

三维空间中两点之间的距离公式:√((x2-x1)² + (y2-y1)² + (z2-z1)²)

返回值的单位是米,所以乘以 100 转成厘米。

第五步:页面布局

  reset() {
    this.points = [];
    this.distance = '';
    this.status = '请点击第一个点';
  }

  build() {
    Column() {
      Text(this.status)
        .fontSize(16)
        .margin({ top: 20 })
        .textAlign(TextAlign.Center)

显示状态文字。

      // 测量结果显示
      if (this.distance) {
        Text(this.distance)
          .fontSize(48)
          .fontWeight(FontWeight.Bold)
          .fontColor('#2196f3')
          .margin({ top: 30 })

        Text('两点之间的距离')
          .fontSize(14)
          .fontColor('#999999')
          .margin({ top: 5 })
      }

显示测量结果。

      // AR 视图区域
      // 实际项目中,这里会放一个 AR 视图组件
      // 用户可以点击视图中的平面来标记点

      // 操作按钮
      Row() {
        Button('重置')
          .margin(10)
          .onClick(() => this.reset())

        Button('撤销')
          .margin(10)
          .onClick(() => {
            if (this.points.length > 0) {
              this.points.pop();
              this.distance = '';
              this.status = '已撤销,请重新标记';
            }
          })
      }
      .margin({ top: 30 })

添加重置和撤销按钮。

      // 平面信息
      if (this.planes.length > 0) {
        Text(`已检测到 ${this.planes.length} 个平面`)
          .fontSize(12)
          .fontColor('#999999')
          .margin({ top: 20 })
      }
    }
    .width('100%')
    .height('100%')
  }

  aboutToDisappear() {
    if (this.arSession != null) {
      this.arSession.stop();
      this.arSession.release();
    }
  }
}

显示检测到的平面数量,页面销毁时释放资源。

命中检测流程

命中检测将屏幕坐标转换为3D空间坐标:

1个

2个

用户点击屏幕

获取屏幕坐标x,y

调用hitTest方法

从摄像头发射射线

检测射线与平面交点

是否命中平面?

获取命中点位姿

提示未命中

提取3D坐标tx,ty,tz

保存测量点

已有几个点?

等待第二个点

计算距离

进阶改进

1. 多点测量

支持标记多个点,计算折线总长度:

calculateTotalDistance(): number {
  let total = 0;
  for (let i = 1; i < this.points.length; i++) {
    total += this.calculateDistance(this.points[i - 1], this.points[i]);
  }
  return total;
}

2. 面积测量

标记 4 个点,计算矩形面积:

calculateArea(p1: Point3D, p2: Point3D, p3: Point3D, p4: Point3D): number {
  let width = this.calculateDistance(p1, p2);
  let height = this.calculateDistance(p2, p3);
  return width * height;
}

3. 精度提示

AR 测量的精度受很多因素影响:

  • 光线条件
  • 平面纹理(纯色平面检测效果差)
  • 手机移动的稳定性

你可以在 UI 上显示一个"精度指示器",告诉用户当前测量的可靠程度。

进阶改进方向

AR测量工具可以通过以下方式增强功能:

AR测量工具

多点测量

面积测量

精度优化

标记多个点

计算折线总长度

标记4个点

计算矩形面积

考虑光线条件

平面纹理影响

手机稳定性

显示精度指示器

小结

物体测量的核心流程:

  1. arEngine.createSession 创建 AR Session,启用平面检测
  2. getAllTrackables(arEngine.ARPlane) 获取检测到的平面
  3. hitTest(x, y) 从屏幕坐标转换到 3D 坐标
  4. 用三维距离公式计算两点之间的距离

AR Engine 提供了平面检测和命中检测能力,距离计算就是简单的数学。实际应用中,难点在于 UI 交互和精度优化。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐