基于红外传感器与Arduino的防瞌睡眼镜DIY:从原理到实践
2026/5/31 17:45:54 网站建设 项目流程

1. 项目概述与核心思路

最近在整理工作室的旧项目时,翻出了一个几年前做的防瞌睡眼镜原型。这玩意儿虽然看起来简陋,但核心思路非常直接有效,特别适合电子爱好者入门或学生做课程设计。它的目标很简单:在你开车、值夜班或者任何需要保持清醒但容易犯困的场景下,通过检测你是否“闭上了眼睛”,及时用振动和声音把你“叫醒”。

这个项目的核心,就是利用一个极其常见的元件——红外传感器。你可能在自动感应水龙头、商场自动门或者扫地机器人上见过它。我们这次把它“请”到眼镜框上,让它对准你的眼睛。当你的眼睛睁开时,眼球表面会反射红外光;一旦你因困倦而长时间闭眼,反射信号就会消失或减弱。Arduino微控制器负责捕捉这个变化,并判断你是否进入了“瞌睡状态”,一旦确认,就会驱动一个振动马达(模拟手机振动)和一个蜂鸣器同时工作,通过触觉和听觉双重刺激来提醒你。

整个制作过程不复杂,成本也低,但涉及了传感器应用、微控制器编程和简单的机械组装,是一个综合性很强的练手项目。下面,我就把这个项目的完整制作过程、背后的原理、编程细节以及我踩过的几个坑,毫无保留地分享出来。

2. 核心元件选型与原理深度解析

2.1 红外传感器:项目的“眼睛”

我们用的红外传感器模块,通常是一个集成了发射管、接收管和信号处理电路的小板子。市面上最常见的是那种三四块钱一个的“红外避障传感器”或“红外反射传感器”。它上面一般有三个引脚:VCC(电源正极)、GND(电源负极)和 OUT(信号输出)。

它是怎么“看见”你闭眼的?

  1. 发射:传感器上的红外发射管(像个小LED)会持续发出人眼不可见的红外光。
  2. 反射:当这束光照射到物体(比如你睁开的眼球)表面时,一部分光会被反射回来。
  3. 接收与判断:传感器上的红外接收管(一个对红外光敏感的光电元件)会捕捉这些反射光,并将其转换为微弱的电信号。模块内部的比较器电路会将这个信号与一个预设的“阈值”进行比较。
  4. 输出:当反射光足够强(意味着物体很近,反射率高),接收信号超过阈值,模块的OUT引脚通常会输出低电平(LOW,接近0V);当没有足够反射光(物体太远或没反射,比如闭眼时眼皮吸收/漫反射了大部分光),OUT引脚则输出高电平(HIGH,比如5V或3.3V)。

注意:不同厂家、不同型号的传感器,其输出逻辑可能相反(即检测到物体输出高电平)。务必在使用前用代码简单测试一下,拿手在传感器前晃动,观察串口输出的信号变化。这是第一个容易踩坑的地方。

为什么选它?相比摄像头方案,红外传感器成本极低、响应速度快、功耗相对较小,且数据处理简单,非常适合这种单一功能的检测场景。而且,红外光不受可见光环境变化的影响,在白天黑夜都能稳定工作。

2.2 微控制器:项目的大脑——Arduino Pro Mini

原项目使用了Arduino Pro Mini,这是一个非常精简的型号,去掉了USB转串口芯片,所以体积小、价格便宜。它的核心是一颗ATmega328P单片机,和经典的Arduino Uno主控芯片一样,只是封装和外围电路更简洁。

选型考量:

  • 尺寸与集成度:Pro Mini体积小巧,非常适合集成到眼镜这样空间有限的穿戴设备上。
  • 功耗:在3.3V版本下,其运行功耗可以比Uno低不少,有利于电池供电。
  • 成本:比Uno便宜。

给新手的提醒:Pro Mini没有内置USB接口,编程时需要借助一个“USB转TTL串口模块”或者另一块Arduino Uno作为编程器。这对初学者可能是个小门槛,但操作一次就会了,下文会详细讲。

备选方案:如果你手头只有Arduino Nano,完全可以替代。Nano集成了USB芯片,用一根Micro USB线就能编程,方便很多,只是体积稍大一点点。电路连接逻辑完全一致。

2.3 执行机构:如何有效“唤醒”

警报需要足够引起注意,但又不能过于吓人。我们采用了双模警报:

  1. 振动马达:从旧手机或寻呼机上拆下来的扁平振动电机。它的作用是提供触觉提醒,通过皮肤直接传递振动信号,在嘈杂环境中或戴耳机时尤其有效。
  2. 有源蜂鸣器:就是那种给电就持续响的“滴滴”声蜂鸣器。提供听觉提醒,声音尖锐,穿透力强。

驱动电路:Arduino的IO引脚驱动能力有限(通常每个引脚最大输出20mA左右),无法直接驱动振动马达(工作电流可能达到50-100mA)和蜂鸣器。因此,我们使用一个最常见的NPN型三极管BC547作为电子开关。

  • 工作原理:当Arduino的D3引脚输出高电平(5V)时,电流通过一个限流电阻(如4.7kΩ)流入三极管的基极(B),三极管导通,相当于开关闭合,振动马达和蜂鸣器的负极被接通到GND,形成回路,开始工作。当D3输出低电平(0V)时,三极管关闭,警报停止。
  • 为什么加电阻?基极限流电阻(4.7kΩ)必不可少,用于防止过大的基极电流烧毁三极管或Arduino引脚。其阻值根据三极管的放大倍数和负载电流计算而来,4.7kΩ是一个常用值,能确保三极管饱和导通。

2.4 电源与其它

  • 电池:项目使用一块3.7V的锂电池(如常见的10440或手机旧电池)。Arduino Pro Mini有低压差稳压器,3.7V输入足以让其稳定工作在5V逻辑电平(对于5V版本的Pro Mini)。如果使用3.3V版本的Pro Mini,则匹配得更好。
  • 眼镜框:任何一副旧眼镜框或便宜的平光镜框都可以,最好是塑料材质,方便粘贴和打孔。

3. 电路设计与焊接实操要点

3.1 电路连接图与解析

虽然原文没有提供标准的原理图,但根据描述,我们可以整理出清晰的连接关系。整个系统可以看作两个部分:传感输入部分警报驱动部分

传感输入部分:

  • 红外传感器
    • VCC引脚 → 接 Arduino Pro Mini 的VCC引脚。
    • GND引脚 → 接 Arduino Pro Mini 的GND引脚。
    • OUT引脚 → 接 Arduino Pro Mini 的模拟引脚A1(这里用作数字输入)。接模拟引脚的好处是,万一你想后期改成读取模拟值做更精细的判断,无需改硬件。

警报驱动部分:

  • 三极管开关电路
    • Arduino Pro Mini 的D3引脚 → 接一个4.7kΩ电阻的一端。
    • 4.7kΩ电阻的另一端 → 接 NPN三极管BC547的基极(B)。
    • 三极管BC547的发射极(E) → 接 Arduino Pro Mini 的GND
    • 三极管BC547的集电极(C) → 同时连接振动马达的负极有源蜂鸣器的负极
    • 振动马达的正极有源蜂鸣器的正极→ 并联后,接 Arduino Pro Mini 的VCC
  • 电源
    • 电池正极 → 接 Arduino Pro Mini 的RAW引脚(如果电池电压在3.5V-12V之间)或VCC引脚(如果电池是精确的5V或3.3V且Pro Mini对应版本)。通常锂电池接RAW
    • 电池负极 → 接 Arduino Pro Mini 的GND引脚。
  • 开关:在电池正极到ArduinoRAW/VCC之间串联一个轻触开关或拨动开关,用于控制总电源。

实操心得:在面包板上先搭建整个电路并测试功能,确认一切正常后再进行焊接。焊接时,尽量使用细导线(如网线里的单股铜丝)和热缩管,这样成品更轻便、整洁。给振动马达焊接导线时,由于其不断振动,焊点容易脱落,一定要焊牢固,并点上一点热熔胶或环氧胶加固。

3.2 元件布局与眼镜改装

这是将电子项目变成可穿戴设备的关键一步,目标是牢固、隐蔽、舒适。

  1. 主控板与传感器固定

    • 传感器:用热熔胶或双面泡棉胶,将红外传感器模块粘贴在眼镜框的右镜腿内侧,位置要确保其发射/接收窗口能正对右眼眼角外侧的眼皮区域。这是检测的关键,距离眼皮大约10-15毫米为佳。太远信号弱,太近可能不舒服。
    • Arduino Pro Mini:可以粘贴在右镜腿靠后的位置,或者如果空间够,粘贴在眼镜框的横梁(鼻梁架)上。用热熔胶固定四周即可,注意别堵住芯片上的复位按钮。
  2. 警报模块固定

    • 振动马达:粘贴在左镜腿末端,靠近耳朵后方的位置。这里是颞骨区域,对振动敏感,且佩戴时贴合皮肤。
    • 蜂鸣器:可以贴在左镜腿靠近振动马达的地方,出声孔不要被完全堵住。可以考虑用小钻头在镜腿上钻几个小孔作为出声孔。
    • 将BC547三极管、4.7kΩ电阻以及马达/蜂鸣器的连接点,用热缩管包裹或集中固定在一块小洞洞板上,再整体粘贴,比飞线更可靠。
  3. 电池与开关

    • 将小巧的锂电池用扎带或胶带固定在左镜腿上,与警报模块同侧,以平衡左右重量。
    • 开关可以选用超小的拨动开关,安装在左镜腿外侧方便拇指操作的位置。
  4. 走线管理

    • 所有连接线尽量沿着镜腿背面走,用胶带或细线捆扎固定。
    • 左右镜腿之间的连线(如电源正负极),可以从眼镜框的铰链处或横梁上方小心穿过并固定。

踩坑记录:我第一次做的时候,把传感器粘得太靠近眼球,稍微一动就会误触发。后来调整到距离眼皮约1厘米,并稍微向下倾斜,只检测下眼睑和脸颊交界处的皮肤反射,稳定性大大提升。因为即使你眨眼,这个区域的变化也不像眼球表面那么剧烈,但真正闭眼时,眼皮会完全覆盖这个区域,信号变化非常明显。

4. 代码编写与逻辑剖析

原项目的代码提供了一个最基础的框架,但存在一些可以优化和必须理解的地方。我们来逐行分析并改进。

4.1 基础代码解读与问题

原代码如下(已整理格式):

int Sinput = A1; // 传感器信号引脚 int Buz = 3; // 控制警报的引脚 void setup() { pinMode(Sinput, INPUT); // 设置A1为输入 pinMode(Buz, OUTPUT); // 设置D3为输出 } void loop() { if(digitalRead(Sinput) == LOW) { // 如果检测到物体(睁眼?这里逻辑需确认!) delay(2000); // 等待2秒 digitalWrite(Buz, HIGH); // 触发警报 } else { if(digitalRead(Sinput) == HIGH) { digitalWrite(Buz, LOW); // 关闭警报 } } }

关键问题分析:

  1. 逻辑陷阱:代码中,当SinputLOW时,等待2秒后直接触发警报。这基于一个假设:传感器检测到物体(反射信号强)输出LOW代表“睁眼”。但如前所述,传感器逻辑可能相反。我们必须先测试确定:对着传感器伸手,看串口监视器里digitalRead(A1)0还是1。我们假设最常见的情况:有反射(睁眼)时输出LOW,无反射(闭眼)时输出HIGH
  2. 逻辑错误:即使假设正确,原逻辑也是“睁眼2秒后报警”,这显然不对。我们需要的是“闭眼持续超过一个阈值(比如2秒)才报警”。
  3. 警报复位:原代码中,一旦触发警报 (Buz设为HIGH),只有在Sinput重新变成HIGH时才会关闭。但在我们的场景下,触发警报后,人会被惊醒并睁眼,此时Sinput应变为LOW,但else分支里的条件if(digitalRead(Sinput) == HIGH)永远不会成立,导致警报无法自动停止!这是一个严重的逻辑缺陷。

4.2 优化后的可靠代码

以下是修正并优化后的代码,增加了状态判断和消抖逻辑:

// 防瞌睡眼镜 - 优化版 const int sensorPin = A1; // 红外传感器连接引脚 const int alertPin = 3; // 警报控制引脚(连接三极管基极) const unsigned long drowsyThreshold = 2000; // 判定为瞌睡的闭眼时间阈值(毫秒) unsigned long eyeClosedStartTime = 0; // 记录闭眼开始的时间 bool alertActive = false; // 警报当前是否激活的标志 void setup() { pinMode(sensorPin, INPUT); pinMode(alertPin, OUTPUT); digitalWrite(alertPin, LOW); // 初始确保警报关闭 // 初始化串口,用于调试(完成后可注释掉以省电) Serial.begin(9600); Serial.println("Anti-Sleep Glasses Started."); } void loop() { // 读取传感器状态:根据实际测试确定! // 假设:睁眼(有反射) -> LOW, 闭眼(无反射) -> HIGH int eyeState = digitalRead(sensorPin); // 调试输出(完成后可注释掉) Serial.print("Eye State: "); Serial.println(eyeState); // 逻辑核心:状态机 if (eyeState == HIGH) { // 当前状态:闭眼(或无反射) if (!alertActive) { // 如果警报还没响,检查是否刚进入闭眼状态 if (eyeClosedStartTime == 0) { // 第一次检测到闭眼,记录开始时间 eyeClosedStartTime = millis(); Serial.println("Eye closed. Timer started."); } else { // 已经在闭眼状态,检查持续时间 if (millis() - eyeClosedStartTime > drowsyThreshold) { // 闭眼时间超过阈值,触发警报 digitalWrite(alertPin, HIGH); alertActive = true; Serial.println("ALERT! Drowsiness detected!"); } } } // 如果警报已经激活(alertActive == true),则保持警报,直到睁眼 } else { // 当前状态:睁眼(有反射) // 无论之前状态如何,睁眼就重置闭眼计时器 eyeClosedStartTime = 0; // 如果警报正在响,则关闭它 if (alertActive) { digitalWrite(alertPin, LOW); alertActive = false; Serial.println("Alert stopped. Eye opened."); } } // 一个小延迟,防止循环过快。50ms的周期对于检测眨眼/闭眼足够快。 delay(50); }

代码逻辑详解:

  1. 状态判断:我们明确定义了eyeState == HIGH为闭眼(无反射),LOW为睁眼。请务必根据你的传感器实际测试结果来调整这个判断条件。
  2. 计时与阈值:使用millis()函数进行非阻塞式计时。只有当连续检测到闭眼状态超过drowsyThreshold(这里设2秒)时,才判定为瞌睡。
  3. 状态机:引入了alertActive标志位。这确保了警报一旦触发,会持续响铃/振动,直到检测到睁眼信号后才停止。这比原代码的逻辑更合理。
  4. 消抖与重置:每次检测到睁眼,都会将闭眼开始时间eyeClosedStartTime重置为0。这样,正常的眨眼(通常小于300毫秒)不会累积计时,避免了误触发。
  5. 调试信息:串口输出有助于你实时观察传感器状态和程序逻辑,方便调试。项目完成后,可以注释掉Serial相关的代码以节省电量。

4.3 如何给Arduino Pro Mini烧录程序

由于Pro Mini没有USB接口,你需要一个“中介”:

  1. 使用USB转TTL串口模块(推荐):这是最通用的方法。你需要一个像CH340G、CP2102或FT232RL芯片的USB转TTL模块。

    • 连接方式
      • TTL模块的VCC→ Pro Mini的VCC(注意电压匹配,5V模块接Pro Mini的5V版本,3.3V模块接3.3V版本)。
      • TTL模块的GND→ Pro Mini的GND
      • TTL模块的TX→ Pro Mini的RX
      • TTL模块的RST→ Pro Mini的RST
    • 在Arduino IDE中选择板卡为“Arduino Pro or Pro Mini”,处理器选择正确的型号和电压,端口选择TTL模块对应的COM口。点击上传前,需要手动让Pro Mini进入复位状态:在点击“上传”按钮后,Arduino IDE开始编译,当出现“正在上传…”字样时,快速短接一下Pro Mini的RST引脚和GND引脚,然后松开,程序即可开始上传。
  2. 使用Arduino Uno作为编程器:如原文所述,将Uno上的主控芯片取下,利用其USB转串口电路。

    • 连接方式
      • Uno的5V→ Pro Mini的VCC
      • Uno的GND→ Pro Mini的GND
      • Uno的RX(0号引脚) → Pro Mini的TX
      • Uno的TX(1号引脚) → Pro Mini的RX
      • Uno的RST→ Pro Mini的RST
    • 在IDE中选择板卡为“Arduino Pro or Pro Mini”,端口选择Uno的端口,然后直接上传即可。Uno的16U2芯片会自动处理复位信号。

5. 传感器校准、调试与优化

5.1 传感器灵敏度校准

在将传感器粘到眼镜上之前,必须进行校准:

  1. 将传感器接上电(通过Arduino),用螺丝刀调节传感器模块上的蓝色可调电位器
  2. 将你的手指(模拟眼皮)放在传感器前方约1-1.5厘米处。
  3. 一边调节电位器,一边观察传感器上的信号指示灯(如果有)或通过串口监视器读取输出值。目标是:当手指在有效距离内时,输出状态为“睁眼”状态(例如LOW);当手指移开或模拟闭眼(比如用深色物体完全贴近吸收光线)时,输出状态变为“闭眼”状态(例如HIGH)。
  4. 反复测试,找到最稳定、不易受环境光干扰的临界点。调好后,可以用一点胶水固定电位器,防止震动导致偏移。

5.2 阈值时间调整

代码中的drowsyThreshold变量(原代码的2000毫秒)是关键参数。

  • 2秒(2000ms):这是一个比较宽松的设置,能过滤掉正常的眨眼,但可能对快速点头式的瞌睡反应稍慢。
  • 建议测试范围:可以在1.5秒到3秒之间调整。时间太短容易误报(比如长时间凝视或思考时眨眼较慢),时间太长则预警延迟,失去意义。你可以通过串口修改这个值,烧录测试,找到最适合自己反应习惯的时间。

5.3 功耗优化考虑

如果想延长电池续航,可以进行以下优化:

  1. 使用Arduino的低功耗模式:在loop()中,可以使用delay()的替代方案,如LowPower.idle()或设置定时中断唤醒,但这需要更复杂的编程。
  2. 关闭无关模块:最终版本中,移除所有Serial.print()语句,因为串口通信比较耗电。
  3. 选择低功耗元件:使用3.3V版本的Pro Mini,并选择工作电压为3V的蜂鸣器和振动马达。
  4. 电源管理:在电池和系统之间增加一个物理开关,不用时彻底断电。

6. 常见问题与故障排查实录

在制作和调试过程中,你可能会遇到以下问题,这里提供我的排查思路:

问题现象可能原因排查步骤与解决方案
上电后毫无反应1. 电源未接通或电池没电。
2. Arduino Pro Mini未正确烧录引导程序(Bootloader)。
3. 电源线接反或短路。
1. 用万用表测量电池电压,检查开关是否导通。
2. 尝试给Pro Mini重新烧录Bootloader(需借助编程器)。
3. 检查所有电源连接,特别是电池正负极是否接反。
传感器指示灯常亮或常灭,状态不变1. 传感器供电错误(电压不符)。
2. 电位器调节过度,灵敏度极端。
3. 传感器损坏。
1. 确认传感器VCC接的是5V还是3.3V,匹配供电。
2. 逆时针或顺时针多旋转几圈电位器,看状态是否变化。
3. 更换一个传感器测试。
警报(振动/蜂鸣)不工作1. 三极管BC547接错引脚(B/C/E)。
2. 限流电阻(4.7kΩ)开路或阻值过大。
3. 警报器件本身损坏。
4. Arduino D3引脚未正确输出高电平。
1. 确认BC547引脚:平面朝自己,从左至右为E, B, C。
2. 用万用表测量电阻值,检查焊接。
3. 直接将马达/蜂鸣器正负极接电池(注意电压),测试是否工作。
4. 用代码让D3输出HIGH,并用万用表测量其电压是否为~5V。
频繁误报警(没闭眼也响)1. 传感器安装位置不佳,被睫毛、眼镜框或外部光线干扰。
2. 灵敏度调得太高。
3. 代码中的判定阈值drowsyThreshold设置过短。
1. 调整传感器角度和距离,确保只对准眼睑区域,避免环境光直射接收管。
2. 重新校准传感器,适当降低灵敏度(逆时针调节电位器)。
3. 增加阈值时间,如从2000ms改为2500ms。
该报警时不报警(闭眼很久也不响)1. 传感器逻辑弄反(睁眼/闭眼状态判断错误)。
2. 传感器距离眼皮太远或角度不对,反射信号始终很弱。
3. 阈值时间设置过长。
1. 用串口监视器确认eyeState在睁眼和闭眼时的真实值,修正代码中的if判断条件。
2. 重新安装传感器,确保其距离眼皮1-1.5cm且正对。
3. 减少阈值时间,如改为1500ms。
电池消耗极快1. 振动马达或蜂鸣器工作电流大,且可能被持续触发。
2. 存在短路或漏电。
3. 未使用低功耗模式。
1. 检查代码逻辑,确保警报只在需要时触发,并能自动关闭。
2. 断电后,用万用表测量电池接口间的电阻,排除短路。
3. 考虑使用更大容量的电池(如600mAh的锂电池),或优化代码进入休眠。

一个真实的踩坑经历:我第一次测试时,警报响个不停。查了半天,发现是传感器输出的信号线在眼镜铰链处被反复弯折,内部铜丝几乎断裂,导致接触不良,Arduino读取到的信号一直在随机跳变。后来换用更柔软的硅胶导线,并在弯折处做了应力保护,问题就解决了。所以,穿戴设备内部的连线,一定要考虑柔韧性和耐用性

7. 项目扩展与进阶思路

这个基础版本完成后,你可以根据兴趣进行扩展:

  1. 多级警报系统:不要一上来就“全功率”警报。可以设计成分级预警:闭眼1.5秒,轻微振动一下;闭眼2.5秒,振动加强;闭眼3.5秒,振动+蜂鸣器齐鸣。这样更人性化。
  2. 加入蓝牙模块:添加一个HC-05或HM-10蓝牙模块,将瞌睡警报信号发送到手机APP。APP可以记录打瞌睡的频率和时间点,用于分析驾驶习惯。
  3. 使用模拟值进行更精准判断:将传感器的数字接口改为模拟接口(analogRead)。通过读取反射信号的强度模拟值,可以更细腻地区分“半闭眼”、“眯眼”和“全闭眼”状态,减少误判。
  4. 改进佩戴体验:使用更轻薄的柔性电路板(FPC)代替杜邦线和洞洞板,将电池改为更小巧的纽扣电池组,并设计3D打印的外壳来包裹所有元件,让眼镜看起来更接近普通眼镜。
  5. 引入机器学习(高级):如果使用像Arduino Nano 33 BLE Sense这样带有更强大处理器的板子,可以尝试采集一段时间内的眼睑活动数据,用简单的算法来学习佩戴者的正常眨眼模式,从而更准确地识别出异常的、疲劳相关的闭眼。

这个防瞌睡眼镜项目,从想法到实现,涉及了硬件选型、电路设计、嵌入式编程和机械组装,是一个典型的微型智能穿戴设备原型。它最大的价值不在于其工业级的可靠性,而在于完整地走通了一个“感知-判断-执行”的闭环。希望这份详细的拆解,能帮你不仅做出一个能用的设备,更能理解其中每一步的“所以然”。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询