尧图精选

基于YOLOv8的健身动作指导系统:从姿态估计到评分部署全流程

🕒 发布时间:2026/10/2 3:30:58 📁 来源:尧图网络
简介这份资源是面向计算机、人工智能、自动化等专业学生与教师的YOLOv8健身动作指导系统完整项目包可作为毕业设计、课程设计或大作业的参考方案解决从模型训练到可视化演示的全流程落地问题。压缩包共97个文件约24.21MB以70个Python源码文件为核心辅以4个pt权重文件、5个xml配置、12个pyc缓存及mp4演示视频、ico图标等覆盖模型训练、检测推理、服务封装与界面资源。项目已包含完整数据集、可视化页面与部署说明可生成核心指标曲线、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果及标签分布图并附有README说明与训练脚本便于快速复现与二次修改。目前已有88人学习下载适合希望以较低部署成本完成目标检测类毕设或课设的读者参考使用。1. 从一份能跑起来的 YOLOv8 健身动作指导系统说起健身房里最常见的场景是新手对着镜子做深蹲膝盖内扣、腰背弓起自己却完全看不出来。教练不可能一直盯着手机 App 又大多只做计数不做姿态纠错。这正是「基于 YOLOv8 的健身动作指导系统」要解决的问题用摄像头实时识别骨骼关键点判断动作是否标准给出语音或画面提示。它属于典型的「检测 姿态估计 规则判定」三段式方案核心不是训练一个多复杂的模型而是把 YOLOv8 的推理结果和动作评分逻辑串成一条能落地的链路。适合做毕设或课程设计的同学也适合想快速验证姿态类产品的开发者。整套东西包含源码、数据集、可视化界面和部署教程简单部署即可运行下面我按自己实际搭过一遍的顺序把选型、数据、训练、界面和踩坑讲清楚。2. 为什么选 YOLOv8 做姿态而不是直接上分类网络2.1 健身动作指导到底需要模型输出什么很多人第一反应是「动作分类」把深蹲、卧推、硬拉分成几类。但真正做指导系统时分类远远不够。你需要知道手肘有没有过伸、膝盖有没有超过脚尖、躯干前倾多少度这些是连续量不是离散标签。所以系统真正需要的是人体关键点坐标再基于坐标算角度。YOLOv8 本身是目标检测框架但它的 pose 分支直接输出 17 个 COCO 关键点一次前向就能拿到人和骨骼。相比「先检测人再跑单独的姿态网络」的两阶段方案YOLOv8-pose 端到端更快在普通笔记本 CPU 上也能跑到接近实时。这就是选它的核心理由一个模型同时解决「人在哪」和「骨架长什么样」。常见做法是直接用yolov8n-pose.pt做推理n 版本参数量小、速度快精度对健身场景够用。如果你要做多人同时指导或者动作幅度特别大导致关键点遮挡严重可以换yolov8s-pose或yolov8m-pose代价是帧率下降。2.2 环境配置Ubuntu 20.04 上跑通 CPU 版本标题里提到「简单部署即可运行」但环境这一步最容易翻车。我一般先在 Ubuntu 20.04 上把 CPU 版本跑通确认逻辑没问题再考虑 GPU。CPU 版本的好处是不用折腾驱动坏处是帧率低适合调试。# 创建独立环境避免和系统 Python 冲突 conda create -n fitness python3.10 -y conda activate fitness # 安装 PyTorch CPU 版本注意版本要和 ultralytics 兼容 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 安装 ultralytics它会自带 YOLOv8 的推理和训练接口 pip install ultralytics opencv-python numpy # 验证安装能打印版本号就说明基础环境通了 python -c import ultralytics; print(ultralytics.__version__)这段命令的逻辑是先隔离环境再装 CPU 版 PyTorch最后装 ultralytics。参数上要注意python3.103.11 以上某些依赖轮子还不全容易卡在编译。--index-url指向 CPU 专用源不加的话 pip 可能拉 GPU 版体积大还跑不起来。装完后跑一句推理验证from ultralytics import YOLO # 加载 pose 模型第一次运行会自动下载权重 model YOLO(yolov8n-pose.pt) # 对一张测试图做推理saveTrue 会把结果存到 runs 目录 results model(test.jpg, saveTrue) # 打印关键点数量正常应该是 17 个 for r in results: if r.keypoints is not None: print(关键点形状:, r.keypoints.data.shape)逻辑说明YOLO(yolov8n-pose.pt)加载的是姿态模型而不是检测模型这点很关键加载错了就没有 keypoints 输出。r.keypoints.data的形状是[人数, 17, 3]最后一维是 x、y、置信度。如果打印出来是空的说明图里没检测到人换张有人物的图再试。提示CPU 版本单帧推理大概 100 到 300 毫秒做实时指导会卡。调试阶段可以接受正式演示建议至少用一张入门级 GPU比如 GTX 1660 Ti 就能跑得比较顺。3. 数据集怎么处理才能喂给 YOLOv8 训练3.1 健身动作数据集的标注格式与目录结构标题里说包含完整数据集但你要清楚它大概率是「图片 关键点标注」的形式。YOLOv8-pose 训练需要的标注是每个关键点的 x、y、可见性归一化到 0 到 1 之间。常见来源有两种一种是 COCO 格式的关键点标注一种是 Labelme 标完再转换。我一般会把数据集整理成这样的目录fitness_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml是训练的入口配置内容大致如下# 数据集根路径建议写绝对路径避免找不到 path: /home/user/fitness_dataset train: images/train val: images/val # 关键点配置健身场景一般沿用 COCO 的 17 点 kpt_shape: [17, 3] flip_idx: [0, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 16, 15] # 类别只有一个人 names: 0: person参数说明kpt_shape: [17, 3]表示 17 个关键点每个点 3 个值x、y、可见性。flip_idx是左右翻转时的关键点映射做数据增强时用得到写错了会导致翻转后左右手互换模型学歪。names里只有 person 一类因为姿态任务不区分动作类别动作判定交给后面的规则逻辑。3.2 用 Labelme 标注转 YOLOv8 关键点格式如果你自己补数据Labelme 标出来的是 JSON需要转成 YOLOv8 的 txt。转换脚本核心逻辑如下import json import os import numpy as np # Labelme 的关键点名称顺序必须和 COCO 17 点对齐 KEYPOINT_NAMES [ nose, left_eye, right_eye, left_ear, right_ear, left_shoulder, right_shoulder, left_elbow, right_elbow, left_wrist, right_wrist, left_hip, right_hip, left_knee, right_knee, left_ankle, right_ankle ] def convert(json_path, img_w, img_h, out_path): with open(json_path, r, encodingutf-8) as f: data json.load(f) lines [] for shape in data[shapes]: if shape[label] ! person: continue # 取外接框并归一化 points np.array(shape[points]) x_min, y_min points.min(axis0) x_max, y_max points.max(axis0) cx (x_min x_max) / 2 / img_w cy (y_min y_max) / 2 / img_h w (x_max - x_min) / img_w h (y_max - y_min) / img_h # 按顺序填 17 个关键点缺失的填 0 kpts [] for name in KEYPOINT_NAMES: found False for p in shape.get(keypoints, []): if p[2] name: kpts.extend([p[0] / img_w, p[1] / img_h, 2]) found True break if not found: kpts.extend([0, 0, 0]) line f0 {cx:.6f} {cy:.6f} {w:.6f} {h:.6f} .join(f{v:.6f} for v in kpts) lines.append(line) with open(out_path, w, encodingutf-8) as f: f.write(\n.join(lines))逻辑说明先读 JSON遍历每个 person 标注算外接框并归一化。关键点按KEYPOINT_NAMES顺序填找不到的点填0 0 0表示不可见。可见性用 2 表示可见0 表示缺失这是 YOLOv8 的约定。参数上img_w、img_h必须和原图一致否则归一化坐标全错。注意转换完一定要抽查几张用可视化脚本把关键点画回图上确认鼻子在鼻子上、膝盖在膝盖上。我见过因为关键点顺序对不上训练出来的模型把脚踝当手腕血泪经验。3.3 启动训练与关键参数怎么设数据准备好后训练命令很直接yolo pose train \ datafitness_dataset/data.yaml \ modelyolov8n-pose.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.001 \ patience20 \ projectruns/pose \ namefitness_v1参数说明epochs100是上限配合patience20表示 20 轮没提升就早停避免过拟合。imgsz640是输入尺寸健身动作里关节细节重要不建议降到 320。batch16看显存CPU 训练基本跑不动建议 GPU。lr00.001是初始学习率YOLOv8 默认值就够用数据量小可以降到 0.0005。训练过程中重点看box_loss和pose_loss两条曲线。pose_loss降不下去多半是关键点标注有问题box_loss正常但pose_loss高说明框对了但点错了。训练完在runs/pose/fitness_v1/weights/best.pt拿到权重。4. 动作判定逻辑与可视化界面怎么串起来4.1 用关键点角度做动作标准度评分模型只给坐标判断动作标不标准要靠几何。以深蹲为例核心是膝盖角度和躯干倾角import numpy as np def angle(a, b, c): # 计算 b 点处的夹角a、b、c 是 (x, y) 坐标 a, b, c np.array(a), np.array(b), np.array(c) ba a - b bc c - b cos np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc) 1e-6) return np.degrees(np.arccos(np.clip(cos, -1.0, 1.0))) def squat_score(kpts): # kpts 是 17x3 数组索引对应 COCO 顺序 left_hip, left_knee, left_ankle kpts[11][:2], kpts[13][:2], kpts[15][:2] left_shoulder kpts[5][:2] knee_angle angle(left_hip, left_knee, left_ankle) # 躯干倾角肩到髋的连线与竖直方向夹角 torso left_shoulder - left_hip torso_angle np.degrees(np.arctan2(abs(torso[0]), abs(torso[1]) 1e-6)) score 100 if knee_angle 70: score - 30 # 蹲太深 if knee_angle 120: score - 20 # 没蹲下去 if torso_angle 45: score - 30 # 躯干前倾过多 return max(score, 0), knee_angle, torso_angle逻辑说明angle用向量点积算夹角加了1e-6防止除零。深蹲评分里膝盖角度小于 70 度算蹲太深大于 120 度算没蹲到位躯干前倾超过 45 度扣分。这些阈值不是死的不同身高比例的人标准不一样实际用的时候最好按用户身高做一次标定。参数上kpts的索引必须和 COCO 顺序一致写错一个索引整个判定就废了。置信度低于 0.5 的关键点建议直接跳过该帧不然噪声会让评分乱跳。4.2 可视化界面用 Gradio 快速搭一个能演示的交互页毕设演示最怕界面丑或者跑不起来。我一般用 Gradio几十行就能出一个带摄像头输入的页面import gradio as gr import cv2 from ultralytics import YOLO model YOLO(runs/pose/fitness_v1/weights/best.pt) def process(frame): results model(frame, verboseFalse) annotated results[0].plot() # 自带骨架绘制 for r in results: if r.keypoints is not None and len(r.keypoints.data) 0: kpts r.keypoints.data[0].cpu().numpy() score, ka, ta squat_score(kpts) cv2.putText(annotated, fScore: {score}, (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) return annotated demo gr.Interface( fnprocess, inputsgr.Image(sources[webcam], typenumpy), outputsgr.Image(typenumpy), liveTrue ) demo.launch(server_name0.0.0.0, server_port7860)逻辑说明results[0].plot()直接把关键点和框画好省得自己写绘制。liveTrue让页面实时刷新适合摄像头场景。server_name0.0.0.0是为了局域网内其他设备也能访问演示时用手机看也方便。参数上verboseFalse关掉推理日志不然控制台刷屏。squat_score只取了第一个人多人场景要遍历r.keypoints.data。如果页面卡把输入分辨率降下来或者把live改成手动触发。提示Gradio 默认端口 7860被占用就换一个。部署到服务器时记得开防火墙对应端口否则本地能跑、远程打不开这种问题排查起来最费时间。5. 部署上线时最容易翻车的几个点5.1 模型加载报错与权重路径问题现象运行时报FileNotFoundError或加载后 keypoints 为空。原因通常是权重路径写错或者加载了检测模型而不是 pose 模型。解决确认文件名带-pose用绝对路径加载先单独跑一次推理验证权重可用。5.2 摄像头读不到帧或帧率极低现象界面一直黑屏或者延迟好几秒。原因一是 OpenCV 的VideoCapture索引不对二是 CPU 推理太慢。解决把索引从 0 试到 3找到正确摄像头推理慢就降imgsz到 480或者跳帧处理每两帧推理一次。5.3 关键点抖动导致评分乱跳现象人站着不动分数却在 60 到 90 之间跳。原因是单帧关键点有噪声。解决对关键点做滑动平均取最近 5 帧的均值再算角度或者对分数做低通滤波避免显示值剧烈波动。5.4 多人场景只识别一个人现象画面里两个人只画出一个骨架。原因是代码里只取了r.keypoints.data[0]。解决遍历所有检测结果给每个人单独算分并标注界面上可以只显示分数最高的那个。5.5 部署到服务器后无法访问界面现象本地localhost:7860能开远程 IP 打不开。原因是launch()默认只绑定本地。解决加server_name0.0.0.0并确认服务器安全组放行了对应端口。6. 把评分阈值做成可配置才算真正能交付前面那套阈值是写死的换个动作、换个人就不准。真正要交付的系统应该把判定规则抽成配置文件让用户能调。我的习惯是建一个rules.yamlsquat: knee_angle_min: 70 knee_angle_max: 120 torso_angle_max: 45 weight: 1.0 pushup: elbow_angle_min: 80 elbow_angle_max: 160 body_angle_min: 160 weight: 1.0然后在代码里读进来按动作类型取对应规则。这样加新动作不用改代码只加一段配置就行。验证方法也简单找几个人分别做标准和不标准动作看分数能不能拉开差距。如果标准动作 85 分、不标准 80 分说明阈值太松要收紧如果标准动作都不到 60 分说明阈值太严要放宽。我踩过最大的坑是一开始把所有逻辑写在一个main.py里改一个阈值要翻两百行代码。后来拆成detector.py、scorer.py、rules.yaml三块调试效率完全不一样。做毕设的同学如果时间紧至少把评分规则单独放一个文件答辩时老师问「阈值怎么定的」你能直接打开配置文件讲比翻代码体面得多。还有一个实用技巧把每次推理的关键点和分数写进 CSV事后用 pandas 画曲线能直观看到哪个动作、哪个阶段扣分最多。这个日志在调试期比任何可视化都管用。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →