鸿蒙AI健身动作矫正系统开发指南 原创

进修的泡芙
发布于 2025-6-20 14:13
浏览
0收藏

鸿蒙AI健身动作矫正系统开发指南

一、系统架构设计

基于HarmonyOS的AI健身动作矫正系统,利用姿态识别和分布式能力实现以下功能:
实时姿态分析:通过@ohos.ai.body精准识别人体关键点

动作矫正:针对不同训练动作提供专业指导建议

多设备协同:手机捕捉动作,大屏展示矫正反馈

训练同步:多设备实时同步训练状态和进度

!https://example.com/harmony-ai-fitness-arch.png

二、核心代码实现
姿态识别服务封装

// PoseDetectionService.ets
import body from ‘@ohos.ai.body’;
import distributedData from ‘@ohos.distributedData’;

class PoseDetectionService {
private static instance: PoseDetectionService = null;
private bodyDetector: body.BodyDetector;
private dataManager: distributedData.DataManager;
private poseListeners: PoseListener[] = [];

private constructor() {
this.initBodyDetector();
this.initDataManager();
public static getInstance(): PoseDetectionService {

if (!PoseDetectionService.instance) {
  PoseDetectionService.instance = new PoseDetectionService();

return PoseDetectionService.instance;

private async initBodyDetector(): Promise<void> {

try {
  const context = getContext() as common.Context;
  this.bodyDetector = await body.createBodyDetector(context);
  
  await this.bodyDetector.setConfig({
    processMode: body.ProcessMode.PROCESS_MODE_VIDEO,
    performanceMode: body.PerformanceMode.PERFORMANCE_MODE_FAST
  });

catch (err) {

  console.error('初始化姿态检测器失败:', JSON.stringify(err));

}

private initDataManager(): void {
this.dataManager = distributedData.createDataManager({
bundleName: ‘com.example.aifitness’,
area: distributedData.Area.GLOBAL,
isEncrypted: true
});

this.dataManager.registerDataListener('pose_sync', (data) => {
  this.handleSyncData(data);
});

public async detectPose(pixelMap: image.PixelMap): Promise<PoseDetectionResult> {

const result: PoseDetectionResult = {
  keypoints: [],
  corrections: []
};

try {
  const detectionResults = await this.bodyDetector.detect(pixelMap);
  result.keypoints = this.parseKeyPoints(detectionResults.keyPoints);
  
  // 同步姿态数据
  this.syncPoseData(result);

catch (err) {

  console.error('姿态检测失败:', JSON.stringify(err));

return result;

private parseKeyPoints(keyPoints: body.KeyPoint[]): Map<body.PointType, Point> {

const keyPointsMap = new Map<body.PointType, Point>();

keyPoints.forEach(point => {
  keyPointsMap.set(point.pointType, {
    x: point.coordinateX,
    y: point.coordinateY,
    score: point.score
  });
});

return keyPointsMap;

private syncPoseData(result: PoseDetectionResult): void {

this.dataManager.syncData('pose_sync', {
  type: 'pose_data',
  keypoints: result.keypoints,
  timestamp: Date.now(),
  deviceId: this.dataManager.getDeviceId()
});

private handleSyncData(data: any): void {

if (!data || data.type !== 'pose_data') return;

const result: PoseDetectionResult = {
  keypoints: data.keypoints,
  corrections: []
};

this.poseListeners.forEach(listener => {
  listener.onPoseDetected(result);
});

public addPoseListener(listener: PoseListener): void {

if (!this.poseListeners.includes(listener)) {
  this.poseListeners.push(listener);

}

public removePoseListener(listener: PoseListener): void {
this.poseListeners = this.poseListeners.filter(l => l !== listener);
}

interface PoseListener {
onPoseDetected(result: PoseDetectionResult): void;
interface PoseDetectionResult {

keypoints: Map<body.PointType, Point>;
corrections: Correction[];
interface Point {

x: number;
y: number;
score: number;
interface Correction {

part: string;
message: string;
severity: ‘low’ ‘medium’
‘high’;
export const poseService = PoseDetectionService.getInstance();

动作分析服务

// MotionAnalysisService.ets
import { poseService } from ‘./PoseDetectionService’;

class MotionAnalysisService {
private static instance: MotionAnalysisService = null;
private currentExercise: Exercise | null = null;
private analysisListeners: AnalysisListener[] = [];

private constructor() {
this.setupPoseListener();
public static getInstance(): MotionAnalysisService {

if (!MotionAnalysisService.instance) {
  MotionAnalysisService.instance = new MotionAnalysisService();

return MotionAnalysisService.instance;

private setupPoseListener(): void {

poseService.addPoseListener({
  onPoseDetected: (result) => {
    if (this.currentExercise) {
      this.analyzePose(result);

}

});

public setCurrentExercise(exercise: Exercise): void {

this.currentExercise = exercise;

private analyzePose(result: PoseDetectionResult): void {

if (!this.currentExercise) return;

const corrections: Correction[] = [];

switch (this.currentExercise.id) {
  case 'squat':
    corrections.push(...this.analyzeSquat(result.keypoints));
    break;
  case 'pushup':
    corrections.push(...this.analyzePushup(result.keypoints));
    break;
  case 'lunge':
    corrections.push(...this.analyzeLunge(result.keypoints));
    break;

// 通知监听器

this.analysisListeners.forEach(listener => {
  listener.onAnalysisResult(corrections);
});

// 同步矫正建议
this.syncCorrections(corrections);

private analyzeSquat(keypoints: Map<body.PointType, Point>): Correction[] {

const corrections: Correction[] = [];
const leftHip = keypoints.get(body.PointType.POINT_TYPE_HIP_LEFT);
const rightHip = keypoints.get(body.PointType.POINT_TYPE_HIP_RIGHT);
const leftKnee = keypoints.get(body.PointType.POINT_TYPE_KNEE_LEFT);
const rightKnee = keypoints.get(body.PointType.POINT_TYPE_KNEE_RIGHT);

// 膝盖超过脚尖检测
if (leftKnee && leftHip && leftKnee.x < leftHip.x) {
  corrections.push({
    part: 'left_knee',
    message: '左膝盖超过脚尖,请调整姿势',
    severity: 'high'
  });

if (rightKnee && rightHip && rightKnee.x < rightHip.x) {

  corrections.push({
    part: 'right_knee',
    message: '右膝盖超过脚尖,请调整姿势',
    severity: 'high'
  });

return corrections;

private syncCorrections(corrections: Correction[]): void {

poseService.syncData('pose_sync', {
  type: 'corrections',
  corrections: corrections,
  timestamp: Date.now(),
  deviceId: poseService.getDeviceId()
});

public addAnalysisListener(listener: AnalysisListener): void {

if (!this.analysisListeners.includes(listener)) {
  this.analysisListeners.push(listener);

}

public removeAnalysisListener(listener: AnalysisListener): void {
this.analysisListeners = this.analysisListeners.filter(l => l !== listener);
}

interface AnalysisListener {
onAnalysisResult(corrections: Correction[]): void;
interface Exercise {

id: string;
name: string;
description: string;
difficulty: ‘beginner’ ‘intermediate’
‘advanced’;
export const motionService = MotionAnalysisService.getInstance();

手机端动作捕捉界面

// PhoneCaptureScreen.ets
import { motionService } from ‘./MotionAnalysisService’;
import { poseService } from ‘./PoseDetectionService’;
import camera from ‘@ohos.multimedia.camera’;

@Component
export struct PhoneCaptureScreen {
@State currentExercise: Exercise | null = null;
@State corrections: Correction[] = [];
@State isProcessing: boolean = false;
@State frameCount: number = 0;

private cameraManager: camera.CameraManager;
private previewOutput: camera.PreviewOutput | null = null;

build() {
Column() {
// 摄像头预览
XComponent({
id: ‘cameraPreview’,
type: ‘surface’,
controller: this.cameraController
})
.width(‘100%’)
.height(‘60%’)
.onLoad(() => {
this.initCamera();
})

  // 训练信息
  if (this.currentExercise) {
    Column() {
      Text(this.currentExercise.name)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 8 })
      
      Text(this.currentExercise.description)
        .fontSize(14)
        .fontColor('#666666')

.padding(16)

    .width('90%')
    .backgroundColor('#F5F5F5')
    .borderRadius(8)
    .margin({ bottom: 16 })

// 矫正建议

  if (this.corrections.length > 0) {
    Column() {
      Text('矫正建议:')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 8 })
      
      ForEach(this.corrections, (correction) => {
        Row() {
          Image(this.getSeverityIcon(correction.severity))
            .width(20)
            .height(20)
            .margin({ right: 10 })
          
          Text(correction.message)
            .fontSize(16)

.margin({ bottom: 8 })

      })

.padding(16)

    .width('90%')
    .backgroundColor('#FFF3E0')
    .borderRadius(8)
    .margin({ bottom: 16 })

// 控制按钮

  Button(this.isProcessing ? '结束训练' : '开始训练')
    .width('80%')
    .height(50)
    .onClick(() => {
      this.toggleProcessing();
    })

.width(‘100%’)

.height('100%')
.padding(20)
.onAppear(() => {
  motionService.addAnalysisListener({
    onAnalysisResult: (corrections) => {
      this.corrections = corrections;

});

})
.onDisappear(() => {
  motionService.removeAnalysisListener({
    onAnalysisResult: () => {}
  });
  this.releaseCamera();
})

private async initCamera(): Promise<void> {

try {
  this.cameraManager = await camera.getCameraManager(getContext(this));
  const cameras = await this.cameraManager.getSupportedCameras();
  
  if (cameras.length === 0) {
    console.error('未找到可用摄像头');
    return;

// 使用后置摄像头

  const cameraDevice = cameras.find(c => c.position === camera.CameraPosition.CAMERA_POSITION_BACK) || cameras[0];
  
  // 创建相机输入
  const cameraInput = await this.cameraManager.createCameraInput(cameraDevice);
  await cameraInput.open();
  
  // 创建预览输出
  const surfaceId = this.cameraController.getSurfaceId();
  this.previewOutput = await this.cameraManager.createPreviewOutput(surfaceId);
  
  // 创建会话
  const captureSession = await this.cameraManager.createCaptureSession();
  await captureSession.beginConfig();
  await captureSession.addInput(cameraInput);
  await captureSession.addOutput(this.previewOutput);
  await captureSession.commitConfig();
  await captureSession.start();
  
  // 开始帧捕获
  this.startFrameCapture();

catch (err) {

  console.error('初始化相机失败:', JSON.stringify(err));

}

private startFrameCapture(): void {
this.previewOutput?.on(‘frameStart’, () => {
if (this.isProcessing && this.frameCount % 5 === 0) { // 每5帧处理一次
this.captureAndProcess();
this.frameCount++;

});

private async captureAndProcess(): Promise<void> {

try {
  const imageReceiver = await this.previewOutput?.createImageReceiver();
  if (!imageReceiver) return;
  
  const image = await imageReceiver.readNextImage();
  const pixelMap = await image.createPixelMap();
  
  // 进行姿态检测
  await poseService.detectPose(pixelMap);
  
  image.close();

catch (err) {

  console.error('捕获和处理帧失败:', JSON.stringify(err));

}

private toggleProcessing(): void {
this.isProcessing = !this.isProcessing;
private getSeverityIcon(severity: string): Resource {

switch (severity) {
  case 'high': return $r('app.media.ic_error');
  case 'medium': return $r('app.media.ic_warning');
  default: return $r('app.media.ic_info');

}

大屏矫正反馈界面

// TVFeedbackScreen.ets
import { motionService } from ‘./MotionAnalysisService’;
import { poseService } from ‘./PoseDetectionService’;

@Component
export struct TVFeedbackScreen {
@State currentExercise: Exercise | null = null;
@State corrections: Correction[] = [];
@State poseView: PoseView | null = null;

build() {
Column() {
// 标题
Text(‘AI健身教练’)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })

  // 3D姿态展示
  Row() {
    // 标准动作
    Column() {
      Text('标准动作')
        .fontSize(18)
        .margin({ bottom: 10 })
      
      PoseView({
        pose: this.getStandardPose(),
        color: '#4CAF50'
      })
      .width('90%')
      .height(300)

.width(‘50%’)

    // 用户动作
    Column() {
      Text('你的动作')
        .fontSize(18)
        .margin({ bottom: 10 })
      
      PoseView({
        pose: this.poseView,
        color: '#2196F3'
      })
      .width('90%')
      .height(300)

.width(‘50%’)

.margin({ bottom: 20 })

  // 矫正建议
  if (this.corrections.length > 0) {
    Column() {
      Text('矫正建议:')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 10 })
      
      ForEach(this.corrections, (correction) => {
        Row() {
          Image(this.getSeverityIcon(correction.severity))
            .width(20)
            .height(20)
            .margin({ right: 10 })
          
          Text(correction.message)
            .fontSize(16)

.margin({ bottom: 8 })

      })

.width(‘90%’)

    .padding(20)
    .backgroundColor('#FFF3E0')
    .borderRadius(8)

else if (this.currentExercise) {

    Text('动作标准,继续保持!')
      .fontSize(18)
      .fontColor('#4CAF50')
      .margin({ top: 20 })

}

.width('100%')
.height('100%')
.padding(20)
.onAppear(() => {
  motionService.addAnalysisListener({
    onAnalysisResult: (corrections) => {
      this.corrections = corrections;

});

  poseService.addPoseListener({
    onPoseDetected: (result) => {
      this.poseView = this.createPoseView(result.keypoints);

});

})
.onDisappear(() => {
  motionService.removeAnalysisListener({
    onAnalysisResult: () => {}
  });
  
  poseService.removePoseListener({
    onPoseDetected: () => {}
  });
})

private getStandardPose(): PoseView | null {

if (!this.currentExercise) return null;

// 根据当前训练动作返回标准姿态
const standardPose = new Map<body.PointType, Point>();

switch (this.currentExercise.id) {
  case 'squat':
    standardPose.set(body.PointType.POINT_TYPE_SHOULDER_LEFT, { x: 300, y: 200, score: 1 });
    standardPose.set(body.PointType.POINT_TYPE_SHOULDER_RIGHT, { x: 500, y: 200, score: 1 });
    standardPose.set(body.PointType.POINT_TYPE_HIP_LEFT, { x: 320, y: 400, score: 1 });
    standardPose.set(body.PointType.POINT_TYPE_HIP_RIGHT, { x: 480, y: 400, score: 1 });
    standardPose.set(body.PointType.POINT_TYPE_KNEE_LEFT, { x: 350, y: 600, score: 1 });
    standardPose.set(body.PointType.POINT_TYPE_KNEE_RIGHT, { x: 450, y: 600, score: 1 });
    break;
  
  case 'pushup':
    // 俯卧撑标准姿态
    break;

return this.createPoseView(standardPose);

private createPoseView(keypoints: Map<body.PointType, Point>): PoseView {

return {
  keypoints: keypoints,
  connections: this.getPoseConnections(keypoints)
};

private getPoseConnections(keypoints: Map<body.PointType, Point>): Connection[] {

const connections: Connection[] = [];

// 左臂
if (keypoints.has(body.PointType.POINT_TYPE_SHOULDER_LEFT) && 
    keypoints.has(body.PointType.POINT_TYPE_ELBOW_LEFT)) {
  connections.push({
    from: body.PointType.POINT_TYPE_SHOULDER_LEFT,
    to: body.PointType.POINT_TYPE_ELBOW_LEFT
  });

// 右臂

if (keypoints.has(body.PointType.POINT_TYPE_SHOULDER_RIGHT) && 
    keypoints.has(body.PointType.POINT_TYPE_ELBOW_RIGHT)) {
  connections.push({
    from: body.PointType.POINT_TYPE_SHOULDER_RIGHT,
    to: body.PointType.POINT_TYPE_ELBOW_RIGHT
  });

// 躯干

if (keypoints.has(body.PointType.POINT_TYPE_SHOULDER_LEFT) && 
    keypoints.has(body.PointType.POINT_TYPE_HIP_LEFT)) {
  connections.push({
    from: body.PointType.POINT_TYPE_SHOULDER_LEFT,
    to: body.PointType.POINT_TYPE_HIP_LEFT
  });

// 更多连接…

return connections;

}

interface PoseView {
keypoints: Map<body.PointType, Point>;
connections: Connection[];
interface Connection {

from: body.PointType;
to: body.PointType;

三、项目配置与权限
权限配置

// module.json5
“module”: {

"requestPermissions": [

“name”: “ohos.permission.CAMERA”,

    "reason": "捕捉训练动作"
  },

“name”: “ohos.permission.DISTRIBUTED_DATASYNC”,

    "reason": "同步训练状态"
  },

“name”: “ohos.permission.INTERNET”,

    "reason": "加载AI模型"
  },

“name”: “ohos.permission.READ_MEDIA”,

    "reason": "读取训练资料"
  },

“name”: “ohos.permission.ACCESS_DISTRIBUTED_DEVICE_MANAGER”,

    "reason": "发现和连接其他设备"

],

"abilities": [

“name”: “MainAbility”,

    "type": "page",
    "visible": true
  },

“name”: “CameraAbility”,

    "type": "page",
    "visible": true
  },

“name”: “TVAbility”,

    "type": "page",
    "visible": true
  },

“name”: “PoseDetectionAbility”,

    "type": "service",
    "backgroundModes": ["dataTransfer"]

]

}

四、总结与扩展

本AI健身动作矫正系统实现了以下核心功能:
精准识别:实时捕捉人体关键点并分析动作

智能矫正:针对不同训练提供专业指导建议

多屏互动:手机捕捉动作,大屏展示反馈

训练同步:多设备实时同步训练状态

扩展方向:
训练计划:根据用户水平制定个性化训练方案

进度追踪:记录训练数据并可视化进步曲线

社交功能:分享训练成果,好友间挑战

健康数据:接入穿戴设备监测心率等指标

VR集成:结合VR设备提供沉浸式训练体验

通过HarmonyOS的分布式能力和AI框架,我们构建了一个智能、互联的健身指导系统,让家庭健身更加科学高效。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
收藏
回复
举报
回复
    相关推荐