鸿蒙健身动作纠正应用开发指南 原创

进修的泡芙
发布于 2025-6-22 17:42
浏览
0收藏

鸿蒙健身动作纠正应用开发指南

一、系统架构设计

基于HarmonyOS的AI能力和分布式技术,我们设计了一套健身动作纠正系统,主要功能包括:
动作捕捉:通过摄像头实时捕捉用户健身动作

姿态分析:AI分析用户动作与标准动作的差异

实时反馈:即时提供动作纠正建议

训练记录:记录每次训练的动作数据

多设备协同:支持跨设备同步训练计划和进度

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

二、核心代码实现
姿态检测服务

// PoseDetectionService.ets
import camera from ‘@ohos.multimedia.camera’;
import image from ‘@ohos.multimedia.image’;
import pose from ‘@ohos.ai.pose’;

class PoseDetectionService {
private static instance: PoseDetectionService;
private cameraManager: camera.CameraManager;
private poseDetector: pose.PoseDetector;
private cameraInput: camera.CameraInput | null = null;
private previewOutput: camera.PreviewOutput | null = null;
private isDetecting: boolean = false;

private constructor() {
this.cameraManager = camera.getCameraManager();
this.poseDetector = pose.createPoseDetector();
public static getInstance(): PoseDetectionService {

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

return PoseDetectionService.instance;

public async initCamera(previewSurfaceId: string): Promise<void> {

const cameras = this.cameraManager.getSupportedCameras();
if (cameras.length === 0) {
  throw new Error('No camera available');

this.cameraInput = this.cameraManager.createCameraInput(cameras[0]);

await this.cameraInput.open();

const previewProfile = this.cameraManager.getSupportedOutputCapability(
  cameras[0],
  camera.ProfileMode.PROFILE_MODE_DEFAULT
).previewProfiles[0];

this.previewOutput = this.cameraManager.createPreviewOutput(
  previewProfile,
  previewSurfaceId
);

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

const detectionOptions = {
  modelType: pose.ModelType.MODEL_TYPE_HUMAN_POSE_2D,
  processMode: pose.ProcessMode.PROCESS_MODE_FAST
};

return new Promise((resolve, reject) => {
  this.poseDetector.detect(image, detectionOptions, (err, result) => {
    if (err) {
      reject(err);

else {

      resolve(this.processPoseResult(result));

});

});

private processPoseResult(rawData: any): PoseDetectionResult {

if (!rawData || rawData.poses.length === 0) {
  return { poseDetected: false };

const pose = rawData.poses[0];

return {
  poseDetected: true,
  keypoints: pose.keypoints,
  boundingBox: pose.boundingBox,
  confidence: pose.confidence,
  timestamp: Date.now()
};

public startRealTimeDetection(callback: (result: PoseDetectionResult) => void): void {

this.isDetecting = true;

const interval = setInterval(async () => {
  if (!this.isDetecting) {
    clearInterval(interval);
    return;

try {

    const image = await this.captureFrame();
    const result = await this.detectPose(image);
    callback(result);

catch (error) {

    console.error('Detection error:', error);

}, 300); // 每300ms检测一次

public stopRealTimeDetection(): void {

this.isDetecting = false;

private async captureFrame(): Promise<image.Image> {

// 简化实现,实际应从预览流获取帧
return new Promise((resolve) => {
  const mockImage = {};
  resolve(mockImage as image.Image);
});

}

export const poseDetectionService = PoseDetectionService.getInstance();

动作分析服务

// ExerciseAnalysisService.ets
class ExerciseAnalysisService {
private static instance: ExerciseAnalysisService;

private constructor() {}

public static getInstance(): ExerciseAnalysisService {
if (!ExerciseAnalysisService.instance) {
ExerciseAnalysisService.instance = new ExerciseAnalysisService();
return ExerciseAnalysisService.instance;

public analyzeExercise(

currentPose: PoseDetectionResult,
exerciseType: ExerciseType

): ExerciseAnalysis {
const standardPose = this.getStandardPose(exerciseType);
const comparison = this.comparePoses(currentPose, standardPose);

return {
  exerciseType,
  accuracy: comparison.accuracy,
  deviations: comparison.deviations,
  suggestions: this.generateSuggestions(comparison.deviations, exerciseType),
  timestamp: Date.now()
};

private getStandardPose(exerciseType: ExerciseType): StandardPose {

// 简化实现,实际应从模型或数据库获取标准姿势
const standardPoses: Record<ExerciseType, StandardPose> = {
  'squat': {
    keypoints: [

name: ‘left_shoulder’, x: 0.4, y: 0.3 },

name: ‘right_shoulder’, x: 0.6, y: 0.3 },

      // 其他关键点...
    ],
    angles: [

name: ‘knee_angle’, min: 90, max: 120 }

},

  'pushup': {
    keypoints: [

name: ‘left_shoulder’, x: 0.4, y: 0.4 },

name: ‘right_shoulder’, x: 0.6, y: 0.4 },

      // 其他关键点...
    ],
    angles: [

name: ‘elbow_angle’, min: 90, max: 120 }

}

  // 其他运动类型...
};

return standardPoses[exerciseType] || standardPoses.squat;

private comparePoses(

currentPose: PoseDetectionResult,
standardPose: StandardPose

): PoseComparison {
const deviations: PoseDeviation[] = [];
let matchedPoints = 0;

// 比较关键点位置
standardPose.keypoints.forEach(stdKp => {
  const currentKp = currentPose.keypoints.find(kp => kp.name === stdKp.name);
  if (currentKp) {
    const distance = this.calculateDistance(currentKp, stdKp);
    if (distance < 0.1) { // 距离阈值
      matchedPoints++;

else {

      deviations.push({
        pointName: stdKp.name,
        deviation: distance,
        direction: this.getDirection(currentKp, stdKp)
      });

}

});

// 比较关键角度
standardPose.angles.forEach(stdAngle => {
  const currentAngle = this.calculateAngle(currentPose, stdAngle.name);
  if (currentAngle) {
    if (currentAngle < stdAngle.min || currentAngle > stdAngle.max) {
      deviations.push({
        pointName: stdAngle.name,
        deviation: Math.abs(currentAngle - (currentAngle < stdAngle.min ? stdAngle.min : stdAngle.max)),
        direction: currentAngle < stdAngle.min ? 'too_small' : 'too_large'
      });

}

});

const accuracy = matchedPoints / standardPose.keypoints.length;
return { accuracy, deviations };

private generateSuggestions(deviations: PoseDeviation[], exerciseType: ExerciseType): string[] {

const suggestions: string[] = [];

deviations.forEach(dev => {
  const suggestion = this.getSuggestionForDeviation(dev, exerciseType);
  if (suggestion) {
    suggestions.push(suggestion);

});

if (suggestions.length === 0) {
  suggestions.push('动作标准,继续保持!');

return suggestions;

private getSuggestionForDeviation(dev: PoseDeviation, exerciseType: ExerciseType): string | null {

const suggestionMap: Record<string, Record<string, string>> = {
  squat: {
    knee_angle_too_small: '膝盖弯曲角度不足,尝试蹲得更深一些',
    knee_angle_too_large: '膝盖弯曲过度,注意不要超过脚尖',
    back_angle_too_small: '背部过于直立,适当前倾保持平衡'
  },
  pushup: {
    elbow_angle_too_small: '肘部弯曲不足,尝试下降得更低',
    elbow_angle_too_large: '肘部弯曲过度,注意不要完全伸直',
    hip_height_too_high: '臀部位置过高,保持身体成一条直线'

// 其他运动类型的建议…

};

const key = {dev.pointName}_{dev.direction};
return suggestionMap[exerciseType]?.[key] || null;

private calculateDistance(kp1: Keypoint, kp2: Keypoint): number {

return Math.sqrt(Math.pow(kp1.x - kp2.x, 2) + Math.pow(kp1.y - kp2.y, 2));

private getDirection(currentKp: Keypoint, stdKp: Keypoint): string {

const dx = currentKp.x - stdKp.x;
const dy = currentKp.y - stdKp.y;

if (Math.abs(dx) > Math.abs(dy)) {
  return dx > 0 ? 'too_right' : 'too_left';

else {

  return dy > 0 ? 'too_low' : 'too_high';

}

private calculateAngle(pose: PoseDetectionResult, angleName: string): number | null {
// 简化实现,实际应根据关键点计算角度
const angleMap: Record<string, number> = {
knee_angle: 110,
elbow_angle: 100,
back_angle: 170
};

return angleMap[angleName] || null;

}

export const exerciseAnalysisService = ExerciseAnalysisService.getInstance();

训练记录服务

// WorkoutRecordService.ets
import distributedData from ‘@ohos.data.distributedData’;

class WorkoutRecordService {
private static instance: WorkoutRecordService;
private kvManager: distributedData.KVManager;
private kvStore: distributedData.KVStore;

private constructor() {
this.initKVStore();
private async initKVStore(): Promise<void> {

const config = {
  bundleName: 'com.example.fitnessCoach',
  userInfo: { userId: 'currentUser' }
};

this.kvManager = distributedData.createKVManager(config);
this.kvStore = await this.kvManager.getKVStore('workout_records', {
  createIfMissing: true
});

this.kvStore.on('dataChange', (data) => {
  this.handleRemoteUpdate(data);
});

public static getInstance(): WorkoutRecordService {

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

return WorkoutRecordService.instance;

public async saveWorkoutSession(session: WorkoutSession): Promise<void> {

await this.kvStore.put(session_${session.id}, JSON.stringify(session));

public async getWorkoutSessions(): Promise<WorkoutSession[]> {

const entries = await this.kvStore.getEntries('session_');
return Array.from(entries)
  .map(([_, value]) => JSON.parse(value))
  .sort((a, b) => b.startTime - a.startTime);

public async getWorkoutSession(id: string): Promise<WorkoutSession | null> {

const value = await this.kvStore.get(session_${id});
return value ? JSON.parse(value) : null;

public async getExerciseHistory(exerciseType: ExerciseType): Promise<ExerciseHistory[]> {

const sessions = await this.getWorkoutSessions();
const history: ExerciseHistory[] = [];

sessions.forEach(session => {
  session.exercises.forEach(exercise => {
    if (exercise.type === exerciseType) {
      history.push({
        date: session.startTime,
        accuracy: exercise.accuracy,
        duration: exercise.duration,
        count: exercise.count
      });

});

});

return history.sort((a, b) => a.date - b.date);

private handleRemoteUpdate(data: distributedData.ChangeInfo): void {

if (data.deviceId === deviceInfo.deviceId) return;

const key = data.key as string;
if (key.startsWith('session_')) {
  const session = JSON.parse(data.value);
  EventBus.emit('workoutSessionUpdated', session);

}

export const workoutRecordService = WorkoutRecordService.getInstance();

三、主界面实现
实时训练界面

// WorkoutView.ets
@Component
struct WorkoutView {
@State currentExercise: ExerciseType = ‘squat’;
@State isExercising: boolean = false;
@State analysis: ExerciseAnalysis | null = null;
@State session: WorkoutSession | null = null;
@State previewSurfaceId: string = ‘’;

aboutToAppear() {
this.initCamera();
build() {

Stack() {
  // 相机预览
  XComponent({
    id: 'workoutCameraPreview',
    type: 'surface',
    libraryname: 'libcamera.so',
    controller: this.previewController
  })
  .onLoad(() => {
    this.previewSurfaceId = this.previewController.getXComponentSurfaceId();
    poseDetectionService.initCamera(this.previewSurfaceId);
  })
  .width('100%')
  .height('100%')
  
  // 训练信息
  Column() {
    Text(this.getExerciseName(this.currentExercise))
      .fontSize(24)
      .fontWeight(FontWeight.Bold)
    
    if (this.session) {
      Text(组数: {this.session.currentSet}/{this.session.totalSets})
        .fontSize(18)
        .margin({ top: 8 })
      
      Text(次数: ${this.session.exercises.find(e => e.type === this.currentExercise)?.count || 0})
        .fontSize(18)
        .margin({ top: 4 })

}

  .alignSelf(ItemAlign.Start)
  .margin({ top: 16, left: 16 })
  
  // 分析结果
  if (this.analysis) {
    ExerciseFeedbackView({ analysis: this.analysis })
      .position({ x: '50%', y: '30%' })

// 控制按钮

  Button(this.isExercising ? '结束训练' : '开始训练')
    .onClick(() => this.toggleWorkout())
    .position({ x: '50%', y: '90%' })

}

private async initCamera(): Promise<void> {
await poseDetectionService.initCamera(this.previewSurfaceId);
private toggleWorkout(): void {

if (this.isExercising) {
  this.stopWorkout();

else {

  this.startWorkout();

}

private async startWorkout(): Promise<void> {
this.session = {
id: generateId(),
startTime: Date.now(),
endTime: 0,
exercises: [],
currentSet: 1,
totalSets: 3
};

this.isExercising = true;
poseDetectionService.startRealTimeDetection((result) => {
  if (result.poseDetected) {
    this.analysis = exerciseAnalysisService.analyzeExercise(result, this.currentExercise);
    
    // 记录当前动作
    this.recordExercise(this.analysis);

});

private async stopWorkout(): Promise<void> {

this.isExercising = false;
poseDetectionService.stopRealTimeDetection();

if (this.session) {
  this.session.endTime = Date.now();
  await workoutRecordService.saveWorkoutSession(this.session);

}

private recordExercise(analysis: ExerciseAnalysis): void {
if (!this.session) return;

let exercise = this.session.exercises.find(e => e.type === analysis.exerciseType);
if (!exercise) {
  exercise = {
    type: analysis.exerciseType,
    count: 0,
    duration: 0,
    accuracy: 0,
    analyses: []
  };
  this.session.exercises.push(exercise);

exercise.count++;

exercise.duration += 0.3; // 每次检测间隔0.3秒
exercise.accuracy = (exercise.accuracy * (exercise.count - 1) + analysis.accuracy) / exercise.count;
exercise.analyses.push(analysis);

private getExerciseName(type: ExerciseType): string {

const names: Record<ExerciseType, string> = {
  squat: '深蹲',
  pushup: '俯卧撑',
  lunge: '弓步',
  plank: '平板支撑',
  'sit-up': '仰卧起坐'
};

return names[type] || type;

}

@Component
struct ExerciseFeedbackView {
private analysis: ExerciseAnalysis;

build() {
Column() {
Text(准确度: ${Math.round(this.analysis.accuracy * 100)}%)
.fontSize(20)
.fontColor(this.getAccuracyColor(this.analysis.accuracy))

  if (this.analysis.suggestions.length > 0) {
    Column() {
      Text('建议改进:')
        .fontSize(16)
        .margin({ top: 8 })
      
      ForEach(this.analysis.suggestions, (suggestion) => {
        Text(• ${suggestion})
          .fontSize(14)
          .margin({ top: 4 })
      })

.margin({ top: 8 })

}

.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.shadow({ radius: 4, color: '#00000020' })

private getAccuracyColor(accuracy: number): string {

if (accuracy > 0.9) return '#4CAF50';
if (accuracy > 0.7) return '#FFC107';
return '#F44336';

}

训练历史界面

// HistoryView.ets
@Component
struct HistoryView {
@State sessions: WorkoutSession[] = [];
@State selectedSession: WorkoutSession | null = null;

aboutToAppear() {
this.loadSessions();
EventBus.on(‘workoutSessionUpdated’, () => this.loadSessions());
build() {

Column() {
  Text('训练历史')
    .fontSize(24)
    .fontWeight(FontWeight.Bold)
    .margin({ top: 16 })
  
  if (this.sessions.length === 0) {
    Text('暂无训练记录')
      .fontSize(16)
      .margin({ top: 32 })

else {

    List({ space: 10 }) {
      ForEach(this.sessions, (session) => {
        ListItem() {
          SessionItem({ session })
            .onClick(() => this.selectedSession = session)

})

.layoutWeight(1)

}

.padding(16)

private async loadSessions(): Promise<void> {

this.sessions = await workoutRecordService.getWorkoutSessions();

}

@Component
struct SessionItem {
private session: WorkoutSession;

build() {
Row() {
Column() {
Text(formatDate(this.session.startTime))
.fontSize(18)

    Text(时长: ${formatDuration(this.session.endTime - this.session.startTime)})
      .fontSize(14)
      .fontColor('#666666')
      .margin({ top: 4 })

.layoutWeight(1)

  Column() {
    Text(${this.session.exercises.length}个动作)
      .fontSize(16)
    
    Text(${this.session.totalSets}组)
      .fontSize(14)
      .fontColor('#666666')
      .margin({ top: 4 })

}

.padding(12)

}

function formatDate(timestamp: number): string {
const date = new Date(timestamp);
return {date.getFullYear()}-{date.getMonth() + 1}-{date.getDate()} {date.getHours()}:${date.getMinutes().toString().padStart(2, ‘0’)};
function formatDuration(ms: number): string {

const seconds = Math.floor(ms / 1000);
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
return {mins}分{secs.toString().padStart(2, ‘0’)}秒;

动作详情界面

// ExerciseDetailView.ets
@Component
struct ExerciseDetailView {
@State exerciseType: ExerciseType = ‘squat’;
@State history: ExerciseHistory[] = [];
@State standardPose: StandardPose | null = null;

aboutToAppear() {
this.loadHistory();
this.loadStandardPose();
build() {

Column() {
  Text(this.getExerciseName(this.exerciseType))
    .fontSize(24)
    .fontWeight(FontWeight.Bold)
    .margin({ top: 16 })
  
  // 标准动作演示
  if (this.standardPose) {
    Text('标准动作')
      .fontSize(18)
      .margin({ top: 16 })
    
    StandardPoseView({ pose: this.standardPose })
      .height(200)
      .margin({ top: 8 })

// 历史数据图表

  if (this.history.length > 0) {
    Text('进步曲线')
      .fontSize(18)
      .margin({ top: 24 })
    
    ExerciseProgressChart({ history: this.history })
      .height(150)
      .margin({ top: 8 })

else {

    Text('暂无历史数据')
      .fontSize(16)
      .margin({ top: 32 })

// 常见错误

  Text('常见错误')
    .fontSize(18)
    .margin({ top: 24 })
  
  CommonMistakesView({ exerciseType: this.exerciseType })
    .margin({ top: 8 })

.padding(16)

private async loadHistory(): Promise<void> {

this.history = await workoutRecordService.getExerciseHistory(this.exerciseType);

private async loadStandardPose(): Promise<void> {

this.standardPose = exerciseAnalysisService.getStandardPose(this.exerciseType);

private getExerciseName(type: ExerciseType): string {

const names: Record<ExerciseType, string> = {
  squat: '深蹲',
  pushup: '俯卧撑',
  lunge: '弓步',
  plank: '平板支撑',
  'sit-up': '仰卧起坐'
};

return names[type] || type;

}

@Component
struct StandardPoseView {
private pose: StandardPose;

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

const canvas = this.$canvas;
const ctx = canvas.getContext('2d');

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

// 绘制关键点
this.pose.keypoints.forEach(kp => {
  const x = kp.x * canvas.width;
  const y = kp.y * canvas.height;
  
  ctx.beginPath();
  ctx.arc(x, y, 5, 0, Math.PI * 2);
  ctx.fillStyle = '#2196F3';
  ctx.fill();
  
  // 绘制关键点名称
  ctx.font = '12px sans-serif';
  ctx.fillStyle = '#000000';
  ctx.fillText(kp.name, x + 8, y + 5);
});

// 绘制骨架连线
const connections = this.getPoseConnections();
connections.forEach(conn => {
  const kp1 = this.pose.keypoints.find(kp => kp.name === conn[0]);
  const kp2 = this.pose.keypoints.find(kp => kp.name === conn[1]);
  
  if (kp1 && kp2) {
    ctx.beginPath();
    ctx.moveTo(kp1.x  canvas.width, kp1.y  canvas.height);
    ctx.lineTo(kp2.x  canvas.width, kp2.y  canvas.height);
    ctx.strokeStyle = '#2196F3';
    ctx.lineWidth = 2;
    ctx.stroke();

});

private getPoseConnections(): [string, string][] {

const connections: Record<ExerciseType, [string, string][]> = {
  squat: [
    ['left_shoulder', 'left_elbow'],
    ['left_elbow', 'left_wrist'],
    ['right_shoulder', 'right_elbow'],
    ['right_elbow', 'right_wrist'],
    ['left_hip', 'left_knee'],
    ['left_knee', 'left_ankle'],
    ['right_hip', 'right_knee'],
    ['right_knee', 'right_ankle']
  ],
  pushup: [
    ['left_shoulder', 'left_elbow'],
    ['left_elbow', 'left_wrist'],
    ['right_shoulder', 'right_elbow'],
    ['right_elbow', 'right_wrist'],
    ['left_hip', 'left_knee'],
    ['left_knee', 'left_ankle'],
    ['right_hip', 'right_knee'],
    ['right_knee', 'right_ankle']

// 其他动作的连接…

};

return connections[this.pose.type] || [];

}

四、高级功能实现
多设备训练同步

// WorkoutSyncService.ets
class WorkoutSyncService {
private static instance: WorkoutSyncService;

private constructor() {}

public static getInstance(): WorkoutSyncService {
if (!WorkoutSyncService.instance) {
WorkoutSyncService.instance = new WorkoutSyncService();
return WorkoutSyncService.instance;

public async syncWorkoutPlan(deviceId: string, plan: WorkoutPlan): Promise<void> {

const ability = await featureAbility.startAbility({
  bundleName: 'com.example.fitnessCoach',
  abilityName: 'WorkoutPlanAbility',
  deviceId
});

await ability.call({
  method: 'receiveWorkoutPlan',
  parameters: [plan]
});

public async syncLiveWorkout(deviceId: string, session: WorkoutSession): Promise<void> {

const ability = await featureAbility.startAbility({
  bundleName: 'com.example.fitnessCoach',
  abilityName: 'LiveWorkoutAbility',
  deviceId
});

await ability.call({
  method: 'receiveLiveWorkout',
  parameters: [session]
});

public async syncExerciseAnalysis(deviceId: string, analysis: ExerciseAnalysis): Promise<void> {

const ability = await featureAbility.startAbility({
  bundleName: 'com.example.fitnessCoach',
  abilityName: 'ExerciseAnalysisAbility',
  deviceId
});

await ability.call({
  method: 'receiveExerciseAnalysis',
  parameters: [analysis]
});

public async broadcastToAllDevices(session: WorkoutSession): Promise<void> {

const devices = await deviceManager.getTrustedDevices();
await Promise.all(devices.map(device => 
  this.syncLiveWorkout(device.id, session)
));

}

export const workoutSyncService = WorkoutSyncService.getInstance();

训练计划服务

// WorkoutPlanService.ets
class WorkoutPlanService {
private static instance: WorkoutPlanService;

private constructor() {}

public static getInstance(): WorkoutPlanService {
if (!WorkoutPlanService.instance) {
WorkoutPlanService.instance = new WorkoutPlanService();
return WorkoutPlanService.instance;

public generatePlanForGoal(goal: FitnessGoal): WorkoutPlan {

const plans: Record<FitnessGoal, WorkoutPlan> = {
  'strength': {
    id: generateId(),
    name: '力量训练计划',
    exercises: [

type: ‘squat’, sets: 4, reps: 8 },

type: ‘pushup’, sets: 3, reps: 10 },

type: ‘lunge’, sets: 3, reps: 12 }

    ],
    frequency: 3,
    duration: 4 // 周
  },
  'endurance': {
    id: generateId(),
    name: '耐力训练计划',
    exercises: [

type: ‘pushup’, sets: 3, reps: 15 },

type: ‘sit-up’, sets: 3, reps: 20 },

type: ‘plank’, sets: 3, duration: 60 } // 秒

    ],
    frequency: 4,
    duration: 6 // 周
  },
  'flexibility': {
    id: generateId(),
    name: '柔韧性训练计划',
    exercises: [

type: ‘stretch’, sets: 2, duration: 30 },

type: ‘yoga’, sets: 3, duration: 45 }

    ],
    frequency: 5,
    duration: 8 // 周

};

return plans[goal] || plans.strength;

public adjustPlanBasedOnProgress(plan: WorkoutPlan, progress: ProgressReport): WorkoutPlan {

const adjustedPlan = { ...plan };

// 根据进步情况调整计划
progress.exercises.forEach(exProgress => {
  const planExercise = adjustedPlan.exercises.find(e => e.type === exProgress.type);
  if (planExercise) {
    // 如果完成度超过90%,增加难度
    if (exProgress.completionRate > 0.9) {
      if (planExercise.reps) {
        planExercise.reps = Math.floor(planExercise.reps * 1.1);

else if (planExercise.duration) {

        planExercise.duration = Math.floor(planExercise.duration * 1.1);

}

    // 如果完成度低于70%,减少难度
    else if (exProgress.completionRate < 0.7) {
      if (planExercise.reps) {
        planExercise.reps = Math.floor(planExercise.reps * 0.9);

else if (planExercise.duration) {

        planExercise.duration = Math.floor(planExercise.duration * 0.9);

}

});

return adjustedPlan;

public getDailyPlan(plan: WorkoutPlan, day: number): DailyWorkout {

const exercisesPerDay = Math.ceil(plan.exercises.length / plan.frequency);
const startIndex = (day % plan.frequency) * exercisesPerDay;
const dailyExercises = plan.exercises.slice(startIndex, startIndex + exercisesPerDay);

return {
  date: new Date(Date.now() + day  24  60  60  1000),
  exercises: dailyExercises,
  completed: false
};

}

export const workoutPlanService = WorkoutPlanService.getInstance();

智能提醒服务

// WorkoutReminderService.ets
import reminderAgent from ‘@ohos.reminderAgent’;

class WorkoutReminderService {
private static instance: WorkoutReminderService;

private constructor() {}

public static getInstance(): WorkoutReminderService {
if (!WorkoutReminderService.instance) {
WorkoutReminderService.instance = new WorkoutReminderService();
return WorkoutReminderService.instance;

public async scheduleWorkoutReminders(plan: WorkoutPlan): Promise<void> {

await this.cancelAllReminders();

for (let day = 0; day < plan.duration * 7; day++) {
  const dailyPlan = workoutPlanService.getDailyPlan(plan, day);
  if (dailyPlan.exercises.length > 0) {
    const reminderTime = this.getOptimalWorkoutTime(dailyPlan.date);
    
    const reminderRequest: reminderAgent.ReminderRequest = {
      reminderType: reminderAgent.ReminderType.REMINDER_TYPE_TIMER,
      actionButton: [{ title: '开始训练' }, { title: '稍后提醒' }],
      wantAgent: {
        pkgName: 'com.example.fitnessCoach',
        abilityName: 'WorkoutReminderAbility'
      },
      ringDuration: 60,
      snoozeTimes: 2,
      triggerTime: reminderTime.getTime(),
      title: '训练时间到',
      content: 今日训练: ${dailyPlan.exercises.map(e => this.getExerciseName(e.type)).join(', ')},
      expiredContent: "训练提醒已过期"
    };
    
    await reminderAgent.publishReminder(reminderRequest);

}

public async scheduleRestDayReminders(plan: WorkoutPlan): Promise<void> {

for (let day = 0; day < plan.duration * 7; day++) {
  const dailyPlan = workoutPlanService.getDailyPlan(plan, day);
  if (dailyPlan.exercises.length === 0) {
    const reminderTime = this.getOptimalRestDayTime(dailyPlan.date);
    
    const reminderRequest: reminderAgent.ReminderRequest = {
      reminderType: reminderAgent.ReminderType.REMINDER_TYPE_TIMER,
      actionButton: [{ title: '知道了' }],
      wantAgent: {
        pkgName: 'com.example.fitnessCoach',
        abilityName: 'RestDayReminderAbility'
      },
      triggerTime: reminderTime.getTime(),
      title: '休息日',
      content: '今天是休息日,让身体充分恢复',
      expiredContent: "休息日提醒已过期"
    };
    
    await reminderAgent.publishReminder(reminderRequest);

}

public async cancelAllReminders(): Promise<void> {

const reminders = await reminderAgent.getValidReminders();
await Promise.all(reminders.map(rem => 
  reminderAgent.cancelReminder(rem.id)
));

private getOptimalWorkoutTime(date: Date): Date {

// 根据用户偏好返回最佳训练时间
const time = new Date(date);
time.setHours(18, 0, 0, 0); // 默认下午6点
return time;

private getOptimalRestDayTime(date: Date): Date {

const time = new Date(date);
time.setHours(10, 0, 0, 0); // 上午10点
return time;

private getExerciseName(type: ExerciseType): string {

const names: Record<ExerciseType, string> = {
  squat: '深蹲',
  pushup: '俯卧撑',
  lunge: '弓步',
  plank: '平板支撑',
  'sit-up': '仰卧起坐'
};

return names[type] || type;

}

export const workoutReminderService = WorkoutReminderService.getInstance();

五、总结

本健身动作纠正应用实现了以下核心价值:
精准识别:AI技术准确识别用户健身动作

实时反馈:即时提供动作纠正建议

个性化训练:根据用户目标制定训练计划

多设备协同:支持跨设备同步训练进度

进步追踪:记录训练历史并可视化进步曲线

扩展方向:
增加更多健身动作支持

开发社交分享功能

集成健康数据(心率、卡路里等)

增加AR虚拟教练功能

注意事项:
需要申请ohos.permission.CAMERA权限

动作识别准确率受光线和角度影响

训练建议仅供参考,请根据身体状况调整

多设备协同需保持网络连接

首次使用建议完成身体评估

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