文章目录
-
- 每日一句正能量
- 前言
- [一、Face AR & Body AR 能力概述与技术亮点](#一、Face AR & Body AR 能力概述与技术亮点)
- 二、项目实战:"虚实融合健身镜"架构设计
-
- [2.1 应用场景与功能规划](#2.1 应用场景与功能规划)
- [2.2 技术架构图](#2.2 技术架构图)
- 三、环境配置与权限声明
-
- [3.1 模块依赖配置](#3.1 模块依赖配置)
- [3.2 权限声明(module.json5)](#3.2 权限声明(module.json5))
- 四、核心代码实战
-
- [4.1 AR 会话初始化与双模态配置](#4.1 AR 会话初始化与双模态配置)
- [4.2 Face AR:微表情追踪与虚拟面具渲染](#4.2 Face AR:微表情追踪与虚拟面具渲染)
- [4.3 Body AR:骨骼关键点识别与动作计数](#4.3 Body AR:骨骼关键点识别与动作计数)
- [4.4 主页面:AR 视图与 UI 叠加层整合](#4.4 主页面:AR 视图与 UI 叠加层整合)
- 五、关键性能优化策略
-
- [5.1 双模态并发性能调优](#5.1 双模态并发性能调优)
- [5.2 骨骼点渲染优化](#5.2 骨骼点渲染优化)
- 六、调试与常见问题排查
- 七、总结与展望

每日一句正能量
心随春风暖,景随春光柔。行于温柔的春日,静看繁花满枝头。花色润了心境,花香醉了温柔。不忧花期短长,心有芬芳自赏。一路向阳而行,四季皆有花香。
前言
摘要:HarmonyOS 6(API 23)重磅引入了 Face AR 与 Body AR 能力,为开发者提供了人脸微表情追踪(BlendShape)和人体骨骼关键点识别两大核心能力。本文将带你从零实战,基于 AR Engine 6.1.0 开发一款"虚实融合健身镜"应用,实现实时面部特效叠加、人体姿态检测与运动计数,探索空间交互的无限可能。
一、Face AR & Body AR 能力概述与技术亮点
HarmonyOS 6.1.0(API 23)在 AR Engine 中新增了 Face AR 和 Body AR 两大核心模块 :
| 能力模块 | 核心特性 | 技术参数 |
|---|---|---|
| Face AR | 人脸位姿跟踪、Mesh 建模、微表情(BlendShape)跟踪 | 4000+ 顶点、7000+ 三角面片、64 种表情参数 |
| Body AR | 人体骨骼关键点跟踪、轮廓虚实遮挡 | 20+ 骨骼关键点、支持单人/多人追踪 |
技术亮点:
- 端侧实时处理:所有图像数据仅在端侧处理,不上传云端,保障用户隐私安全
- 高精度 BlendShape:支持眼睛、眉毛、眼球、嘴巴、舌头等 64 种微表情参数实时输出
- 多模态融合:Face AR 与 Body AR 可并发运行,实现"面部+肢体"的全方位空间交互
- ARView 组件化 :通过
@hms.core.ar.arview提供声明式 AR 视图,降低 3D 渲染门槛
二、项目实战:"虚实融合健身镜"架构设计
2.1 应用场景与功能规划
本应用面向家庭健身场景,核心功能包括:
- AR 面部特效:实时叠加虚拟面具、动态贴纸,增强运动趣味性
- 姿态检测与计数:识别深蹲、开合跳等动作,自动计数并纠正姿势
- 虚实融合渲染:虚拟教练形象与真人同屏展示,提供动作指导
2.2 技术架构图
┌─────────────────────────────────────────────────────────┐
│ UI Layer (ArkUI) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ XComponent │ │ ARView │ │ StatsOverlay │ │
│ │ (相机预览) │ │ (3D渲染) │ │ (运动数据面板) │ │
│ └──────┬──────┘ └──────┬──────┘ └─────────────────┘ │
└─────────┼────────────────┼─────────────────────────────┘
│ │
┌─────────▼────────────────▼─────────────────────────────┐
│ AR Engine 6.1.0 (API 23) │
│ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ Face AR Track │ │ Body AR Track │ │
│ │ · ARFaceAnchor │ │ · ARBodySkeleton │ │
│ │ · ARBlendShapes │ │ · ARBodyLandmark2D │ │
│ │ · ARGeometry │ │ · acquireBodySkeleton() │ │
│ └─────────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
三、环境配置与权限声明
3.1 模块依赖配置
在 oh-package.json5 中添加 AR Engine 依赖:
json
{
"dependencies": {
"@hms.core.ar.arengine": "^6.1.0",
"@hms.core.ar.arview": "^6.1.0"
}
}
3.2 权限声明(module.json5)
json
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.CAMERA",
"reason": "$string:camera_permission_reason"
},
{
"name": "ohos.permission.INTERNET",
"reason": "$string:network_permission_reason"
}
]
}
}
注意:AR Engine 所有数据处理均在端侧完成,无需网络权限即可运行核心功能 。
四、核心代码实战
4.1 AR 会话初始化与双模态配置
代码亮点 :HarmonyOS 6 支持通过 ARFeatureType 同时启用 Face 和 Body 追踪,并可通过 ARConfig 配置多脸模式和最大检测人体数量。
typescript
// arengine/ARSessionManager.ets
import { arEngine, ARConfig, ARFeatureType, ARMultiFaceMode } from '@hms.core.ar.arengine';
export class ARSessionManager {
private session: arEngine.ARSession | null = null;
private config: ARConfig | null = null;
/**
* 初始化AR会话,同时启用Face AR和Body AR
* 关键配置:
* 1. featureType: 同时启用 FACE | BODY 能力
* 2. multiFaceMode: 支持多人脸追踪(健身镜场景可能需要双人PK)
* 3. maxDetectedBodyNum: 最大检测人体数,建议设为2(支持双人健身)
*/
async initialize(context: Context): Promise<void> {
try {
// 检查AR Engine是否已安装
const isReady = await arEngine.isAREngineReady(context);
if (!isReady) {
// 引导用户前往应用市场安装AR Engine
await this.promptInstallAREngine(context);
return;
}
// 创建AR会话
this.session = new arEngine.ARSession(context);
// 配置双模态追踪
this.config = new ARConfig();
this.config.featureType = ARFeatureType.ARENGINE_FEATURE_TYPE_FACE |
ARFeatureType.ARENGINE_FEATURE_TYPE_BODY;
// 多人脸模式:支持双人同时健身时的面部特效
this.config.multiFaceMode = ARMultiFaceMode.MULTIFACE_ENABLE;
// 最大检测人体数:2人(适合家庭双人健身场景)
this.config.maxDetectedBodyNum = 2;
// 相机镜头:前置摄像头(健身镜场景)
this.config.cameraLensFacing = arEngine.ARCameraLensFacing.FRONT;
// 配置并启动会话
this.session.configure(this.config);
await this.session.start();
console.info('AR Session 启动成功,Face AR + Body AR 双模态已就绪');
} catch (error) {
console.error('AR Session 初始化失败:', error);
throw error;
}
}
/**
* 获取当前帧的AR数据(Face + Body)
*/
acquireFrameData(): { faces: arEngine.ARFaceAnchor[], bodies: arEngine.ARBody[] } | null {
if (!this.session) return null;
const frame = this.session.acquireFrame();
if (!frame) return null;
// 获取人脸追踪数据
const faceAnchors = frame.getFaceAnchors ? frame.getFaceAnchors() : [];
// 获取人体骨骼数据(HarmonyOS 6新增API)
const bodies = frame.acquireBodySkeleton ? frame.acquireBodySkeleton() : [];
return { faces: faceAnchors, bodies };
}
/**
* 释放资源
*/
async release(): Promise<void> {
if (this.session) {
await this.session.stop();
this.session = null;
}
}
private async promptInstallAREngine(context: Context): Promise<void> {
// 跳转应用市场下载AR Engine
// 具体实现略...
}
}
4.2 Face AR:微表情追踪与虚拟面具渲染
代码亮点 :通过 ARBlendShapes 获取 64 种表情参数,驱动 3D 虚拟面具实时形变;ARLandmark 提供 2D/3D 关键点用于贴纸精准贴合。
typescript
// face/FaceARController.ets
import { arEngine, ARFaceAnchor, ARBlendShapeType } from '@hms.core.ar.arengine';
import { ARViewContext, LandmarkType } from '@hms.core.ar.arview';
export class FaceARController {
private arViewContext: ARViewContext | null = null;
/**
* 处理人脸追踪数据,驱动虚拟面具
* @param faceAnchor 人脸锚点数据
*/
processFaceTracking(faceAnchor: ARFaceAnchor): void {
const face = faceAnchor.getFace();
if (!face) return;
// 1. 获取人脸几何网格(4000+顶点,用于3D面具贴合)
const geometry = face.getGeometry();
console.info(`人脸Mesh顶点数: ${geometry.verticesSize}, 三角面数: ${geometry.triangleIndicesCount}`);
// 2. 获取微表情BlendShape参数(64种表情系数)
const blendShapes = face.getBlendShapes();
const shapeData = blendShapes.getData();
const shapeTypes = blendShapes.getTypes();
// 解析关键表情参数
const expressionMap = this.parseBlendShapes(shapeTypes, shapeData);
// 3. 获取人脸关键点(用于2D贴纸定位)
const landmark = face.getLandmark();
const vertices2D = landmark.getVertices2D(); // 2D屏幕坐标
const vertices3D = landmark.getVertices3D(); // 3D空间坐标
// 4. 驱动虚拟形象
this.driveVirtualAvatar(expressionMap, vertices3D);
// 5. 叠加动态贴纸(如运动时的汗水、火焰特效)
this.overlayDynamicStickers(vertices2D, expressionMap);
}
/**
* 解析BlendShape数据为可读Map
* 重点关注健身场景的表情:张嘴呼吸、眨眼、皱眉等
*/
private parseBlendShapes(types: ARBlendShapeType[], data: ArrayBuffer): Map<ARBlendShapeType, number> {
const result = new Map<ARBlendShapeType, number>();
const floatView = new Float32Array(data);
types.forEach((type, index) => {
result.set(type, floatView[index]);
});
// 输出关键表情参数(调试用)
console.info(`左眼眨眼: ${result.get(ARBlendShapeType.EYE_BLINK_LEFT)?.toFixed(3)}`);
console.info(`张嘴程度: ${result.get(ARBlendShapeType.JAW_OPEN)?.toFixed(3)}`);
return result;
}
/**
* 驱动虚拟形象表情
* 利用ARView的setBlendShapeWeight将真实表情映射到3D模型
*/
private driveVirtualAvatar(expressionMap: Map<ARBlendShapeType, number>, vertices3D: ArrayBuffer): void {
if (!this.arViewContext) return;
const avatarNode = this.arViewContext.getScene().getNodeByName('avatar_root');
if (!avatarNode) return;
// 映射真实表情到虚拟形象
expressionMap.forEach((weight, type) => {
// setBlendShapeWeight: 将真实人脸的blendshape权重应用到3D模型对应节点
this.arViewContext!.setBlendShapeWeight(avatarNode, type, weight);
});
}
/**
* 根据表情状态叠加动态贴纸
* 例如:检测到高强度运动时(张嘴+皱眉),触发火焰特效
*/
private async overlayDynamicStickers(vertices2D: ArrayBuffer, expressionMap: Map<ARBlendShapeType, number>): Promise<void> {
if (!this.arViewContext) return;
// 检测运动强度(通过嘴部开合和眉毛动作判断)
const jawOpen = expressionMap.get(ARBlendShapeType.JAW_OPEN) || 0;
const browDown = expressionMap.get(ARBlendShapeType.BROW_DOWN_LEFT) || 0;
const isHighIntensity = jawOpen > 0.6 && browDown > 0.4;
if (isHighIntensity) {
// 在额头位置加载火焰特效贴纸
await this.arViewContext.loadAsset('assets/effects/fire_sticker.gltf', LandmarkType.CENTER_OF_FACE);
} else {
// 移除特效
await this.arViewContext.removeAsset(LandmarkType.CENTER_OF_FACE);
}
}
setARViewContext(context: ARViewContext): void {
this.arViewContext = context;
}
}
4.3 Body AR:骨骼关键点识别与动作计数
代码亮点 :ARBody 提供 20+ 骨骼关键点(含 2D 坐标、置信度、关键点类型),支持实时姿态估计与动作计数。
typescript
// body/BodyARController.ets
import { arEngine, ARBody, ARBodyLandmarkType, ARBodyLandmark2D } from '@hms.core.ar.arengine';
/**
* 动作类型枚举
*/
export enum ExerciseType {
SQUAT = 'squat', // 深蹲
JUMPING_JACK = 'jumping_jack', // 开合跳
HIGH_KNEE = 'high_knee' // 高抬腿
}
/**
* 动作状态机
*/
export enum MotionState {
IDLE = 'idle',
PREPARING = 'preparing',
EXECUTING = 'executing',
COMPLETING = 'completing'
}
export class BodyARController {
private currentExercise: ExerciseType = ExerciseType.SQUAT;
private motionState: MotionState = MotionState.IDLE;
private repCount: number = 0;
private lastStateTime: number = 0;
// 动作阈值配置
private readonly SQUAT_THRESHOLD = {
hipKneeAngleMin: 80, // 深蹲到底角度
hipKneeAngleMax: 160, // 站立角度
confidenceMin: 0.7 // 最小置信度
};
/**
* 处理人体骨骼数据
* @param bodies 检测到的人体数组(支持多人)
*/
processBodyTracking(bodies: ARBody[]): void {
if (bodies.length === 0) {
console.info('未检测到人体');
return;
}
// 取置信度最高的人体进行追踪(健身镜通常聚焦单人)
const primaryBody = this.selectPrimaryBody(bodies);
const landmarks = primaryBody.getLandmarks2D();
// 过滤低置信度关键点
const validLandmarks = landmarks.filter(lm => lm.confidence > 0.6);
// 根据当前训练类型执行动作识别
switch (this.currentExercise) {
case ExerciseType.SQUAT:
this.detectSquat(validLandmarks);
break;
case ExerciseType.JUMPING_JACK:
this.detectJumpingJack(validLandmarks);
break;
case ExerciseType.HIGH_KNEE:
this.detectHighKnee(validLandmarks);
break;
}
}
/**
* 深蹲动作识别算法
* 核心逻辑:通过髋-膝-踝三点计算膝关节角度,判断下蹲深度
*/
private detectSquat(landmarks: ARBodyLandmark2D[]): void {
// 提取关键骨骼点
const leftHip = this.findLandmark(landmarks, ARBodyLandmarkType.LEFT_HIP);
const leftKnee = this.findLandmark(landmarks, ARBodyLandmarkType.LEFT_KNEE);
const leftAnkle = this.findLandmark(landmarks, ARBodyLandmarkType.LEFT_ANKLE);
const rightHip = this.findLandmark(landmarks, ARBodyLandmarkType.RIGHT_HIP);
const rightKnee = this.findLandmark(landmarks, ARBodyLandmarkType.RIGHT_KNEE);
const rightAnkle = this.findLandmark(landmarks, ARBodyLandmarkType.RIGHT_ANKLE);
if (!leftHip || !leftKnee || !leftAnkle || !rightHip || !rightKnee || !rightAnkle) {
return;
}
// 计算左右膝关节角度
const leftKneeAngle = this.calculateAngle(leftHip, leftKnee, leftAnkle);
const rightKneeAngle = this.calculateAngle(rightHip, rightKnee, rightAnkle);
const avgKneeAngle = (leftKneeAngle + rightKneeAngle) / 2;
// 状态机转换
const now = Date.now();
switch (this.motionState) {
case MotionState.IDLE:
if (avgKneeAngle < this.SQUAT_THRESHOLD.hipKneeAngleMax - 20) {
this.motionState = MotionState.PREPARING;
console.info('准备下蹲...');
}
break;
case MotionState.PREPARING:
if (avgKneeAngle < this.SQUAT_THRESHOLD.hipKneeAngleMin + 10) {
this.motionState = MotionState.EXECUTING;
console.info('下蹲到位!');
}
break;
case MotionState.EXECUTING:
if (avgKneeAngle > this.SQUAT_THRESHOLD.hipKneeAngleMax - 10) {
this.motionState = MotionState.COMPLETING;
this.repCount++;
this.lastStateTime = now;
console.info(`完成第 ${this.repCount} 个深蹲!`);
// 触发完成特效(通过Face AR叠加庆祝贴纸)
this.triggerRepCompleteEffect();
}
break;
case MotionState.COMPLETING:
// 防抖:500ms后才能开始下一次
if (now - this.lastStateTime > 500) {
this.motionState = MotionState.IDLE;
}
break;
}
}
/**
* 开合跳动作识别
* 核心逻辑:通过手腕与肩膀的相对位置判断开合状态
*/
private detectJumpingJack(landmarks: ARBodyLandmark2D[]): void {
const leftWrist = this.findLandmark(landmarks, ARBodyLandmarkType.LEFT_WRIST);
const rightWrist = this.findLandmark(landmarks, ARBodyLandmarkType.RIGHT_WRIST);
const leftShoulder = this.findLandmark(landmarks, ARBodyLandmarkType.LEFT_SHOULDER);
const rightShoulder = this.findLandmark(landmarks, ARBodyLandmarkType.RIGHT_SHOULDER);
if (!leftWrist || !rightWrist || !leftShoulder || !rightShoulder) return;
// 判断手臂是否举过头顶(手腕Y坐标 < 肩膀Y坐标,注意屏幕坐标系Y向下)
const isArmsUp = leftWrist.y < leftShoulder.y - 50 && rightWrist.y < rightShoulder.y - 50;
// 判断手臂是否放下
const isArmsDown = leftWrist.y > leftShoulder.y + 20 && rightWrist.y > rightShoulder.y + 20;
if (this.motionState === MotionState.IDLE && isArmsUp) {
this.motionState = MotionState.EXECUTING;
} else if (this.motionState === MotionState.EXECUTING && isArmsDown) {
this.repCount++;
this.motionState = MotionState.IDLE;
console.info(`完成第 ${this.repCount} 个开合跳!`);
}
}
/**
* 三点计算角度(余弦定理)
*/
private calculateAngle(p1: ARBodyLandmark2D, p2: ARBodyLandmark2D, p3: ARBodyLandmark2D): number {
const radians = Math.atan2(p3.y - p2.y, p3.x - p2.x) -
Math.atan2(p1.y - p2.y, p1.x - p2.x);
let angle = Math.abs(radians * 180.0 / Math.PI);
if (angle > 180.0) angle = 360 - angle;
return angle;
}
/**
* 在骨骼点数组中查找指定类型的关键点
*/
private findLandmark(landmarks: ARBodyLandmark2D[], type: ARBodyLandmarkType): ARBodyLandmark2D | undefined {
return landmarks.find(lm => lm.type === type && lm.isValid);
}
/**
* 选择置信度最高的人体
*/
private selectPrimaryBody(bodies: ARBody[]): ARBody {
return bodies.reduce((prev, current) => {
const prevLandmarks = prev.getLandmarks2D();
const currLandmarks = current.getLandmarks2D();
const prevConfidence = prevLandmarks.reduce((sum, lm) => sum + lm.confidence, 0) / prevLandmarks.length;
const currConfidence = currLandmarks.reduce((sum, lm) => sum + lm.confidence, 0) / currLandmarks.length;
return currConfidence > prevConfidence ? current : prev;
});
}
/**
* 触发完成特效(与Face AR联动)
*/
private triggerRepCompleteEffect(): void {
// 通过事件总线通知Face AR控制器叠加庆祝特效
// 具体实现略...
}
getRepCount(): number { return this.repCount; }
getCurrentExercise(): ExerciseType { return this.currentExercise; }
setExercise(type: ExerciseType): void {
this.currentExercise = type;
this.repCount = 0;
this.motionState = MotionState.IDLE;
}
}
4.4 主页面:AR 视图与 UI 叠加层整合
代码亮点 :使用 XComponent 承载相机预览,ARView 负责 3D 渲染,通过 Stack 叠加运动数据面板,实现"虚实融合"的沉浸体验。
typescript
// pages/FitnessMirrorPage.ets
import { arEngine } from '@hms.core.ar.arengine';
import { ARView, ARViewContext } from '@hms.core.ar.arview';
import { ARSessionManager } from '../arengine/ARSessionManager';
import { FaceARController } from '../face/FaceARController';
import { BodyARController, ExerciseType } from '../body/BodyARController';
@Entry
@Component
struct FitnessMirrorPage {
private sessionManager: ARSessionManager = new ARSessionManager();
private faceController: FaceARController = new FaceARController();
private bodyController: BodyARController = new BodyARController();
@State repCount: number = 0;
@State currentExercise: string = '深蹲';
@State calories: number = 0;
@State faceStatus: string = '未检测到人脸';
@State bodyStatus: string = '未检测到人体';
// ARView引用
private arViewContext: ARViewContext | null = null;
aboutToAppear() {
this.initializeAR();
}
aboutToDisappear() {
this.sessionManager.release();
}
async initializeAR() {
const context = getContext(this);
await this.sessionManager.initialize(context);
// 启动AR数据循环
this.startARLoop();
}
/**
* AR数据主循环:每帧获取Face + Body数据并处理
*/
startARLoop() {
const loop = () => {
const frameData = this.sessionManager.acquireFrameData();
if (frameData) {
// 处理Face AR
if (frameData.faces.length > 0) {
this.faceStatus = `检测到 ${frameData.faces.length} 张人脸`;
frameData.faces.forEach(face => this.faceController.processFaceTracking(face));
} else {
this.faceStatus = '未检测到人脸';
}
// 处理Body AR
if (frameData.bodies.length > 0) {
this.bodyStatus = `检测到 ${frameData.bodies.length} 人`;
this.bodyController.processBodyTracking(frameData.bodies);
// 更新UI状态
this.repCount = this.bodyController.getRepCount();
this.calories = Math.floor(this.repCount * 0.5); // 简单热量估算
} else {
this.bodyStatus = '未检测到人体';
}
}
// 下一帧
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
}
build() {
Stack({ alignContent: Alignment.Bottom }) {
// 层1:相机预览(XComponent)
XComponent({
id: 'camera_preview',
type: XComponentType.SURFACE,
controller: new XComponentController()
})
.width('100%')
.height('100%')
.backgroundColor(Color.Black)
.onLoad(() => {
// 绑定相机预览Surface
this.sessionManager.bindPreviewSurface(/* surfaceId */);
})
// 层2:AR 3D渲染视图(虚拟面具、骨骼连线、虚拟教练)
ARView({
onReady: (context: ARViewContext) => {
this.arViewContext = context;
this.faceController.setARViewContext(context);
// 加载虚拟教练模型
context.loadAsset('assets/models/coach_avatar.gltf', LandmarkType.CENTER_OF_FACE);
}
})
.width('100%')
.height('100%')
.backgroundColor(Color.Transparent)
// 层3:运动数据叠加层(沉浸光感设计)
Column() {
// 顶部状态栏
Row() {
Text(this.faceStatus)
.fontSize(14)
.fontColor(Color.White)
.padding(8)
.backgroundColor('rgba(0,0,0,0.5)')
.borderRadius(4)
Text(this.bodyStatus)
.fontSize(14)
.fontColor(Color.White)
.padding(8)
.backgroundColor('rgba(0,0,0,0.5)')
.borderRadius(4)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding(16)
Blank()
// 底部运动面板(悬浮卡片 + 沉浸光感)
Column() {
// 当前动作类型
Text(this.currentExercise)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
// 计数器大数字
Text(`${this.repCount}`)
.fontSize(80)
.fontWeight(FontWeight.Bold)
.fontColor('#00D26A') // 鸿蒙品牌绿
.shadow({ radius: 10, color: 'rgba(0,210,106,0.5)' })
Text('次')
.fontSize(16)
.fontColor('rgba(255,255,255,0.7)')
// 热量消耗
Row() {
Image($r('app.media.icon_fire'))
.width(20)
.height(20)
Text(`${this.calories} kcal`)
.fontSize(14)
.fontColor('#FF6B6B')
}
.margin({ top: 8 })
// 动作切换按钮
Row({ space: 12 }) {
Button('深蹲')
.type(ButtonType.Capsule)
.backgroundColor(this.currentExercise === '深蹲' ? '#00D26A' : 'rgba(255,255,255,0.2)')
.onClick(() => this.switchExercise(ExerciseType.SQUAT))
Button('开合跳')
.type(ButtonType.Capsule)
.backgroundColor(this.currentExercise === '开合跳' ? '#00D26A' : 'rgba(255,255,255,0.2)')
.onClick(() => this.switchExercise(ExerciseType.JUMPING_JACK))
Button('高抬腿')
.type(ButtonType.Capsule)
.backgroundColor(this.currentExercise === '高抬腿' ? '#00D26A' : 'rgba(255,255,255,0.2)')
.onClick(() => this.switchExercise(ExerciseType.HIGH_KNEE))
}
.margin({ top: 16 })
}
.width('90%')
.padding(24)
.margin({ bottom: 40 })
.backgroundColor('rgba(20,20,30,0.85)')
.borderRadius(24)
.backdropBlur(20) // 背景模糊,增强沉浸感
.shadow({ radius: 20, color: 'rgba(0,0,0,0.3)' })
}
.width('100%')
.height('100%')
.padding(16)
}
.width('100%')
.height('100%')
.backgroundColor(Color.Black)
}
switchExercise(type: ExerciseType) {
this.bodyController.setExercise(type);
const nameMap = {
[ExerciseType.SQUAT]: '深蹲',
[ExerciseType.JUMPING_JACK]: '开合跳',
[ExerciseType.HIGH_KNEE]: '高抬腿'
};
this.currentExercise = nameMap[type];
}
}
五、关键性能优化策略
5.1 双模态并发性能调优
Face AR 与 Body AR 同时运行时,需关注以下优化点:
typescript
// 优化1:降低相机分辨率(健身镜场景无需超高精度)
this.config.imageResolution = { width: 1280, height: 720 };
// 优化2:控制检测频率(人体追踪可降频至15fps,人脸保持30fps)
this.config.bodyTrackingFPS = 15;
this.config.faceTrackingFPS = 30;
// 优化3:及时释放ARFrame资源,防止内存泄漏
frame.release(); // 每帧处理完后必须调用
5.2 骨骼点渲染优化
typescript
// 使用ARKit的GPU实例化渲染骨骼连线,避免每帧创建销毁绘制对象
// 预创建骨骼连线Mesh,运行时仅更新顶点坐标
六、调试与常见问题排查
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| AR Session 启动失败 | AR Engine 未安装 | 引导用户前往华为应用市场安装 |
| 人脸追踪抖动 | 光线不足或逆光 | 建议用户面向光源,或启用自动曝光补偿 |
| 骨骼点漂移 | 人体部分遮挡 | 提示用户全身入镜,避免遮挡关节 |
| 表情映射不自然 | BlendShape权重未归一化 | 检查 getData() 返回的 Float32Array 范围 |
| 多人场景性能下降 | maxDetectedBodyNum 设置过大 | 根据场景需求合理设置(建议 ≤ 2) |
七、总结与展望
本文基于 HarmonyOS 6(API 23)的 Face AR 与 Body AR 能力,完整实战了一款"虚实融合健身镜"应用。核心要点总结:
- 双模态并发架构 :通过
ARFeatureType同时启用 Face 和 Body 追踪,实现"面部表情+肢体动作"的全方位交互 - 微表情驱动 :利用
ARBlendShapes的 64 种表情参数,精准驱动虚拟形象,实现"真人表情→虚拟面具"的实时映射 - 骨骼姿态估计:基于 20+ 关键点的 2D/3D 坐标,构建动作状态机,实现深蹲、开合跳等常见健身动作的自动计数
- 端侧隐私安全:所有图像数据本地处理,不上传云端,符合鸿蒙系统的隐私设计理念
未来扩展方向:
- 结合 HarmonyOS PC 的大屏优势,将健身镜应用扩展为家庭智慧屏应用
- 接入 运动健康服务,将运动数据同步至华为运动健康生态
- 利用 分布式软总线,实现手机、平板、智慧屏的多设备协同健身
转载自:https://blog.csdn.net/u014727709/article/details/160360756
欢迎 👍点赞✍评论⭐收藏,欢迎指正