简介:这是一套基于Python搭建的AI健身评分系统实现资源,面向姿态估计、动作识别及运动分析方向的开发者与健身科技爱好者,可应用于体育训练辅助、动作规范检测等场景。项目以举哑铃动作为例,先提取人体关键点,再计算骨骼向量角并与标准动作实时比对,最终输出各肢体评分和总评分。压缩包共114个文件,内容覆盖源代码、训练好的h5/pb模型、依赖库与脚本、启动工具、演示视频和说明文档,其中包含41个Python源码以及C++/Cython扩展模块,整体约225.1MB,目录结构便于按模块查阅。目前已有42人学习下载。资源提供了从关键点检测、特征计算到评分展示的完整工程文件,配合配套文章可快速掌握姿态估计在健身评分中的落地思路,适合作为动作评估类项目的开发基础或毕业设计参考。
1. 健身评分系统到底在评什么:不是数次数,是把动作质量变成分数
健身App里的自动计数先火过一轮,可真正落到私教场景时你会发现,次数是最没价值的信息。一个只下蹲到120度的半程深蹲,和一个蹲到90度以下的标准深蹲,计数都是1,但前者对股四头肌的刺激可能只有后者的一半。健身评分系统要解决的,正是把“动作完成质量”量化成用户能感知的分数。
标题里的 Python 搭建 AI 健身评分系统,本质上是一条完整的视觉计算流水线:用摄像头采集画面,用姿态估计模型从单帧图像中提取人体骨架关键点,再按动作标准计算关节角度、动作速度和稳定性,最后映射成 0 到 100 的分数。这个方案适合三类人:做健身App想要自动纠错反馈的产品团队,做体育测试系统需要客观评分标准的工程师,以及想在本地跑通一个 Python+AI 视觉项目的人。整套链路不依赖云端,一台普通笔记本就能跑出可用效果。
业务逻辑的设计顺序是:先选姿态估计的骨架方案,再把角度算准,接着把它转成规则引擎下的分数,最后用防抖和置信度过滤把分数稳定下来。这个顺序决定了整套实现的主线。需要先说明的是,后面的内容默认你具备基础的 Python 语法水平,能读懂函数和字典取值;如果你还处在学入门 Python 教程的阶段,建议先把代码复制跑起来,再回头把每一行拆着看。
2. 技术选型与原理:为什么用 MediaPipe Pose 算关节角度而不是直接训练分类模型
2.1 MediaPipe Pose 的 33 个关键点:轻量级骨架方案为什么比 OpenPose 更合适
当我第一次搭建健身评分系统时,候选方案只有三个:OpenPose、MediaPipe Pose 和 YOLO-Pose。OpenPose 精度确实高,但 Python 环境配置要装 Caffe 和一堆 CMake 依赖,模型权重动辄两三百MB,在 8GB 内存的笔记本上做实时推理每帧要 100ms 以上。YOLO-Pose 更偏向目标检测场景,对单人的健身动作反而杀鸡用牛刀。MediaPipe Pose 是当时平衡得最好的选择:模型权重只有几十MB,pip 安装一次到位,CPU 上单帧推理大约 20 到 50ms,更关键的是它直接输出 33 个归一化关键点坐标,省掉了自己写坐标解码的步骤。
这 33 个关键点覆盖了健身评分需要的全部部位:左右眼、鼻子、耳朵属于头面部;左右肩、肘、腕是手臂;左右髋、膝、踝、脚趾是下肢。对应 MediaPipe Python SDK 里的mp_pose.PoseLandmark枚举,每个关键点都有固定索引。深蹲和俯卧撑主要用肩、髋、膝、踝四个关节,开合跳会多用到肘和腕。索引从 0 到 32,建议在代码里始终用枚举名而不是裸数字,比如mp_pose.PoseLandmark.LEFT_KNEE.value。这样代码可读性高,后续扩展动作时也不会把左右手弄混。
在mp_pose.Pose构造函数里,有三个关键参数需要理解:model_complexity、min_detection_confidence、min_tracking_confidence。model_complexity取 0、1、2 三档,0 是速度最快精度最低,2 是精度最高速度最慢。我的经验是 CPU 上默认取 1,GPU 上可以取 2;如果目标是移动端,取 0 也可接受。min_detection_confidence是首次检测到人体时需要的最低置信度,min_tracking_confidence是后续帧里持续跟踪关键点所需置信度。默认 0.5 对大多数场景够用,如果画面里明显有人但检测不到,把前者调到 0.7;如果关键点乱跳,把后者调到 0.8。调参时先固定摄像头分辨率和 Python 解释器版本,再动这两个值,否则最后很难归因是哪一步改动导致结果变化。
2.2 坐标读取与归一化:直接拿原始像素算角度是第一个大坑
MediaPipe 返回的landmark.x和landmark.y不是像素坐标,而是归一化坐标。x表示关键点在图像宽度上的比例,y表示在图像高度上的比例,范围都在 0 到 1 之间;z是相对髋部中心的深度值,数值的相对意义大于绝对意义。如果直接把这些值当成像素坐标去画图、去算距离,做出来的效果会完全错误。要还原成像素坐标,必须拿到当前帧的宽高,再乘回去:
# 还原像素坐标的示例,frame_w 和 frame_h 是当前帧的宽高 pixel_x = int(landmark.x * frame_w) pixel_y = int(landmark.y * frame_h)在健身评分系统的代码里,这个乘法只在需要把关键点叠加绘制到画面上的时候才用得上。关节角度计算需要的本质是“方向”,而两个关键点之间的方向由坐标差决定。归一化坐标本身就是各向同性的缩放,只要宽高比例一致,算出来的角度不会受影响。所以我在实现里把归一化坐标直接喂给角度计算函数,省掉一次乘法和一次坐标转换,还避免了浮点精度损失。
还有一个容易被忽略的坐标系细节是镜像问题。用普通摄像头采集画面时,人会本能地希望画面像照镜子一样左右翻转,但翻转后左肩和右肩的坐标也要跟着交换。MediaPipe 处理的是翻转后的图像,LEFT_SHOULDER对应的是画面中左侧的肩,也就是用户自己的右手侧。如果只用单侧关键点做评分,左右混淆影响不大;但如果你把左右腿的角度都算进去做对称性评估,就必须理清这个映射。建议在做了cv2.flip(frame, 1)镜像预览之后,先打印一遍所有关键点坐标,验证左右关系与预期一致。
2.3 关节角度计算:为什么用 atan2 而不是余弦定理
深蹲评分最重要的量是膝关节处“髋-膝-踝”的夹角。计算三个关键点的夹角,中学数学里有两条路:向量点积加反余弦,或者两个方向角的差。工程上我强烈建议用 atan2 方向角差值法,原因在于数值稳定性。向量点积法先算两条向量的点积,再除以模长得到余弦值,最后取反余弦。这个流程在角度接近 0 度和 180 度时,浮点计算会把余弦值截断,导致最终角度误差变大。而 atan2 直接接收两个分量的差,返回范围是 -π 到 π,天然处理了象限问题。
下面是完整的角度计算函数:
import math def calc_angle(a, b, c): """计算由三个关键点构成的夹角,顶点是 b。 a, b, c 各自需要有 .x 和 .y 属性,推荐直接传 landmark 对象。 """ # 两条边的方向向量,这里直接用归一化坐标的差值 v1_x = a.x - b.x v1_y = a.y - b.y v2_x = c.x - b.x v2_y = c.y - b.y # 两条边各自的方位角 rad1 = math.atan2(v1_y, v1_x) rad2 = math.atan2(v2_y, v2_x) # 做差并转换到 0~180 度区间 diff = abs(rad1 - rad2) * 180.0 / math.pi if diff > 180.0: diff = 360.0 - diff return diff逻辑说明:函数第一步用顶点 b 到 a 和 c 的向量差,明确两条边的方向。调用atan2的好处是,当向量指向第三象限时能自动返回正确的负角度,无需额外判断象限。最后把超过 180 度的差折算回锐角范围,对正常关节角度输出已经够用。
调用calc_angle时,传入的是 MediaPipe 的landmark对象而不是 Python 内置列表。取关键点时保持原样,不要先转成 List 再传。如果转成 List,属性名丢失,代码可读性下降。另一个常见误用是直接把角度存成int,导致后续平滑滤波精度不足。建议中间计算始终用float,只在最后显示时取整。
3. 用 Python 从零搭建健身评分系统:摄像头采集、骨架可视化与动作阶段判断
3.1 环境准备:安装 OpenCV、MediaPipe 与 NumPy
搭建这套系统只需要四个 Python 依赖:opencv-python、mediapipe、numpy,外加标准库math。MediaPipe 目前对 Python 3.8 到 3.11 的兼容性最好,如果你用的是 3.12 以上,先查一下版本对应关系,否则可能在导入阶段就报AttributeError。在 VSCode 配置 Python 环境时,记得先给当前项目创建虚拟环境,再用终端执行安装,避免污染全局环境的包管理。
# 创建并激活虚拟环境 python -m venv venv # Windows 下激活 venv\Scripts\activate # macOS/Linux 下激活 source venv/bin/activate # 安装依赖,国内网络慢时可以加 -i 镜像源 pip install opencv-python mediapipe numpy参数说明:venv不是必须的,但对这种带媒体库依赖的项目,强烈建议加一层隔离。pip install的版本建议锁定到当前稳定版,不要追 rc 版。如果遇到mediapipe安装失败,先检查 Python 版本,再检查是否缺wheel包,通常pip install wheel就能解决。第一次装完可以跑一句import mediapipe验证导入,能过说明依赖链路没问题。
3.2 实时摄像头采集与骨架可视化:先让用户看见自己
第一步先做最小可视化闭环,让摄像头画面里的骨架线条实时叠加上来。这段代码既是后续所有功能的底座,也是调试时最直观的反馈工具。
import cv2 import mediapipe as mp mp_pose = mp.solutions.pose mp_draw = mp.solutions.drawing_utils pose = mp_pose.Pose( static_image_mode=False, # 视频流模式 model_complexity=1, # CPU 上的精度/速度折中 min_detection_confidence=0.5, min_tracking_confidence=0.5, ) cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) # 降低分辨率换帧率 cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) while cap.isOpened(): ret, frame = cap.read() if not ret: break # 镜像翻转,让预览画面符合镜子直觉 frame = cv2.flip(frame, 1) rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = pose.process(rgb_frame) if results.pose_landmarks: mp_draw.draw_landmarks( frame, results.pose_landmarks, mp_pose.POSE_CONNECTIONS, ) # 顺便取一个膝盖角度打印出来观察 hip = results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_HIP.value] knee = results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_KNEE.value] ankle = results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_ANKLE.value] angle = calc_angle(hip, knee, ankle) cv2.putText(frame, f"Knee: {int(angle)}", (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.imshow("AI Fitness", frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()逻辑说明:cap.set是在打开摄像头后调整采集分辨率,640x480 是实测过实时性最稳的设置,继续升到 1280x720 会明显感到推理跟不上。pose.process接受 RGB 帧,所以要先做cvtColor转换。draw_landmarks会把骨架线和关键点画到原图上,POSE_CONNECTIONS是 MediaPipe 预定义的连线关系。打印膝盖角度的目的是让你提前确认坐标系方向:人站立时角度应在 160 度以上,下蹲时会降到 110 度以下。
3.3 动作阶段状态机:以深蹲为例的计数逻辑
有了角度,下一步是把连续的角度序列翻译成“站立”和“蹲下”两个离散状态。这里我用状态机来做,避免每一帧独立判断带来的临界抖动。深蹲计数不能只在某一帧角度小于阈值时直接加 1,因为一个完整动作包含下降和上升两个过程,必须等待状态完成一次“站立→蹲下→站立”的闭环。
# 阶段状态机参数 STAND_ANGLE_MIN = 160 # 膝角大于该值视为直立 SQUAT_ANGLE_MAX = 110 # 膝角小于该值视为蹲下 state = "stand" count = 0 smoothed_angle = 170.0 # 初始值,适配直立状态 while cap.isOpened(): ret, frame = cap.read() if not ret: break frame = cv2.flip(frame, 1) rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = pose.process(rgb_frame) if results.pose_landmarks: hip = results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_HIP.value] knee = results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_KNEE.value] ankle = results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_ANKLE.value] raw_angle = calc_angle(hip, knee, ankle) # 一阶低通滤波,抑制单帧抖动 alpha = 0.25 smoothed_angle = alpha * raw_angle + (1 - alpha) * smoothed_angle # 状态转换 if state == "stand" and smoothed_angle < SQUAT_ANGLE_MAX: state = "squat" elif state == "squat" and smoothed_angle > STAND_ANGLE_MIN: state = "stand" count += 1 print(f"count: {count}") cv2.destroyAllWindows()参数说明:SQUAT_ANGLE_MAX = 110而不是 90,STAND_ANGLE_MIN = 160而不是 170,是在给状态判断留滞回区间。如果直接用 90 和 170 作为切换点,膝盖角度在边界附近轻微抖动会导致一帧内反复切换状态,把一次动作算成两次,这是这类系统最常见的翻车原因。alpha = 0.25表示新帧权重较低,平滑效果明显;如果你发现动作太慢导致漏计数,可以把alpha提到 0.4,实时性更强,但抖动也会增加。
4. 健身评分规则的参数设计与代码实现:幅度、速度、稳定性怎么量化
4.1 动作幅度评分:角度阈值与连续偏差打分
计数解决的是“做了没有”,评分解决的是“做得怎么样”。动作幅度的定义是:在动作最低点,目标关节角度距离标准角度的偏差。以深蹲为例,标准是膝关节角度达到 90 度左右;蹲得越低,角度越小,幅度越好。这里我用分段线性函数来给幅度打分,满分 60:
def score_amplitude(min_angle, target_angle=90): """根据深蹲最低点膝角打分,目标角度默认 90 度。 min_angle 越小代表蹲得越深,幅度越好。 """ if min_angle <= target_angle: return 60 elif min_angle <= target_angle + 15: # 每超 1 度扣 1 分,线性递减 return 60 - (min_angle - target_angle) elif min_angle <= target_angle + 45: # 超过 15 度后进入惩罚更重的区间 return 45 - (min_angle - target_angle - 15) * 0.5 else: return 15参数说明:target_angle是动作标准,不同动作不一样。深蹲用 90,俯卧撑的肘关节标准角度是 90 到 100 度,开合跳的肩关节外展角度则要结合上肢和下肢四条链一起评。分段线性有两个目的:一是对轻微不达标保留梯度差异,避免所有人挤在 40 分里;二是对严重不达标直接压到低分,拉开差距。分数设计里没有出现负分和超过 60 分的情况,后续合成总分时也方便归一。
4.2 速度与稳定性评估:用角度标准差和时间窗口过滤噪声
幅度只反映最低点,但一个动作的质量还包含速度和稳定性。速度方面,深蹲的推荐节奏一般是每分钟 8 到 12 次,单次动作从下蹲到起身大约 4 到 7 秒。速度太快说明在借力弹跳,太慢则可能是控制力不足或负重过大。稳定性用动作过程中膝盖角度的标准差来衡量:角度波动越小,说明轨迹越平滑。
import numpy as np def score_stability(angle_history): """传入最近若干帧的膝角序列,返回 0~20 分""" if len(angle_history) < 5: return 10 std = np.std(angle_history) # 标准差越小,动作越稳定 if std < 3: return 20 elif std < 6: return 15 elif std < 10: return 10 else: return 5 def score_speed(squat_time): """squat_time: 从站到蹲再到站的总秒数""" if 4.0 <= squat_time <= 7.0: return 20 elif squat_time < 4.0: # 太快,按偏离程度扣分 return max(5, 20 - (4.0 - squat_time) * 5) else: # 太慢,按偏离程度扣分 return max(5, 20 - (squat_time - 7.0) * 3)逻辑说明:score_stability里angle_history是动作过程中记录的膝盖角度序列,我一般取每次动作从开始到结束的 30 帧左右。标准差小于 3 度说明轨迹相当稳定,10 度以上就需要提醒用户控制身体重心。score_speed用动作总时长做判断,时长必须和状态机配合记录,从状态变为 “squat” 开始计时,恢复到 “stand” 结束。
4.3 综合评分权重设计:把三项指标合成 100 分
把幅度、速度、稳定性三个维度合成 100 分,需要设计权重。我常用的默认权重是幅度占 60%,稳定性占 20%,速度占 20%。这个比例的核心逻辑是:健身动作首先看幅度,幅度不够,其他都免谈;稳定性和速度更多影响训练体验和受伤风险,权重相对低。
| 评分维度 | 满分 | 权重 | 说明 |
|---|---|---|---|
| 动作幅度 | 60 | 60% | 最低点关节角度与标准角度偏差 |
| 动作稳定性 | 20 | 20% | 动作过程中角度标准差 |
| 动作速度 | 20 | 20% | 单次动作总耗时是否符合推荐节奏 |
完整评分器如下:
class FitnessScorer: def __init__(self, target_angle=90): self.target_angle = target_angle self.amp_score = 0 self.stab_score = 0 self.speed_score = 0 self.total_score = 0 def evaluate(self, min_angle, duration_sec, angle_history): self.amp_score = score_amplitude(min_angle, self.target_angle) self.stab_score = score_stability(angle_history) self.speed_score = score_speed(duration_sec) # 按权重合成总分 self.total_score = round( 0.6 * self.amp_score + 0.2 * self.stab_score + 0.2 * self.speed_score, 1 ) return self.total_score这套评分器的输出是 0 到 100 的分数,60 以上算合格,75 以上算良好,90 以上算优秀。实际使用时,我通常还会把min_angle和angle_history记进日志,方便后来做回归测试时看出分数是怎么算出来的。评分这件事最怕黑匣子,用户看到分数时如果完全不知道扣在哪,就谈不上纠正动作。
5. 健身评分系统的避坑指南:抖动、遮挡、视角与延迟的 4 个实战问题
5.1 姿态抖动导致角度跳变和分数来回震荡
现象:用户保持直立时,实测膝盖角度在 150 到 170 度之间来回跳,有时一帧直接掉到 135 度,导致状态机在 “stand” 和 “squat” 之间反复切换,计数多算一次,稳定性分数也明显偏低。
原因:MediaPipe 的姿态估计是基于单帧图像的,相邻两帧的关键点坐标本身有随机噪声,幅度在小范围内波动属于正常。如果摄像头帧率低或者光线偏暗,波动会更明显。直接把原始角度喂给状态机,等于把噪声也喂了进去。
解决:在角度进入状态机之前做一阶低通滤波,用平滑后的角度做判断。核心代码就一行:
alpha = 0.3 smoothed_angle = alpha * raw_angle + (1 - alpha) * smoothed_anglealpha是平滑系数,值越大越跟踪原始信号,值越小越平滑。我一般取 0.2 到 0.35 之间。注意滤波会引入相位延迟,如果用户动作很慢,延迟几乎无感;如果做快速开合跳,alpha小于 0.2 会导致状态转换滞后,漏掉动作。
5.2 身体遮挡时评分反而虚高:置信度阈值必须加
现象:用户下蹲时手正好放在膝盖前方,或者身体半转朝向侧面,画面里的膝盖关键点被遮挡,评分器反而给出了接近满分的幅度分。实际动作是变形的,系统却识别成了标准深蹲。
原因:姿态估计模型在关键点被遮挡时会脑补位置,也就是根据先验姿势推断被遮挡的关节点坐标。推理出来的坐标置信度很低,但坐标数值仍然存在,评分器不检查置信度就直接算角度,等于把模型的臆测当成真实结果。
解决:评分前必须检查关键点的visibility字段,低于阈值时跳过评分或给出提示。阈值一般取 0.6,光线不足时可以放宽到 0.5,但不能更低。
MIN_VISIBILITY = 0.6 def check_visibility(landmark): """检查单个关键点是否可信""" return landmark.visibility > MIN_VISIBILITY在评分流程里,我的习惯是每次动作完成时先检查髋、膝、踝三个关键点的visibility,任何一个低于阈值就丢弃这次动作,不参与计数和评分。宁可少算一个,也不能把错误数据计入总分。
5.3 摄像头角度偏移让深蹲角度系统性偏差
现象:摄像头从侧面 45 度角拍摄,用户明明蹲到了标准深度,膝关节角度算出来却比实际大 10 到 15 度,分数被系统性压低。摄像头装在高处向下俯拍时,偏差方向相反,蹲得不够深也会被算成达标。
原因:关节角度计算基于二维投影,只有当摄像头光轴与矢状面接近垂直时,三维关节角度才能被准确投影到二维平面。摄像头斜了,投影就失真了,这不是模型精度的问题,而是成像几何的固有限制。
解决:在交互界面强制要求用户侧身对着摄像头,让身体的矢状面尽量与镜头平面平行。具体到实现,可以在画面上绘制一个半透明的姿势引导框,用户站进框里再开始评分。还要在评分日志里保存摄像头角度设置,避免不同位置的摄像头测试结果直接对比。这个误差无法在算法层面完全消除,只能从采集侧规范化。
5.4 实时评分卡顿:分辨率、模型复杂度与帧率取舍
现象:画面流畅,但分数和骨架线条有半秒以上的滞后,用户已经站起身了,界面上还显示着蹲下的角度。做开合跳时动作快,系统几乎无法实时追踪。
原因:默认摄像头分辨率是 1280x720 甚至更高,MediaPipe 在 CPU 上处理 720p 帧时,每帧推理时间会超过 80ms,加上画面采集和绘制,总延迟就上去了。另外model_complexity=2在某些机器上也会拖慢推理。
解决:优先降低采集分辨率到 640x480,然后按机器性能调整模型复杂度。这两步对实时体验的提升最明显。
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)如果还想再快,可以跳帧处理,每两帧跑一次姿态估计,中间一帧直接复用上一帧的关键点。注意跳帧会降低动作追踪的平滑度,对速度评分结果有影响,不建议在需要精确计时评分的场景里使用。
6. 进阶:让评分系统真正可投入——回归测试、分数校准与多动作扩展
6.1 用录制视频做回归测试:每次改参数不再怕翻车
评分系统的参数一旦改动,就存在影响其他动作的风险。我习惯准备三段录制好的视频,分别是标准深蹲、半程深蹲、快速借力深蹲,作为回归测试集。每次改完阈值或权重,用同一个脚本跑一遍视频,检查计数和分数是否符合预期。实现上只需把VideoCapture(0)换成VideoCapture("squat_standard.mp4"),跑完打印最终的计数和总分。有了这个测试集,调参就不是玄学,而是可回放、可对比的过程。
6.2 评分校准:把分数拉到合理分布而不只是盯阈值
刚跑通时,你会发现分数经常集中在 85 到 95 分,或者全部压在 40 分以下,区分度很差。原因是你定的阈值和真实用户的分布不匹配。校准方法很简单:收集 20 次真实动作的评分,记录最小值和最大值,再做线性映射到 60 到 95 区间:
calibrated = 60 + (raw_score - min_score) / (max_score - min_score) * 35这样能让普通用户看到 70 到 80 分有继续提升的动力,专业用户看到 90 分以上获得正反馈。校准后记得把min_score和max_score持久化保存,不要每次启动重新计算,否则不同时段的分数不可比。
6.3 从深蹲扩展到俯卧撑与开合跳:状态机重用的经验
深蹲的这套状态机思路可以直接迁移到俯卧撑和开合跳。俯卧撑主要看肘关节角度,状态切换阈值是“撑起时肘角大于 160 度、下落时小于 100 度”,评分维度换成躯干平直度。开合跳则要同时看肩关节外展角度和双脚间距,状态机逻辑要从单角度判断改成多关键点联合判断。我现在的做法是定义一套动作配置文件,每种动作包含关键点组合、标准角度、状态切换阈值和权重,评分器只读配置不写业务代码。这样加一个新动作只需要录一段标准视频,标注目标角度,基本不用改 Python 代码。
这套系统我前后迭代了三个版本,最大的教训是别急着加功能,先把角度计算和防抖做扎实。很多看起来花哨的问题,比如分数飘忽、状态闪烁,根源都在最基础的角度噪声没处理好。你如果照着文章把代码跑通,我建议先拿自己的深蹲动作测一测,看看站立时角度稳不稳、下蹲时能不能稳定触发状态切换。等这两个点都稳了,再去调评分权重和做校准,进度会快很多。希望帮到你。
本文还有配套的精品资源,点击获取