AR测量:实现AR尺子与测距功能(198)
在鸿蒙HarmonyOS应用开发中基于 AR Engine 实现 AR 尺子与测距功能核心依赖于平面检测与命中检测HitTest能力的结合。通过获取用户在现实空间中点击的两个点的三维坐标即可计算出两点间的实际物理距离。一、 核心测量原理与流程AR 测距的底层逻辑是“空间坐标获取 几何距离计算”初始化与会话启动创建 AR Session并配置启用平面检测模式如HORIZONTAL_AND_VERTICAL以识别现实世界中的水平面如桌面、地面或垂直面如墙面。命中检测HitTest当用户在屏幕上点击时系统从屏幕坐标发射一条射线检测其与真实平面的交点并返回该交点的 3D 世界坐标Pose。距离计算获取起点与终点的三维坐标 (x1,y1,z1)(x1,y1,z1) 与 (x2,y2,z2)(x2,y2,z2) 通过三维空间欧几里得距离公式计算实际距离。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); } } }三、 性能优化按需启动与资源释放不要在应用启动时默认开启 AR 测量。应在用户点击“开始测量”时启动 Session获取数据后立即调用stop()并释放资源避免设备快速发热和耗电。清晰的 UI 引导AR 测量强依赖用户的物理操作。必须在 UI 上提供清晰的引导提示如“请缓慢移动手机扫描平面”、“请在平面上点击两个点”并在状态变化时给予明确反馈如“检测到平面”、“重建完成”。轻量级替代方案如果应用仅需简单的长度或面积测量优先使用通用的“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): Promisestring { // 检查设备是否支持高精几何重建 (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 空间中的物理坐标。射线检测与坐标获取当用户点击屏幕时获取屏幕的二维坐标(x, y)并调用arFrame.hitTest(x, y)。系统会从相机位置向屏幕点击点发射一条射线返回射线与真实世界平面的交点列表ARHitResult。提取 3D 世界坐标从命中结果中提取交点的位姿Pose通过调用hitResult.getHitPose()获取位姿对象进而通过tx()、ty()、tz()方法提取该点在真实世界中的三维坐标 (x,y,z)(x,y,z) 。状态机与交互反馈在应用层维护一个状态机记录用户当前是处于“寻找平面”、“等待点击起点”还是“等待点击终点”的状态。当成功获取第一个点时在 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能力。启用语义稠密重建在 AR 配置中开启semanticDense模式通过frame.acquireSemanticDense()获取当前帧的 HPG 数据并调用semanticDense.acquireCubeData()返回ARSemanticDenseCube[]数组。提取物体包围盒每个 Cube 包含目标物体在世界坐标系中的center中心点、size长宽高和orientation旋转四元数。通过遍历这些立方体抽象算法能自动从背景中提取出包裹实体直接计算出物体的体积。严格的资源释放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): Promisevoid { 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 测量对设备的传感器和算力要求极高工程化落地时必须遵循以下规范能力探测与优雅降级HPG 仅支持搭载麒麟 9000S/9010 ToF/深度传感器的部分高端机型。在初始化前必须通过canIUse(SystemCapability.Collaboration.AREngine.HighPrecisionGeometry)进行能力探测。若不支持应优雅降级为普通的“平面两点测距”模式。最佳测量环境与引导测量精度受环境影响极大。应用应引导用户在距离被测物0.3m - 1.5m的范围内操作并提示“缓慢移动手机”。必须明确告知用户避开透明、反光、纯白墙面或光线过暗的场景否则会导致平面检测失败或坐标漂移。按需启用与防发热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): Promisevoid { 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 测量会话已安全销毁释放资源); } } }