尧图精选

Python+MediaPipe实战:健身动作识别与姿态估计指南

🕒 发布时间:2026/10/2 5:20:56 📁 来源:尧图网络
简介这份源码资源面向希望将计算机视觉落地到健身场景的Python开发者与AI学习者针对无教练指导时动作不规范、影响训练效果甚至造成损伤的痛点提供一套可运行的健身动作识别与指导方案。压缩包共20个文件约83KB以8个py源码文件为核心辅以7个xml配置、2个txt说明、1个md文档及license、iml等工程文件涵盖主程序入口、src模块、data数据与utils工具等目录结构清晰便于二次开发。已有88人学习下载。源码涉及人体姿态估计、动作识别算法、数据采集处理与实时反馈机制等关键环节读者可据此理解摄像头捕捉、关键点比对与动作规范性判断的完整流程并借助OpenCV、TensorFlow或PyTorch等框架进行扩展适合作为课程设计、毕业项目或视觉识别练手参考。1. 从一份健身动作识别源码说起它到底能解决什么问题健身房里最常见的场景一个人对着镜子做深蹲膝盖内扣了自己却看不见腰背弓了也没察觉等到疼了才反应过来动作早就跑偏了。私教能纠正但私教不可能每分钟都盯着你。这份「基于 Python 的利用视觉识别技术对健身动作进行识别指导」的源码瞄准的就是这个缺口——用摄像头加姿态估计模型实时判断你的动作是否标准并在屏幕上给出反馈。它适合三类人想入门计算机视觉的 Python 开发者、做智能健身硬件或 App 的团队、以及体育院校里想用数据量化动作质量的研究者。核心链路并不复杂视频帧采集 → 人体姿态估计 → 关键点坐标提取 → 动作规则判定 → 可视化反馈。整条链路用 Python 就能串起来依赖的库也都是开源生态里成熟的东西。读完这篇你能在自己的电脑上跑通一套最小可用的动作识别流程知道每个环节该用什么工具、参数怎么调、哪里最容易翻车。2. 姿态估计选型为什么 MediaPipe 是健身场景的默认答案2.1 从 OpenPose 到 MediaPipe精度与速度的取舍做人体姿态估计摆在面前的选择不少。OpenPose 是学术界的经典方案精度高、支持多人但它对 GPU 的依赖很重推理速度在普通笔记本上很难做到实时。AlphaPose 精度也不错但部署链路长对新手不够友好。MediaPipe 是 Google 推出的轻量级方案CPU 上就能跑到 30 FPS 以上单人场景下关键点精度完全够用而且 Python 接口极其简洁。健身动作识别有个特点绝大多数时候画面里只有一个人且这个人占据画面主体。这个前提让 MediaPipe 的优势被放大——它不需要处理多人遮挡的复杂情况把算力全花在单人的关键点精度上。MediaPipe Pose 输出 33 个关键点覆盖了肩、肘、腕、髋、膝、踝等主要关节对于深蹲、卧推、硬拉、弓步这些常见动作来说关键点数量是够用的。选型时还要考虑一个现实问题你的代码最终跑在哪里。如果是手机 AppMediaPipe 有移动端 SDK如果是桌面端或 Web 端Python 版和 JS 版都能用。这种跨平台一致性是 OpenPose 给不了的。2.2 用 pip 装好环境并跑通第一个姿态估计脚本先确认 Python 版本。MediaPipe 目前对 Python 3.8 到 3.11 支持最好3.12 部分版本可能有兼容问题。用python --version看一眼如果是 3.12 以上建议用 conda 建一个 3.10 的虚拟环境。# 创建虚拟环境避免污染全局包 python -m venv fitness_env source fitness_env/bin/activate # Windows 用 fitness_env\Scripts\activate # 安装核心依赖 pip install mediapipe opencv-python numpy装完之后用下面这段代码验证环境是否正常。它会打开摄像头实时画出人体骨架。import cv2 import mediapipe as mp # 初始化 MediaPipe Pose 模块 mp_pose mp.solutions.pose mp_draw mp.solutions.drawing_utils # static_image_modeFalse 表示视频流模式会做帧间跟踪速度更快 # model_complexity1 是默认值0 最快但精度低2 最准但慢 pose mp_pose.Pose( static_image_modeFalse, model_complexity1, smooth_landmarksTrue, min_detection_confidence0.5, min_tracking_confidence0.5 ) cap cv2.VideoCapture(0) # 0 表示默认摄像头 while cap.isOpened(): ret, frame cap.read() if not ret: break # MediaPipe 要求输入 RGB 格式OpenCV 默认是 BGR rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results pose.process(rgb) if results.pose_landmarks: # 在帧上绘制骨架连接线 mp_draw.draw_landmarks( frame, results.pose_landmarks, mp_pose.POSE_CONNECTIONS ) cv2.imshow(Pose, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码里几个参数值得说清楚。static_image_modeFalse是视频流模式的关键它会让 MediaPipe 在相邻帧之间做关键点跟踪而不是每帧都从头检测速度能快一倍以上。smooth_landmarksTrue会对关键点做时间平滑减少抖动但如果你要做快速动作的瞬时判断可以关掉它。min_detection_confidence和min_tracking_confidence分别控制检测和跟踪的置信度阈值光线差的环境可以适当降到 0.3但太低会引入误检。跑通之后你会看到摄像头画面里出现了彩色骨架线。如果骨架闪烁严重多半是光照不足或者背景太杂乱这是第一个要过的坎。3. 动作判定逻辑把关键点坐标变成「标准或不标准」的结论3.1 用关节角度做规则引擎而不是训练一个端到端模型很多人第一反应是收集大量标准动作和不标准动作的视频训练一个分类模型。这条路不是不行但成本高、可解释性差而且一旦要加新动作就得重新标注数据。对于健身动作识别这个场景更务实的做法是用关节角度做规则引擎。原理很简单人体动作的本质是关节在空间中的角度变化。深蹲时膝关节角度从站立时的约 170 度降到最低点的 70 到 90 度卧推时肘关节角度从伸展的 160 度降到胸前的约 90 度。只要算出这些角度再和标准范围对比就能判断动作是否到位。MediaPipe 输出的关键点有归一化的 x、y、z 坐标。用三个点就能算夹角比如髋、膝、踝三个点算出膝关节角度。公式就是向量点积的反余弦。import numpy as np def calculate_angle(a, b, c): 计算三个关键点构成的夹角b 是顶点 a, b, c 格式为 [x, y] 或 [x, y, z] 返回角度值单位度 a np.array(a[:2]) # 只用 x, y忽略 z 轴减少噪声 b np.array(b[:2]) c np.array(c[:2]) # 构造两个向量b-a 和 b-c ba a - b bc c - b # 点积公式求夹角 cosine np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc) 1e-6) angle np.degrees(np.arccos(np.clip(cosine, -1.0, 1.0))) return angle这里有个细节np.clip把余弦值限制在 -1 到 1 之间防止浮点误差导致arccos报错。分母加1e-6是防止向量长度为零时除零。这些看起来是小事但在实际运行中不加就会偶发崩溃属于典型的血泪经验。3.2 深蹲动作的完整判定流程与阈值设定以深蹲为例把判定逻辑串起来。需要关注三个指标膝关节角度、髋关节角度、躯干倾斜角。def analyze_squat(landmarks): landmarks: MediaPipe 输出的关键点列表每个元素有 x, y, z 返回判定结果字典 # 提取关键点坐标MediaPipe 索引 left_hip [landmarks[23].x, landmarks[23].y] left_knee [landmarks[25].x, landmarks[25].y] left_ankle [landmarks[27].x, landmarks[27].y] left_shoulder [landmarks[11].x, landmarks[11].y] # 膝关节角度 knee_angle calculate_angle(left_hip, left_knee, left_ankle) # 髋关节角度 hip_angle calculate_angle(left_shoulder, left_hip, left_knee) # 躯干倾斜角肩到髋的连线与垂直方向的夹角 torso_vector np.array(left_shoulder) - np.array(left_hip) torso_angle np.degrees(np.arctan2( abs(torso_vector[0]), abs(torso_vector[1]) 1e-6 )) feedback [] # 下蹲深度判定 if knee_angle 120: feedback.append(下蹲深度不够继续往下) elif knee_angle 60: feedback.append(下蹲过深注意膝盖压力) # 躯干前倾判定 if torso_angle 45: feedback.append(躯干过度前倾挺直腰背) # 髋膝协调判定 if hip_angle 120 and knee_angle 90: feedback.append(先屈髋再屈膝注意动作顺序) return { knee_angle: round(knee_angle, 1), hip_angle: round(hip_angle, 1), torso_angle: round(torso_angle, 1), feedback: feedback if feedback else [动作标准] }阈值设定是这套逻辑的核心。膝关节角度 120 度以上算下蹲不足60 度以下算过深这两个数字不是拍脑袋来的——参考了运动生物力学里对深蹲安全范围的研究大腿与地面平行时膝关节角度大约在 90 到 100 度之间。躯干倾斜角超过 45 度就属于明显弓背需要提醒。但要注意这些阈值是给健康成年人做参考的。如果用户有膝关节伤病或者做的是相扑深蹲这类变式阈值需要调整。实际产品里通常会提供几档预设让用户自己选。3.3 把判定结果实时叠加到视频画面上判定逻辑有了接下来要把它变成用户能看到的反馈。最直接的方式是在视频帧上用 OpenCV 画文字和标记。def draw_feedback(frame, result, landmarks): 在视频帧上绘制角度数值和文字反馈 h, w frame.shape[:2] # 在画面左上角显示角度数据 cv2.putText(frame, fKnee: {result[knee_angle]}, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) cv2.putText(frame, fHip: {result[hip_angle]}, (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) # 在画面底部显示反馈文字红色表示需要纠正 for i, msg in enumerate(result[feedback]): color (0, 0, 255) if msg ! 动作标准 else (0, 255, 0) cv2.putText(frame, msg, (10, h - 30 - i * 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2) # 在膝关节位置画一个圆点标记 knee landmarks[25] cv2.circle(frame, (int(knee.x * w), int(knee.y * h)), 8, (255, 0, 0), -1) return frame把这段和前面的姿态估计代码接在一起就形成了一个完整的闭环摄像头采集 → 姿态估计 → 角度计算 → 规则判定 → 画面反馈。整个流程在一台普通笔记本上跑延迟可以控制在 100 毫秒以内用户几乎感觉不到滞后。注意OpenCV 的putText不支持中文如果要在画面上显示中文反馈需要用 PIL 绘制或者提前把文字渲染成图片再叠加。这是新手最容易卡住的地方之一。4. 避坑与排查姿态估计在真实场景里的五个翻车点4.1 关键点抖动严重角度数值跳来跳去现象画面里人站着不动但膝关节角度在 160 到 175 之间反复跳动导致反馈文字频繁闪烁。原因MediaPipe 在每一帧独立做检测帧与帧之间的关键点位置存在微小误差经过角度计算后被放大。另外光照变化和背景纹理也会干扰检测。解决开启smooth_landmarksTrue这是第一道防线。如果还不够在角度层面再做一次滑动窗口平均取最近 5 帧的中位数。不要用均值均值对异常值太敏感中位数更稳。from collections import deque angle_buffer deque(maxlen5) def stable_angle(new_angle): angle_buffer.append(new_angle) return float(np.median(angle_buffer))4.2 侧面拍摄时远侧关节被遮挡角度算出来是错的现象用户侧对摄像头做深蹲靠近镜头的那条腿角度正常但系统偶尔会跳到另一条腿的关键点导致角度突变。原因MediaPipe 会同时输出左右两侧的关键点当一侧被遮挡时该侧关键点的置信度会下降但坐标仍然存在直接使用就会得到错误角度。解决在计算角度前先检查关键点的可见性visibility。MediaPipe 的每个关键点都有一个visibility属性低于 0.5 的就不要用。同时固定使用一侧的关键点比如始终用左侧不要混用。def is_visible(landmark, threshold0.5): return landmark.visibility threshold # 使用前检查 if is_visible(landmarks[23]) and is_visible(landmarks[25]) and is_visible(landmarks[27]): knee_angle calculate_angle(left_hip, left_knee, left_ankle) else: knee_angle None # 标记为不可用跳过本帧判定4.3 摄像头帧率不稳定动作快了就漏帧现象用户快速做动作时系统反馈明显滞后甚至错过整个下蹲过程。原因cap.read()是阻塞式的如果姿态估计耗时超过帧间隔就会丢帧。另外 OpenCV 默认的摄像头缓冲区会积压旧帧导致画面延迟越来越大。解决设置摄像头缓冲区为 1确保每次读到的都是最新帧。同时把姿态估计放到独立线程里主线程只负责采集和显示。cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)如果还是卡把model_complexity降到 0牺牲一点精度换速度。在健身场景里动作的宏观趋势比关键点的毫米级精度重要得多。4.4 不同身高体型的人用同一套阈值误判率高现象个子高的人做标准深蹲系统提示「下蹲深度不够」个子矮的人蹲得很浅系统却显示「动作标准」。原因关节角度阈值没有考虑个体差异。腿长的人在下蹲到同样深度时膝关节角度天然会比腿短的人大。解决做一次校准。让用户在站立状态下先采集几帧记录初始的髋、膝、踝位置然后根据腿长比例动态调整阈值。简单做法是用身高做归一化把角度阈值乘以一个基于腿长的系数。4.5 光线不足时关键点直接丢失现象傍晚或者室内灯光偏暗时骨架完全消失系统没有任何输出。原因MediaPipe 的检测模型对光照敏感低于一定亮度时置信度会骤降。解决在采集环节做亮度补偿。用 OpenCV 的convertScaleAbs做简单的对比度拉伸或者用 CLAHE 做自适应直方图均衡。如果还不行就得加补光灯——这是硬件层面的事软件只能缓解不能根治。# CLAHE 自适应直方图均衡对光照不均效果较好 clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) lab cv2.cvtColor(frame, cv2.COLOR_BGR2LAB) lab[:, :, 0] clahe.apply(lab[:, :, 0]) frame cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)5. 从能跑到好用三个让识别指导更靠谱的进阶技巧5.1 用动作状态机替代逐帧判定逐帧判定有个天然缺陷它不知道用户当前处于动作的哪个阶段。深蹲的起始站立、下蹲、底部停顿、起身是四个不同阶段每个阶段该关注的角度指标不一样。逐帧判定会在站立时也提示「下蹲深度不够」这显然不合理。更好的做法是引入状态机。用膝关节角度的变化趋势来判断阶段角度持续减小说明在下蹲角度持续增大说明在起身角度在某个范围内波动说明在底部停顿。每个阶段只检查该阶段相关的指标。class SquatStateMachine: def __init__(self): self.state standing # standing / descending / bottom / ascending self.prev_angle 170 def update(self, knee_angle): delta knee_angle - self.prev_angle if self.state standing and delta -3: self.state descending elif self.state descending and abs(delta) 2: self.state bottom elif self.state bottom and delta 3: self.state ascending elif self.state ascending and knee_angle 160: self.state standing self.prev_angle knee_angle return self.state状态机的好处是反馈更精准。在 descending 阶段提示「继续下蹲」在 bottom 阶段检查深度和躯干角度在 ascending 阶段提示「保持核心收紧」。用户感受到的是一套有节奏的指导而不是一堆零散的判断。5.2 用录制回放做动作质量评分实时反馈解决的是「当下」的问题但用户往往想知道「我这一组做得怎么样」。加一个录制回放功能把整个动作过程的关键点序列存下来动作结束后做一次离线分析给出一个综合评分。评分维度可以包括下蹲深度是否达标、躯干是否稳定、动作节奏是否均匀、左右是否对称。每个维度打分后加权求和最后给一个百分制分数。这个功能对用户的激励效果比实时反馈更强因为它提供了可比较的量化指标。def score_squat_session(angle_sequence): 对一组深蹲的关键点序列做离线评分 min_knee min(angle_sequence) depth_score 100 if 70 min_knee 100 else max(0, 100 - abs(min_knee - 85) * 2) # 节奏评分下蹲和起身的帧数比例越接近 1:1 越好 # 这里简化处理实际需要根据状态机切分阶段 stability_score 100 - np.std(angle_sequence) * 0.5 total depth_score * 0.6 stability_score * 0.4 return { depth: round(depth_score, 1), stability: round(stability_score, 1), total: round(total, 1) }5.3 多动作扩展时的代码组织方式一份源码如果只支持深蹲价值有限。要扩展到卧推、硬拉、弓步代码结构得提前设计好。我的习惯是定义一个动作基类把关键点提取、角度计算、状态机、评分逻辑都做成可覆写的方法。每个具体动作只需要实现自己的阈值和判定规则。模块职责是否可复用姿态估计调用 MediaPipe 输出关键点完全复用角度计算三点夹角、向量夹角完全复用状态机动作阶段划分需按动作定制阈值配置各阶段的角度范围需按动作定制反馈生成文字提示和可视化部分复用评分逻辑多维度打分需按动作定制这样组织之后加一个新动作的工作量大概就是写一个配置文件加一个状态机子类半天能搞定。如果一开始就把所有逻辑写在一个大函数里后面每加一个动作都是灾难。最后说一个我自己的习惯每次调完阈值我都会用手机录一段自己的动作然后离线跑一遍代码把关键点角度曲线画出来看。曲线比数字直观得多哪里抖动、哪里跳变、哪里阈值设得不合理一眼就能看出来。这个习惯帮我省了很多次「改了参数不知道有没有变好」的纠结。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →