在鸿蒙(HarmonyOS)应用开发中,基于 AR Engine 实现 AR 尺子与测距功能,核心依赖于平面检测命中检测(HitTest)能力的结合。通过获取用户在现实空间中点击的两个点的三维坐标,即可计算出两点间的实际物理距离。

一、 核心测量原理与流程

AR 测距的底层逻辑是“空间坐标获取 + 几何距离计算”:

  1. 初始化与会话启动:创建 AR Session,并配置启用平面检测模式(如 HORIZONTAL_AND_VERTICAL),以识别现实世界中的水平面(如桌面、地面)或垂直面(如墙面)。
  2. 命中检测(HitTest):当用户在屏幕上点击时,系统从屏幕坐标发射一条射线,检测其与真实平面的交点,并返回该交点的 3D 世界坐标(Pose)。
  3. 距离计算:获取起点与终点的三维坐标 (x1,y1,z1)(x1​,y1​,z1​) 与 (x2,y2,z2)(x2​,y2​,z2​) ,通过三维空间欧几里得距离公式计算实际距离。
  4. UI 可视化:在 3D 场景中将两个坐标点进行连线,并在连线中点或 UI 面板上实时渲染测量结果。
// ARMeasureTool.ets
import { arEngine } from '@kit.AREngine';

// 定义 3D 坐标接口
interface Point3D {
    x: number;
    y: number;
    z: number;
}

export class ARMeasureTool {
    // 1. 命中检测:获取屏幕点击对应的 3D 坐标
    public static hitTestAndGetPoint(x: number, y: number, frame: arEngine.ARFrame): Point3D | null {
        try {
            let hitResults = frame.hitTest(x, y);
            if (hitResults && hitResults.length > 0) {
                let hitPose = hitResults[0].getHitPose();
                return {
                    x: hitPose.tx(),
                    y: hitPose.ty(),
                    z: hitPose.tz()
                };
            }
        } catch (error) {
            console.error('HitTest 失败:', error);
        }
        return null;
    }

    // 2. 距离计算:三维空间欧几里得距离公式
    public static calculateDistance(p1: Point3D, p2: Point3D): number {
        const dx = p1.x - p2.x;
        const dy = p1.y - p2.y;
        const dz = p1.z - p2.z;
        return Math.sqrt(dx * dx + dy * dy + dz * dz);
    }
}

二、 进阶能力:高精几何重建(HPG)

对于需要更高精度和复杂空间感知的测量场景(如测量不规则包裹体积、房间整体尺寸),AR Engine 提供了高精几何重建(HPG)能力:

  • 纯软件高精度:无需依赖 LiDAR(激光雷达)等专用硬件,仅通过标准 RGB 摄像头结合 IMU(惯性测量单元)数据,即可实现厘米级的高精度测量。
  • 语义感知与稠密点云:算法能够自动从复杂的家庭背景中提取出目标物体(如包裹实体),并生成高精稠密点云,误差可控制在 3% 以内,且 3D 模型重建耗时小于 5 秒。
  • 工程化建议:由于 HPG 功耗较高且仅部分高端机型支持,建议将其作为“可选增强体验”,采用“能力探测 + 优雅降级”策略,仅在用户明确触发扫描时按需开启。
// HPGVolumeMeasurement.ets
import { arEngine, arViewController } from '@kit.AREngine';

class HPGCallback extends arViewController.ARViewCallback {
    // 1. 在配置中开启体积测量识别能力
    public initHPGConfig() {
        return {
            type: arEngine.ARType.WORLD,
            semanticDenseMode: arEngine.ARSemanticDenseMode.CUBE_VOLUME, // 核心:开启 HPG
            planeFindingMode: arEngine.ARPlaneFindingMode.HORIZONTAL_AND_VERTICAL,
            focusMode: arEngine.ARFocusMode.AUTO
        };
    }

    // 2. 帧更新回调:获取立方体体积数据
    onFrameUpdate(ctx: arViewController.ARViewContext, sysBootTs: number): void {
        if (!ctx.session) return;
        try {
            let frame = ctx.session.getFrame();
            // 获取高精几何重建对象数据
            let semanticDense = frame.acquireSemanticDense();
            if (semanticDense) {
                // 获取识别到的立方体数据列表
                let cubeDataList = semanticDense.acquireCubeData();
                if (cubeDataList && cubeDataList.length > 0) {
                    for (let cube of cubeDataList) {
                        // 提取长宽高及体积信息
                        console.info(`物体尺寸: ${cube.size.x} x ${cube.size.y} x ${cube.size.z}`);
                    }
                }
                // ⚠️ 必须调用 release 防止内存泄漏
                semanticDense.release(); 
            }
        } catch (error) {
            console.error('HPG 数据处理失败:', error);
        }
    }
}

三、 性能优化

  1. 按需启动与资源释放:不要在应用启动时默认开启 AR 测量。应在用户点击“开始测量”时启动 Session,获取数据后立即调用 stop() 并释放资源,避免设备快速发热和耗电。
  2. 清晰的 UI 引导:AR 测量强依赖用户的物理操作。必须在 UI 上提供清晰的引导提示(如“请缓慢移动手机扫描平面”、“请在平面上点击两个点”),并在状态变化时给予明确反馈(如“检测到平面”、“重建完成”)。
  3. 轻量级替代方案:如果应用仅需简单的长度或面积测量,优先使用通用的“AR 平面检测 + 用户点击两点计算”方案,无需引入高功耗的 HPG 模块,这能兼容绝大多数支持 AR 的设备。
// ARSafeManager.ets
import { arEngine } from '@kit.AREngine';
import { common } from '@kit.AbilityKit';

export class ARSafeManager {
    // 1. 能力探测与优雅降级
    public static async checkAndInitAR(context: common.UIAbilityContext): Promise<string> {
        // 检查设备是否支持高精几何重建 (HPG)
        let supportHPG = canIUse('SystemCapability.Collaboration.AREngine.HighPrecisionGeometry');
        if (supportHPG) {
            return 'HPG_MODE'; // 返回高级模式标识
        } else {
            // 降级为普通的平面检测模式
            let isSupportBasic = arEngine.isSupport(context);
            return isSupportBasic ? 'BASIC_MODE' : 'NOT_SUPPORT';
        }
    }

    // 2. 严格的资源释放与生命周期管理
    public static async safeStopAR(arContext: arViewController.ARViewContext | null) {
        if (arContext) {
            try {
                arContext.pause(); // 暂停 AR 会话
                arContext.destroy(); // 彻底销毁释放资源
                console.info('AR 资源已安全释放');
            } catch (error) {
                console.error('AR 资源释放失败:', error);
            }
        }
    }
}

四、 AR 测量实战:HitTest 命中检测与空间坐标提取

在鸿蒙的 ArkTS 开发中,实现 AR 尺子的核心在于精准捕获用户在屏幕上的点击,并将其映射为 3D 空间中的物理坐标。

  1. 射线检测与坐标获取
    当用户点击屏幕时,获取屏幕的二维坐标 (x, y),并调用 arFrame.hitTest(x, y)。系统会从相机位置向屏幕点击点发射一条射线,返回射线与真实世界平面的交点列表(ARHitResult)。
  2. 提取 3D 世界坐标
    从命中结果中提取交点的位姿(Pose),通过调用 hitResult.getHitPose() 获取位姿对象,进而通过 tx()ty()tz() 方法提取该点在真实世界中的三维坐标 (x,y,z)(x,y,z) 。
  3. 状态机与交互反馈
    在应用层维护一个状态机,记录用户当前是处于“寻找平面”、“等待点击起点”还是“等待点击终点”的状态。当成功获取第一个点时,在 3D 场景中渲染一个指示球体;获取第二个点时,计算距离并渲染连线。
// ARMeasureInteraction.ets
import { arEngine } from '@kit.AREngine';

// 定义 3D 坐标接口
interface Point3D { x: number; y: number; z: number; }

export class ARMeasureInteraction {
    private points: Point3D[] = [];
    
    // 1. 射线检测与 3D 坐标提取
    public handleScreenTap(x: number, y: number, frame: arEngine.ARFrame): Point3D | null {
        try {
            // 从屏幕坐标发射射线检测交点
            let hitResults = frame.hitTest(x, y);
            if (hitResults && hitResults.length > 0) {
                let hitPose = hitResults[0].getHitPose();
                // 提取真实世界中的三维坐标 (单位: 米)
                const point: Point3D = {
                    x: hitPose.tx(),
                    y: hitPose.ty(),
                    z: hitPose.tz()
                };
                // 状态机:记录点击的点
                this.points.push(point);
                return point;
            }
        } catch (error) {
            console.error('HitTest 命中检测失败:', error);
        }
        return null;
    }

    // 2. 获取当前状态机中的点集(用于 UI 渲染连线或指示球)
    public getPoints(): Point3D[] {
        return this.points;
    }

    // 重置测量状态
    public reset() {
        this.points = [];
    }
}

五、 进阶场景:高精几何重建(HPG)与体积测量

对于复杂的测量需求(如顺丰速运的“拍照识别体积”),基础的平面两点测距已无法满足,需要引入高精几何重建(HPG)能力。

  1. 启用语义稠密重建
    在 AR 配置中开启 semanticDense 模式,通过 frame.acquireSemanticDense() 获取当前帧的 HPG 数据,并调用 semanticDense.acquireCubeData() 返回 ARSemanticDenseCube[] 数组。
  2. 提取物体包围盒
    每个 Cube 包含目标物体在世界坐标系中的 center(中心点)、size(长宽高)和 orientation(旋转四元数)。通过遍历这些立方体抽象,算法能自动从背景中提取出包裹实体,直接计算出物体的体积。
  3. 严格的资源释放
    HPG 数据极其消耗内存。在获取并处理完 semanticDense 数据后,必须调用 semanticDense.release() 释放底层资源,以防止严重的内存泄漏。
// HPGVolumeMeasure.ets
import { arEngine, arViewController } from '@kit.AREngine';

class HPGViewCallback extends arViewController.ARViewCallback {
    // 1. 在 onFrameUpdate 中获取 HPG 数据
    async onFrameUpdate(ctx: arViewController.ARViewContext, sysBootTs: number): Promise<void> {
        let arSession: arEngine.ARSession = ctx.session;
        try {
            let frame: arEngine.ARFrame = arSession.getFrame();
            if (frame) {
                // 获取高精几何重建对象数据
                let semanticData: arEngine.ARSemanticDenseData = frame.acquireSemanticDense();
                if (semanticData && semanticData.cubeDataSize > 0) {
                    // 提取识别到的立方体(包裹实体)数据
                    let cubeDataList = semanticData.acquireCubeData();
                    for (let cube of cubeDataList) {
                        // 提取长宽高及置信度
                        let vertexData = cube.vertexData;
                        let confidence = cube.confidence;
                        console.info(`识别到物体,置信度: ${confidence}`);
                    }
                }
                // ⚠️ 核心:必须严格释放底层资源,防止内存泄漏
                if (semanticData) await semanticData.release();
            }
        } catch (error) {
            console.error('HPG 帧数据更新失败:', error);
        }
    }
}

六、 性能优化与工程避坑指南

AR 测量对设备的传感器和算力要求极高,工程化落地时必须遵循以下规范:

  1. 能力探测与优雅降级
    HPG 仅支持搭载麒麟 9000S/9010 + ToF/深度传感器的部分高端机型。在初始化前,必须通过 canIUse('SystemCapability.Collaboration.AREngine.HighPrecisionGeometry') 进行能力探测。若不支持,应优雅降级为普通的“平面两点测距”模式。
  2. 最佳测量环境与引导
    测量精度受环境影响极大。应用应引导用户在距离被测物 0.3m - 1.5m 的范围内操作,并提示“缓慢移动手机”。必须明确告知用户避开透明、反光、纯白墙面或光线过暗的场景,否则会导致平面检测失败或坐标漂移。
  3. 按需启用与防发热
    AR 测量(尤其是 HPG)会导致设备快速发热和耗电。严禁在 App 启动时默认开启 AR,仅在用户明确点击“开始测量”时启动 Session。获取到最终测量结果后,应立即调用 stop() 并释放资源。
// ARMeasurementConfig.ets
import { arEngine, arViewController } from '@kit.AREngine';
import { Scene } from '@kit.ArkGraphics3D';

export class ARMeasurementConfig {
    private viewContext: arViewController.ARViewContext | null = null;

    // 1. 能力探测与优雅降级配置
    public async initARScene(isHPGSupported: boolean): Promise<void> {
        let scene = await Scene.load();
        this.viewContext = new arViewController.ARViewContext();
        this.viewContext.scene = scene;
        this.viewContext.callback = new HPGViewCallback();

        // 根据设备能力动态配置 AR 参数
        this.viewContext.config = {
            type: arEngine.ARType.WORLD,
            planeFindingMode: arEngine.ARPlaneFindingMode.HORIZONTAL_AND_VERTICAL,
            // 核心:支持 HPG 则开启体积测量,否则降级为普通模式
            semanticDenseMode: isHPGSupported 
                ? arEngine.ARSemanticDenseMode.CUBE_VOLUME 
                : arEngine.ARSemanticDenseMode.NONE, 
            poseMode: arEngine.ARPoseMode.GRAVITY,
            focusMode: arEngine.ARFocusMode.AUTO
        };
        await this.viewContext.init();
    }

    // 2. 按需启用与防发热:严格的资源释放
    public async safeStopAndRelease() {
        if (this.viewContext) {
            this.viewContext.pause();
            this.viewContext.destroy();
            this.viewContext = null;
            console.info('AR 测量会话已安全销毁,释放资源');
        }
    }
}

 

 

Logo

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

更多推荐