1. 产品 Demo 效果展示

打开手机或电脑摄像头,站在镜头前做深蹲或俯卧撑,界面会实时显示骨骼关键点连线,并自动统计动作次数。动作不标准时,计数器不会增加——比如深蹲没蹲到90度,俯卧撑肘关节没弯曲到位。

这是我用 MediaPipe Pose + React 20 分钟搭出来的原型:

AI健身Demo

左侧是实时视频流叠加骨骼图,右侧显示动作名称和计数。核心代码不到 60 行,其中计数逻辑只有 15 行。

2. 技术选型

组件 选择 理由
姿态估计 MediaPipe Pose 浏览器端运行,纯JS,轻量,支持68个关键点,精度够用
前端框架 React + Vite 快速开发,HMR热更新,适合原型
摄像头 navigator.mediaDevices 无需后端,纯前端处理
部署 Vercel 免费,支持SPA,一键部署

为什么不选更重的模型(OpenPose、HRNet)?因为我们的目标是 能跑、够用。MediaPipe 在普通笔记本上稳定 30fps,移动端也能 15fps 以上,而 OpenPose 需要 GPU,部署成本太高。对 Demo 来说,用户要的是“卡”还是“流畅”?你猜答案。

3. 核心代码实现

3.1 初始化姿态检测

javascript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
import { Pose } from '@mediapipe/pose';
import { Camera } from '@mediapipe/camera_utils';

const pose = new Pose({
  locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/pose/${file}`
});

pose.setOptions({
  modelComplexity: 1,  // 0轻量 1标准 2高精度
  smoothLandmarks: true,
  minDetectionConfidence: 0.5,
  minTrackingConfidence: 0.5
});

pose.onResults(onPoseResults);

const camera = new Camera(
  document.getElementById('video'),
  { onFrame: async () => { await pose.send({ image: video }); } }
);
camera.start();

这段代码是通用的“摄像头→MediaPipe→回调”管道。注意 modelComplexity: 1 是性能和精度的平衡点。如果设备老旧,改为 0 能提帧率但会损失关节稳定性。

3.2 动作计数逻辑(核心 15 行)

以深蹲为例,关键角度是髋关节-膝关节-踝关节的夹角。当角度小于 110° 且当前未计数时,算一次深蹲;然后标记“已蹲”,等角度恢复 >160° 时重置标记,避免重复计数。

javascript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
let squatCount = 0;
let isSquatting = false;

function countSquat(landmarks) {
  // 取左髋、左膝、左踝关键点 (MediaPipe Pose索引: 23,25,27)
  const hip = landmarks[23];
  const knee = landmarks[25];
  const ankle = landmarks[27];

  // 计算膝关节角度(向量hip→knee 与 ankle→knee 的夹角)
  const v1 = { x: hip.x - knee.x, y: hip.y - knee.y };
  const v2 = { x: ankle.x - knee.x, y: ankle.y - knee.y };
  const dot = v1.x * v2.x + v1.y * v2.y;
  const mag = Math.hypot(v1.x, v1.y) * Math.hypot(v2.x, v2.y);
  const angle = Math.acos(dot / mag) * (180 / Math.PI);

  if (angle < 110 && !isSquatting) {
    squatCount++;
    isSquatting = true;
    console.log(`深蹲次数: ${squatCount}`);
  }
  if (angle > 160) {
    isSquatting = false;
  }
}

这段代码的核心是状态机isSquatting 防止在蹲下过程中反复触发。阈值 110° 和 160° 是我自己测试的标准深蹲阈值——如果你的用户是 56 岁的 Colman Domingo,可能需要放宽到 120° 和 150°,自己调。

3.3 完整 onPoseResults 回调

javascript
1 2 3 4 5 6 7 8 9 10
function onPoseResults(results) {
  if (!results.poseLandmarks) return;

  // 在canvas上绘制骨骼
  drawCanvas(results);

  // 执行动作计数
  countSquat(results.poseLandmarks);
  // pushUpCounting 同理,用肘关节角度
}

绘制骨骼的 drawCanvas 函数直接用 MediaPipe 提供的 drawConnectorsdrawLandmarks,不用自己写,省力。

4. 项目结构和配置

文件结构简单到没朋友:

text
1 2 3 4 5 6 7 8 9
pose-fitness/
├── index.html
├── package.json
├── src/
│   ├── main.jsx
│   ├── App.jsx
│   └── poseModule.js
├── vercel.json
└── vite.config.js

package.json 关键依赖:

json
1 2 3 4 5 6 7 8 9 10 11 12
{
  "dependencies": {
    "react": "^18.0",
    "react-dom": "^18.0",
    "@mediapipe/pose": "^0.5.0",
    "@mediapipe/camera_utils": "^0.3.0",
    "@mediapipe/drawing_utils": "^0.3.0"
  },
  "devDependencies": {
    "vite": "^5.0"
  }
}

vercel.json 只需 { "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] },因为 Vite 构建是 SPA。

5. 上线要注意的坑

5.1 HTTPS 必需

浏览器摄像头 API 要求安全上下文(HTTPS 或 localhost)。部署到 Vercel 默认 HTTPS,但本地开发用 localhost。如果用 127.0.0.1 可能被某些浏览器阻止,统一用 localhost

5.2 移动端兼容性

MediaPipe Pose 在 iOS Safari 上会遇到 WebGL 上下文丢失的问题。解决方案:在 onResults 回调中加 if (glContextLost) return;,但更稳定的做法是用 @mediapipe/tasks-vision(新一代 API),体积更小。

5.3 动作阈值要可配置

不同用户(年轻人 vs 56岁健身者)的动作幅度差异很大。Colman Domingo 的训练计划强调“长寿优先”(原文:training for longevity),关节活动度可能不如年轻人。因此我建议将阈值做成滑块 UI,让用户自己调。不过 Demo 阶段可以先写死,上线前加上配置。

5.4 隐私提示

用户对摄像头有天然的警惕。在页面显眼位置添加“视频仅在本地处理,不上传服务器”声明。这不仅是合规要求,也是用户信任的基础。用 onPoseResults 回调中直接处理 video 帧,代码里没有任何 fetch 上传,是事实。

5.5 性能优化

  • modelComplexity: 0 在低端机上更友好
  • 去掉 smoothLandmarks: true 可以省一点 CPU,但会抖动
  • minDetectionConfidence 从 0.5 降到 0.7 可减少误报,但可能漏检
  • 不要每帧都执行 drawCanvas,改为每秒30帧渲染,计算逻辑保持60Hz监听——不过 Mediapipe 本身会控制帧率。

最后,说点个人看法

Colman Domingo 的健身故事其实给了我们一个启发:AI 产品不是越复杂越好。他56岁不再追求“大块头”,转而追求“保持运动能力”。我们的 AI 健身 Demo 也别追求“评测动作100%准确”——能做到80%的可用性,配合用户手动确认,就是一个好工具。下次如果有人让你做个“完美姿态纠正AI”,你可以反问:用户真的需要完美吗?还是先能跑起来?

mediapipe pose keypoints diagram for squat counting

完整项目代码已上传 GitHub(搜 pose-fitness-demo),欢迎 star 和 PR。