公司动态
基于Arduino的紫外线与红外传感器验钞装置设计与实现
1. 项目缘起为什么需要自己动手验钞在电子支付普及的今天现金交易似乎变少了但验钞的需求并没有消失。无论是经营小本生意、处理家庭财务还是出于对技术的好奇能快速、可靠地识别纸币真伪依然是一个很实用的技能。市面上的验钞机体积不小价格也从几十到几百不等对于偶尔使用或者想深入了解其原理的爱好者来说可能并不是最优选择。这个项目的核心就是利用Arduino这个开源硬件平台结合紫外线UV和红外IR传感器自己动手搭建一个低成本、高可靠性的验钞装置。它背后的原理其实和商用验钞机是相通的真钞在特定位置使用了特殊的荧光油墨和红外吸收材料这些是普通复印或打印难以完美复制的防伪特征。通过检测这些特征我们就能做出有效的判断。我最初想做这个是因为手头有一些闲置的Arduino Uno和传感器模块总想折腾点既有趣又有实际用途的东西。网上关于单个传感器验钞的教程不少但将UV和IR结合起来并给出完整软硬件方案及避坑指南的并不多。这次我就把从元器件选型、电路连接、代码编写到实际测试调优的全过程以及中间踩过的那些“坑”毫无保留地分享出来。无论你是刚接触Arduino的新手还是想找一个综合性小项目练手的老玩家相信都能从中获得可以直接“抄作业”的干货。2. 核心传感器选型与工作原理深度解析验钞的核心在于检测真钞的物理防伪特征我们主要依赖两种传感器紫外线UV传感器和红外IR传感器。选对传感器项目就成功了一半。2.1 紫外线UV传感器捕捉荧光印记真钞在印刷时会在特定区域如面额数字、图案使用一种特殊的荧光油墨。这种油墨在普通可见光下看起来与普通油墨无异但在特定波长的紫外线通常是365nm左右的UVA照射下会激发出明亮的可见光通常是蓝白色或绿色这种现象称为荧光反应。而绝大多数假钞使用的是普通印刷油墨或劣质荧光粉要么没有荧光反应要么荧光颜色、亮度、图案与真钞有显著差异。传感器选型建议市面上常见的Arduino兼容UV传感器模块主要有两种GUVA-S12SD UV传感器模块这类模块实际上是一个紫外光电二极管输出的是与紫外线强度成比例的模拟电压值。它的优点是价格非常低廉通常几块钱可以量化环境中的紫外线强度。但用于验钞有一个致命缺点它检测的是环境中的紫外光强度而不是物体被照射后激发的荧光。你需要用额外的紫外LED灯去照射钞票然后用这个传感器去“看”反射回来的紫外光不这思路错了。真钞的荧光油墨是吸收紫外光发出可见光它并不强烈反射紫外光。因此这类传感器不适合直接用于检测荧光图案更适合做紫外线指数监测。紫外线感应管/荧光检测传感器模块这才是我们需要的正确选择。这种模块通常集成了一个紫外LED发射约365nm紫外线和一个对可见光敏感的光敏电阻或光电三极管。其工作原理是模块上的紫外LED发出紫外线照射被测物体 - 如果物体含有荧光物质如真钞则激发出可见光 - 模块上的光敏元件检测到这部分可见光输出信号变化。这种模块的输出通常是数字开关量高/低电平或模拟量直接反映了“有无荧光反应”及“荧光强度”。避坑指南购买时一定要和卖家确认模块是用于“荧光检测”的而不是单纯测量环境紫外强度的。描述中带有“纸币检测”、“验钞”关键词的模块通常就是前者。我最初就买错了用GUVA-S12SD折腾半天也没反应。工作原理连线示意图以数字输出模块为例VCC - Arduino 5VGND - Arduino GNDOUT - Arduino 数字引脚 (如 D2) 当检测到足够强度的荧光时OUT引脚输出低电平或高电平依模块设计而定否则输出相反电平。2.2 红外IR传感器透视图案与材质红外防伪是另一项关键技术。真钞的纸张和部分油墨对红外光具有特殊的吸收和透射特性。具体表现为红外透射用红外LED照射钞票在另一侧用红外接收管观察真钞的某些图案会“消失”或变淡因为该区域油墨允许红外光透过而假钞的普通碳基油墨会强烈吸收红外光图案依然清晰。红外吸收另一种方式是真钞的某些区域使用了对红外光有强烈吸收的特殊油墨在红外光照射下这些区域会比周围更“暗”。我们的项目采用更常见的“反射式”红外检测即发射和接收在同一侧。传感器选型建议TCRT5000红外反射传感器模块这是最通用、廉价的选择。它内部集成了一个红外发射二极管和一个红外接收三极管。模块输出可以是模拟量AO反映反射回来的红外光强度也可以是数字量DO通过一个电位器设定阈值当反射强度高于/低于阈值时输出电平翻转。专用红外验钞传感器对管有些模块会将红外发射管和接收管分离需要自己配对和安装精度可能更高但电路稍复杂。对于验钞我们关心的是钞票上特定区域如红外标记处对红外光的反射率是否在真钞的标准范围内。真钞的反射率是特定的而假钞往往不同。工作原理与连线我们使用TCRT5000的模拟输出AO。VCC - Arduino 5VGND - Arduino GNDAO - Arduino 模拟输入引脚 (如 A0) 将传感器对准钞票上的红外特征区域如某些钞票人像旁边的花纹读取模拟值。真钞会得到一个特定的读数范围假钞的读数通常会落在这个范围之外。2.3 传感器安装与机械结构考量如何让钞票稳定地通过传感器检测区域是影响检测可靠性的关键。这里有几个方案固定式检测推荐给初学者将UV和IR传感器并排固定在一个暗盒中两者相距1-2厘米。检测时手动将钞票上需要检测的部位如荧光区和红外标记区依次滑过两个传感器的正下方。这种方式最简单不需要复杂的机械结构适合验证原理和少量检测。滑槽式检测用亚克力板或黑色卡纸制作一个带有狭窄滑槽的暗盒。传感器安装在滑槽下方。将钞票插入滑槽并匀速拉动传感器即可依次检测。这能保证检测位置相对固定重复性好。滚筒式自动进钞进阶如果想实现接近商用验钞机的自动化可以考虑用小电机驱动橡胶轮模拟进钞机构。但这涉及到机械设计、电机控制、速度同步等更复杂的问题除非你对机械部分特别感兴趣否则初期不建议尝试。一个重要的经验无论采用哪种结构遮光都非常重要环境光尤其是日光灯也含有微量紫外和太阳光会严重干扰传感器的读数。务必为传感器检测区域制作一个密闭或半密闭的暗盒内部最好贴上黑色哑光胶带以减少反光。3. 硬件电路搭建与Arduino连接详解有了前面的理论铺垫现在我们来动手搭建电路。这个项目的电路部分并不复杂但细节决定成败。3.1 所需元器件清单主控Arduino Uno R3 (或兼容板) x1UV荧光检测模块(数字或模拟输出) x1红外反射传感器(TCRT5000带模拟输出) x1显示与交互OLED显示屏 (I2C接口128x64) x1 — 用于显示状态和结果比LCD更省电、清晰。绿色LED x1红色LED x1 — 直观显示通过/失败。220欧姆电阻 x2 — 用于限流保护LED。轻触开关 x1 — 用作开始检测的按钮。电源USB线供电或外部7-12V DC电源通过Arduino的电源插座供电。其他面包板、杜邦线公对公、公对母若干、用于制作暗盒的材料如黑色亚克力板、卡纸。3.2 电路连接图与引脚定义为了避免接线错误请严格按照下表连接元器件引脚连接至 Arduino 引脚说明UV传感器模块VCC5V供电GNDGND接地OUT (或 AO)数字 D2(若为模拟输出则接A1)检测信号输出TCRT5000模块VCC5V供电GNDGND接地AO模拟 A0红外反射强度模拟信号OLED显示屏VCC5V供电GNDGND接地SCL模拟 A5I2C时钟线SDA模拟 A4I2C数据线绿色LED长脚 (阳极)数字 D3通过220Ω电阻连接短脚 (阴极)GND直接接地红色LED长脚 (阳极)数字 D4通过220Ω电阻连接短脚 (阴极)GND直接接地轻触开关一脚数字 D5信号端对角脚GND接地(另外两脚内部连通可空)接线心得强烈建议先在面包板上搭建测试电路确认所有功能正常后再考虑焊接或使用洞洞板制作永久版本。接线时电源5V和GND务必连接牢固接触不良是大多数诡异问题的根源。给数字引脚连接的LED别忘了串联一个220欧姆的限流电阻直接接上去可能会烧毁LED或损坏Arduino引脚。3.3 供电与抗干扰注意事项供电如果使用电脑USB供电通常足够驱动所有传感器和OLED。但如果红外发射管功率较大或未来添加电机建议使用9V电池或稳压电源通过Arduino的DC插座供电以提供更稳定的电流。信号干扰数字传感器和模拟传感器最好分开布置。模拟传感器如TCRT5000的AO的读数极易受到电源噪声干扰。如果发现红外读数跳动剧烈可以尝试在Arduino的5V和GND之间靠近模拟传感器电源引脚处并联一个10uF-100uF的电解电容和一个0.1uF的瓷片电容用于滤波。I2C上拉电阻大多数OLED模块内部已经集成了I2C总线的上拉电阻通常为4.7kΩ或10kΩ。如果你的模块没有或者通信不稳定需要在SDA和SCL线上分别外接一个4.7kΩ电阻到5V。4. 软件程序设计从数据采集到智能判断硬件是躯体软件是灵魂。我们的程序需要完成传感器数据读取、阈值判断、逻辑综合和结果展示。4.1 开发环境与库准备我们使用最经典的Arduino IDE进行开发。需要提前安装以下库Adafruit_SSD1306和Adafruit_GFX用于驱动OLED显示屏。可以通过Arduino IDE的“库管理器”直接搜索安装。可选如果你使用的UV模块是模拟输出并且想进行更复杂的信号处理可能不需要额外库。项目代码结构规划setup(): 初始化串口、引脚模式、OLED显示屏。loop(): 循环检测按钮状态。当按钮按下时执行checkBanknote()函数。checkBanknote(): 核心检测函数。依次读取UV和IR传感器数据与预设阈值比较综合判断并控制LED和OLED显示结果。4.2 核心检测算法与阈值标定这是整个项目最关键的环节。阈值不是凭空想象的必须通过实验标定。步骤一采集真钞样本数据准备几张确定是真的、不同新旧程度的钞票。将它们分别放在传感器下在loop函数中连续读取并打印传感器值到串口监视器。void loop() { int uvValue digitalRead(UV_PIN); // 假设UV是数字输出 int irValue analogRead(IR_PIN); // 读取红外模拟值 Serial.print(UV: ); Serial.print(uvValue); Serial.print( | IR: ); Serial.println(irValue); delay(500); // 慢点打印方便观察 }对于数字UV模块观察当真钞荧光区划过时输出是0还是1根据模块说明。记录这个状态。 对于模拟IR传感器观察真钞红外特征区域下的读数范围。例如你可能会发现真钞的读数稳定在200-400之间这个值取决于传感器距离、钞票颜色等仅举例。步骤二采集假钞或普通白纸数据用假钞如果有或普通白纸、复印纸进行同样操作。记录它们的传感器读数。步骤三确定阈值分析数据找到一个能最大程度区分真伪的阈值。UV判断如果真钞有荧光输出LOW假钞无荧光输出HIGH那么判断逻辑就是检测是否为LOW。IR判断假设真钞读数在IR_TRUE_MIN和IR_TRUE_MAX之间假钞读数可能小于IR_TRUE_MIN或大于IR_TRUE_MAX。我们可以设定一个安全区间。步骤四编写判断函数基于以上分析checkBanknote()函数内部逻辑如下bool checkUV() { // 假设有荧光时模块输出LOW if(digitalRead(UV_PIN) LOW) { return true; // 通过UV检测 } else { return false; // 未通过UV检测 } } bool checkIR() { int irReading analogRead(IR_PIN); const int IR_TRUE_MIN 250; const int IR_TRUE_MAX 450; if(irReading IR_TRUE_MIN irReading IR_TRUE_MAX) { return true; // 通过IR检测 } else { return false; // 未通过IR检测 } } void checkBanknote() { bool uvPass checkUV(); bool irPass checkIR(); // 综合判断逻辑两者都需通过 if(uvPass irPass) { // 真钞 digitalWrite(GREEN_LED, HIGH); digitalWrite(RED_LED, LOW); displayResult(PASS, Genuine Note); } else { // 假钞 digitalWrite(GREEN_LED, LOW); digitalWrite(RED_LED, HIGH); displayResult(FAIL, Check Failed); // 可以进一步提示是哪项失败 if(!uvPass) displayDetail(UV Fail); if(!irPass) displayDetail(IR Fail); } }4.3 OLED显示与用户交互优化良好的用户界面能极大提升使用体验。我们使用OLED来显示实时状态和最终结果。#include Adafruit_SSD1306.h #include Adafruit_GFX.h #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, Wire, OLED_RESET); void setup() { // ... 其他初始化 if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // 地址可能是0x3C或0x3D Serial.println(F(SSD1306 allocation failed)); for(;;); // 死循环初始化失败 } display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0,0); display.println(Banknote Checker); display.println(Ready...); display.display(); } void displayResult(const char* title, const char* msg) { display.clearDisplay(); display.setCursor(0,0); display.setTextSize(2); display.println(title); display.setTextSize(1); display.setCursor(0, 30); display.println(msg); display.display(); } void displayDetail(const char* detail) { display.setTextSize(1); display.setCursor(0, 50); display.println(detail); display.display(); }在loop中可以增加一个“待机界面”显示当前传感器实时值或等待按钮按下的提示。5. 系统集成、测试与调优实战把所有部分组装起来才是真正的挑战开始。这一步会遇到很多理论上遇不到的问题。5.1 组装与初步通电测试机械组装按照之前的设计将UV和IR传感器固定在暗盒内。确保它们的探测面朝向一致且距离待测钞票表面的距离合适通常2-5mm参考传感器数据手册。距离太远信号弱太近可能刮擦钞票。可以用小块亚克力或塑料片作为导槽限定钞票高度。电路集成将面包板上的电路仔细检查一遍然后通电。此时先不要放钞票。上电自检编写一个自检程序让所有LED闪烁一遍OLED显示欢迎信息并打印各传感器初始值到串口。确保所有硬件都被正确识别和初始化。5.2 传感器阈值现场校准与调试即使你之前标定了阈值在新的物理结构下环境光泄露、传感器安装角度微小差异都会影响读数。因此现场校准是必须的。编写校准模式在代码中增加一个“校准模式”可以通过按住按钮上电或通过串口发送特定指令进入。在校准模式下程序循环读取传感器值并显示在OLED和串口上但不做判断。采集背景噪声不放任何东西记录下UV和IR的读数。这是环境背景值。采集真钞基准放入一张较新的真钞确保荧光区和红外区正对传感器。缓慢移动钞票观察读数变化找到最稳定、最具代表性的值范围。将这个范围记录下来更新代码中的阈值常量IR_TRUE_MIN,IR_TRUE_MAX。对于数字UV模块确认触发状态。测试多张真钞用不同新旧程度、不同面额如果设计支持多面额的真钞测试观察阈值是否依然有效。可能需要放宽阈值范围以适应磨损。测试干扰物用白纸、杂志彩页、塑料片等测试确保它们会被判定为“失败”。如果某种干扰物意外“通过”了某一项检测就需要调整阈值或增加检测逻辑例如增加检测时间窗口内的信号稳定性判断。5.3 常见问题排查与解决方案在实际测试中你几乎一定会遇到下面这些问题问题1UV传感器始终无反应常亮或常灭。排查首先确认模块类型是否正确是荧光检测不是环境光检测。用一张含有荧光增白剂的A4纸在紫外灯下会亮白测试看是否有反应。检查接线是否正确紫外LED是否亮起有些模块的LED肉眼可见微弱紫光切勿直视。解决确保检测环境足够暗。调整传感器与钞票的距离。如果模块有灵敏度电位器尝试调节它。问题2IR传感器读数不稳定跳动很大。排查这是最常见的问题。首先检查供电是否稳定尝试在传感器电源端并联滤波电容。其次红外光可能受到环境光中的红外成分如太阳光、白炽灯干扰务必做好遮光。最后被测物表面是否平整褶皱的钞票会导致反射距离变化。解决加强暗盒的遮光性。在软件上可以采用“多次采样取平均值”或“中值滤波”来稳定读数。例如int readStableIR(int samples) { int sum 0; for(int i0; isamples; i) { sum analogRead(IR_PIN); delay(5); // 短暂延时避免读取过快 } return sum / samples; }问题3真钞偶尔被判为假或反之。排查这是阈值设置不合理或检测时机不对。可能钞票没有放到位或者传感器检测到了边界。解决引入“检测窗口”概念。在按钮按下后程序持续采样一小段时间比如500毫秒在这段时间内只有UV和IR信号持续满足条件才判定为通过。这比单次采样要稳定得多。同时可以适当放宽红外阈值范围以容纳钞票的个体差异和磨损。问题4OLED显示乱码或不显示。排查检查I2C地址是否正确常用0x3C或0x3D尝试在begin()函数中更换地址。检查SDA和SCL线是否接反A4接SDAA5接SCL。检查是否安装了正确的库。解决参考Adafruit SSD1306库的示例代码进行测试。5.4 性能优化与功能扩展思路基础功能稳定后可以考虑以下优化和扩展多国货币支持不同国家的钞票其荧光图案位置、红外特征可能不同。可以在代码中定义不同的“检测方案”通过拨码开关或按钮循环切换。面额识别进阶通过检测荧光图案的形状、位置或者结合多个IR传感器的读数模式理论上可以区分不同面额。这需要更复杂的图案识别算法甚至简单的机器学习可以作为一个长期的挑战项目。数据记录与统计增加一个SD卡模块将每次检测的结果时间、传感器原始值、判断结果记录下来便于后期分析和审计。电池供电与低功耗如果希望做成便携设备可以改用Arduino Pro Mini等低功耗主板并优化代码在待机时关闭传感器和OLED仅由按钮中断唤醒大幅延长电池续航。使用Visuino进行图形化编程如果对编写代码感到困难可以尝试使用Visuino这款图形化Arduino编程软件。你可以通过拖放组件如Digital Filter, Analog Filter, Logic等来处理传感器信号和实现判断逻辑它会自动生成Arduino代码。这对于快速原型设计和理解信号流非常有帮助。不过对于复杂的逻辑和优化最终可能还是需要直接修改代码。这个项目从原理到实践涵盖了硬件选型、电路搭建、软件编程和系统调试的全过程。它不仅仅是一个验钞工具更是一个非常好的传感器应用、信号处理和嵌入式系统综合练习。最关键的一步就是拿起手边的元件开始动手搭建。过程中遇到的所有问题都会让你对“信号”、“阈值”、“抗干扰”这些概念有更深的理解。祝你制作顺利