AR引擎:基于AREngine的平面检测与物体识别(197)
·
在鸿蒙(HarmonyOS)应用开发中,AR Engine 提供了强大的增强现实能力,支持平面检测、图像追踪、深度感知和网格重建等核心功能。基于 ArkTS 和 ArkGraphics3D 框架,开发者可以快速构建虚实融合的交互体验。
一、 核心架构与开发准备
构建 AR 应用需要依赖特定的系统 Kit,并正确配置 AR 会话的生命周期。
- 核心模块导入
开发 AR 应用需导入@kit.AREngine(AR引擎)、@kit.ArkGraphics3D(3D渲染)等模块。 - 会话生命周期管理
通过ARViewContext管理整个 AR 会话的生命周期。必须在页面显示时调用resume()恢复 AR,页面隐藏时调用pause()暂停,页面销毁时调用destroy()释放资源,防止内存泄漏。 - 配置 AR 参数
在初始化时,需配置 AR 类型(如ARType.WORLD)、平面检测模式(如水平与垂直面HORIZONTAL_AND_VERTICAL)、功耗模式及对焦模式等。
// ARSessionManager.ets
import { arEngine, arViewController } from '@kit.AREngine';
import { Scene } from '@kit.ArkGraphics3D';
export class ARSessionManager {
private arContext: arViewController.ARViewContext | null = null;
// 1. 初始化 AR 会话与场景
public async initARView(scene: Scene): Promise<void> {
let viewContext = new arViewController.ARViewContext();
viewContext.scene = scene;
viewContext.callback = new ARViewCallbackImpl(); // 绑定帧更新回调
// 配置 AR 参数
viewContext.config = {
type: arEngine.ARType.WORLD,
planeFindingMode: arEngine.ARPlaneFindingMode.HORIZONTAL_AND_VERTICAL,
powerMode: arEngine.ARPowerMode.NORMAL,
focusMode: arEngine.ARFocusMode.AUTO
};
await viewContext.init();
this.arContext = viewContext;
}
// 2. 严格的会话生命周期管理
public resume() { this.arContext?.resume(); }
public pause() { this.arContext?.pause(); }
public destroy() {
this.arContext?.destroy();
this.arContext = null;
}
}
二、 环境平面检测与渲染
平面检测是 AR 场景中最基础的能力,用于让虚拟物体“站”在现实世界中。
- 获取平面数据
通过实现ARViewCallback的onFrameUpdate方法,在每帧更新时调用session.getAllTrackables(ARTrackableType.PLANE)获取当前识别到的所有平面。 - 提取平面几何信息
通过ARPlane.getPolygonXZ()获取平面的 2D 顶点数组,结合ARTrackable.getPose().getMatrix()获取位姿矩阵,即可计算出平面在 3D 空间中的真实顶点坐标。 - 平面可视化
使用SceneResourceFactory创建自定义材质和 Shader,结合计算出的顶点数据生成 Mesh,即可在屏幕上渲染出半透明的平面覆盖层。
// PlaneDetection.ets
import { arEngine, arViewController } from '@kit.AREngine';
import { Matrix4 } from '@kit.ArkUI';
import { Vec3 } from '@kit.ArkGraphics3D';
class ARViewCallbackImpl extends arViewController.ARViewCallback {
// 每帧更新回调
onFrameUpdate(ctx: arViewController.ARViewContext, sysBootTs: number): void {
if (!ctx.session) return;
try {
let frame = ctx.session.getFrame();
let camera = frame.getCamera();
// 仅在相机正常追踪时获取平面
if (camera.state === arEngine.ARTrackingState.TRACKING) {
let planes = ctx.session.getAllTrackables(arEngine.ARTrackableType.PLANE);
for (let plane of planes) {
// 获取平面的 2D 顶点和位姿矩阵
let polygonXZ = (plane as arEngine.ARPlane).getPolygonXZ();
let matrix = plane.getPose().getMatrix();
// 将 2D 顶点转换为 3D 空间坐标
let vertices = this.getVertices(matrix, polygonXZ);
// TODO: 将 vertices 传入 SceneResourceFactory 生成 Mesh 并渲染
}
}
} catch (error) {
console.error('获取平面数据失败:', error);
}
}
// 坐标转换工具函数
private getVertices(mat: Matrix4, point: number[]): Vec3[] {
let result: Vec3[] = [];
for (let i = 0; i < point.length; i += 2) {
result.push({
x: mat[2] * point[i] + mat[10] * point[i + 1] + mat[14],
y: mat[1] * point[i] + mat[9] * point[i + 1] + mat[13],
z: -(mat[0] * point[i] + mat[8] * point[i + 1] + mat[12])
});
}
return result;
}
}
三、 虚实融合:物体放置与交互
将虚拟物体精准地锚定在现实平面上,是 AR 应用的核心交互。
- 射线检测(HitTest)
监听用户的屏幕点击事件,将 2D 屏幕坐标转换为 3D 射线,调用frame.hitTest(x, y)检测射线与现实中平面的交点。 - 边界与有效性校验
获取ARHitResult后,需通过hitPlane.isPoseInPolygon(hitPose)检查交点是否落在平面的有效多边形边界内,并校验距离是否大于 0。 - 创建锚点与放置物体
校验通过后,调用hitResult.createAnchor()创建空间锚点。随后使用CubeGeometry或加载外部.gltf模型,将其位置设置为锚点的translation坐标,即可完成虚拟物体的放置。
// ObjectPlacement.ets
import { arEngine } from '@kit.AREngine';
import { Vec3 } from '@kit.ArkGraphics3D';
export class ObjectPlacement {
// 监听屏幕点击事件
public handleScreenTap(x: number, y: number, frame: arEngine.ARFrame): Vec3 | null {
try {
// 1. 射线检测
let hitResults = frame.hitTest(x, y);
if (!hitResults || hitResults.length === 0) return null;
for (let hitResult of hitResults) {
let trackable = hitResult.getTrackable();
// 2. 确保命中目标是平面
if (trackable.type === arEngine.ARTrackableType.PLANE) {
let hitPlane = trackable as arEngine.ARPlane;
let hitPose = hitResult.getHitPose();
// 3. 边界与有效性校验
let inPolygon = hitPlane.isPoseInPolygon(hitPose);
let distance = hitResult.distance;
if (inPolygon && distance > 0) {
// 4. 创建空间锚点并返回坐标
let anchor = hitResult.createAnchor();
return anchor.getPose().translation;
}
}
}
} catch (error) {
console.error('命中检测失败:', error);
}
return null;
}
}
四、 性能优化
- 及时清理旧节点
在onFrameUpdate中,每帧都应清除上一帧渲染的平面网格和临时几何体,避免节点无限累积导致严重的内存泄漏和渲染卡顿。 - 硬件兼容性检查
AR 平面识别能力依赖设备的 SLAM 硬件特性。在应用启动前,应检查设备是否支持ARENGINE_FEATURE_TYPE_SLAM,避免在不支持的设备上强行运行导致崩溃。 - 权限与省电模式
必须在module.json5中声明CAMERA权限并在运行时动态申请。此外,部分设备在开启“省电模式”时会禁用后台摄像头,导致 AR 预览黑屏,需引导用户关闭省电模式或将应用加入白名单。
// ARSafetyCheck.ets
import { arEngine } from '@kit.AREngine';
import { abilityAccessCtrl, common } from '@kit.AbilityKit';
export class ARSafetyCheck {
// 1. 硬件兼容性检查
public static async checkARSupport(context: common.UIAbilityContext): Promise<boolean> {
try {
// 检查设备是否支持 SLAM 及平面识别特性
let isSupported = arEngine.isSupport(context);
if (!isSupported) {
console.warn('当前设备不支持 AR Engine 平面识别能力');
}
return isSupported;
} catch (err) {
console.error('AR 能力检测异常:', err);
return false;
}
}
// 2. 运行时动态申请相机权限
public static async requestCameraPermission(context: common.UIAbilityContext): Promise<boolean> {
try {
let atManager = abilityAccessCtrl.createAtManager();
let result = await atManager.requestPermissionsFromUser(context, ['ohos.permission.CAMERA']);
return result.authResults[0] === 0;
} catch (err) {
console.error('相机权限申请失败:', err);
return false;
}
}
}
五、 AR 引擎实战:ArkTS 平面检测与网格渲染
在鸿蒙的 ArkTS 开发中,平面检测与渲染是构建虚实融合场景的基础。通过 ARViewCallback 的帧更新回调,可以实时获取环境中的平面数据并进行可视化。
- 实时获取平面数据
在onFrameUpdate回调中,当相机处于TRACKING状态时,通过arSession.getAllTrackables(arEngine.ARTrackableType.PLANE)获取当前帧识别到的所有平面。 - 自定义网格生成与渲染
由于平面是由三角形拼接而成的多边形,需要编写自定义方法将 2D 顶点数组转换为 3D 空间顶点坐标(getVertices),生成三角形索引(generateMeshIndex),并打包为 Mesh 数据(generateMeshInput),最终交由 ArkGraphics3D 引擎进行渲染。
// PlaneMeshRenderer.ets
import { arEngine, arViewController } from '@kit.AREngine';
import { Matrix4 } from '@kit.ArkUI';
import { Vec3 } from '@kit.ArkGraphics3D';
class ARViewCallbackImpl extends arViewController.ARViewCallback {
// 每帧更新回调
onFrameUpdate(ctx: arViewController.ARViewContext, sysBootTs: number): void {
if (!ctx.session) return;
try {
let frame = ctx.session.getFrame();
let camera = frame.getCamera();
// 仅在相机正常追踪时获取平面
if (camera.state === arEngine.ARTrackingState.TRACKING) {
let planes = ctx.session.getAllTrackables(arEngine.ARTrackableType.PLANE);
for (let plane of planes) {
// 获取平面的 2D 顶点和位姿矩阵
let polygonXZ = (plane as arEngine.ARPlane).getPolygonXZ();
let matrix = plane.getPose().getMatrix();
// 将 2D 顶点转换为 3D 空间坐标
let vertices = this.getVertices(matrix, polygonXZ);
// TODO: 将 vertices 传入 SceneResourceFactory 生成 Mesh 并渲染
}
}
} catch (error) {
console.error('获取平面数据失败:', error);
}
}
// 坐标转换工具函数
private getVertices(mat: Matrix4, point: number[]): Vec3[] {
let result: Vec3[] = [];
for (let i = 0; i < point.length; i += 2) {
result.push({
x: mat[2] * point[i] + mat[10] * point[i + 1] + mat[14],
y: mat[1] * point[i] + mat[9] * point[i + 1] + mat[13],
z: -(mat[0] * point[i] + mat[8] * point[i + 1] + mat[12])
});
}
return result;
}
}
六、 进阶场景:命中检测与虚拟物体摆放
将虚拟物体精准放置在现实世界中,需要结合屏幕坐标与空间射线检测(HitTest)。
- 命中检测(HitTest)
当用户点击屏幕时,获取屏幕坐标并调用arFrame.hitTest(x, y)。系统会从相机位置向屏幕点击点发射一条射线,返回射线与真实世界平面的交点列表(ARHitResult)。 - 创建空间锚点(Anchor)
从命中结果中提取交点的位置与姿态(Pose),并调用createAnchor()在现实世界中“钉”下一个锚点。 - 驱动 3D 节点
在onAnchorAdd回调中,获取新创建的 Anchor,将其位姿矩阵绑定到 3D 场景中的虚拟物体节点(Node)上,从而实现虚拟物体与现实世界的完美融合与持续跟踪。
// VirtualObjectPlacer.ets
import { arEngine, arViewController } from '@kit.AREngine';
import { Vec3, Node } from '@kit.ArkGraphics3D';
export class VirtualObjectPlacer {
// 1. 命中检测与锚点创建
public handleScreenTap(x: number, y: number, frame: arEngine.ARFrame): Vec3 | null {
try {
// 射线检测
let hitResults = frame.hitTest(x, y);
if (!hitResults || hitResults.length === 0) return null;
for (let hitResult of hitResults) {
let trackable = hitResult.getTrackable();
// 确保命中目标是平面
if (trackable.type === arEngine.ARTrackableType.PLANE) {
let hitPlane = trackable as arEngine.ARPlane;
let hitPose = hitResult.getHitPose();
// 边界与有效性校验
let inPolygon = hitPlane.isPoseInPolygon(hitPose);
let distance = hitResult.distance;
if (inPolygon && distance > 0) {
// 创建空间锚点并返回坐标
let anchor = hitResult.createAnchor();
return anchor.getPose().translation;
}
}
}
} catch (error) {
console.error('命中检测失败:', error);
}
return null;
}
// 2. 锚点回调:驱动 3D 节点
onAnchorAdd(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void {
// 将新创建的 Anchor 位姿绑定到 3D 场景中的虚拟物体节点上
let pose = anchor.getPose();
node.position = pose.translation;
// TODO: 设置旋转和缩放等
}
}
AR 应用对设备性能和内存管理要求极高,工程化落地时必须遵循以下规范:
- 严格的资源释放
Frame、Trackable、Anchor等对象本质上是底层资源的句柄,而非纯数据。在每帧处理完毕后,必须及时调用相应的release()方法释放资源,否则极易引发内存泄漏和严重的掉帧。 - 按需启用高级能力
AR Engine 提供了深度估计(Depth)、环境 Mesh 扫描、高精几何重建(HPG)等高级能力。这些能力功耗极高且仅部分高端机型支持。建议在工程上采用“能力探测 + 优雅降级”策略,仅在用户明确触发(如点击“3D扫描”)时按需开启,用完即关。 - 锚点池管理
不要无限制地创建锚点。每个锚点都会带来持续的跟踪计算成本。对于不需要长期保留的虚拟物体,应及时调用detach()和release()回收锚点资源。
// ARResourceGuard.ets
import { arEngine, arViewController } from '@kit.AREngine';
export class ARResourceGuard {
private anchorPool: arEngine.ARAnchor[] = [];
private readonly MAX_ANCHORS = 10; // 锚点上限
// 1. 严格的资源释放(必须在 onFrameUpdate 末尾调用)
public safeReleaseFrame(frame: arEngine.ARFrame) {
try {
frame.release(); // 释放帧数据,防止底层缓冲区耗尽
} catch (err) {
console.error('Frame 释放失败:', err);
}
}
// 2. 锚点池管理:防止无限制创建锚点
public manageAnchorPool(newAnchor: arEngine.ARAnchor) {
this.anchorPool.push(newAnchor);
// 超过上限时,移除并释放最早的锚点
if (this.anchorPool.length > this.MAX_ANCHORS) {
const oldAnchor = this.anchorPool.shift();
if (oldAnchor) {
oldAnchor.detach(); // 从追踪中移除
oldAnchor.release(); // 释放底层资源
}
}
}
// 3. 页面销毁时清理所有锚点
public destroyAllAnchors() {
for (let anchor of this.anchorPool) {
anchor.detach();
anchor.release();
}
this.anchorPool = [];
}
}
更多推荐



所有评论(0)