1. 项目概述与核心思路
最近在捣鼓一个挺有意思的小项目,起因是身边不少朋友抱怨说在家做仰卧起坐、卷腹这类基础健身动作时,总是数不清次数,动作标不标准心里也没底。市面上专业的健身追踪设备要么太贵,要么功能过于复杂。作为一个喜欢动手的嵌入式爱好者,我琢磨着能不能用最基础的电子元件,自己做一个简单、低成本但又足够实用的智能健身辅助工具。于是,就有了这个基于Arduino和超声波传感器的智能健身训练器。
这个装置的核心功能就两点:自动计数和姿态监测。它通过一个超声波传感器来“感知”你身体(比如头部或胸口)在运动过程中的位置变化,从而判断你是否完成了一次有效动作,并在一个LCD屏幕上实时显示你的训练次数。听起来是不是有点像健身房里的某些器械?但我们的成本可能还不到它们的一个零头,而且完全开源、可定制。整个项目非常适合对Arduino、传感器应用或者物联网小玩意儿感兴趣的初学者和爱好者,你不需要有很深的电子背景,跟着步骤一步步来,就能亲手做出一个能真正解决实际问题的小设备。
2. 核心硬件选型与原理剖析
2.1 为什么选择Arduino Leonardo?
在这个项目中,我选择了Arduino Leonardo作为主控板,而不是更常见的Uno。这里有几个关键的考量点。首先,Leonardo板载的ATmega32u4微控制器原生支持USB通信,这意味着它可以被电脑识别为鼠标或键盘等HID设备。虽然我们当前项目没用到这个高级功能,但它为未来扩展(比如将训练数据自动录入电脑表格)预留了可能性。其次,Leonardo的引脚布局和资源与Uno大部分兼容,学习成本低,但它的模拟输入引脚更多,数字IO也足够我们使用。最重要的是,Leonardo的性价比在支持原生USB的板卡中很有优势,对于这种功能明确、不需要极高性能的原型项目来说,是“够用且好用”的典型。
2.2 超声波传感器:非接触式测量的基石
姿态监测的核心是测量距离的实时变化,我选择了最常见的HC-SR04超声波传感器。它的工作原理非常直观:Trig引脚触发一个至少10微秒的高电平脉冲,传感器就会发射一束40kHz的超声波。这束声波遇到障碍物(比如你的身体)后会反射回来,被接收器捕捉。Echo引脚会输出一个高电平脉冲,其持续时间与声波往返的时间成正比。
计算距离的公式是:距离 = (高电平时间 × 声速) / 2。在常温下,声速约340米/秒。Arduino通过pulseIn()函数可以轻松读取Echo引脚高电平的持续时间(单位微秒),从而计算出以厘米为单位的距离。例如,如果测得的脉冲时间为588微秒,那么距离就是 (588 * 0.034) / 2 ≈ 10厘米。这种非接触式测量方式非常适合健身场景,你不需要在身上贴任何东西,传感器隔着空气就能工作,既卫生又方便。
注意:超声波传感器的测量有一定角度(HC-SR04约15度),且对柔软、吸音的表面(如厚窗帘)探测能力会下降。但在监测人体这种较大的目标时,完全够用。安装时要确保传感器正对运动区域,中间没有其他小物体干扰。
2.3 I2C LCD显示屏:简洁的信息窗口
为了实时显示次数,我选用了一块16x2字符的LCD屏,并搭配了I2C转接板。这是非常关键的一个“用户体验”优化。传统的LCD需要连接多达6根数据线和控制线,接线复杂且占用大量IO口。而I2C版本只需要4根线(VCC, GND, SDA, SCL),通过串行通信传输数据,极大地简化了布线。SDA和SCL是Arduino上标准的I2C通信引脚,在Leonardo上对应的是D2和D3(但通常我们使用专门的SDA/SCL引脚)。I2C总线允许多个设备共享同一组通信线,通过地址区分,这为后续添加其他传感器(如心率模块)提供了便利。
3. 系统搭建与电路连接详解
3.1 物料清单与工具准备
在开始动手之前,请再次清点你的“装备”。除了项目正文里列出的核心部件,我还建议你准备以下工具,会让制作过程更顺利:
- 焊接工具(可选但推荐):虽然面包板适合原型验证,但一个稳定的作品最好还是将杜邦线焊接在扩展板或洞洞板上,避免接触不良。
- 万用表:用于检查电源是否接通、线路是否短路,是电子制作的“眼睛”。
- 热熔胶枪或双面胶:用于在盒子内部固定Arduino、面包板等组件,防止它们因晃动导致松脱。
- 一把好用的美工刀和钢尺:用于在包装盒上精确开孔,这直接决定了成品的外观是否整洁。
核心物料清单如下,你可以对照检查:
- 主控:Arduino Leonardo x1
- 感知核心:HC-SR04超声波传感器 x1
- 显示单元:带I2C转接板的1602 LCD显示屏 x1
- 原型平台:面包板(建议中号或大号)x1
- 连接线:公对公杜邦线 若干(建议准备20根左右,不同颜色区分电源、地、信号)
- 供电系统:移动电源(5V输出)x1, Micro USB数据线(用于给Arduino供电)x1
- 外壳:一个足够大的硬质纸盒或塑料盒(用来容纳所有元件并保护它们)
- 制作工具:铅笔、尺子、切割垫、美工刀。
3.2 一步一步连接你的电路
电路连接是项目的骨架,务必仔细。下图清晰地展示了所有元件的连接关系,你可以边看图边操作: (此处本应有一幅清晰的Fritzing接线图,图中显示:Arduino Leonardo的5V和GND分别连接到面包板的正负电源轨;HC-SR04的Vcc接正极,Gnd接负极,Trig接数字引脚13,Echo接数字引脚12;LCD I2C模块的VCC和GND分别接正负极,SDA接Leonardo的SDA引脚(靠近AREF),SCL接SCL引脚;移动电源通过USB线为Arduino供电。)
文字版接线步骤解析:
- 建立公共电源:首先,用两根杜邦线将Arduino Leonardo上的5V引脚和GND引脚分别连接到面包板上的正极(+)电源轨和负极(-)电源轨。这样,面包板上的整个电源轨就都有了电和地。
- 连接超声波传感器:
- Vcc-> 面包板正极(+5V)
- Gnd-> 面包板负极(GND)
- Trig-> Arduino数字引脚13(D13)。这个引脚负责发送触发信号。
- Echo-> Arduino数字引脚12(D12)。这个引脚负责接收回波信号。
- 为什么选D12和D13?没有特殊原因,这只是Leonardo上两个普通的数字IO。你也可以换成其他引脚,但记得在代码里同步修改。
- 连接LCD I2C显示屏:
- VCC-> 面包板正极(+5V)
- GND-> 面包板负极(GND)
- SDA-> Arduino上标有SDA的引脚。在Leonardo上,它位于AREF引脚旁边。
- SCL-> Arduino上标有SCL的引脚。在Leonardo上,紧挨着SDA引脚。
- I2C设备接线非常简单,但务必注意:SDA和SCL是信号线,板上通常有上拉电阻,如果遇到显示问题,可以尝试在SDA和SCL到正极之间各接一个4.7kΩ的电阻(大多数I2C模块已集成)。
- 供电:最后,用移动电源的USB线插入Arduino Leonardo的Micro USB口,为整个系统供电。此时,Arduino的电源指示灯应亮起,LCD屏幕可能背光亮起(但尚无内容)。
实操心得:接线时,强烈建议使用不同颜色的杜邦线来区分功能。我的习惯是:红色代表正极(5V),黑色或棕色代表负极(GND),黄色或绿色代表信号线。这样在调试时,一眼就能看清线路走向,万一接错了也容易排查。另外,在给面包板插线时,确保插到底,接触牢固,很多“诡异”的问题都源于接触不良。
4. 代码解析与核心逻辑实现
电路搭建好后,我们需要给它注入“灵魂”——程序。代码决定了传感器如何工作、数据如何计算、屏幕如何显示。
4.1 库的安装与代码结构
首先,你需要安装LCD所需的库。打开Arduino IDE,依次点击工具 -> 管理库...,在搜索框中输入“LiquidCrystal I2C”,找到由Frank de Brabander开发的版本进行安装。这个库完美支持我们使用的I2C LCD模块。
完整的项目代码如下,我已经添加了详细的注释,你可以直接复制到IDE中,但更重要的是理解每一部分的作用:
// 引入I2C LCD驱动库 #include <Wire.h> #include <LiquidCrystal_I2C.h> // 初始化LCD对象,参数:I2C地址(通常为0x27或0x3F),列数,行数 // 如果你的屏幕不显示,尝试将0x27改为0x3F LiquidCrystal_I2C lcd(0x27, 16, 2); // 定义超声波传感器引脚 const int trigPin = 13; const int echoPin = 12; // 定义运动状态变量 long duration; // 存储高电平脉冲时间 int distance; // 计算出的距离 int lastDistance = 0; // 上一次测量的距离 int count = 0; // 动作计数器 bool isCounting = false; // 是否正在计数一次动作(防重复计数标志) // 阈值设置:根据你的安装位置和身高调整这两个值 const int UPPER_THRESHOLD = 30; // 厘米,身体远离传感器的“上限”距离,代表躺下 const int LOWER_THRESHOLD = 15; // 厘米,身体靠近传感器的“下限”距离,代表坐起 void setup() { // 初始化串口通信,用于调试(可选) Serial.begin(9600); // 初始化传感器引脚模式 pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); // 初始化LCD lcd.init(); lcd.backlight(); // 打开背光 lcd.setCursor(0, 0); // 将光标设置到第0列,第0行(左上角) lcd.print("Fitness Monitor"); // 打印静态标题 lcd.setCursor(0, 1); lcd.print("Count: "); updateDisplay(); // 更新显示计数 // 初始测距,校准起始位置 lastDistance = getDistance(); } void loop() { // 1. 获取当前距离 distance = getDistance(); // 2. 调试输出(通过串口监视器查看,方便调整阈值) Serial.print("Distance: "); Serial.print(distance); Serial.println(" cm"); // 3. 核心逻辑:判断是否完成一次有效动作 // 逻辑:当从“躺下”(距离远)状态移动到“坐起”(距离近)状态,并返回时,计一次数 if (distance <= LOWER_THRESHOLD) { // 身体靠近传感器(坐起状态) if (!isCounting) { // 如果之前不是在计数过程中,则标记开始计数 isCounting = true; } } else if (distance >= UPPER_THRESHOLD && isCounting) { // 身体远离传感器(躺下状态)并且之前已经标记为“坐起过” count++; // 完成一次完整的仰卧起坐,计数加1 isCounting = false; // 重置标志,等待下一次动作 updateDisplay(); // 更新屏幕显示 Serial.print("Count incremented! Total: "); Serial.println(count); } // 4. 更新上一次的距离记录 lastDistance = distance; // 5. 加入短暂延迟,避免采样过快导致误触发或处理器过载 delay(100); // 100毫秒的延迟,即每秒采样10次,对于人体运动足够 } // 自定义函数:获取超声波测距值 int getDistance() { // 确保Trig引脚为低电平 digitalWrite(trigPin, LOW); delayMicroseconds(2); // 发送10微秒的高脉冲触发信号 digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); // 读取Echo引脚高电平持续时间(单位:微秒) duration = pulseIn(echoPin, HIGH); // 计算距离(厘米),声速按340米/秒计算,除以2是往返距离 return duration * 0.034 / 2; } // 自定义函数:更新LCD显示 void updateDisplay() { lcd.setCursor(7, 1); // “Count: ”后面是第7列 lcd.print(" "); // 先清空原有数字(用空格覆盖) lcd.setCursor(7, 1); lcd.print(count); // 打印新的计数值 }4.2 核心算法:如何判断一次有效动作?
代码中最关键的部分在loop()函数的第3步。这里实现了一个简单的状态机逻辑来防止重复计数,这是本项目稳定性的核心。
- 设置两个距离阈值:
UPPER_THRESHOLD(上限,例如30cm)代表你完全躺下时,身体(如胸口)到传感器的距离。LOWER_THRESHOLD(下限,例如15cm)代表你完全坐起时,身体到传感器的最近距离。 - 引入
isCounting标志位:这个布尔变量用来记录是否已经检测到“坐起”的起始动作。 - 计数逻辑:
- 当实测距离小于等于下限阈值时,系统认为你“正在坐起”或“已经坐起”,此时将
isCounting标记为true。 - 只有当
isCounting为true,并且实测距离大于等于上限阈值时,系统才认为你完成了一次“从坐到躺”的完整动作,计数器count加1,然后将isCounting重置为false。 - 这样,只有经历了“远 -> 近 -> 远”一个完整循环,才会计数一次。如果你在“近”的位置(坐起状态)来回晃动,也不会重复计数。
- 当实测距离小于等于下限阈值时,系统认为你“正在坐起”或“已经坐起”,此时将
参数调整心得:
UPPER_THRESHOLD和LOWER_THRESHOLD是必须根据你的个人体型和传感器安装位置来调整的。最好的方法是:
- 上传代码后,打开Arduino IDE的串口监视器(波特率设为9600)。
- 将传感器固定在预定位置(比如床边的椅子上,正对你的胸口)。
- 你分别做出标准的“完全躺平”和“完全坐起”姿势。
- 观察串口监视器输出的距离数值,记录下这两个状态下的稳定读数。
- 将记录的数值,再放宽2-3厘米作为阈值,填入代码中。例如,躺平读数为32cm,坐起读数为12cm,则可以设
UPPER_THRESHOLD = 30,LOWER_THRESHOLD = 15。这提供了一个缓冲区间,防止因呼吸或微小晃动导致的误触发。
5. 外壳制作与设备部署
一个可靠的外壳不仅能保护电路,还能让项目看起来更专业,使用起来更安全。
5.1 测量与开孔
- 规划布局:将Arduino、面包板、LCD屏幕、超声波传感器在盒子内部大致摆放一下,确定一个既紧凑又便于接线的布局。特别注意超声波传感器的探测方向,它前方必须正对运动区域,且无遮挡。
- 精确测量:用尺子量出LCD屏幕显示区域和超声波传感器感应头的精确尺寸。
- 标记与开孔:
- LCD窗口:在盒子正面,用铅笔轻轻画出比屏幕显示区域稍小的矩形。开孔时,可以先在内部用美工刀划出痕迹,再从外部小心切割。孔洞边缘要平整。
- 传感器孔:在盒子侧面(根据你的部署方案决定),开一个刚好能让传感器感应头露出来的圆孔或方孔。确保传感器能牢固地卡住或粘住,不会晃动。
- 电源线孔:在盒子侧面或背面开一个小孔,用于USB电源线的引入。
- 固定元件:使用热熔胶或强力双面胶,将Arduino、面包板、移动电源牢固地粘贴在盒子底部。粘贴传感器和LCD时,确保它们与开孔对齐且位置端正。
5.2 部署与使用流程
- 放置设备:将制作好的盒子放在你进行训练的地面或矮凳上。关键点在于:超声波传感器的中心轴线应对准你运动时身体(建议是胸口或额头)轨迹的中点。
- 校准测试:接通电源,观察LCD是否显示“Fitness Monitor”和“Count: 0”。你可以在传感器前来回移动一本书或手掌,模拟身体运动,观察计数是否增加,并在串口监视器查看实时距离,确认阈值设置是否合理。
- 开始训练:躺下,将设备放在你身侧约30-50厘米处(具体距离取决于你的臂长和传感器角度)。开始做仰卧起坐,确保每次坐起时,胸口能进入
LOWER_THRESHOLD范围内,躺下时能回到UPPER_THRESHOLD范围外。LCD屏幕会实时显示你的完成次数。 - 复位:目前代码没有硬件复位按钮。如果你想清零计数,可以短时间关闭移动电源再打开,或者按一下Arduino上的复位键(RESET)。
6. 常见问题排查与进阶优化
在实际制作和使用的过程中,你可能会遇到一些问题。这里我整理了一份排查清单,涵盖了最常见的情况:
| 问题现象 | 可能原因 | 排查与解决方法 |
|---|---|---|
| LCD屏幕不亮或无显示 | 1. 电源未接通或接反。 2. I2C地址不正确。 3. 对比度不合适。 | 1. 检查VCC和GND是否接对,用万用表测电压是否为5V。 2. 尝试将代码中的 0x27改为0x3F,或运行I2C扫描程序查找地址。3. 很多I2C模块背面有一个蓝色的电位器,用螺丝刀微调以改变对比度。 |
| 超声波传感器读数固定为0或超大值 | 1. 接线错误(Trig/Echo接反)。 2. 传感器前方有强吸音材料或障碍物太近/太远。 3. 传感器本身故障。 | 1. 仔细检查Trig和Echo引脚是否与代码定义一致。 2. 确保传感器正对面积较大的障碍物(如墙壁)测试,距离在2cm-400cm之间。 3. 更换一个传感器测试。 |
| 计数不准确(多计、漏计) | 1. 距离阈值(UPPER_THRESHOLD,LOWER_THRESHOLD)设置不合理。2. 动作不规范,未达到阈值范围。 3. 传感器安装位置不佳,未能对准运动轨迹。 | 1.务必使用串口监视器,观察实际运动时的距离变化,重新校准阈值。 2. 规范动作,确保每次运动幅度一致。 3. 调整传感器角度和位置,使其正对运动部位。 |
| 计数出现连续跳动(重复计数) | 1. 防抖逻辑不够健壮,在阈值附近抖动导致多次触发。 2. 延迟时间太短,处理速度过快。 | 1. 可以增加“状态保持”时间,例如坐起状态需保持200毫秒才认为有效。 2. 适当增加 loop()中的delay值,如改为150或200毫秒,降低采样频率。 |
| 移动电源供电不久后设备重启 | 移动电源输出电流不足或进入休眠模式。 | 使用输出电流≥1A的移动电源。有些移动电源在低负载时会自动关机,可以在Arduino的5V和GND之间接一个约100欧姆的电阻作为“假负载”来维持输出。 |
6.1 项目进阶优化思路
这个基础版本已经可以工作,但还有很大的提升空间,你可以尝试以下方向:
- 增加姿态判断:目前的逻辑只能判断“远-近-远”的循环。你可以尝试记录从“远”到“近”的速度或时间。如果速度过快,可能是借力甩头,可以在屏幕上显示“姿势过快”的警告。这需要记录时间戳并进行简单计算。
- 添加蜂鸣器反馈:连接一个无源蜂鸣器到Arduino引脚。每次计数成功时,发出“滴”一声提示;当动作过快时,发出“滴滴”警报声。提供即时的听觉反馈,无需看屏幕。
- 设计更友好的交互:增加一个按钮,用于手动清零计数。增加一个模式切换按钮,可以在“仰卧起坐模式”、“深蹲模式”(传感器朝上放置于地面)之间切换,不同模式使用不同的阈值。
- 数据记录与可视化:利用Leonardo的HID功能,或者通过蓝牙模块(如HC-05)将每次训练的次数、时间戳发送到手机APP或电脑,生成训练日志和统计图表。
- 改善电源管理:设计一个简单的开关电路,或者编写代码让设备在检测到长时间无运动后,自动关闭LCD背光进入低功耗模式,挥挥手再唤醒。
这个项目最大的乐趣不在于复现,而在于改造。当你理解了传感器如何感知世界,代码如何控制逻辑,你就可以让它适应更多的场景——比如监测引体向上的次数、记录跳绳数量,甚至改装成洗手间的自动感应皂液器。嵌入式开发的魅力,正在于用简单的工具,创造出解决身边问题的智能小装置。