HarmonyOS鸿蒙Next中类似华为AR测量的功能如何实现,有没有相关案例可以参考
HarmonyOS鸿蒙Next中类似华为AR测量的功能如何实现,有没有相关案例可以参考 【问题描述】:通过AR测量两点之间的距离,两个点的位置坐标上放置一个图片标记一下点的位置,两个点之间用直线连接,在直线的中间显示两个点的距离长度,现在主要卡点在于怎么在AR中绘制
【问题现象】:就类似于华为AR测量的那种效果一样
尊敬的开发者,您好,您可以使用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
加油,

生态市场搜索: AR测量组件 ,需要登录。
这个组件提供了识别空间立方体和嵌入式立方体空间的长、宽、高的能力,可用于测量立方体体积和嵌入式空间的大小。
或者直接在 DevEco Studio 里查找:View -> Tool Windows -> Partner Component

- 本组件中的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 仓库包含 ARMeasure 或 DistanceMeasurement 案例,支持点对点测距、面积测量等。参考 arkui_ace_engine 下 AR 相关示例代码即能快速上手。



