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

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

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

一、系统架构设计

基于HarmonyOS的AI健身动作矫正系统,利用分布式能力和姿态识别API,实现多设备协同的健身指导:
多设备协同:手机摄像头捕捉动作,大屏设备展示矫正反馈

实时分析:通过@ohos.ai.body进行姿态识别与动作分析

跨端同步:分布式数据同步确保多设备状态一致

3D展示:在智慧屏上展示标准动作与用户动作对比

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

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

// PoseDetectionService.ets
import body from ‘@ohos.ai.body’;
import image from ‘@ohos.multimedia.image’;
import common from ‘@ohos.app.ability.common’;

class PoseDetectionService {
private static instance: PoseDetectionService = null;
private bodyDetector: body.BodyDetector;
private isDetecting: boolean = false;
private detectionCallbacks: DetectionCallback[] = [];

private constructor() {
this.initBodyDetector();
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);
  
  // 配置检测参数
  const config: body.BodyDetectConfig = {
    processMode: body.ProcessMode.PROCESS_MODE_VIDEO,
    detectorMode: body.DetectorMode.DETECTOR_MODE_BACKGROUND,
    preferredResolution: body.PreferredResolution.PREFERRED_RESOLUTION_1080P
  };
  
  await this.bodyDetector.setConfig(config);

catch (err) {

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

}

public async startDetection(imageSource: image.ImageSource): Promise<void> {
if (this.isDetecting || !this.bodyDetector) return;

this.isDetecting = true;

try {
  // 创建图像数组
  const images: image.Image[] = [];
  const imageObj = await imageSource.createPixelMap();
  images.push(imageObj);
  
  // 开始检测
  const results = await this.bodyDetector.detect(images);
  
  if (results && results.length > 0) {
    this.handleDetectionResult(results[0]);

} catch (err) {

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

finally {

  this.isDetecting = false;

}

private handleDetectionResult(result: body.BodyDetectionResult): void {
if (!result || result.bodyList.length === 0) return;

// 获取第一个检测到的人体
const bodyInfo = result.bodyList[0];
const keyPoints = this.parseKeyPoints(bodyInfo.keyPoints);

// 通知所有回调
this.detectionCallbacks.forEach(callback => {
  callback.onDetectionResult(keyPoints);
});

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

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

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

return keyPointsMap;

public addDetectionCallback(callback: DetectionCallback): void {

if (!this.detectionCallbacks.includes(callback)) {
  this.detectionCallbacks.push(callback);

}

public removeDetectionCallback(callback: DetectionCallback): void {
this.detectionCallbacks = this.detectionCallbacks.filter(cb => cb !== callback);
public async release(): Promise<void> {

if (this.bodyDetector) {
  await this.bodyDetector.release();
  this.bodyDetector = null;

}

interface DetectionCallback {

onDetectionResult(keyPoints: Map<body.PointType, body.Point>): void;
interface Point {

x: number;
y: number;
score: number;
export const poseService = PoseDetectionService.getInstance();

摄像头采集服务

// CameraService.ets
import camera from ‘@ohos.multimedia.camera’;
import image from ‘@ohos.multimedia.image’;
import { poseService } from ‘./PoseDetectionService’;

class CameraService {
private static instance: CameraService = null;
private cameraManager: camera.CameraManager;
private cameraInput: camera.CameraInput | null = null;
private previewOutput: camera.PreviewOutput | null = null;
private imageReceiver: image.ImageReceiver | null = null;
private captureSession: camera.CaptureSession | null = null;

private constructor() {
this.initCameraManager();
public static getInstance(): CameraService {

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

return CameraService.instance;

private async initCameraManager(): Promise<void> {

try {
  this.cameraManager = await camera.getCameraManager(getContext(this));

catch (err) {

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

}

public async startCameraPreview(surfaceId: string): Promise<void> {
if (!this.cameraManager) return;

try {
  // 获取摄像头列表
  const cameras = this.cameraManager.getSupportedCameras();
  if (cameras.length === 0) {
    throw new Error('未找到可用摄像头');

// 使用后置摄像头

  const cameraDevice = cameras.find(c => c.position === camera.CameraPosition.CAMERA_POSITION_BACK) || cameras[0];
  
  // 创建相机输入
  this.cameraInput = this.cameraManager.createCameraInput(cameraDevice);
  await this.cameraInput.open();
  
  // 创建预览输出
  this.previewOutput = this.cameraManager.createPreviewOutput(surfaceId);
  
  // 创建图像接收器用于姿态分析
  this.imageReceiver = image.createImageReceiver(
    1080, 1920, 
    image.ImageFormat.JPEG, 

);

  const captureOutput = this.cameraManager.createPhotoOutput(this.imageReceiver);
  
  // 创建会话
  this.captureSession = this.cameraManager.createCaptureSession();
  await this.captureSession.beginConfig();
  
  // 添加输入输出
  await this.captureSession.addInput(this.cameraInput);
  await this.captureSession.addOutput(this.previewOutput);
  await this.captureSession.addOutput(captureOutput);
  
  await this.captureSession.commitConfig();
  await this.captureSession.start();
  
  // 开始帧捕获
  this.startFrameCapture();

catch (err) {

  console.error('启动相机预览失败:', JSON.stringify(err));
  this.release();

}

private async startFrameCapture(): Promise<void> {
if (!this.imageReceiver) return;

this.imageReceiver.on('imageArrival', async () => {
  try {
    const img = await this.imageReceiver.readNextImage();
    const imageSource = image.createImageSource(img);
    await poseService.startDetection(imageSource);
    img.release();

catch (err) {

    console.error('处理图像帧失败:', JSON.stringify(err));

});

public async release(): Promise<void> {

if (this.captureSession) {
  await this.captureSession.stop();
  this.captureSession = null;

if (this.cameraInput) {

  await this.cameraInput.close();
  this.cameraInput = null;

this.previewOutput = null;

this.imageReceiver = null;

}

export const cameraService = CameraService.getInstance();

动作矫正分析服务

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

class MotionAnalysisService {
private static instance: MotionAnalysisService = null;
private currentExercise: Exercise | null = null;
private analysisCallbacks: AnalysisCallback[] = [];
private dataManager: distributedData.DataManager;

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

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

return MotionAnalysisService.instance;

private initDataManager(): void {

this.dataManager = distributedData.createDataManager({
  bundleName: 'com.example.aifitness',
  area: distributedData.Area.GLOBAL,
  isEncrypted: true
});

// 监听矫正数据同步
this.dataManager.registerDataListener('motion_correction', (data) => {
  this.handleSyncData(data);
});

private registerPoseListener(): void {

poseService.addDetectionCallback({
  onDetectionResult: (keyPoints) => {
    this.analyzePose(keyPoints);

});

public setCurrentExercise(exercise: Exercise): void {

this.currentExercise = exercise;

// 同步到其他设备
this.syncExercise(exercise);

private analyzePose(keyPoints: Map<body.PointType, body.Point>): void {

if (!this.currentExercise) return;

// 获取关键点
const leftShoulder = keyPoints.get(body.PointType.POINT_TYPE_SHOULDER_LEFT);
const rightShoulder = keyPoints.get(body.PointType.POINT_TYPE_SHOULDER_RIGHT);
const leftElbow = keyPoints.get(body.PointType.POINT_TYPE_ELBOW_LEFT);
const rightElbow = keyPoints.get(body.PointType.POINT_TYPE_ELBOW_RIGHT);
const leftWrist = keyPoints.get(body.PointType.POINT_TYPE_WRIST_LEFT);
const rightWrist = keyPoints.get(body.PointType.POINT_TYPE_WRIST_RIGHT);
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);
const leftAnkle = keyPoints.get(body.PointType.POINT_TYPE_ANKLE_LEFT);
const rightAnkle = keyPoints.get(body.PointType.POINT_TYPE_ANKLE_RIGHT);

// 根据当前训练动作进行分析
let corrections: Correction[] = [];

switch (this.currentExercise.id) {
  case 'squat':
    corrections = this.analyzeSquat(
      leftShoulder, rightShoulder,
      leftHip, rightHip,
      leftKnee, rightKnee,
      leftAnkle, rightAnkle
    );
    break;
  
  case 'pushup':
    corrections = this.analyzePushup(
      leftShoulder, rightShoulder,
      leftElbow, rightElbow,
      leftWrist, rightWrist,
      leftHip, rightHip
    );
    break;
  
  case 'lunge':
    corrections = this.analyzeLunge(
      leftHip, rightHip,
      leftKnee, rightKnee,
      leftAnkle, rightAnkle
    );
    break;

// 通知回调

this.analysisCallbacks.forEach(callback => {
  callback.onAnalysisResult(corrections);
});

// 同步矫正数据
this.syncCorrections(corrections);

private analyzeSquat(

leftShoulder: Point | undefined, 
rightShoulder: Point | undefined,
leftHip: Point | undefined, 
rightHip: Point | undefined,
leftKnee: Point | undefined, 
rightKnee: Point | undefined,
leftAnkle: Point | undefined, 
rightAnkle: Point | undefined

): Correction[] {
const corrections: Correction[] = [];

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

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

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

// 检查背部是否挺直

if (leftShoulder && rightShoulder && leftHip && rightHip) {
  const shoulderMidY = (leftShoulder.y + rightShoulder.y) / 2;
  const hipMidY = (leftHip.y + rightHip.y) / 2;
  
  if (Math.abs(shoulderMidY - hipMidY) < 50) {
    corrections.push({
      part: 'back',
      message: '背部未挺直,请保持脊柱中立',
      severity: 'medium'
    });

}

// 检查膝盖内扣
if (leftKnee && rightKnee && leftAnkle && rightAnkle) {
  const kneeDistance = Math.abs(leftKnee.x - rightKnee.x);
  const ankleDistance = Math.abs(leftAnkle.x - rightAnkle.x);
  
  if (kneeDistance < ankleDistance * 0.7) {
    corrections.push({
      part: 'knees',
      message: '膝盖内扣,请保持膝盖与脚尖方向一致',
      severity: 'high'
    });

}

return corrections;

private analyzePushup(

leftShoulder: Point | undefined, 
rightShoulder: Point | undefined,
leftElbow: Point | undefined, 
rightElbow: Point | undefined,
leftWrist: Point | undefined, 
rightWrist: Point | undefined,
leftHip: Point | undefined, 
rightHip: Point | undefined

): Correction[] {
const corrections: Correction[] = [];

// 检查身体是否成直线
if (leftShoulder && rightShoulder && leftHip && rightHip && leftAnkle && rightAnkle) {
  const shoulderMidY = (leftShoulder.y + rightShoulder.y) / 2;
  const hipMidY = (leftHip.y + rightHip.y) / 2;
  const ankleMidY = (leftAnkle.y + rightAnkle.y) / 2;
  
  const angle1 = this.calculateAngle(shoulderMidY, hipMidY, ankleMidY);
  
  if (Math.abs(angle1 - 180) > 10) {
    corrections.push({
      part: 'body',
      message: '身体未成直线,收紧核心',
      severity: 'high'
    });

}

// 检查肘部角度
if (leftShoulder && leftElbow && leftWrist) {
  const angle = this.calculateAngle(
    leftShoulder.x, leftShoulder.y,
    leftElbow.x, leftElbow.y,
    leftWrist.x, leftWrist.y
  );
  
  if (angle < 70) {
    corrections.push({
      part: 'left_elbow',
      message: '左肘部角度过小,可能导致肩部压力过大',
      severity: 'medium'
    });

}

if (rightShoulder && rightElbow && rightWrist) {
  const angle = this.calculateAngle(
    rightShoulder.x, rightShoulder.y,
    rightElbow.x, rightElbow.y,
    rightWrist.x, rightWrist.y
  );
  
  if (angle < 70) {
    corrections.push({
      part: 'right_elbow',
      message: '右肘部角度过小,可能导致肩部压力过大',
      severity: 'medium'
    });

}

return corrections;

private calculateAngle(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number): number {

const v1x = x1 - x2;
const v1y = y1 - y2;
const v2x = x3 - x2;
const v2y = y3 - y2;

const dot = v1x  v2x + v1y  v2y;
const mag1 = Math.sqrt(v1x  v1x + v1y  v1y);
const mag2 = Math.sqrt(v2x  v2x + v2y  v2y);

const angle = Math.acos(dot / (mag1  mag2))  (180 / Math.PI);
return angle;

private syncExercise(exercise: Exercise): void {

this.dataManager.syncData('motion_correction', {
  type: 'exercise',
  exercise: exercise,
  timestamp: Date.now()
});

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

this.dataManager.syncData('motion_correction', {
  type: 'corrections',
  corrections: corrections,
  timestamp: Date.now()
});

private handleSyncData(data: any): void {

if (!data) return;

switch (data.type) {
  case 'exercise':
    this.currentExercise = data.exercise;
    break;
  
  case 'corrections':
    this.analysisCallbacks.forEach(callback => {
      callback.onAnalysisResult(data.corrections);
    });
    break;

}

public addAnalysisCallback(callback: AnalysisCallback): void {
if (!this.analysisCallbacks.includes(callback)) {
this.analysisCallbacks.push(callback);
}

public removeAnalysisCallback(callback: AnalysisCallback): void {
this.analysisCallbacks = this.analysisCallbacks.filter(cb => cb !== callback);
}

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

id: string;
name: string;
description: string;
difficulty: ‘beginner’ ‘intermediate’
‘advanced’;
interface Correction {

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

手机端摄像头界面

// PhoneCameraView.ets
import { cameraService } from ‘./CameraService’;
import { motionService } from ‘./MotionAnalysisService’;

@Component
export struct PhoneCameraView {
@State currentExercise: Exercise | null = null;
@State corrections: Correction[] = [];
@State surfaceId: string = ‘’;

aboutToAppear() {
// 创建XComponent表面
this.surfaceId = ‘camera_preview_’ + Date.now();

// 监听矫正结果
motionService.addAnalysisCallback({
  onAnalysisResult: (corrections) => {
    this.corrections = corrections;

});

build() {

Stack() {
  // 相机预览
  XComponent({
    id: this.surfaceId,
    type: 'surface',
    libraryname: 'camera_preview'
  })
  .width('100%')
  .height('100%')
  .onLoad(() => {
    cameraService.startCameraPreview(this.surfaceId);
  })
  
  // 矫正提示
  Column() {
    ForEach(this.corrections, (correction) => {
      if (correction.severity === 'high') {
        Text(correction.message)
          .fontSize(16)
          .fontColor(Color.Red)
          .backgroundColor(Color.White)
          .padding(10)
          .borderRadius(8)
          .margin({ bottom: 10 })

})

.width(‘100%’)

  .alignItems(HorizontalAlign.Start)
  .padding(20)
  
  // 底部控制栏
  Column() {
    // 练习选择器
    ExerciseSelector({
      onExerciseSelect: (exercise) => {
        this.currentExercise = exercise;
        motionService.setCurrentExercise(exercise);

})

    // 控制按钮
    Row() {
      Button('切换摄像头')
        .width('40%')
        .height(50)
        .margin({ right: 10 })
      
      Button('结束训练')
        .width('40%')
        .height(50)
        .onClick(() => {
          cameraService.release();
          router.back();
        })

.margin({ top: 20 })

.width(‘100%’)

  .position({ bottom: 30 })

.width(‘100%’)

.height('100%')
.onDisappear(() => {
  cameraService.release();
})

}

@Component
struct ExerciseSelector {
@Link onExerciseSelect: (exercise: Exercise) => void;
@State exercises: Exercise[] = [
id: ‘squat’,

  name: '深蹲',
  description: '锻炼下肢力量的基础动作',
  difficulty: 'beginner'
},

id: ‘pushup’,

  name: '俯卧撑',
  description: '锻炼上肢和核心力量的标准动作',
  difficulty: 'beginner'
},

id: ‘lunge’,

  name: '弓步蹲',
  description: '单侧下肢训练动作',
  difficulty: 'intermediate'

];

@State selectedIndex: number = 0;

build() {
Row() {
Text(‘当前练习:’)
.fontSize(16)
.margin({ right: 10 })

  Select(this.exercises.map(ex => ex.name))
    .selected(this.selectedIndex)
    .onSelect((index) => {
      this.selectedIndex = index;
      this.onExerciseSelect(this.exercises[index]);
    })

.padding(10)

.backgroundColor(Color.White)
.borderRadius(8)

}

大屏3D矫正视图

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

@Component
export struct TVCorrectionView {
@State currentExercise: Exercise | null = null;
@State corrections: Correction[] = [];
@State standardPose: Map<body.PointType, Point> = new Map();
@State userPose: Map<body.PointType, Point> = new Map();

aboutToAppear() {
// 监听练习变化
motionService.addAnalysisCallback({
onAnalysisResult: (corrections) => {
this.corrections = corrections;
});

// 监听姿态变化
poseService.addDetectionCallback({
  onDetectionResult: (keyPoints) => {
    this.userPose = keyPoints;

});

build() {

Column() {
  // 标题
  Text(this.currentExercise?.name || '请选择训练动作')
    .fontSize(24)
    .fontWeight(FontWeight.Bold)
    .margin({ bottom: 20 })
  
  // 3D对比视图
  Row() {
    // 标准动作
    Column() {
      Text('标准动作')
        .fontSize(18)
        .margin({ bottom: 10 })
      
      PoseView(this.standardPose, '#4CAF50')
        .width('90%')
        .height(300)

.width(‘50%’)

    // 用户动作
    Column() {
      Text('你的动作')
        .fontSize(18)
        .margin({ bottom: 10 })
      
      PoseView(this.userPose, '#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(() => {
  // 加载标准姿势
  this.loadStandardPose();
})

private loadStandardPose(): void {

// 根据当前训练动作加载标准姿势
if (!this.currentExercise) return;

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 });
    standardPose.set(body.PointType.POINT_TYPE_ANKLE_LEFT, { x: 350, y: 800, score: 1 });
    standardPose.set(body.PointType.POINT_TYPE_ANKLE_RIGHT, { x: 450, y: 800, score: 1 });
    break;
  
  case 'pushup':
    // 俯卧撑标准姿势关键点
    standardPose.set(body.PointType.POINT_TYPE_SHOULDER_LEFT, { x: 300, y: 300, score: 1 });
    standardPose.set(body.PointType.POINT_TYPE_SHOULDER_RIGHT, { x: 500, y: 300, score: 1 });
    standardPose.set(body.PointType.POINT_TYPE_ELBOW_LEFT, { x: 250, y: 400, score: 1 });
    standardPose.set(body.PointType.POINT_TYPE_ELBOW_RIGHT, { x: 550, y: 400, score: 1 });
    standardPose.set(body.PointType.POINT_TYPE_WRIST_LEFT, { x: 200, y: 500, score: 1 });
    standardPose.set(body.PointType.POINT_TYPE_WRIST_RIGHT, { x: 600, y: 500, score: 1 });
    standardPose.set(body.PointType.POINT_TYPE_HIP_LEFT, { x: 350, y: 400, score: 1 });
    standardPose.set(body.PointType.POINT_TYPE_HIP_RIGHT, { x: 450, y: 400, score: 1 });
    break;

this.standardPose = standardPose;

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');

}

@Component

struct PoseView {
private pose: Map<body.PointType, Point>;
private color: string;

build() {
Canvas()
.width(‘100%’)
.height(‘100%’)
.onReady((context: CanvasRenderingContext2D) => {
this.drawPose(context);
})
private drawPose(ctx: CanvasRenderingContext2D): void {

if (!this.pose || this.pose.size === 0) return;

const width = ctx.width;
const height = ctx.height;

// 清空画布
ctx.clearRect(0, 0, width, height);

// 绘制骨骼连线
this.drawBone(ctx, 
  body.PointType.POINT_TYPE_SHOULDER_LEFT,
  body.PointType.POINT_TYPE_ELBOW_LEFT,
  width, height
);

this.drawBone(ctx, 
  body.PointType.POINT_TYPE_ELBOW_LEFT,
  body.PointType.POINT_TYPE_WRIST_LEFT,
  width, height
);

this.drawBone(ctx, 
  body.PointType.POINT_TYPE_SHOULDER_RIGHT,
  body.PointType.POINT_TYPE_ELBOW_RIGHT,
  width, height
);

this.drawBone(ctx, 
  body.PointType.POINT_TYPE_ELBOW_RIGHT,
  body.PointType.POINT_TYPE_WRIST_RIGHT,
  width, height
);

this.drawBone(ctx, 
  body.PointType.POINT_TYPE_SHOULDER_LEFT,
  body.PointType.POINT_TYPE_SHOULDER_RIGHT,
  width, height
);

this.drawBone(ctx, 
  body.PointType.POINT_TYPE_SHOULDER_LEFT,
  body.PointType.POINT_TYPE_HIP_LEFT,
  width, height
);

this.drawBone(ctx, 
  body.PointType.POINT_TYPE_SHOULDER_RIGHT,
  body.PointType.POINT_TYPE_HIP_RIGHT,
  width, height
);

this.drawBone(ctx, 
  body.PointType.POINT_TYPE_HIP_LEFT,
  body.PointType.POINT_TYPE_HIP_RIGHT,
  width, height
);

this.drawBone(ctx, 
  body.PointType.POINT_TYPE_HIP_LEFT,
  body.PointType.POINT_TYPE_KNEE_LEFT,
  width, height
);

this.drawBone(ctx, 
  body.PointType.POINT_TYPE_HIP_RIGHT,
  body.PointType.POINT_TYPE_KNEE_RIGHT,
  width, height
);

this.drawBone(ctx, 
  body.PointType.POINT_TYPE_KNEE_LEFT,
  body.PointType.POINT_TYPE_ANKLE_LEFT,
  width, height
);

this.drawBone(ctx, 
  body.PointType.POINT_TYPE_KNEE_RIGHT,
  body.PointType.POINT_TYPE_ANKLE_RIGHT,
  width, height
);

// 绘制关节点
this.pose.forEach((point, type) => {
  if (point.score > 0.3) { // 置信度阈值
    const x = point.x * width / 1000; // 假设坐标范围0-1000
    const y = point.y * height / 1000;
    
    ctx.beginPath();
    ctx.arc(x, y, 5, 0, 2 * Math.PI);
    ctx.fillStyle = this.color;
    ctx.fill();

});

private drawBone(

ctx: CanvasRenderingContext2D,
type1: body.PointType,
type2: body.PointType,
width: number,
height: number

): void {
const point1 = this.pose.get(type1);
const point2 = this.pose.get(type2);

if (point1 && point2 && point1.score > 0.3 && point2.score > 0.3) {
  const x1 = point1.x * width / 1000;
  const y1 = point1.y * height / 1000;
  const x2 = point2.x * width / 1000;
  const y2 = point2.y * height / 1000;
  
  ctx.beginPath();
  ctx.moveTo(x1, y1);
  ctx.lineTo(x2, y2);
  ctx.strokeStyle = this.color;
  ctx.lineWidth = 3;
  ctx.stroke();

}

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

// module.json5
“module”: {

"requestPermissions": [

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

    "reason": "使用摄像头捕捉动作"
  },

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

    "reason": "同步矫正数据"
  },

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

    "reason": "加载标准动作数据"
  },

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

    "reason": "读取媒体文件"
  },

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

    "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"]

]

}

资源文件

// resources/base/media/media.json
“media”: [

“name”: “ic_error”,

  "type": "svg",
  "src": "media/error.svg"
},

“name”: “ic_warning”,

  "type": "svg",
  "src": "media/warning.svg"
},

“name”: “ic_info”,

  "type": "svg",
  "src": "media/info.svg"
},

“name”: “squat_pose”,

  "type": "png",
  "src": "media/squat.png"
},

“name”: “pushup_pose”,

  "type": "png",
  "src": "media/pushup.png"
},

“name”: “lunge_pose”,

  "type": "png",
  "src": "media/lunge.png"

]

四、总结与扩展

本AI健身动作矫正系统实现了以下核心功能:
实时姿态识别:通过@ohos.ai.body精准捕捉人体关键点

智能分析:针对不同训练动作提供专业矫正建议

多设备协同:手机捕捉动作,大屏展示3D对比与矫正提示

分布式同步:实时同步训练状态与矫正数据

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

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

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

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

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

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

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