基于IMU传感器的智能健身动作计数器设计与实现

1. 项目概述:从“小馒头”到智能健身伙伴

最近在健身圈和创客社区里,一个叫“小馒头撸铁计数器”的小玩意儿讨论度挺高。光看这个标题,“肌肉是练出来的,戴上小馒头撸铁计数器加油干”,一股子硬核又带点萌的混搭感就出来了。这本质上是一个为力量训练者设计的可穿戴式动作计数与训练辅助设备。它不像那些主打心率、卡路里的智能手表或手环,它的核心任务非常聚焦:在你进行卧推、深蹲、引体向上这类重复性力量动作时,精准地帮你数数。

为什么我们需要专门数数?老铁们都懂,训练中记组数、次数(Reps)是基本功,但练到力竭、注意力集中在发力感和动作标准性上时,很容易记混或忘记。“这组是第8个还是第9个了?”这种瞬间的迟疑不仅打断训练节奏,更可能影响训练安全和效果。传统的解决办法要么是靠脑子硬记,要么是用训练本、手机APP手动点击记录,前者容易出错,后者则破坏了训练的连贯性。“小馒头”瞄准的就是这个看似微小却真实存在的痛点,它试图通过无感、自动化的计数,把训练者从“计数”这个认知负担中解放出来,让你能更专注地感受肌肉收缩、控制动作轨迹。

“小馒头”这个昵称也很形象,暗示了设备可能具备体积小巧、佩戴舒适(像个小馒头一样贴在身上)、甚至价格亲民的特点。它大概率通过内置的惯性测量单元(IMU,包含加速度计和陀螺仪)来捕捉肢体运动的特征模式,结合算法识别出特定的训练动作并计数。这听起来简单,但要做好却涉及传感器融合、姿态识别、模式匹配等一系列嵌入式开发和算法调优的工作。对于健身爱好者来说,它是一个实用的训练工具;对于硬件开发者和创客而言,它是一个非常棒的、软硬件结合的实战项目,涵盖了从传感器数据采集、滤波处理、动作识别算法到低功耗设计、用户交互的全流程。

2. 核心设计思路与方案选型

做一个撸铁计数器,听起来方案有很多,但每种方案背后都有其特定的优缺点和适用场景。我们需要根据“精准、无感、低成本、易用”这几个核心目标来拆解和选型。

2.1 技术路径对比:为什么选择IMU方案?

首先,我们得确定用什么来“感知”动作。常见的有以下几种思路:

  1. 视觉方案:用摄像头捕捉。优点是信息丰富,能分析全身姿态。缺点太明显了:隐私问题、受环境光线和遮挡影响大、无法在健身房多设备场景下稳定工作,最关键的是,它要求设备固定,违背了“可穿戴”和“无感”的初衷。PASS。
  2. 声音/振动方案:通过麦克风捕捉杠铃片碰撞声,或通过接触式传感器感知器械振动。这种方式过于依赖特定环境和器械,通用性极差。哑铃弯举、自重深蹲这类没有明显碰撞声的动作就无法识别。PASS。
  3. 压力或张力传感器方案:集成在杠铃杆或拉力绳上。这能直接测量力,数据非常精准,但意味着你要改造健身器械,成本高且不通用,只适合固定场所的专业运动员。不适合大众健身爱好者。PASS。
  4. 惯性测量单元(IMU)方案:这也是“小馒头”最可能采用的方案。IMU通常包含三轴加速度计和三轴陀螺仪,有的还会集成磁力计。它能测量载体(即我们的手臂、躯干)在空间中的加速度和角速度变化。

为什么IMU方案胜出?

  • 佩戴自由:可以做成腕带、臂环、甚至贴在衣服上,真正实现无感佩戴。
  • 数据丰富:加速度数据能反映动作的“起止”和“爆发力”,陀螺仪数据能刻画动作的“轨迹”和“旋转”。两者结合,足以描绘出大多数力量训练动作的独特“指纹”。
  • 成本可控:市面上成熟的IMU芯片(如MPU6050、MPU9250)价格已经非常低廉,为消费级产品提供了可能。
  • 隐私与普适性:不依赖外部环境,在任何健身房、家庭场景下都能工作。

所以,“小馒头”的技术内核,基本可以确定为一套基于IMU传感器的动作模式识别系统。

2.2 系统架构设计:从数据到交互

确定了感知层,我们再来规划整个系统如何运作。一个最小可用的“小馒头”系统,通常包含以下几个模块:

  1. 传感器模块:核心是IMU芯片,负责以高频率(例如50-100Hz)采集原始的加速度和角速度数据。
  2. 微控制器(MCU):这是设备的大脑。它需要实时读取传感器数据,进行初步处理(滤波),运行动作识别算法,管理计数逻辑,并驱动输出。对MCU的要求是:足够的计算能力运行轻量级算法、低功耗以支持长时间续航、具备蓝牙等无线通信能力。常见的选型有ESP32(集成Wi-Fi/蓝牙,功能强但功耗稍高)、nRF52832(蓝牙低功耗专家)或STM32系列(稳定可靠,生态丰富)。
  3. 数据处理与算法模块:这是项目的技术核心。原始IMU数据噪声很大,需要先经过滤波(如互补滤波、卡尔曼滤波)得到更稳定的姿态估计。然后,我们需要从处理后的数据流中提取特征(例如,一次卧推过程中,胸肌发力推起时在垂直方向的加速度特征曲线),并通过算法(如阈值判断、动态时间规整DTW,或更复杂的轻量级机器学习模型)来匹配预设的动作模板,从而判定一次有效动作的完成。
  4. 用户交互模块:如何让用户知道计数?常见方式有:小型振动马达(每完成一次动作震动一下)、迷你OLED屏幕(显示当前次数/组数)、或简单的LED指示灯。振动反馈是最佳选择,因为它不干扰视觉,且能提供即时的触觉确认。
  5. 电源与续航模块:采用小容量锂电池(如100-200mAh)供电,配合低功耗MCU和优化后的软件策略(如动态调整传感器采样率),目标是实现至少一周的待机或连续数小时训练的续航。
  6. 无线通信与APP(可选):通过蓝牙将训练数据(总次数、组数、预估消耗等)同步到手机APP,用于长期记录和分析。这属于增强功能,对于MVP(最小可行产品)版本,可以优先保证离线计数功能的稳定。

2.3 产品形态与佩戴方式考量

“小馒头”这个称呼暗示了其形态。它不应该是个笨重的手表。更可能的设计是:

  • 臂环式:佩戴在上臂靠近肱二头肌/肱三头肌的位置。这个位置对于大多数推、拉、弯举动作的肢体运动捕捉都很敏感。
  • 腕带式:类似运动手环,但更小巧。对于涉及手腕大量旋转的动作(如锤式弯举)识别可能更有优势,但对于深蹲、硬拉等核心发力动作,信号可能不如臂环明显。
  • 核心考量:佩戴位置需要尽可能减少与动作无关的“噪声运动”。例如,做卧推时,上臂的运动轨迹相对固定且有规律,而手腕可能因握姿不同有微小差异。因此,上臂或胸部可能是更优的传感器放置点。在实际开发中,可能需要通过实验来确定最佳位点。

3. 核心算法与动作识别实现详解

这是整个项目的灵魂所在。如何让一堆加速度和陀螺仪的数字,变成准确的“一次卧推”计数?下面我们拆解最关键的几个步骤。

3.1 数据预处理:从“毛坯”到“精装”

直接从IMU读出的数据是不能直接用的,它充满了噪声和误差。想象一下,你的手微微颤抖、设备本身没戴紧产生的滑动,都会产生无用的信号。

第一步:校准。这是很多初学者会忽略但至关重要的一步。传感器存在零偏误差,即静止时输出的值可能不是真正的零。我们需要在设备启动时,让其静止数秒钟,计算这段时间内各轴数据的平均值,作为“零偏”,并在后续所有读数中减去它。

第二步:滤波。这是为了平滑数据,保留动作的主趋势,滤除高频抖动。对于实时性要求高的可穿戴设备,互补滤波是一个经典且高效的选择。它巧妙地将加速度计和陀螺仪的数据融合:加速度计在长期静态下测量倾角准确,但动态响应慢;陀螺仪短期积分计算角度变化非常准,但随时间会产生累积漂移。互补滤波取两者之长,用高通滤波器滤除加速度计的低频噪声(动态误差),用低通滤波器滤除陀螺仪的高频噪声(漂移),再将结果融合,得到相对稳定可靠的姿态角(如俯仰角、横滚角)。

// 简化的互补滤波伪代码示例(用于计算俯仰角Pitch) float alpha = 0.98; // 权重系数,通常0.98左右,可根据实际情况调整 float dt = 0.01; // 采样时间间隔,假设100Hz采样,dt=0.01秒 // 从加速度计计算倾角(单位:弧度) float accel_pitch = atan2(accelY, sqrt(accelX*accelX + accelZ*accelZ)); // 从陀螺仪计算角度变化(角速度积分) float gyro_pitch_rate = gyroX; // 假设X轴角速度对应俯仰变化 pitch_gyro += gyro_pitch_rate * dt; // 积分,注意这会漂移! // 互补滤波融合 pitch = alpha * (pitch + gyro_pitch_rate * dt) + (1 - alpha) * accel_pitch;

这段代码的核心思想是:当前姿态角(pitch)主要由上一时刻姿态角加上陀螺仪积分的结果主导(权重alpha),同时用加速度计测得的绝对角度(accel_pitch)进行微量校正(权重1-alpha),以此来抑制陀螺仪的漂移。

3.2 特征提取与动作模板

经过滤波,我们得到了干净的三轴加速度和三轴角速度数据,或者融合后的姿态角数据。接下来,我们需要从中找到能代表“一次标准动作”的特征。

杠铃卧推为例,一个完整的动作周期(Rep)可以分解为:

  1. 下放阶段:杠铃从最高点受控下降至胸部。
  2. 底部停顿(非必须,但可作为特征):在胸部短暂的停顿。
  3. 推起阶段:发力将杠铃推回最高点。

在传感器数据上,这会体现为:

  • 加速度计(Z轴,垂直方向):下放阶段,加速度值在重力加速度(g)附近波动;推起阶段初期,会产生一个明显的正向加速度峰值(爆发力),然后回落。
  • 陀螺仪/姿态角(俯仰角):手臂从伸直(角度小)到弯曲(角度变大),再回到伸直,俯仰角会经历一个先增大后减小的类正弦波变化。

我们可以定义一个“特征窗口”,例如,寻找俯仰角由小变大再变小的一个完整波形,并且配合Z轴加速度在波谷(对应动作最低点)后出现的一个显著峰值。这个“角度波形+加速度峰值”的组合,就是一个非常强的卧推动作特征。

如何定义“模板”?在算法初期,我们可以采用阈值法。即,通过实验采集一批标准卧推的数据,观察并设定一组阈值:

  • pitch_angle必须大于某个最小值(比如30度),才算完成有效下放。
  • accel_zpitch_angle从最大值开始减小时(即开始推起时),必须超过一个阈值(比如1.5g)。
  • 两次有效动作之间必须有一定的时间间隔(比如1秒),防止重复计数。

更高级的方法可以使用动态时间规整(DTW)或训练一个简单的机器学习分类器(如运行在MCU上的TinyML模型)。DTW可以计算实时数据流与预存的标准动作数据序列之间的相似度,即使动作快慢稍有不同,也能有效匹配。而机器学习模型则可以通过学习大量标注数据,自动提取更复杂的特征,识别精度和鲁棒性会更高,但对MCU算力和数据收集的要求也更高。

3.3 计数逻辑与状态机实现

识别出一次动作后,计数逻辑并非简单的“检测到特征就+1”那么简单。我们需要一个状态机来管理整个计数过程,防止误触发。

一个典型的状态机可以设计为以下几个状态:

  • IDLE(空闲):设备待机,等待训练开始。
  • DETECT_START(检测开始):检测到可能代表动作开始的特征(如姿态角开始持续变化)。
  • IN_REP(动作进行中):确认动作开始,正在监测动作特征,在此状态下忽略新的“开始”信号。
  • REP_COMPLETE(动作完成):检测到完整的动作特征(如角度回位且伴随加速度峰值),计数加一,并进入一个短暂的DEBOUNCE(去抖)状态。
  • DEBOUNCE(去抖):在这个状态停留几百毫秒,屏蔽所有输入,防止因动作末端的微小颤动导致重复计数。之后返回IDLE或DETECT_START,准备下一次计数。

这种状态机的引入,极大地提高了计数的可靠性。例如,你做动作做到一半突然停顿调整,状态机会停留在IN_REP,不会错误计数;完成动作后手臂的轻微抖动,会被DEBOUNCE状态过滤掉。

4. 硬件选型与电路设计要点

有了算法思路,我们需要一个物理载体来实现它。硬件选型直接决定了设备的性能、续航和成本。

4.1 核心元器件选型清单

组件推荐型号关键参数与选型理由预估成本
主控MCUnRF52832蓝牙5.0, Cortex-M4F内核,功耗极低,专为可穿戴设计。集成度高,适合作为最终产品。
ESP32-C3性价比高,集成Wi-Fi/蓝牙,RISC-V内核,功耗控制也不错。开发资源丰富,适合原型快速验证。
IMU传感器MPU6050经典6轴(3轴加速度+3轴陀螺仪),DMP(数字运动处理器)可硬件解算姿态,减轻MCU负担。性价比之王。极低
ICM-209489轴(加速度+陀螺仪+磁力计),性能更优,抗干扰更强。磁力计可用于校正方向漂移,但算法更复杂。
电源管理TP4056单节锂电池充电管理芯片,电路简单可靠,自带充电状态指示。极低
电池602030 3.7V 200mAh扁平方形锂电池,体积小,易于集成到“小馒头”形态中。200mAh容量可供连续工作数小时至数天。
振动马达1020扁平振动马达扁平化设计,节省空间,提供清晰的触觉反馈。极低
显示(可选)0.96寸 OLED (SSD1306)I2C接口,功耗低,可显示次数、组数、电量等信息。会增加功耗和体积。

注意:对于初版原型,强烈建议使用ESP32开发板 + MPU6050模块的组合。它们可以通过杜邦线连接,无需焊接,能让你快速搭建系统,验证算法和逻辑。待功能稳定后,再设计集成度更高的定制PCB。

4.2 电路设计核心注意事项

  1. 电源稳定性:模拟传感器(如MPU6050)对电源噪声非常敏感。务必在IMU的电源引脚附近放置一个0.1μF的陶瓷去耦电容,并可能再并联一个10μF的钽电容,以滤除高频和低频噪声,确保数据稳定。
  2. I2C上拉电阻:MPU6050通过I2C与MCU通信。I2C总线是开漏输出,必须接上拉电阻(通常4.7kΩ)到VCC,否则通信无法进行。很多模块已集成,自制PCB时别忘了。
  3. 电池保护:锂电池必须配备保护板,防止过充、过放和短路,这是安全底线。
  4. 振动马达驱动:马达是感性负载,关断时会产生反向电动势。必须在马达两端并联一个续流二极管(如1N4148),阴极接电源正极,阳极接马达正极,以保护驱动三极管或MOS管不被击穿。
  5. 布局与屏蔽:如果设计PCB,尽量将数字部分(MCU)和模拟部分(IMU)的电源走线分开,并在模拟地附近单点接地,减少数字噪声对传感器信号的干扰。

5. 软件框架与代码实现解析

硬件是身体,软件是灵魂。我们以Arduino框架(兼容ESP32)为例,勾勒出核心代码框架。

5.1 主程序逻辑与状态机实现

#include <Wire.h> #include <MPU6050_light.h> // 使用一个现成的MPU6050库简化开发 MPU6050 mpu(Wire); enum TrainState { IDLE, DETECT_START, IN_REP, REP_COMPLETE, DEBOUNCE }; TrainState currentState = IDLE; int repCount = 0; unsigned long lastRepTime = 0; const unsigned long DEBOUNCE_DELAY = 300; // 去抖延时300ms void setup() { Serial.begin(115200); Wire.begin(); mpu.begin(); mpu.calcOffsets(true, true); // 自动校准零偏 pinMode(VIBRATE_PIN, OUTPUT); // 初始化振动马达引脚 } void loop() { mpu.update(); // 更新MPU数据,库内部可能已包含滤波 float pitch = mpu.getAngleX(); // 获取俯仰角 float accelZ = mpu.getAccZ(); // 获取Z轴加速度 unsigned long currentTime = millis(); switch (currentState) { case IDLE: // 检测动作开始:俯仰角变化速率超过阈值 if (abs(pitch - lastPitch) > ANGLE_RATE_THRESHOLD) { currentState = DETECT_START; repStartPitch = pitch; } break; case DETECT_START: // 确认进入有效动作区间:角度变化达到一定范围 if (abs(pitch - repStartPitch) > ANGLE_START_THRESHOLD) { currentState = IN_REP; minPitch = pitch; // 开始记录动作过程中的最小角度(最低点) } else if (abs(pitch - lastPitch) < ANGLE_RATE_THRESHOLD_SMALL) { // 变化太小,可能是误触发,退回IDLE currentState = IDLE; } break; case IN_REP: // 更新最低点角度 if (pitch < minPitch) minPitch = pitch; // 检测是否开始回程(角度从变小变为变大),并且加速度超过阈值 if ((pitch - lastPitch > 0) && (lastPitch - pitchBeforeLast <= 0)) { // 角度变化率由负转正,意味着开始推起 if (accelZ > ACCEL_PEAK_THRESHOLD) { currentState = REP_COMPLETE; } } break; case REP_COMPLETE: repCount++; Serial.print("Rep Count: "); Serial.println(repCount); digitalWrite(VIBRATE_PIN, HIGH); // 振动反馈 delay(50); // 短振动 digitalWrite(VIBRATE_PIN, LOW); lastRepTime = currentTime; currentState = DEBOUNCE; break; case DEBOUNCE: if (currentTime - lastRepTime > DEBOUNCE_DELAY) { currentState = IDLE; // 可选:重置一些中间变量 minPitch = 180.0; } break; } lastPitch = pitch; pitchBeforeLast = lastPitch; delay(10); // 控制循环频率约100Hz }

这段代码展示了一个基于阈值和状态机的简易计数逻辑。你需要根据实际实验数据来调整ANGLE_RATE_THRESHOLD,ANGLE_START_THRESHOLD,ACCEL_PEAK_THRESHOLD这些魔法数字。

5.2 低功耗优化策略

如果希望“小馒头”有更长的续航,低功耗设计必不可少。对于nRF52832这类芯片,可以采取以下措施:

  1. 外设电源管理:不用IMU时,通过MOS管切断其电源。MPU6050本身也有低功耗睡眠模式。
  2. MCU睡眠模式:在IDLE状态且长时间无动作时,让MCU进入深度睡眠(Deep Sleep),仅靠外部中断(如由IMU的“运动中断”引脚触发)唤醒。这能极大降低待机功耗。
  3. 动态采样率:检测到运动时,以全速(如100Hz)采样;在IDLE状态时,降低IMU采样率和MCU的处理频率。
  4. 无线连接管理:蓝牙仅在需要同步数据时连接,完成后立即断开。

6. 实测调优与常见问题排查

算法和代码写好了,但直接用到实际训练中,肯定会遇到各种问题。下面是一些典型的“坑”和解决方法。

6.1 校准与安装:成败的第一步

问题:计数不准,时而过敏感,时而没反应。

  • 排查1:校准是否到位?一定要在设备静止、水平放置的情况下进行校准。如果佩戴在手臂上校准,手臂的轻微紧张或角度都会导致零偏不准。最好的方法是把设备放在平整桌面上进行校准。
  • 排查2:佩戴是否牢固?设备与皮肤或紧身衣之间不能有相对滑动。任何滑动都会产生额外的加速度信号,干扰识别。使用弹性好的臂带,并确保佩戴紧实。
  • 排查3:传感器轴向对齐了吗?算法假设传感器的Z轴垂直向上。如果设备戴歪了,那么加速度和角度的基准就全错了。可以在固件中增加一个“佩戴校准”步骤:让用户站立,手臂自然下垂,按一下按钮,将此姿态记录为“初始姿态”,后续所有数据都以此为基础进行坐标变换。

6.2 阈值调参:没有银弹,只有实验

问题:对于不同的人、不同的动作速度,计数效果差异大。

  • 解决:参数个性化与自适应。固定的阈值很难适应所有人。可以引入一个“学习模式”:
    1. 让用户先做5次标准动作。
    2. 系统记录这5次动作中,关键特征(如最大角度变化、平均加速度峰值)的平均值和方差。
    3. 将这些统计值作为该用户、该动作的个性化阈值基准。
    4. 在正式计数时,使用这些个性化阈值,并允许在一定范围内浮动。

6.3 干扰动作过滤:别把擦汗当成了一次推举

问题:训练间歇的擦汗、喝水等小动作被误计数。

  • 解决:增加动作持续时间和幅度判断。一个有效的训练动作,其姿态角变化幅度大、持续时间相对固定(比如0.5秒到3秒)。而擦汗动作可能角度变化小且快。可以在状态机中增加判断:只有在IN_REP状态持续了超过最短时间(如0.3秒),并且角度变化超过最小幅度(如15度)后完成的动作,才被计入。这能有效过滤掉大部分小幅度干扰。

6.4 不同动作的识别区分

问题:设备能数卧推,但做俯卧撑或肩推时也计数。

  • 解决:多模板识别。这是产品化的关键。需要为每个想要识别的动作(卧推、深蹲、弯举等)建立独立的特征模板或模型。
    • 阈值法进阶:不同动作的发力轴、角度变化范围、加速度特征不同。例如,深蹲的俯仰角变化主要发生在躯干,而弯举发生在手臂。可以同时监测多个特征(如俯仰角、横滚角、合加速度)的组合阈值。
    • 机器学习法:这是终极方案。收集大量标注好的不同动作传感器数据,训练一个分类模型(如卷积神经网络CNN处理时间序列数据)。在设备端,模型不仅能判断“这是一次有效动作”,还能判断“这是卧推”还是“深蹲”,从而实现自动识别动作类型并分别计数。这需要TensorFlow Lite Micro等框架的支持,对MCU有更高要求。

7. 产品化思考与扩展方向

如果不仅仅满足于一个自用的极客玩具,而是想把它变成一个真正的产品,还有一些重要的维度需要考虑。

用户体验优化:

  • 自动组间休息计时:检测到一组动作结束后(例如,10秒内无新动作),自动启动60-90秒的休息倒计时,并通过振动提示休息结束。
  • 力竭预测与提醒:通过分析连续动作的速度下降(每次推起的时间变长)或轨迹稳定性变化,在用户接近力竭时给予提示。
  • 手机APP同步与数据分析:通过蓝牙将每组的次数、组间休息时间同步到手机,形成训练日志。长期数据可以分析训练容量、疲劳度、进步曲线。

硬件工艺改进:

  • 防水防汗:健身设备必须考虑汗液腐蚀,需要达到IP67级别的防护。
  • 佩戴舒适性:使用医用级硅胶或透气织物,设计符合人体工学的弧线。
  • 无线充电:舍弃裸露的USB口,采用Qi无线充电,提升整体感和耐用性。

商业模式的延伸:

  • 社群与挑战:APP内可以设立挑战赛,用户使用“小馒头”完成特定训练计划并上传数据,参与排名。
  • 虚拟教练指导:结合动作识别,在用户动作变形(如深蹲膝盖内扣)时,通过振动模式给出实时反馈。

从一颗“小馒头”开始,它可以是你健身路上的忠实计数伙伴,也可以是一个充满挑战和乐趣的硬核开发项目。它的核心价值在于,用技术解决了训练中一个微小但确切的痛点,让训练者更能专注于感受肌肉、突破极限。无论是自己动手制作一个,还是期待市场上出现成熟的产品,这种专注于提升单一体验的智能硬件思路,都值得我们关注和尝试。在实际开发中,最大的成就感往往来自于一次次调试后,设备终于能稳定准确地数出“1, 2, 3...”,那一刻,你会觉得所有对着传感器数据波形图的纠结,都值了。