news 2026/10/22 9:31:03

基于YOLO姿态估计的健身动作智能计数系统实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于YOLO姿态估计的健身动作智能计数系统实战

从朋友让我帮他做一个“自动数俯卧撑”的小工具开始,这个项目前前后后折腾了将近一个月。一开始我也以为这不就是“看着视频里人的轮廓变高变低”嘛,但真正落到代码和实测之后才发现,把一个健身动作拆成“可以量化的数字”,里面全是细节。最后我选了 YOLO 系列姿态估计作为核心方案,基于它构建了一套健身动作智能计数系统,能同时覆盖俯卧撑和引体向上两种动作。这篇博文就把整个实战过程完整记录下来,包括方案选型、数据处理、模型训练、动作判定、实时部署和一堆踩过的坑,给同样想用视觉技术做运动计数、动作分析的朋友一个可以直接抄作业的参考。

1. 项目需求拆解:为什么偏要用姿态估计做计数

1.1 智能计数的核心逻辑

健身动作计数这件事,本质上是“把连续的视频帧转换成离散的次数”。人的动作再复杂,在物理上也可以抽象成一组关键点的空间位置随时间的变化。俯卧撑就是躯干下沉再撑起,引体向上就是身体被拉上去再放下来。只要我们能稳定追踪到肩膀、手肘、手腕、髋、膝等关键点的坐标,再通过角度、距离、相对高度这些几何量判断动作所处的阶段,计数就是水到渠成的事。

这里有两个关键点:第一,要知道人的身体部位在哪里,也就是目标检测;第二,要知道这些部位之间的连接关系,也就是关键点回归。YOLO系列姿态估计恰好把这两件事合并成一次推理,输出目标框外加每个关键点的坐标和置信度,比“先检测人再单独跑关键点模型”的两段式方案省掉一半时间,对实时计数来说特别友好。

1.2 技术选型对比:视觉方案与传统方案的差距

在做技术选型时,我认真比较过几种常见路线:

方案类型优点缺点适合场景
穿戴式传感器/加速度计精度高、不受遮挡影响需要额外硬件、用户嫌麻烦专业运动研究
普通目标检测+规则实现简单没有关节信息,动作阶段难以细分只数人头、检测动作开始
通用姿态估计框架关键点丰富、准确速度慢、部署成本高离线分析
YOLO系列姿态估计速度快、关键点与检测一体、工程生态成熟对遮挡和极端视角仍敏感实时动作计数

实测下来,YOLO系列姿态估计的推理速度比其他通用姿态估计框架快一倍以上,在普通独立显卡上即使不做过多的优化也能跑到几十毫秒一帧,完全满足摄像头实时计数需求。

2. 数据准备与关键点定义

2.1 俯卧撑和引体向上的关键点建模

训练一个能用的姿态估计模型,第一步是明确关键点语义。官方姿态估计模型通常输出人体的多个关键点,比如鼻子、眼睛、耳朵、双肩、双肘、双腕、双髋、双膝、双踝等。我们在计数任务中真正关心的子集是:

动作核心关键点作用
俯卧撑肩膀、手肘、手腕、髋部、膝盖、脚踝判断肘关节角度、躯干是否平直、身体离地高度
引体向上鼻子、肩膀、手肘、手腕、髋部、膝盖、脚踝判断下巴/鼻子是否超过横杠高度、手臂是否伸直

实际标注时,我采用了侧视角拍摄为主,因为侧面对算法最友好。俯卧撑在侧面能看到手肘明显弯曲角度,引体向上在侧面也能看到下巴过杠瞬间。我自己采集了一部分模拟数据,同时补充了公开数据集里包含运动场景的图像,最终整理出一份包含约两万张图像的数据集。标注工具用的是常见的开源标注工具,每个关键点记录归一化坐标和可见性标志,即:

关键点编号, x, y, 可见性

这里有一个很重要的经验:可见性标志不要随便填。关键点被身体遮挡时,标注为不可见,而不是强行标一个猜测坐标。模型训练初期如果可见性标注混乱,推理阶段输出的关键点置信度会非常不稳定,计数也就无从谈起。

2.2 标注规范与数据增强

标注规范直接影响模型效果。我的标注规范是四句话:

  • 侧视角为主,正面和斜侧面作为补充。
  • 所有关键点严格对应解剖学位置,手肘不标在手臂外侧,膝盖不标在裤腿褶皱处。
  • 遮挡时标为不可见,宁可缺失不预测。
  • 每张图像至少保证双肩、双肘、双腕、双髋可见,否则不进入训练集。

数据增强方面,我用了旋转、镜像翻转、随机缩放、亮度扰动和随机遮挡。这里特别注意:俯卧撑和引体向上本身有上下方向语义,不能做垂直翻转,否则模型会混淆支撑和悬挂动作。随机遮挡用来模拟身体部分被器械或镜头边缘挡住的情况,能明显提升模型在真实场景下的鲁棒性。

我实际使用中发现,增强比例控制在适中范围比较稳,旋转角度在正负15度以内,缩放比例在正负20%以内,亮度扰动按原图灰度的10%左右。增强过猛会造成模型在合成分布上训练,真实场景反而掉点。

3. 模型训练与调优

3.1 模型选择与输入设置

YOLO系列姿态估计的模型体量有很多档,从轻量级到高精度级都有。我的选型原则是:实时计数对延迟敏感,优先选轻量级和次轻量级。轻量级模型在普通独立显卡上速度很快,我的笔记本无外部供电环境都能跑实时;次轻量级精度更高,适合对计数准确率更挑剔的场景。

输入尺寸我选的 640 乘 640。这个尺寸是速度与精度的较好平衡点。如果你希望细节更好,用 768 或 1024,但推理时间会明显上涨。摄像头画面本身是横屏,直接缩放会变形吗?不会,YOLO系列姿态估计会把图像保持比例后填充到目标尺寸,推理后再把坐标映射回原图。所以代码里不要忘记做坐标的缩放回去处理,否则画骨架时会错位。

3.2 训练参数和优化技巧

训练时我先把预训练权重加载进来,让它基于通用姿态估计能力做微调。直接用随机权重从头训练,两万张图像是不够的,效果会差很多。下面是几个关键参数:

参数设定值说明
输入尺寸640x640平衡速度与精度
批次大小16根据显存调整,越小越容易收敛但不稳
训练轮数150用早停机制监控验证集损失
初始学习率0.001配合余弦退火降学习率
优化器AdamW比SGD收敛更快,关键点回归更稳
关键点损失权重适当加大关键点回归是本任务核心

训练结果不能只看关键点精度,我额外写了一个评估脚本,直接统计模型输出在真实视频上触发计数动作的成功率。这个比单个关键点的像素误差更有实际意义。

实测下来,轻量级模型训练到后期验证集损失不再下降时,单帧推理时间在显卡上约二三十毫秒,精确率已经足够完成俯卧撑和引体向上的计数。

4. 计数算法:从关键点到动作判定

4.1 俯卧撑计数逻辑

有了姿势关键点之后,最核心的问题就是“怎么算一次”。俯卧撑的标准动作可以定义为:起始时手臂伸直,身体呈平板状;下降时肘关节弯曲到接近90度,胸部靠近地面;撑起时手臂重新伸直。从视觉上看,最稳定的判断信号是肘关节角度。

我定义了一个骨架角度计算函数,输入三个关键点坐标,输出它们形成的角度:

import math def compute_angle(point_a, point_b, point_c): # point_b 是角点,例如手肘 ab_x = point_a[0] - point_b[0] ab_y = point_a[1] - point_b[1] cb_x = point_c[0] - point_b[0] cb_y = point_c[1] - point_b[1] dot = ab_x * cb_x + ab_y * cb_y norm_ab = math.hypot(ab_x, ab_y) norm_cb = math.hypot(cb_x, cb_y) if norm_ab == 0 or norm_cb == 0: return 0.0 cos_angle = max(-1.0, min(1.0, dot / (norm_ab * norm_cb))) angle = math.degrees(math.acos(cos_angle)) return angle

俯卧撑时,以肩膀为原点,手腕、手肘一起算肘关节角度:手肘-肩膀-手腕还是手腕-手肘-肩膀?这里取决于实际输出。我用的核心角度是肩-肘-腕三点构成的肘角。支撑状态下肘角接近180度,撑起时也接近180度,下降到最低点时通常小于90度。

但光靠一个角度还不够,因为人可能半程发力,肘角只到120度就撑起。为了增强鲁棒性,我又加入了两个辅助条件:

  • 肩部与髋部的连线是否保持平直,防止塌腰或撅屁股。
  • 手肘位置相对于肩膀的垂直距离变化。

当肘角小于90度且躯干倾斜角度小于设定容差时,判定为下降到位;当肘角大于150度时,判定为上升到位。

4.2 引体向上计数逻辑

引体向上的判定和俯卧撑不同。引体向上的核心是“下巴过杠”。但直接用下巴检测不稳定,因为下巴在侧视角经常被遮挡或显示不清晰。我在实际项目中改用两个替代信号的组合:

  • 鼻子或头部关键点的纵坐标,代表头部的最高位置。
  • 手腕关键点的纵坐标,代表手握杠的位置。

理论上引体向上拉起来时,头部会明显高于手腕。因此我定义“下巴过杠”的判定为:鼻子关键点纵坐标小于手腕关键点纵坐标,且竖直差距超过某个像素阈值。又因为不同人的身高和手臂长度不同,这个阈值不能是固定的像素值,我会用肩宽或躯干长度做归一化。

引体向上的完整状态机是:初始 hanging,手臂伸直;当头部拉起超过手腕且肘角小于某个角度,进入 up 状态;随后头部下降到重新低于手腕,回到 hanging 状态,计数器加一。

4.3 阈值判定与防抖动

阈值的选取是整个系统最容易出问题的地方。阈值太小会把半程动作误判为完整动作,阈值太大又会漏计。我采用的方案是滞回比较,而不是单阈值。

例如俯卧撑:定义 down 触发阈值是90度,up 触发阈值是150度。动作进入 down 状态后,必须肘角小于90度才算有效;动作回到 up 状态前,肘角必须大于150度才算伸直。中间90度到150度这段是滞回区,不产生状态切换。这样即使用户在最低点有些微抖动,也不会来回计数。

我还对角度做了平滑处理,避免单帧关键点跳变引起误判:

smooth_angle = alpha * raw_angle + (1 - alpha) * smooth_angle

alpha 取0.3到0.4之间。平滑之后,动作状态切换的曲线很干净,实测基本不会出现连续帧抖动导致的重复计数。

5. 实时推理与系统集成

5.1 摄像头帧处理流程

整个实时系统的流程是这样的:

  1. 从摄像头读取一帧画面。
  2. 将画面缩放到模型输入尺寸。
  3. 执行 YOLO 系列姿态估计推理,获得人体框和关键点。
  4. 将关键点坐标映射回原图尺寸。
  5. 根据模式(俯卧撑/引体向上)计算角度和位置关系。
  6. 更新动作状态机,必要时计数器加一。
  7. 在原图上绘制骨架、关键点和计数值,显示出来。

摄像头采集和模型推理要避免放在同一个循环里同步等待。我用了双线程加队列的方案:一个线程持续采集,一个线程做推理。这样就算某帧推理偶尔超时,也不会导致画面卡死或延迟越积越大。

5.2 计数的流式状态机

计数逻辑我单独封装成一个计数器类,里面维护动作状态。这里贴一段伪代码,完整表达了状态机的思路:

class ActionCounter: def __init__(self, mode): self.mode = mode self.state = "up" if mode == "pushup" else "hanging" self.count = 0 self.smooth_angle = 180.0 if mode == "pushup" else 0.0 def update(self, keypoints, frame_height): # 计算角度和位置特征 angle = compute_elbow_angle(keypoints) self.smooth_angle = 0.35 * angle + 0.65 * self.smooth_angle if self.mode == "pushup": return self._update_pushup() else: return self._update_pullup(frame_height) def _update_pushup(self): if self.state == "up" and self.smooth_angle < 90: self.state = "down" elif self.state == "down" and self.smooth_angle > 150: self.state = "up" self.count += 1 return self.count def _update_pullup(self): # 判定头部是否高于手腕 head_above = nose_y < wrist_y if self.state == "hanging" and head_above: self.state = "up" elif self.state == "up" and not head_above: self.state = "hanging" self.count += 1 return self.count

这个状态机的关键点是:一次完整计数必须经历两个边缘,而且每个状态都有明确的进入条件。只满足“下去”但没有“上来”,计数器不会增加;只满足“上来”但没有先进入“下去”,也不会增加。这就杜绝了摆动身体造成的假计数。

5.3 部署细节与性能优化

实时推理的性能优化,我按照优先级做了三件事:

  • 推理尺寸保持640,不盲目调大。
  • 推理时跳过置信度低于阈值的关键点,不参与角度计算。
  • 如果模型较大或帧率仍然不够,可以把相邻几帧的检测结果做时间维度的融合,用上一帧的关键点位置平滑当前帧。

实际测试中,我在一款普通独立显卡的笔记本上做俯卧撑计数,稳定跑满摄像头帧率,CPU占用也不高。换到纯CPU环境后,轻量级模型仍然能跑到接近实时,只是帧率会降到接近15到20帧左右,使用体验在可接受范围。

6. 常见问题与排查实录

6.1 典型问题速查表

这个项目里我踩过的坑不少,整理成一张速查表,方便直接对照:

问题可能原因解决方法
关键点频繁跳动光照变化、身体与背景颜色相近增加数据增强、切换更亮或对比度更高的环境
俯卧撑计数偏多状态机没有锁定半程动作加入滞回区间,提高 up 触发阈值
俯卧撑计数偏少肘角阈值设置过大下调 down 触发角度,例如从90度改到80度
引体向上不计数鼻子/头部和手腕位置关系判断错误改用头部关键点加手腕位置组合判定,加入置信度过滤
推理速度太慢输入尺寸过大、模型体量过大切换到轻量级模型,或降至 512 输入尺寸
人离摄像头太远关键点尺度过小调整摄像头视角,让人体高度占画面的40%以上
多人同框互相干扰只取目标框最大的人根据人体框中心距离摄像头最近的原则选择一个目标
背景复杂误检人形类间干扰加入跟踪机制,只跟踪同一目标

6.2 独家避坑心得

第一,摄像头摆放角度非常重要。俯卧撑尽量从侧面拍,镜头高度和地面上的身体中线大致齐平;引体向上从斜前方拍,不要正对着人脸,否则头部被杠铃遮挡后关键点容易丢失。我实测过正面视角,同一个动作的计数成功率明显低于侧视角,这在投入大量数据采集前就应该想明白。

第二,每个人做动作的幅度不一样。有人俯卧撑只做半程,有人引体向上习惯轻微甩腰。我最后的方案是把角度阈值做成可配置参数,在程序启动时允许用户根据自己的动作习惯微调。不要试图用一套固定阈值适配所有人,这不是模型能力问题,是人体多样性决定的。

第三,关键点置信度要充分利用。很多关键点在快速动作过程中会出现一两帧的错误预测。我的做法是:任何参与角度计算的关键点,置信度低于0.5就直接跳过该帧,不更新状态机。宁可等待一两帧,也不要因为一个错误点造成重复计数。

7. 扩展方向与个人体会

这个系统做完之后,我最大的感受是:模型推理只是整个系统的一小块,真正的难点在于把“人体姿态”翻译成“符合实际语义的动作阶段”。角度计算、状态机设计、阈值滞回这些看似简单的代码,才是决定计数准不准的核心。

如果后续想扩展,我建议可以先加动作质量评估。既然已经有了连续的关键点轨迹,完全可以算出动作速度、幅度、关节角度变化曲线,再结合健身标准给出“偏快”“幅度不足”“躯干不稳”这类提示。再往后,可以做一个人运动档案,长期跟踪每周动作次数和完成的规范性,这就超出计数本身,进入数字健康管理的范畴了。

这个项目让我印象最深的反而是状态机设计。刚开始我也试图用纯粹的连续角度数值来计数,结果半程动作、停顿、抖动全部变成误计。后来老老实实把动作拆成离散状态,每一步都问“现在处于哪个阶段”,整个系统忽然变得异常稳定。如果你也在做类似的计数系统,建议先画出动作状态图,再写代码。这个思路比换更厉害的模型更能解决问题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 9:30:54

基于YOLOv10的铁轨缺陷实时检测系统开发实战

这个项目我从立项到能跑通&#xff0c;前前后后折腾了一个多月。中间踩了不少坑&#xff0c;包括数据集标注不规范导致训练出来的模型把钢轨的阴影当成裂纹&#xff0c;还有UI界面在实时检测时简直卡成PPT&#xff0c;后来才发现是推理线程和界面线程没分离的问题。现在系统已经…

作者头像 李华
网站建设 2026/10/22 9:30:45

基于YOLOv10的课堂行为检测系统设计与实战解析

1. 项目整体拆解&#xff1a;课堂行为检测到底在解决什么问题 第一次看到"基于深度学习的学生课堂行为检测"这类项目标题时&#xff0c;很多人的第一反应是"不就是把摄像头画面丢给YOLO跑一下嘛"。实际上&#xff0c;这类系统的核心难点根本不在目标检测本…

作者头像 李华
网站建设 2026/10/22 9:30:17

Python零基础网络编程入门:动手实现TCP客户端与服务端通信

网络编程这四个字&#xff0c;我第一次看到的时候觉得特别高大上&#xff0c;脑子里浮现的是黑客大战、服务器机房、满屏跳动的日志——直到自己真正写了第一行代码&#xff0c;才恍然大悟&#xff1a;原来网络编程做的就是一件事——让两个程序隔着千山万水也能“说话”。这篇…

作者头像 李华
网站建设 2026/10/22 9:30:12

从Socket到套接字:一个被误读三十年的网络编程术语

先说结论&#xff1a;我从一个骂过"套接字"的人&#xff0c;变成了替它说话的人。第一次在《计算机网络》教材里看到"套接字"这三个字时&#xff0c;我和大多数人一样愣住了。一个好好的 Socket&#xff0c;翻成"插座"不好吗&#xff1f;哪怕叫&…

作者头像 李华
网站建设 2026/10/22 9:30:02

纯Java企业级Agent执行沙箱:BizBuddy架构与工程实践

1. 为什么“纯 Java”做 Agent Harness 是个反直觉但值得深挖的决定“用纯 Java 做企业级 Agent Harness 平台”——这个标题刚看到时&#xff0c;我下意识皱了下眉。身边太多团队在聊 LLM 应用落地&#xff0c;一开口就是 Python LangChain FastAPI&#xff0c;再配上点 Doc…

作者头像 李华
网站建设 2026/10/22 9:29:18

YOLO26+ByteTrack实战:从检测到跟踪的智慧园区人流统计系统搭建指南

1. 先想清楚&#xff1a;多目标跟踪到底解决什么问题 我最早做园区人流统计的时候&#xff0c;以为这活儿很简单&#xff1a;放一个摄像头在门口&#xff0c;检测到人&#xff0c;人数加一&#xff0c;完事。真到现场跑起来才发现&#xff0c;事情完全不是这样。 单帧检测只能…

作者头像 李华