在这里插入图片描述

一、前言

本文聚焦于实战环节——从 DevEco Studio 安装到第一个可运行的 BodyAR 项目,全程手把手指导。

本文将覆盖一个完整鸿蒙 AR 项目的初始化全流程,包括 SDK 配置、权限系统、签名管理和首次运行调试。每个步骤后都附有常见问题的排查方案,遇到任何报错都可以在对应章节找到解决方法。

二、开发环境准备

2.1 硬件要求

项目 最低要求 推荐配置
开发机 OS macOS 12 / Windows 10 macOS 14 / Windows 11
开发机内存 8 GB 16 GB
测试手机 麒麟 9000 芯片 麒麟 9010/9020 芯片
手机系统 HarmonyOS NEXT API 23 HarmonyOS NEXT API 24
数据线 USB-C 支持数据传输 USB 3.0+

2.2 安装 DevEco Studio

前往华为开发者官网下载最新版 DevEco Studio。

安装完成后打开 SDK Manager(Configure → Settings → SDK),确保勾选以下组件:

  • HarmonyOS SDK → API Version 23+ (6.1.0)
  • SDK Tools → Hvigor、Toolchains

2.3 在手机上安装 AR Engine

AR Engine 是预装在华为手机上的系统应用,但可能未更新到最新版。打开手机上的 AppGallery,搜索"AR Engine",如果显示"更新"则点击更新,如果显示"打开"说明已安装最新版。

如果 AppGallery 中搜索不到,说明该机型不支持 AR Engine。

三、创建项目

3.1 新建工程

DevEco Studio → Create Project → Empty Ability,填写以下配置:

配置项 说明
Project name BodyARDemo 项目名称
Bundle name com.example.bodyardemo 应用包名,全局唯一
Module name entry 主模块名
Device type Phone 目标设备
Compatible SDK 6.1.0(23) 最低兼容版本
Language ArkTS 声明式 UI
Ability template Empty Ability 空模板

点击 Finish 完成创建。

3.2 项目初始结构

在这里插入图片描述

BodyARDemo/
├── AppScope/
│   ├── app.json5                    # 应用全局标识
│   └── resources/base/
│       ├── element/string.json      # 应用名称
│       └── media/                   # 应用图标
├── entry/
│   ├── src/main/
│   │   ├── ets/
│   │   │   ├── entryability/        # UIAbility 入口
│   │   │   └── pages/               # 页面组件
│   │   ├── resources/               # 模块级资源
│   │   └── module.json5             # 模块配置(权限在这里)★
│   ├── build-profile.json5          # 模块构建配置
│   └── oh-package.json5            # 模块依赖
├── build-profile.json5              # 工程级构建配置(SDK版本、签名)★
├── hvigor/                          # 构建工具
└── oh-package.json5                 # 工程级依赖

关注带 ★ 的两个文件,它们是最核心的配置入口。

四、权限系统配置

4.1 理解鸿蒙的权限模型

鸿蒙将权限分为两大类:

类型 标识 授予方式 示例
系统授权 (system_grant) "when":"" 安装时自动授予 GYROSCOPE, ACCELEROMETER, INTERNET
用户授权 (user_grant) "when":"inuse" 运行时弹窗用户确认 CAMERA, MICROPHONE, LOCATION

BodyAR 需要三个权限:1 个 user_grant(CAMERA)+ 2 个 system_grant(GYROSCOPE、ACCELEROMETER)。

4.2 在 module.json5 中声明权限

打开 entry/src/main/module.json5,在 module 对象内、abilities 之前添加:

{
  "module": {
    "name": "entry",
    "type": "entry",
    "requestPermissions": [
      {
        "name": "ohos.permission.CAMERA",
        "reason": "$string:camera_permission_reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.GYROSCOPE"
        // system_grant 不需要 reason 和 usedScene
      },
      {
        "name": "ohos.permission.ACCELEROMETER"
      }
    ],
    "abilities": [
      // ...
    ]
  }
}

4.3 权限申请理由文本

entry/src/main/resources/base/element/string.json 中添加:

{
  "string": [
    {
      "name": "camera_permission_reason",
      "value": "相机权限用于获取实时画面以进行人体骨骼追踪"
    }
  ]
}

4.4 运行时请求相机权限

CAMERA 是受限权限,必须在代码中动态申请。在页面的 onStartTracking 方法中添加:

import { abilityAccessCtrl } from '@kit.AbilityKit';

async function requestCameraPermission(): Promise<boolean> {
  const atManager = abilityAccessCtrl.createAtManager();
  try {
    const context = getContext(this);
    const tokenId = context.applicationInfo.accessTokenId;
    const grantStatus = await atManager.checkAccessToken(
      tokenId, 'ohos.permission.CAMERA'
    );

    // 首次使用:显示权限弹窗
    if (grantStatus !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
      const result = await atManager.requestPermissionsFromUser(
        context, ['ohos.permission.CAMERA']
      );
      // authResults[0] === 0 表示用户点击了"允许"
      return result.authResults[0] === 0;
    }

    return true; // 已授权
  } catch (e) {
    console.error('权限检查失败: ' + JSON.stringify(e));
    return false;
  }
}

权限申请弹窗的行为:

  • 用户点击"允许" → authResults[0] === 0,返回 true
  • 用户点击"拒绝" → authResults[0] !== 0,返回 false,显示提示引导用户去设置中开启
  • 用户连续拒绝两次 → 系统不再弹窗,需要引导用户去系统设置手动开启

五、配置 SDK 版本与签名

5.1 SDK 版本配置

打开工程级 build-profile.json5,确认 SDK 版本与目标手机匹配:

{
  "app": {
    "signingConfigs": [],
    "products": [
      {
        "name": "default",
        "signingConfig": "default",
        "targetSdkVersion": "6.1.0(23)",
        "compatibleSdkVersion": "6.1.0(23)",
        "runtimeOS": "HarmonyOS",
        "buildOption": {
          "strictMode": {
            "caseSensitiveCheck": true,
            "useNormalizedOHMUrl": true
          }
        }
      }
    ]
  }
}

三个 SDK 版本字段的关系:

compatibleSdkVersion ≤ targetSdkVersion ≤ compileSdkVersion
  • compatibleSdkVersion:最低支持的 API 版本。低于此版本的设备无法安装应用
  • targetSdkVersion:目标 API 版本。应用在该版本的行为下运行
  • compileSdkVersion:编译使用的 SDK 版本。默认等于 IDE 自带的 SDK 版本

5.2 签名配置

调试签名用于将应用部署到真机。在 DevEco Studio 中:

  1. File → Project Structure → Signing Configs
  2. 勾选 “Automatically generate signature”
  3. 点击 Apply

系统会自动在 ~/.ohos/config/ 目录下生成调试证书、密钥存储文件和 Profile,并将其路径写入 build-profile.json5signingConfigs 中。

如果提交到 Git,务必把 signingConfigs 清空(设为 []),避免泄露密钥密码:

"signingConfigs": [
  // 请通过 DevEco Studio 自动生成,不要硬编码密码提交到仓库
]

六、安装依赖

6.1 核心依赖

BodyAR 依赖两个系统 Kit:

// 不需要在 oh-package.json5 中显式添加,
// 系统 Kit 通过 import 即可使用
import { arEngine, ARView, arViewController } from '@kit.AREngine';
import { Scene, Node } from '@kit.ArkGraphics3D';

注意 @kit.AREngine系统能力SystemCapability.AREngine.Core),不在 npm 仓库中。编译时会产生"not supported on all devices"的警告,这是正常的——AR Engine 只在部分设备上可用。

6.2 创建 AR 目录结构

entry/src/main/ets/ 下创建 ar/ 目录,用于存放 AR 相关模块:

ets/
├── ar/
│   ├── BodyTypes.ets          # 类型定义 + 骨骼连线配置
│   ├── BodyAREngine.ets       # AR 会话封装
│   ├── AngleCalculator.ets    # 关节角度计算
│   ├── ActionCounter.ets      # 动作计数状态机
│   └── DataExporter.ets       # 数据导出
├── entryability/
│   └── EntryAbility.ets
└── pages/
    ├── Index.ets              # 启动页
    └── BodyARPage.ets         # AR 主页面

七、编写最小可运行 Demo

7.1 AR 引擎封装

创建 BodyAREngine.ets,封装 AR 会话的核心生命周期:

import { arEngine, arViewController } from '@kit.AREngine';
import { Scene, Node } from '@kit.ArkGraphics3D';
import { BusinessError } from '@kit.BasicServicesKit';

export class BodyAREngine {
  private viewContext: arViewController.ARViewContext | null = null;
  private scene: Scene | null = null;
  private callback?: (bodies: arEngine.ARBody[]) => void;

  setCallback(cb: (bodies: arEngine.ARBody[]) => void): void {
    this.callback = cb;
  }

  async start(): Promise<arViewController.ARViewContext> {
    // 1. 加载 3D 场景(必须)
    this.scene = await Scene.load();

    // 2. 创建 AR 视图上下文
    const vc = new arViewController.ARViewContext();
    vc.scene = this.scene;

    // 3. 帧回调
    const callbackImpl = new ARViewCallbackImpl();
    callbackImpl.setCallback((bodies) => {
      this.callback?.(bodies);
    });
    vc.callback = callbackImpl;

    // 4. 配置 Body Tracking 模式
    vc.config = {
      type: arEngine.ARType.BODY,
      powerMode: arEngine.ARPowerMode.NORMAL,
      focusMode: arEngine.ARFocusMode.AUTO,
      maxDetectedBodyNum: 1,
      cameraLensFacing: 0  // 0=后置, 1=前置
    };

    // 5. 启动 AR 会话
    await vc.init();
    this.viewContext = vc;
    return vc;
  }

  async stop(): Promise<void> {
    if (this.viewContext) {
      this.viewContext.destroy();
      this.viewContext = null;
    }
    this.scene = null;
  }
}

7.2 AR 视图回调实现

class ARViewCallbackImpl extends arViewController.ARViewCallback {
  private callback?: (bodies: arEngine.ARBody[]) => void;

  setCallback(cb: (bodies: arEngine.ARBody[]) => void): void {
    this.callback = cb;
  }

  onAnchorAdd(ctx: arViewController.ARViewContext,
              node: Node, anchor: arEngine.ARAnchor): void {
    // Body tracking 不使用锚点,空实现即可
  }

  onAnchorUpdate(ctx: arViewController.ARViewContext,
                 node: Node, anchor: arEngine.ARAnchor): void {
    // Body tracking 不使用锚点,空实现即可
  }

  async onFrameUpdate(ctx: arViewController.ARViewContext,
                      sysBootTs: number): Promise<void> {
    if (!ctx.session) return;
    try {
      const session: arEngine.ARSession = ctx.session;
      const frame: arEngine.ARFrame = session.getFrame();
      const bodies: arEngine.ARBody[] = frame.acquireBodySkeleton();
      this.callback?.(bodies);
      await frame.release();  // ★ 必须释放!
    } catch (error) {
      const err: BusinessError = error as BusinessError;
      console.error(`onFrameUpdate: ${err.code} ${err.message}`);
    }
  }
}

三个关键注意事项:

  1. ARViewCallback 是抽象类,必须实现 onAnchorAddonAnchorUpdateonFrameUpdate 三个方法
  2. onAnchorAddonAnchorUpdate 在 Body 模式下不会触发,但必须实现(不能是抽象类)
  3. 每帧的 frame.release() 是硬性要求——不释放会导致底层缓冲区快速耗尽

7.3 ArkUI 页面

import { arEngine, ARView, arViewController } from '@kit.AREngine';
import { BodyAREngine } from '../ar/BodyAREngine';

@Entry
@Component
struct BodyARPage {
  @State arContext: arViewController.ARViewContext | null = null;
  @State isTracking: boolean = false;
  @State statusText: string = '就绪';
  private engine: BodyAREngine = new BodyAREngine();

  aboutToAppear(): void {
    this.engine.setCallback((bodies: arEngine.ARBody[]) => {
      // 在此处理每帧检测到的人体列表
      console.info('检测到 ' + bodies.length + ' 人');
    });
  }

  async onStartTracking(): Promise<void> {
    try {
      this.statusText = '初始化中...';
      this.arContext = await this.engine.start();
      this.isTracking = true;
      this.statusText = '追踪中';
    } catch (e) {
      this.statusText = '错误: ' + JSON.stringify(e);
    }
  }

  build() {
    Column() {
      Stack() {
        if (this.arContext) {
          ARView({ context: this.arContext })
            .width('100%').height('100%')
        }
      }.layoutWeight(1)

      Text(this.statusText).padding(8)

      Button(this.isTracking ? '停止' : '开始')
        .onClick(() => this.isTracking ?
          this.engine.stop() : this.onStartTracking())
    }
  }
}

7.4 注册页面路由

main_pages.json 中注册页面:

{
  "src": [
    "pages/Index",
    "pages/BodyARPage"
  ]
}

7.5 运行与验证

  1. 用 USB 数据线连接手机 → 开启开发者模式 + USB 调试
  2. DevEco Studio → Run → Run ‘entry’
  3. 等待编译部署 → 手机上自动打开应用
  4. 点击"开始" → 弹窗请求相机权限 → 授予
  5. 如果看到相机画面 → AR 会话启动成功

八、常见问题排查手册

问题 1:编译报错 “arkts-no-destruct-decls”

原因:ArkTS 不支持 ES6 解构语法。

// 错误
const { x, y } = point;

// 正确
const x = point.x;
const y = point.y;

问题 2:部署报 “SDK version mismatch”

原因compatibleSdkVersion 与手机系统 API 版本不一致。

解决:检查手机 HarmonyOS 版本对应的 API Level,同步修改 build-profile.json5 中的三个版本号。API 23 对应 "6.1.0(23)"

问题 3:init() 返回错误码 201

原因:缺少权限。

检查清单

  • module.json5 是否声明了 CAMERA、GYROSCOPE、ACCELEROMETER 三个权限?
  • 是否在代码中调用了 requestPermissionsFromUser 请求 CAMERA?
  • 手机上是否已安装/更新 AR Engine 应用?

问题 4:相机画面正常但始终检测不到人体

排查步骤

  1. 确认使用后置摄像头cameraLensFacing: 0)- 大多数设备仅后置支持 Body Tracking
  2. 确认有人在摄像头前 1.5-3 米范围内
  3. 确认光线充足(> 200 lux)
  4. 确认人体全身在画面中(不要被裁剪)
  5. onFrameUpdate 中打印 bodies.length,观察是否始终为 0

问题 5:compatibleSdkVersion 和 releaseType 不匹配

原因runtimeOS 字段配置错误或缺失。

解决:在工程级 build-profile.json5 的 products 中添加 "runtimeOS": "HarmonyOS",注意该字段只能写在工程级,模块级 build-profile.json5 中不要写。

九、开发效率工具

9.1 状态栏调试

在 AR 相机画面上叠加一个状态文本,显示帧计数和检测人数:

this.frameCount++;
this.statusText = '检测: ' + bodies.length +
  ' 人 / 帧#' + this.frameCount;

帧计数器持续增长 = AR 回调正常工作,帧计数器不动 = 回调未触发 = AR 会话异常。

9.2 HiLog 日志

在这里插入图片描述

import { hilog } from '@kit.PerformanceAnalysisKit';
const DOMAIN = 0x0001;

hilog.info(DOMAIN, 'BodyAR', 'ARView init success');
hilog.error(DOMAIN, 'BodyAR', 'Failed: %{public}s', errMsg);

通过 DevEco Studio 的 Log 面板筛选 Domain 查看。

十、小结

本文覆盖了从零搭建 BodyAR 项目的完整流程:

  1. 环境准备:DevEco Studio + HarmonyOS SDK API 23+ + AR Engine 手机应用
  2. 权限系统:3 个必要权限的 module.json5 声明 + CAMERA 运行时申请
  3. SDK 版本:compatibleSdkVersion 与目标手机 API Level 匹配
  4. 核心代码:BodyAREngine 会话封装 + ARViewCallback 帧回调 + ARView 组件
  5. 排查手册:5 个高频问题的解决方案

项目搭建完成后,深入理解 AR 会话的内部机制是写出稳定应用的前提——ARConfig 的每一项参数都直接影响追踪精度和功耗。

Logo

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

更多推荐