ESP32智能安防实战:红外对管+人脸识别+电磁锁联动设计
简介一套基于物联网与人脸识别技术的智能安防系统面向嵌入式开发、物联网工程与安防项目学习者覆盖从传感器采集、人脸识别登录到远程报警的完整链路。系统以ESP32为主控配合红外对管、蜂鸣器、电磁锁等硬件实现暴力开锁检测、收银台解锁与PC端摄像头监控并基于TCP协议与云主机服务器完成数据交互适合用于课程设计、毕业设计或实际场景改造。资源包共71个文件以Python脚本py/pyc为主体包含界面文件、人脸图像样本、OpenCV配置文件、说明文档等整体仅404KB结构紧凑便于快速浏览与二次开发。已有115人下载学习可作为智能安防方案的首选参考。内容涵盖人脸采集与比对、门禁控制逻辑、管理员/收银员双端界面、报警联动等模块同时提供了README、说明文件和附赠文档便于理解系统设计和部署流程可直接在本地运行调试也可作为项目改造的基础框架。1. 一套ESP32安防系统真正难的不是人脸识别而是报警判据拿到这个项目标题时我的第一反应是大部分做这个方向的人会把80%的精力花在人脸识别上最后发现真正让系统整天误报、经常把店员自己锁在门外的反而是红外对管和蜂鸣器的联动逻辑。这个标题里的选型组合很有意思——它没用昂贵的工业门禁控制器而是用一块ESP32开发板把红外对管、蜂鸣器、电磁锁串起来再用PC端摄像头做人脸识别做二次授权最终通过TCP协议把所有事件汇总到云主机。这套方案的本质是把“物理入侵检测”和“身份授权”拆成两条独立的链路ESP32负责感知和动作执行PC端负责重负载的人脸比对两者通过TCP协议通信。适合的人群很明确——做物联网课程设计、门店收银台防入侵改造、或者想低成本验证智能安防方案的开发者。标题里带.zip意味着这是一份可交付的工程包而这类项目最常见的失败点不是代码写不出来而是硬件信号抖动、通信协议字段对不上、报警与解锁互相打架这些边界问题。上个月我帮一个做店铺安防的朋友排查了一晚上最后发现只是红外对管装反了安装方向。所以这篇我把整个方案的链路拆开讲清楚。2. 系统架构与方案选型为什么是ESP32云主机而不做全本地闭环2.1 两条独立链路的设计意图这个系统里最值得先理解的是它刻意把工作拆成了两部分。ESP32开发板负责的是一类任务红外对管采集遮挡信号、驱动蜂鸣器报警、控制电磁锁开合。这些任务要求的是毫秒级响应而且必须在断网时依然能工作。PC端负责的是另一类任务摄像头抓拍人脸、做特征比对、判断是否为已授权人员。这类任务需要的是算力和存储但对实时性的要求没那么苛刻几百毫秒的延迟可以接受。我一般会把这种架构叫“端侧感知 上位机决策 云端留痕”。ESP32不直接做人脸识别不是因为做不了而是因为它跑轻量级人脸模型会占用太多资源导致红外中断响应变慢。与其在一个芯片上硬扛所有事不如把重活交给PC。两端之间用TCP协议通信局域网内延迟基本在几毫秒到十几毫秒完全够用。而云主机服务器的角色是日志中心和远程状态查询——每次开锁、每次报警、每次人脸比对结果都汇总上去这样即使本地设备被破坏云端仍然留有记录。2.2 核心硬件选型的理由与槽点分析先说ESP32开发板这个选择几乎没有悬念。它自带Wi-Fi能力不需要外接无线模块GPIO数量足够接红外对管、蜂鸣器和继电器而且Arduino环境下开发效率很高。和树莓派相比ESP32的优势是实时性好、功耗低、启动快上电到开始执行逻辑只要几百毫秒劣势是算力有限跑不了复杂模型。在这个方案里正好各取所需。红外对管传感器选的是带数字量输出的模块就是黑色半球形封装、四针引脚那种不是裸的红外发射管和接收管。数字量输出的模块内部已经做了比较器输出的高低电平可以直接接GPIO不需要自己去搭放大电路。它的检测距离一般是3到30厘米可调用在收银台抽屉、门框缝隙这类场景是够用的。蜂鸣器要区分有源蜂鸣器和无源蜂鸣器。有源蜂鸣器内部带震荡电路只要给高电平就能响不需要程序生成PWM波形适合安防报警这种只需要“响不响”的场景。无源蜂鸣器需要给特定频率的方波才能发声虽然可以做旋律但在这个项目里没必要。电磁锁则要注意常规的12V电磁锁工作电流在几百毫安到1安培之间千万不能直接接ESP32的GPIO引脚3.3V的引脚连驱动三极管都勉强必须用继电器或MOS管模块中转。关于PC端摄像头做人脸识别不同的实现方案差别不小。常见做法是在PC上跑OpenCV加一个人脸检测器再配合一个开源的人脸识别模型。但也有人用Python的第三方人脸识别库直接封装好的接口。如果只是想快速跑通流程我倾向于先用OpenCV的Haar特征检测人脸区域再用深度学习方法提取特征向量做比对。后面第4章会给出具体实现。2.3 数据流与协议设计谁先说话谁先闭嘴整条数据链路可以归纳成三路信号理解清楚这三路信号后面写代码时就不会混乱。第一路是触发信号红外对管检测到异常遮挡ESP32记录时间戳并置位一个“疑似入侵”标志。第二路是决策信号ESP32通过TCP向PC端发送“请求人脸验证”报文PC端拍摄并比对后返回“通过”或“拒绝”。第三路是执行信号如果验证通过ESP32控制电磁锁开启如果验证失败或超时ESP32拉响蜂鸣器并把事件上报云端。这里有一个很容易翻车的设计点报警信号的优先级必须高于一切。如果程序主循环里先处理TCP收发再检测红外一旦网络拥塞入侵检测就会被卡住。正确的做法是红外对管引脚配置为中断触发无论程序当前在做什么只要红外被触发中断服务函数立即置位报警标志主循环在下一轮处理中优先响应它。用表格来总结各模块的角色分工模块核心职责实时性要求失败后果ESP32开发板信号采集、动作执行、协议转换毫秒级本地安防失效红外对管模块遮挡和开锁动作检测毫秒级漏报或误报蜂鸣器本地声光报警毫秒级威慑作用丧失电磁锁物理执行机构亚秒级门禁失控PC端摄像头人脸采集与比对百毫秒级无法授权开锁云主机服务器日志存储与远程管理秒级事后追溯缺失初学时容易把PC端和云端当成一个东西实际上可以分开也可以合并如果服务器资源紧张把PC端的人脸识别服务和云主机部署在同一台机器上也是可行的只是要处理好端口占用和开机自启问题。我自己的习惯是在开发阶段PC端和云主机分两台机器方便抓包调试。3. ESP32端硬件接线与固件落地红外对管、蜂鸣器、电磁锁的驱动细节3.1 最小电路接线五个引脚把三样外设接完ESP32在这套系统里是绝对的中心节点几乎所有外设都要挂在它的GPIO上。我用的接线方案如下以Arduino环境下常用的GPIO编号为准红外对管的OUT引脚接GPIO4同时在模块的VCC和GND之间并联一个10uF电容——这个电容能极大减少电机或电磁锁动作时的电压跌落导致传感器误触发蜂鸣器的IO引脚接GPIO16且蜂鸣器VCC不直接接3.3V而是通过一个NPN三极管型号常见的S8050即可接到5V电源由GPIO控制三极管基极的通断电磁锁驱动模块继电器的信号引脚接GPIO17继电器VCC接5V电磁锁的12V电源单独供电不要从ESP32开发板的5V引脚取电否则电流一上来板子直接掉电重启。区分开“ESP32的3.3V逻辑”和“外部设备的5V/12V电源系统”是这块最关键的电路知识。GPIO引脚的输出高电平是3.3V足以驱动三极管基极或继电器模块的信号端但不足以直接驱动蜂鸣器大电流或电磁锁的线圈大电流。所以多一层驱动电路不是多此一举而是保护开发板的必要手段。接线完成后先做一次上电自检ESP32启动时先初始化所有GPIO为低电平输出然后依次把蜂鸣器引脚拉高500毫秒、把继电器引脚拉高1秒再循环检测红外对管的电平变化。这样能立刻看出三个外设是否都正常工作节省后面联调时的排查时间。如果上电瞬间蜂鸣器无缘无故响了一下多半是GPIO默认电平不是低电平需要检查代码里的初始化逻辑而不能视而不见。3.2 固件框架中断驱动主循环状态机不能靠轮询固件结构我习惯写成中断服务函数加主循环状态机。红外对管信号变化往往只持续几百毫秒如果用轮询方式每50毫秒查一次引脚电平极有可能漏掉一次短暂遮挡。这里用ESP32的GPIO中断功能把红外对管引脚配置为FALLING和RISING双沿触发每次电平跳变都记录到全局变量里主循环再去解析这些记录的时序。下面给出固件中红外检测部分的参考代码结构使用Arduino框架仅做逻辑示范命名可自行调整// 全局状态定义 volatile unsigned long lastIrFallTime 0; // 上一次红外被遮挡的时刻 volatile unsigned long lastIrRiseTime 0; // 上一次红外恢复的时刻 volatile uint8_t irBlockedCount 0; // 连续遮挡次数 // 红外对管中断服务函数 void IRAM_ATTR irIsr() { if (digitalRead(IR_PIN) LOW) { lastIrFallTime millis(); // 记录遮挡开始时刻 } else { if (lastIrFallTime 0) { unsigned long blockDuration millis() - lastIrFallTime; if (blockDuration 50 blockDuration 5000) { irBlockedCount; // 有效遮挡时长在50ms到5s之间 } } lastIrRiseTime millis(); // 记录遮挡结束时刻 } }这段代码解决的一个核心问题是过滤掉电磁锁开合瞬间产生的振动导致红外对管误触发。电磁锁吸合时会产生几十毫秒的物理抖动如果红外对管安装在同一个门框上抖动会传导给传感器产生一个极短的高低电平跳变。而50毫秒的滤除窗口能把这些抖动挡在有效遮挡之外。5秒的上限也需要注意如果红外被物体长时间挡住比如有人把纸箱堵在传感器前超过5秒就不计入“暴办开锁”检测范围因为暴力开锁通常是短暂的、试探性的动作序列而不是持续遮挡。主循环里的状态机由这个irBlockedCount数值驱动0表示无异常1表示一次短暂遮挡可能只是有人经过2表示短时间内连续出现两次遮挡此时启动蜂鸣器报警并同时向PC端请求人脸验证。连续遮挡比单次遮挡更有指向性因为正常开锁动作通常只触发一次红外遮挡只有撬锁、反复试探才会在一个窗口期里触发多次。3.3 暴力开锁检测的判定逻辑时序比阈值更重要暴力开锁检测最忌讳的是把问题简化成“红外被挡住就报警”。实际上正常使用场景里人要从口袋里掏钥匙、弯腰、伸手这些动作都可能无意间遮挡红外对管。所以我用的判定依据是“时间窗口内的连续触发次数加上单次遮挡时长”而不是单纯的电平状态。一个经过调参的判定规则是这样在10秒滑动窗口内红外对管触发遮挡次数大于等于2次且其中至少一次遮挡持续时间超过200毫秒则判定为暴力开锁嫌疑。这里200毫秒的阈值来自对动作的观察正常插钥匙拧锁手在对管前停留的时间通常在150毫秒以下而撬锁工具在锁孔内试探时停留时间往往超过200毫秒。这两个阈值需要根据安装位置微调——如果对管离锁孔特别近可能需要上调到300毫秒。同时要注意判定逻辑必须支持“蜂鸣器提前响、人脸验证晚一步”的时序。我试过先做人脸验证、通过之后再决定报不报警的方案效果很差因为人脸识别需要几秒时间真正的暴力开锁行为在几秒内可能已经完成了破坏。正确的时序是检测到疑似暴力开锁行为后蜂鸣器立即响起来同时把“请求验证身份”的TCP报文发出去如果验证通过则在响铃3秒后自动解除报警并允许开锁如果验证不通过则持续报警直到手动复位。3.4 电磁锁与蜂鸣器的控制时序电磁锁的控制不是简单的GPIO拉高或拉低它和蜂鸣器之间存在一个协作关系。开锁指令到达后电磁锁的继电器吸合锁舌缩回门可以拉开。但此时蜂鸣器如果还在响会让正常用户困惑为什么我验证通过了还在报警所以需要在协议层定义清楚状态解锁期间蜂鸣器静音解锁超时后重新布防。我实现的开锁动作为收到PC端返回的验证通过报文后GPIO17拉高继电器吸合电磁锁通电开锁同时GPIO16拉低关闭蜂鸣器开锁持续5秒后GPIO17恢复低电平电磁锁断电重新上锁。5秒这个值是经验值太短用户来不及拉门太长则存在安全隐患。如果用于收银台抽屉锁2秒就够如果用于通道门需要5到8秒。这里还有一个容易忽略的问题电磁锁在通电开锁的状态下红外对管如果被门框遮挡或者被手遮挡会触发新一轮的报警检测。因为检测逻辑还在运行它不知道当前处于“已授权开锁”状态。解决方法是引入一个“授权开锁窗口”标志位——在电磁锁通电期间红外遮挡计数不累加但遮挡事件仍然上报云端这样既保证不误报警又保留行为记录。// 主循环中的状态机简版 void loop() { if (lockState LOCK_OPEN millis() - lockOpenTime 5000) { lockState LOCK_CLOSED; digitalWrite(RELAY_PIN, LOW); // 电磁锁断电 } if (lockState LOCK_CLOSED) { handleIrBlockDetection(); // 仅在锁定状态下累计红外遮挡 } handleTcpMessage(); // 处理来自PC端或云端TCP指令 handleCloudUpload(); // 按需上报事件到云主机 }注意主循环里的顺序先处理本地状态切换再处理TCP通信最后上报云端。这个顺序保证本地应急响应永远优先于网络通信。即使TCP连接断开或云主机不可达蜂鸣器报警和电磁锁控制仍然能够按时执行。4. PC端人脸识别与收银台解锁联动从摄像头抓帧到TCP指令下发4.1 为什么把布防和撤防交给PC端管理ESP32端只管感知和执行但它缺乏“决策上下文”。一个典型问题是晚上店铺打烊后收银台区域的任何红外遮挡都值得报警而白天营业时间店员频繁开关抽屉、取放现金红外对管会被触发几十次每次都报警就完全无法使用。所以需要PC端根据时间段和人脸验证结果来动态下发策略。PC端在系统里的角色是“策略中心”它保存了一张授权人脸特征库摄像头持续捕捉画面但只有在收到ESP32发来的“请求验证”报文时才执行人脸比对。比对通过后PC端通过TCP回复一个开锁指令比对失败则回复一个拒绝指令并附带一次告警日志。这种“被动触发而非持续监控”的设计还能有效降低CPU占用率、延长摄像头寿命。当系统处于布防状态时PC端向ESP32下发“灵敏度档位”或“布防/撤防”标志。比如白天撤防时ESP32收到红外触发仍然上报事件但不启动蜂鸣器报警只记录日志。这样做的好处是无论PC端策略怎么切换ESP32端的数据采集不中断事后回溯时可以还原完整的事件链。4.2 人脸特征库的建立与登录流程人脸识别登录的实现分成两个阶段注册阶段和验证阶段。注册阶段需要用户站在摄像头前连续采集多张不同角度的照片提取特征向量存入本地库验证阶段则抓拍一张当前画面提取特征后与库内所有向量计算相似度选出最高分并判定是否超过阈值。基于这个标题里给的条件我可以确认这个方案的识别模块是部署在PC端的而不是在ESP32上。PC端摄像头使用的是普通USB摄像头或笔记本内置摄像头不需要额外接显示器因为验证是自动化的——摄像头拍摄完算法给出结果结果直接封装成TCP报文返回给ESP32。下面是验证阶段的核心流程用Python实现调用OpenCV采集图像并用预训练模型提取特征import cv2 # 假定face_encoder是预先加载好的特征提取模型 def verify_identity(frame, known_encodings, known_names, threshold0.42): # 检测人脸区域 face_locations face_detector.detect(frame) if not face_locations: return None, no_face, 0.0 # 提取人脸部分并编码 face_encoding face_encoder.compute_encoding(frame, face_locations[0]) best_match unknown best_score 0.0 for encoding, name in zip(known_encodings, known_names): distance face_distance(face_encoding, encoding) score 1.0 - distance if score best_score: best_score score best_match name if best_score threshold: return best_match, allow, best_score else: return None, deny, best_scorethreshold参数直接影响安全性。数值设置得越小表示判定越严格因为1减去距离得到的分数需要更大才能通过。0.42这个初始值常见于人脸库中的大角度正面照场景你可以先在本地用10张自己的照片测试。注意识别结果只返回allow或deny不会把姓名之类的个人信息发给ESP32——ESP32不需要知道是谁它只需要知道“允许还是不允许”。姓名记录在云端日志里关联。4.3 TCP报文设计与收银台解锁协议TCP协议在这里做主从式通信ESP32作为客户端主动发起连接PC端作为服务端监听固定端口。为什么不让ESP32做服务端因为ESP32的Wi-Fi连接是动态的IP地址由路由器分配PC端主动连接它需要先知道它当前的IP这增加了发现机制的复杂度。反过来ESP32在启动时通过WiFi.begin()连上路由器后主动向PC端的固定IP和端口发起连接PC端监听这样两端都不需要对方预先知道自己的地址。报文格式我使用简化的JSON便于调试和扩展{ device_id: esp32_cashier_01, event_type: ir_trigger, trigger_count: 2, timestamp: 1700000000 }{ cmd: unlock, token: allow#admin, expire_after: 5000 }第一条是ESP32上报到PC端的事件第二条是PC端返回给ESP32的开锁指令。expire_after字段告诉ESP32这个开锁指令的有效期防止指令重放攻击——如果有中间人截获了这条报文但设备的执行窗口已过重放报文不会生效。虽然ESP32的固件无法做复杂加密但加上有效期已经是成本最低的安全加固。4.4 收银台场景的联动逻辑报警与解锁的优先级收银台解锁的完整事件序列在正常模式下是这样的店员用钥匙开启收银台抽屉抽屉拉出过程中遮挡了红外对管ESP32记录一次遮挡抽屉正常开启长度不会在短时间内再次触发第二次遮挡所以ESP32不启动报警收银员完成现金取放后合上抽屉整个过程结束。而暴力开锁模式下非授权人员试图撬开抽屉可能短时间内多次触发红外遮挡ESP32进入报警状态——蜂鸣器响同时发送人脸验证请求PC端抓拍人脸此时摄像头正对收银台区域识别结果为“非授权用户”PC端拒绝开锁并上传云端如果在多次尝试后识别为“授权用户”PC端返回允许开锁同时云端记录“暴力开锁尝试后由授权用户解除报警”的事件。这个逻辑设计的核心思想是报警不是最终结果而是驱动一次身份验证开始的信号。需要注意的一点人脸识别模块必须设置“失败重试”和“超时锁定”机制。连续3次识别失败后PC端在5分钟内不再响应新的识别请求并将此事件上传云端防止有人用照片对着摄像头反复尝试。5. 避坑指南红外误报、电磁锁抖动、TCP断线三个老大难5.1 红外对管大面积误报问题不在代码在安装位置现象系统白天频繁报警蜂鸣器无缘无故响起而且没有任何人靠近收银台。原因红外对管被安装在靠近窗户的位置午后阳光直射传感器的接收管导致接收端信号饱和。数字量输出的红外对管模块原理是发射管发出红外光接收管检测反射光的强度。当环境光中含有大量红外成分时接收管会一直被“照亮”输出电平被钳位在触发阈值附近信号就会上下抖动。解决把对管安装位置调整到避光处或者给接收管套上一小段黑色热缩管只留出正面的开口。另一个可操作的方案是在对管模块上找到灵敏度调节电位器逆时针稍微调低灵敏度。调低后要注意重新经过一次开锁测试确认遮挡动作仍然能触发避免从一个极端走到另一个极端。5.2 电磁锁吸合瞬间导致红外误触发现象每次正常开锁后系统紧接着会产生一条新的入侵报警记录甚至蜂鸣器响一声又停。原因电磁锁的吸合是一个猛烈的物理动作会产生几毫秒到几十毫秒的机械振动。如果红外对管安装在同一块门框或台面上振动会传递到模块内部使接收管和发射管的相对位置发生微小的改变输出电平随之跳变。解决硬件层面给红外对管加装橡胶垫或海绵垫做物理隔离软件层面在开锁后的300毫秒内对红外中断事件做屏蔽但记录原始事件只是不累加计数。我在固件里加了一个lockDebounceUntil变量在电磁锁继电器拉高后的300毫秒内所有红外中断只记日志不参与计数。这个时间窗要精准调太长会漏掉开门时真实的遮挡太短则过滤不掉振动。5.3 TCP连接断了之后ESP32变成孤岛现象PC端正常显示“设备已连接”但ESP32已经掉线任何红外触发都不再上传但本地蜂鸣器也不响。原因ESP32的Wi-Fi连接或TCP连接断开了程序里的client.connected()返回false但主循环还在按部就班地处理网络发送没有走重连逻辑。报警逻辑如果被放在“连接正常”的分支里就会导致断开时报警也失效。解决把报警逻辑从TCP连接状态中解耦。无论TCP是否连接红外触发、暴力开锁判定、蜂鸣器报警这些本地逻辑都必须正常运行。TCP只在“需要发送数据”时才被调用而且每次发送前判断连接状态如果断开则使用阻塞式重连——最多重试3次每次间隔5秒重试失败就丢弃本次上报并等待下一个事件触发重连。bool sendTcpSafe(WiFiClient client, const String payload) { if (!client.connected()) { for (int i 0; i 3; i) { if (client.connect(PC_IP, PC_PORT)) break; // 尝试重连 delay(5000); } } if (client.connected()) { client.println(payload); return true; } return false; // 重连失败本次上报放弃 }这样改动后即使PC端重启或网络故障ESP32的本地安防功能始终在线。后续事件可以存储在ESP32的NVS内存中等连接恢复后补传关键记录但现实中这个补传逻辑往往优先级不高先把实时报警保住是更重要的。5.4 人脸识别把店员自己锁在门外现象授权用户站在摄像头前系统连续返回“不认识”导致无法开锁。原因人脸特征库中存储的样本和当前环境的光照差异太大。比如注册时是白天自然光凌晨加班时只有荧光灯同样的脸提取出来的特征向量距离会变远识别分数会掉到阈值之下。解决在特征库里为同一个人存储多个光照条件下的样本。最稳妥的做法是每次开锁成功后自动把当前识别帧加入临时样本库标记为“自适应样本”当这个临时样本的验证分数在其他时间段再次被复核通过时升级为正式样本。这个自适应策略有安全风险如果入侵者在授权用户刚离开时使用照片解锁照片样本会被记录所以在加入前需要验证匹配分数足够高而且临时样本不会自动用于开锁授权。5.5 ESP32供电不足导致随机重启现象蜂鸣器报警时ESP32闪断、重启日志记录出现大段空白。原因蜂鸣器报警需要瞬时电流如果是5V有源蜂鸣器直接从ESP32的5V引脚取电瞬时电流可能达到200毫安以上而ESP32开发板的板载稳压芯片通常只提供稳定的500毫安输出当蜂鸣器、继电器同时动作时总电流超过稳压芯片的极限。解决检查电源配置。我踩过这个坑后改成了外部5V 2A电源适配器供电ESP32的VIN引脚接外部5V电源蜂鸣器和继电器分别用独立的三极管驱动不再从ESP32的电感或引脚取大电流。同时在ESP32的电源输入引脚旁并联一个470uF电解电容和104陶瓷电容这样瞬时电流不会引起电压坍塌。6. 进阶技巧给ESP32加心跳与看门狗让它从“能跑”变成“能长期跑”把系统从课程设计水平提升到可部署水平我认为最重要的一个改动是增加心跳机制与看门狗。心跳机制解决的是“TCP连接假死”问题——ESP32与PC端的连接表面还在但实际上网络路径已经不通了数据发不出去、对方报文也收不到。解决办法是让PC端每10秒向ESP32发送一个空检测包ESP32在3秒内未收到就开始重连。反过来ESP32也会每30秒向云主机发送一个心跳包内容是当前设备状态、报警计数器和自由内存大小云主机将这些值写进数据库用来判断设备在线状态。具体做法是在PC端的服务进程里单独开一个线程每隔10秒往所有已连接的socket发一条{cmd:ping}ESP32在socket.onMessage回调里如果收到ping立即回一条{cmd:pong}。如果连续3次30秒没有收到对端消息ESP32主动断开当前socket并从startConnect()重新开始连接。如果Wi-Fi本身也断了则需要先重新执行WiFi.reconnect()再建立TCP连接。这个机制能解决大多数情况下“设备显示在线但实际失联”的假象。第二个建议是启用ESP32的看门狗。ESP32的Arduino框架支持两种看门狗一种是可以被喂狗重置的任务看门狗另一种是硬件看门狗。如果在红外中断处理函数里出现死循环或过长阻塞系统会直接重启。我给系统分了两层看门狗主循环定时喂狗超过5秒未喂则触发重启红外中断函数里绝对不执行Wi-Fi相关操作因为中断上下文中调用阻塞型网络API会导致panic。对于云主机服务器的部署不建议把所有模块都跑在一个脚本里。我习惯把服务端拆成两个进程一个监听ESP32的TCP连接专门负责接收事件和下发指令另一个是HTTP服务给管理人员提供网页端的日志查询和布防/撤防界面。两者通过本地SQLite数据库共享数据。这样即使网页服务崩了TCP通道仍然稳定ESP32的报警数据也不会丢。最后说一个略带教训的经验我在第一次部署这套系统时没有给电磁锁的电源做独立开关导致每次断电重启时电磁锁瞬间吸合一下误开锁了几次。后来在电源进线处加了一个延时继电器上电3秒后才接通电磁锁电源彻底解决了这个问题。硬件调试中的小细节往往比多写几行代码更有价值。如果你准备照着这个方案做一套我的建议是先从最小闭环开始一块ESP32、一个红外对管、一个蜂鸣器跑通中断检测和报警逻辑再接电磁锁和继电器最后才引入PC端人脸识别。每一步都验证了再往前走可以帮你把变量控制在一个合理的范围内不至于最后连在哪一步翻车的都找不着。希望帮到你。本文还有配套的精品资源点击获取