1. 为什么需要AI健身教练?
最近两年在家健身越来越流行,但很多朋友都遇到一个共同问题:没有专业教练指导,动作做错了自己也不知道。我就吃过这个亏,去年跟着视频做深蹲,结果膝盖疼了一个月,后来才知道是动作不规范导致的。传统解决方案要么请私教(太贵),要么对着镜子练(很多角度看不到),而AI健身教练完美解决了这些问题。
AI健身教练的核心是姿态估计技术,它能通过普通摄像头实时捕捉你的身体关键点,就像有双眼睛在时刻盯着你的每个动作。我实测下来,用Python+MediaPipe搭建的系统,在普通笔记本上就能跑出30FPS的流畅效果,延迟不到0.1秒。这对于需要即时反馈的健身场景来说完全够用。
2. 搭建开发环境
2.1 硬件准备
你只需要:
- 任何带摄像头的电脑(我用的是5年前的联想小新)
- 至少4GB内存(处理视频流会占用约2GB)
- 普通USB摄像头(分辨率720P就够用)
注意:如果要用GPU加速,建议NVIDIA显卡+安装CUDA驱动,不过对基础功能不是必须的
2.2 软件安装
创建虚拟环境是个好习惯:
python -m venv ai_coach source ai_coach/bin/activate # Linux/Mac ai_coach\Scripts\activate.bat # Windows安装核心库:
pip install opencv-python mediapipe numpy我遇到过的一个坑:MediaPipe对OpenCV版本有要求,如果报错可以尝试指定版本:
pip install opencv-python==4.5.5.643. 人体姿态检测实战
3.1 基础骨架检测
先来看最简实现:
import cv2 import mediapipe as mp mp_drawing = mp.solutions.drawing_utils mp_pose = mp.solutions.pose cap = cv2.VideoCapture(0) # 0表示默认摄像头 with mp_pose.Pose( min_detection_confidence=0.7, min_tracking_confidence=0.7) as pose: while cap.isOpened(): ret, frame = cap.read() if not ret: continue # 关键点检测 frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = pose.process(frame_rgb) # 绘制骨架 if results.pose_landmarks: mp_drawing.draw_landmarks( frame, results.pose_landmarks, mp_pose.POSE_CONNECTIONS) cv2.imshow('AI Coach', frame) if cv2.waitKey(1) == 27: # ESC退出 break cap.release() cv2.destroyAllWindows()这段代码做了三件事:
- 初始化摄像头和MediaPipe姿态模型
- 实时转换视频帧格式(BGR→RGB)
- 检测并绘制33个人体关键点(包括面部、躯干和四肢)
3.2 关键点坐标解析
MediaPipe返回的landmark对象包含:
- x,y:归一化坐标(0~1之间)
- z:深度估计(数值越小离摄像头越近)
- visibility:可见性置信度
获取具体坐标的实用代码:
landmarks = results.pose_landmarks.landmark # 获取左肩坐标(像素值) height, width = frame.shape[:2] left_shoulder_x = int(landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER].x * width) left_shoulder_y = int(landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER].y * height)4. 动作标准度判断
4.1 深蹲检测算法
标准深蹲有两个要点:
- 膝盖不超过脚尖
- 大腿与地面平行
实现代码:
def is_good_squat(landmarks, frame_width, frame_height): # 获取关键点像素坐标 left_hip = landmarks[mp_pose.PoseLandmark.LEFT_HIP.value] left_knee = landmarks[mp_pose.PoseLandmark.LEFT_KNEE.value] left_ankle = landmarks[mp_pose.PoseLandmark.LEFT_ANKLE.value] # 计算关键角度 hip_knee_angle = calculate_angle( (left_hip.x, left_hip.y), (left_knee.x, left_knee.y), (left_ankle.x, left_ankle.y)) # 判断标准 knee_over_toes = left_knee.x > left_ankle.x hip_below_knee = left_hip.y > left_knee.y return hip_knee_angle > 160 and not knee_over_toes and hip_below_knee def calculate_angle(a, b, c): # 计算三点形成的角度 ba = (a[0]-b[0], a[1]-b[1]) bc = (c[0]-b[0], c[1]-b[1]) dot = ba[0]*bc[0] + ba[1]*bc[1] len_ba = math.sqrt(ba[0]**2 + ba[1]**2) len_bc = math.sqrt(bc[0]**2 + bc[1]**2) angle = math.acos(dot / (len_ba * len_bc)) return math.degrees(angle)4.2 实时反馈系统
在原始画面上叠加提示信息:
if is_good_squat(landmarks, width, height): cv2.putText(frame, "GOOD SQUAT!", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,0), 2) else: feedback = [] if knee_over_toes: feedback.append("膝盖不要超过脚尖") if not hip_below_knee: feedback.append("下蹲再深一些") cv2.putText(frame, "注意: " + ",".join(feedback), (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2)5. 进阶功能扩展
5.1 动作计数
记录深蹲次数:
squat_count = 0 is_down_position = False # 在循环判断中增加: if hip_below_knee and not is_down_position: is_down_position = True elif not hip_below_knee and is_down_position: is_down_position = False squat_count += 1 print(f"完成第{squat_count}次深蹲")5.2 语音反馈
用pyttsx3增加语音提示:
import pyttsx3 engine = pyttsx3.init() # 在检测到错误时调用 engine.say("膝盖超过脚尖了,注意调整") engine.runAndWait()5.3 训练数据收集
为后续机器学习准备数据集:
import pandas as pd columns = [f"{point.name}_x" for point in mp_pose.PoseLandmark] columns += [f"{point.name}_y" for point in mp_pose.PoseLandmark] columns += ["label"] # 动作类型 df = pd.DataFrame(columns=columns) # 每次检测时保存 row = [] for landmark in landmarks: row.extend([landmark.x, landmark.y]) row.append("squat") # 动作标签 df.loc[len(df)] = row df.to_csv("workout_data.csv", index=False)6. 性能优化技巧
6.1 多线程处理
使用线程分离图像采集和AI计算:
from threading import Thread from queue import Queue frame_queue = Queue(maxsize=1) result_queue = Queue(maxsize=1) def capture_thread(): while True: ret, frame = cap.read() if frame_queue.empty(): frame_queue.put(frame) def process_thread(): while True: if not frame_queue.empty(): frame = frame_queue.get() # 处理逻辑... result_queue.put(processed_frame) Thread(target=capture_thread).start() Thread(target=process_thread).start()6.2 模型轻量化
调整MediaPipe参数提升速度:
with mp_pose.Pose( static_image_mode=False, # 视频流模式 model_complexity=1, # 0-2,数字越小越快 enable_segmentation=False, # 关闭分割掩模 min_detection_confidence=0.5, min_tracking_confidence=0.5) as pose: # ...7. 常见问题解决
7.1 关键点抖动问题
我通过两种方式缓解:
- 移动平均滤波
from collections import deque history = deque(maxlen=5) # 保留最近5帧 # 在获取关键点后: history.append(landmarks) smoothed = np.mean(history, axis=0)- 增加tracking_confidence阈值
min_tracking_confidence=0.7 # 默认0.57.2 遮挡处理
当手臂挡住躯干时,可以:
- 使用历史数据补全
- 根据对称性推测(如右肩被挡时用左肩镜像位置)
- 提示用户调整站位
8. 项目部署方案
8.1 桌面应用打包
用PyInstaller生成exe:
pip install pyinstaller pyinstaller --onefile --windowed ai_coach.py8.2 Web服务化
用Flask创建API:
from flask import Flask, Response app = Flask(__name__) @app.route('/video_feed') def video_feed(): return Response(generate_frames(), mimetype='multipart/x-mixed-replace; boundary=frame') def generate_frames(): while True: # 处理帧的逻辑... ret, buffer = cv2.imencode('.jpg', frame) yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + buffer.tobytes() + b'\r\n')9. 扩展更多健身动作
9.1 俯卧撑检测
判断标准:
- 身体保持直线
- 胸部贴近地面
def is_good_pushup(landmarks): shoulder = landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER] hip = landmarks[mp_pose.PoseLandmark.LEFT_HIP] ankle = landmarks[mp_pose.PoseLandmark.LEFT_ANKLE] # 计算身体直线度 angle = calculate_angle( (shoulder.x, shoulder.y), (hip.x, hip.y), (ankle.x, ankle.y)) return 170 < angle < 190 # 接近180度9.2 平板支撑计时
记录持续时间:
start_time = None hold_time = 0 if is_plank_position(landmarks): if start_time is None: start_time = time.time() else: hold_time = time.time() - start_time else: start_time = None10. 实际应用建议
- 光照条件:避免背光,侧光能产生更好的深度感
- 着装建议:穿紧身衣比宽松衣服检测更准
- 摄像头角度:平视角度最佳,俯拍或仰拍会影响判断
- 训练模式:建议先做3-5次标准动作让系统学习你的身体特征
我在实际部署时发现,简单的规则判断已经能满足80%的基础需求。对于更复杂的动作,可以考虑收集用户数据训练专属模型。最近我在尝试用MediaPipe的输出作为特征,配合LightGBM模型来判断高级瑜伽动作,准确率能达到92%以上。