news 2026/6/4 23:46:41

基于Arduino与RFID的智能泳池计数器:嵌入式系统在运动训练中的应用实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Arduino与RFID的智能泳池计数器:嵌入式系统在运动训练中的应用实践

1. 项目概述:一个为游泳训练量身定制的智能计数器

如果你和我一样,是个游泳爱好者,或者正在指导游泳训练,肯定遇到过这样的烦恼:游了多少圈?今天的目标完成了吗?不同泳姿的练习量怎么分开统计?靠脑子记,游着游着就乱了;用传统的手动计数器,又得在池边频繁操作,湿漉漉的手也不方便。几年前,我在指导一个业余游泳队训练时,就深受其扰。于是,我决定动手做一个能自动识别泳者、精准计数,并且操作直观的智能设备——这就是Pisciduino项目的由来。

简单来说,Pisciduino是一个基于Arduino和RFID技术的智能泳池计数器。它的核心功能是:为每位泳者分配一张唯一的RFID卡,当泳者触壁转身时,用卡片靠近读卡器,系统就会自动记录一圈(通常是25米或50米)。设备上配有独立的按钮,可以方便地清零单个泳者的计数,或者查看总里程。这听起来可能像是一个简单的“打卡”装置,但在实际设计和实现过程中,从硬件选型、电路连接、RFID数据处理,到软件逻辑和防误触设计,每一个环节都藏着不少门道。这个项目完美地诠释了嵌入式系统开发中“传感器数据采集与处理”这一核心环节,通过微控制器将物理世界的动作(刷卡)转化为可管理的数据(圈数、里程),是物联网和智能硬件在运动科学领域一个非常接地气的应用。

无论你是想复现一个实用的训练工具,还是希望学习如何将Arduino、RFID模块、按钮和显示屏整合到一个完整的嵌入式系统中,这个项目都能提供从原理到实操的完整路径。接下来,我将从设计思路开始,一步步拆解如何实现这个智能泳池计数器。

2. 核心设计思路与硬件选型解析

在动手焊接第一根线之前,理清设计思路至关重要。这个项目的核心需求很明确:自动识别不同泳者,并对其游泳圈数进行可靠、便捷的计数与管理。围绕这个需求,我拆解出了几个关键的设计决策点。

2.1 为什么选择RFID而非其他传感器?

识别泳者的方式有很多,比如红外对管、压力传感器、摄像头图像识别等。我最终选择RFID(射频识别),主要基于以下几个考量:

  1. 可靠性高:泳池环境潮湿,可能存在水雾、飞溅。RFID采用非接触式读取,读卡器和卡片都可以做防水封装,受环境影响小。红外对管则容易因水渍或雾气导致误触发或失效。
  2. 识别唯一性:每张RFID卡都有全球唯一的ID号,完美对应一位泳者,不存在混淆的可能。
  3. 成本与复杂度平衡:低频RFID模块(如RC522)价格低廉,技术成熟,Arduino社区支持完善,开发难度远低于摄像头方案。
  4. 用户体验好:泳者只需在触壁时用佩戴的卡片轻松一晃,无需精确对准,操作自然快捷。

这里我选用的是最常见的MFRC522 RFID读卡模块,它支持ISO/IEC 14443 A类标准,有效读取距离在几厘米,非常适合这种需要主动、近距离触发的场景。

2.2 主控与外围设备的搭配逻辑

主控芯片选择了Arduino Uno R3。对于这个项目,它的性能绰绰有余:需要处理RFID读卡中断、多个按钮的扫描、四则运算(圈数转里程)以及驱动显示模块。其丰富的数字和模拟IO口,为连接各类外设提供了便利。

外围设备除了RFID读卡器,还包括:

  • 显示模块:为了同时显示多位泳者的数据,我选择了TM1638模块。这是一个“神器”级别的模块,它集成了8位7段数码管、8个LED指示灯和8个独立按键的扫描电路,通过简单的3线串行接口与Arduino通信。这意味着我用一个模块就解决了显示和大部分输入问题,极大简化了电路和编程。
  • 控制按钮:虽然TM1638自带8个按键,但为了实现一些特殊功能(如总里程显示、全部清零),并考虑到按键的可靠性和手感,我额外增加了几个外置的轻触开关。TM1638的按键用于泳者选择、单个清零等高频操作;外置按钮用于全局性、需谨慎操作的功能。
  • 电源:采用9V直流电源适配器供电,经过Arduino板载稳压芯片输出稳定的5V和3.3V,为所有模块供电。务必确保电源功率足够,特别是在所有数码管都点亮时。

2.3 系统工作流程设计

整个系统的工作流程可以概括为一个循环:

  1. 状态监控:Arduino不断扫描TM1638模块的按键和外置按钮,检测是否有操作指令。
  2. 事件触发:当RFID读卡器检测到卡片进入磁场,并成功读取卡号后,会触发一个“读卡事件”。
  3. 身份匹配:程序将读取到的卡号与预先存储在代码中的授权卡号列表进行比对。
  4. 计数逻辑:如果卡号匹配成功,则找到对应的泳者,将其圈数加1。同时,根据预设的泳池长度(如25米),自动计算并更新该泳者的总游进距离。
  5. 显示更新:TM1638模块的数码管会实时更新当前选中泳者的圈数和里程,LED指示灯可以用于显示状态(如哪位泳者被选中)。
  6. 指令处理:如果用户按下按钮,则执行对应的清零、切换显示或全部重置功能。

这个流程看似简单,但实现时需要考虑防抖(按键和RFID读取)、防重复计数(一次刷卡只计一圈)等细节,我们会在软件部分详细探讨。

3. 硬件连接与电路搭建详解

有了清晰的设计图,接下来就是“搬砖”环节——硬件连接。正确的电路连接是项目成功的物理基础,这里我会提供详细的接线表和注意事项。

3.1 主要元件清单

在开始焊接或使用面包板之前,请准备好以下核心元件:

  1. Arduino Uno R3 开发板 x1
  2. MFRC522 RFID读卡器模块 x1
  3. TM1638 按键显示模块 x1
  4. 轻触开关(6x6mm) x4 (用于外置功能按钮)
  5. 10kΩ 电阻 x4 (用于按钮上拉)
  6. RFID卡片或钥匙扣 xN (根据泳者数量,建议使用防水卡片)
  7. 9V DC电源适配器 x1
  8. 面包板、杜邦线(公对公、公对母)若干

3.2 接线图与引脚定义

为了避免接线错误,强烈建议先在面包板上搭建测试电路。以下是各模块与Arduino Uno的引脚连接表:

MFRC522 RFID模块连接:

MFRC522引脚Arduino Uno引脚功能说明
SDA (SS)Digital 10片选/从机选择,可与其它SPI设备区分
SCKDigital 13SPI时钟线
MOSIDigital 11主机输出,从机输入
MISODigital 12主机输入,从机输出
IRQ不连接中断引脚,本项目未使用
GNDGND电源地
RSTDigital 9复位引脚
3.3V3.3V务必接3.3V!接5V会烧毁模块!

注意:MFRC522的工作电压是3.3V,其IO引脚也兼容5V,但电源引脚必须接3.3V。Arduino Uno的3.3V输出引脚可以提供约150mA电流,足够驱动该模块。

TM1638模块连接:TM1638采用类似SPI但更简单的3线串行协议,连接非常简洁。

TM1638引脚Arduino Uno引脚功能说明
VCC5V电源正极,该模块需5V供电
GNDGND电源地
STB (CS)Digital 7片选/使能引脚,低电平有效
CLKDigital 8时钟引脚
DIODigital 9数据输入/输出引脚

外置功能按钮连接:四个轻触开关一端分别接Arduino的数字引脚2, 3, 4, 5,另一端通过一个10kΩ的上拉电阻接至5V。同时,开关的这一端也直接连接到对应的数字引脚。这样,当按钮未按下时,引脚��过上拉电阻保持高电平;按下时,引脚接地变为低电平。

  • 按钮1 -> Digital 2 (清零泳者1)
  • 按钮2 -> Digital 3 (清零泳者2)
  • 按钮3 -> Digital 4 (显示总里程)
  • 按钮组合(7+8)-> Digital 5 (全部清零,实际用一个按钮)

实操心得:在焊接最终版电路时,建议使用排针和排母来连接Arduino和各个模块,方便日后调试和更换。对于外置按钮,可以使用带导线的微动开关,并将其用热缩管或防水盒封装,以适应泳池边潮湿的环境。

3.3 电源与布线注意事项

  1. 共地:所有模块的GND引脚必须连接到Arduino的GND,形成共同的参考地,这是电路正常工作的前提。
  2. 电源去耦:在Arduino的5V和GND之间,靠近板子电源输入的地方,可以并联一个100μF的电解电容和一个0.1μF的陶瓷电容,用于滤除电源噪声,提高系统稳定性,尤其是在数码管动态扫描时。
  3. 线缆整理:使用不同颜色的杜邦线区分电源(红色-5V/3.3V,黑色-GND)、数据线等,可以极大减少接线错误。对于最终成品,可以考虑制作一个简单的PCB或使用穿孔板来固定所有元件,使设备更牢固可靠。

4. 软件实现:从RFID识别到计数逻辑

硬件是骨架,软件才是灵魂。这一部分,我们将深入代码,看看如何让这些电子元件“活”起来,协同工作。

4.1 开发环境与库文件准备

首先,确保你安装了Arduino IDE。然后,需要导入两个关键的库:

  1. MFRC522库:用于驱动RFID读卡器。可以在Arduino IDE的“库管理器”中搜索“MFRC522”并安装。
  2. TM1638库:用于驱动显示模块。同样在库管理器中搜索“TM1638”,通常名为“TM1638”或“Grove 4-Digit Display”的库可能包含所需功能,但更推荐使用专为TM1638编写的库,如TM1638plus。如果库管理器没有,可以手动下载ZIP包,通过“项目” -> “加载库” -> “添加.ZIP库”导入。

4.2 核心代码结构解析

完整的代码较长,我将拆解其核心逻辑部分进行讲解。你可以在文章末尾找到完整代码的获取方式。

第一步:初始化与卡号绑定

#include <SPI.h> #include <MFRC522.h> #include <TM1638.h> // 根据实际库名调整 // 定义引脚 #define RST_PIN 9 #define SS_PIN 10 #define STB_PIN 7 #define CLK_PIN 8 #define DIO_PIN 9 MFRC522 mfrc522(SS_PIN, RST_PIN); // 创建RFID对象 TM1638 module(CLK_PIN, DIO_PIN, STB_PIN); // 创建显示模块对象 // 预定义授权卡号(需要替换成你自己的卡号) byte authorizedCard1[4] = {0xEA, 0x51, 0x56, 0xD3}; // 示例卡号1 byte authorizedCard2[4] = {0x82, 0xA5, 0x56, 0xD3}; // 示例卡号2 // 泳者数据 int swimmer1Laps = 0; int swimmer2Laps = 0; float poolLength = 25.0; // 泳池长度,单位:米

程序开头,我们引入了必要的库,定义了硬件连接的引脚,并初始化了RFID和TM1638对象。最关键的是authorizedCard1authorizedCard2这两个数组,它们存储了合法RFID卡的UID(唯一标识符)。如何获取你自己卡片的UID?这就需要用到我们提供的第二个小程序lector_tarjeta_RFID.ino。将它烧录到Arduino,打开串口监视器(波特率9600),用卡片靠近读卡器,串口就会打印出卡片的UID,将其替换到上述数组中即可。

第二步:RFID读取与匹配逻辑loop()函数中,我们需要周期性地检查是否有新卡片。

void loop() { // 检查是否有新卡片 if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) { // 读取卡片的UID byte* uid = mfrc522.uid.uidByte; byte uidSize = mfrc522.uid.size; // 与授权卡号比对 if (compareUID(uid, authorizedCard1, uidSize)) { // 匹配卡1 swimmer1Laps++; updateDisplay(1); // 更新显示为泳者1的数据 Serial.println("Swimmer 1 lap counted!"); } else if (compareUID(uid, authorizedCard2, uidSize)) { // 匹配卡2 swimmer2Laps++; updateDisplay(2); Serial.println("Swimmer 2 lap counted!"); } else { // 未授权卡片 Serial.println("Unauthorized card!"); module.setLEDs(0b11111111); // 让所有LED闪烁以示警告 delay(500); module.setLEDs(0); } // 停止本次读卡 mfrc522.PICC_HaltA(); } // ... 其他逻辑(按钮扫描等) }

compareUID是一个自定义函数,用于逐字节比较两个UID数组是否完全相同。这里有一个重要的防误触设计PICC_IsNewCardPresent()PICC_ReadCardSerial()的组合,确保了只有一张新卡片被完整读取时才会触发计数,避免了卡片在感应区晃动导致的重复计数。

第三步:按钮扫描与功能实现我们需要同时扫描TM1638的8个内置键和4个外置功能键。

// 扫描TM1638按键 byte keys = module.getButtons(); for (byte i = 0; i < 8; i++) { if (keys & (1 << i)) { switch(i) { case 0: // 假设按键0对应选择泳者1 currentSwimmer = 1; updateDisplay(1); break; case 1: // 按键1对应选择泳者2 currentSwimmer = 2; updateDisplay(2); break; case 2: // 按键2清零当前显示的泳者 if (currentSwimmer == 1) swimmer1Laps = 0; else if (currentSwimmer == 2) swimmer2Laps = 0; updateDisplay(currentSwimmer); break; // ... 其他按键功能 } delay(200); // 简单按键防抖 } } // 扫描外置功能按钮(使用digitalRead) if (digitalRead(BUTTON_TOTAL_PIN) == LOW) { delay(50); // 防抖延时 if (digitalRead(BUTTON_TOTAL_PIN) == LOW) { showTotalMeters(); // 显示总里程函数 while(digitalRead(BUTTON_TOTAL_PIN) == LOW); // 等待按键释放 } } // ... 其他外置按钮扫描

TM1638的getButtons()函数可以一次性读取8个按键的状态,非常高效。外置按钮则使用digitalRead配合软件防抖(延时再检测)来确保每次按下只触发一次动作。showTotalMeters()函数会计算(swimmer1Laps + swimmer2Laps) * poolLength并显示在数码管上。

第四步:显示更新函数updateDisplay()函数负责将选定泳者的圈数和换算成的里程,格式化后显示在TM1638的数码管上。

void updateDisplay(int swimmer) { module.clearDisplay(); int laps = (swimmer == 1) ? swimmer1Laps : swimmer2Laps; float meters = laps * poolLength; // 显示圈数,例如 "LAP: 25" module.setDisplayToString("LAP" + String(laps)); // 短暂延时后显示里程,例如 "M: 625.0" delay(1500); module.clearDisplay(); module.setDisplayToString("M:" + String(meters, 1)); // 保留一位小数 }

TM1638库通常提供setDisplayToString或类似函数,可以直接显示字符串。注意,它可能不支持所有字符,需要参考具体库的文档。

5. 系统调试、优化与功能扩展

代码烧录进去,硬件连接完毕,并不意味着项目结束。真正的挑战往往从第一次通电开始。这一章,我们来解决那些实际运行中可能出现的问题,并探讨如何让这个计数器变得更聪明、更好用。

5.1 常见问题排查实录

在调试过程中,我遇到了以下几个典型问题,希望你的搭建过程能因此更顺利:

  1. RFID模块完全无反应

    • 现象:卡片靠近无任何反应,串口无输出。
    • 排查
      • 电压:首先用万用表确认MFRC522的VCC引脚电压是否为3.3V。这是最常犯的错误。
      • 接线:逐根检查SPI总线(MISO, MOSI, SCK)以及SS和RST引脚是否与代码定义、实际连接一致。特别是SCK(13引脚)和MISO(12引脚)容易接反。
      • 库文件:确认安装了正确的MFRC522库,并且代码中#include <MFRC522.h>#include <SPI.h>都已包含。
      • 串口监视器:打开Arduino IDE的串口监视器,波特率设置为9600,查看是否有初始化成功的提示信息。
  2. TM1638显示乱码或不亮

    • 现象:数码管显示奇怪的符号,或者完全不亮。
    • 排查
      • 电源:TM1638需要5V供电,检查VCC引脚电压。
      • 接线:STB, CLK, DIO三根线是否接错。可以尝试交换CLK和DIO试试(不同库或模块定义可能略有不同)。
      • 库函数:你使用的TM1638库的API可能与示例代码不同。仔细阅读库的说明,确认初始化对象和显示函数的正确用法。例如,有些库需要调用module.setupDisplay(true, 7)来设置亮度。
  3. 刷卡计数延迟或重复计数

    • 现象:刷一次卡,计数增加多次;或者刷卡后要等一会儿才计数。
    • 优化
      • 防重复触发:确保在成功处理一次刷卡事件后,调用mfrc522.PICC_HaltA()让卡片进入休眠状态。同时,在主循环loop()中,一次刷卡处理完成前,不要再次进入读卡判断。
      • 引入“冷却时间”:可以为每个泳者设置一个简单的防重入时间戳。记录上次刷卡成功的时间,在接下来500毫秒内,忽略同一张卡的刷卡事件。
      unsigned long lastSwipeTime[2] = {0, 0}; // 泳者1和2的最后刷卡时间 const unsigned long cooldown = 500; // 冷却时间500ms if (compareUID(uid, authorizedCard1, uidSize)) { if (millis() - lastSwipeTime[0] > cooldown) { swimmer1Laps++; lastSwipeTime[0] = millis(); updateDisplay(1); } }
  4. 按钮响应不灵或连击

    • 现象:按一下按钮,执行了多次操作。
    • 解决:这就是按键抖动。除了代码中简单的delay(50)再检测,更可靠的方法是使用状态机或millis()进行非阻塞式的防抖处理,这能保证系统响应更及时。
    // 更健壮的按钮检测示例(非阻塞) int buttonState = HIGH; int lastButtonState = HIGH; unsigned long lastDebounceTime = 0; const unsigned long debounceDelay = 50; int reading = digitalRead(buttonPin); if (reading != lastButtonState) { lastDebounceTime = millis(); // 重置防抖计时器 } if ((millis() - lastDebounceTime) > debounceDelay) { if (reading != buttonState) { buttonState = reading; if (buttonState == LOW) { // 执行按钮按下动作 } } } lastButtonState = reading;

5.2 功能优化与扩展思路

基础版本实现后,你可以根据实际需求进行优化和扩展:

  1. 数据持久化:目前数据断电即丢失。可以增加一个AT24Cxx系列的EEPROM模块,在每次计数变化时将数据写入,上电时读取。这样训练中途断电也不怕数据丢失。
  2. 无线数据传输:增加一个ESP-01s WiFi模块或HC-05蓝牙模块,将游泳数据实时同步到手机APP或云端服务器,便于长期统计和分析。
  3. 多泳道/多泳者支持:当前设计支持2名泳者。你可以通过扩展RFID读卡器数量(使用不同的SS引脚)或使用支持多标签识别的RFID读卡器,来支持更多泳者。TM1638的显示可以通过模式切换来轮播不同泳者的数据。
  4. 训练模式:在代码中预设几种训练计划(如“10组100米间歇游”),设备可以通过LED或蜂鸣器提示休息间隔和组数完成情况。
  5. 防水封装:这是投入实际使用的关键。可以使用防水接线盒封装整个电子部分,RFID读卡器天线部分用环氧树脂胶做防水处理,外置按钮选用防水型开关。

5.3 从原型到产品的思考

如果你希望这个设备更加稳固耐用,可以考虑以下步骤:

  • PCB设计:使用Eagle或KiCad等软件将面包板电路转化为一块定制PCB,能大大提高稳定性和美观度。
  • 电源管理:改用更大容量的锂电池配合充电管理电路,实现无线便携。
  • 结构设计:使用3D打印或亚克力切割,为设备制作一个专属外壳,将屏幕、读卡区、按钮合理布局。

这个项目从想法到实现,贯穿了需求分析、硬件选型、电路搭建、软件编程、调试优化整个嵌入式开发流程。它不仅仅是一个计数器,更是一个如何将具体问题转化为技术方案,并一步步实现的完整案例。希望这份详细的拆解,能帮助你成功复现它,甚至激发灵感,创造出更适合自己需求的智能训练装备。

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

车载大型导弹发射装置电驱动快速垂直起竖技术解析【附仿真】

✨ 长期致力于多电化、垂直起竖系统、恒功率控制、运动规划、弱磁控制、模糊PID、储能控制研究工作&#xff0c;擅长数据搜集与处理、建模仿真、程序编写、仿真设计。 ✅ 专业定制毕设、代码 ✅ 如需沟通交流&#xff0c;点击《获取方式》 &#xff08;1&#xff09;单级电动缸…

作者头像 李华
网站建设 2026/6/4 23:43:41

用快马ai快速原型:十分钟搭建python图形化下载管理器

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 请生成一个基于python的图形界面文件下载管理器项目代码&#xff0c;要求包含以下核心功能&#xff1a;支持通过输入url链接下载文件&#xff0c;能显示下载进度条和实时速度&…

作者头像 李华
网站建设 2026/6/4 23:40:30

如何用AI智能视频剪辑工具FunClip轻松完成专业级视频处理?

如何用AI智能视频剪辑工具FunClip轻松完成专业级视频处理&#xff1f; 【免费下载链接】FunClip Open-source, accurate and easy-to-use video speech recognition & clipping tool. LLM-based AI clipping integrated. 项目地址: https://gitcode.com/GitHub_Trending/…

作者头像 李华
网站建设 2026/6/4 23:37:21

BOBST 0701238504处理器模块

BOBST 0701238504处理器模块是Bobst设备控制系统的运算核心&#xff0c;负责执行用户程序、处理实时数据并协调各子系统的协同工作&#xff0c;其性能直接影响整机的运行效率。产品特点采用32位高速处理器芯片&#xff0c;运算能力强劲专为BOBST 0701238504系统架构设计主频稳定…

作者头像 李华
网站建设 2026/6/4 23:37:21

终极PSD智能分层指南:3分钟将任何插画变为可编辑图层

终极PSD智能分层指南&#xff1a;3分钟将任何插画变为可编辑图层 【免费下载链接】layerdivider A tool to divide a single illustration into a layered structure. 项目地址: https://gitcode.com/gh_mirrors/la/layerdivider 还在为单图层插画无法单独编辑而烦恼吗&…

作者头像 李华