做这个项目之前,我一直在纠结一个问题:为什么智能化喊了这么多年,公司门口、小区楼道用的还是那张一刷就过的IC卡?卡丢了要补办,卡被复制了毫无感知,访客来了还得保安亲自下来接。后来我把目光放到人脸识别门禁上,干脆自己从零撸了一套完整的智能门禁系统。项目做到一半我才发现,这东西比想象中有意思,也比想象中坑多。
这个项目表面上看是一个“摄像头+单片机开锁”的小玩意,真正落地时你会碰到人脸检测、特征提取、相似度比对、数据库存储、GPIO控制、硬件选型、光线干扰、活体检测等一系列问题。我尽量把整个设计和实操过程都摊开来讲,适合刚入门嵌入式AI的开发者、想做毕业设计或课程项目的学生,也适合想在公司内部搞一套低成本门禁方案的运维同学参考。
1. 项目设计与整体思路
1.1 为什么选择离线本地识别方案
人脸识别门禁市面上成品很多,基本分两类:一类是云端识别,摄像头把照片传到云端,识别完返回结果;另一类是本地离线识别,所有算法都在门口那台设备上跑。
我选择离线方案,原因很直接。公司或小区场景下,人员进出都是隐私数据,人脸照片属于生物特征信息,往第三方云服务器传本身就存在合规风险。再一个是延迟问题,云端识别无论如何都要经过网络往返,高峰期排队反而比刷卡还慢。离线识别的延迟基本可以控制在几百毫秒以内,断网也不影响使用,这对门禁这种可靠性要求高的场景太关键了。
另外成本上也有优势,离线方案不需要买云服务,不需要按调用次数付费,一台终端设备就能搞定所有事情。对于几十人到几百人的中小型团队,这笔账算下来非常划算。
1.2 整体识别流程与核心模块划分
整套系统的数据流,可以拆成下面这几个环节:
摄像头采集画面 → 人脸检测 → 人脸对齐与预处理 → 特征提取 → 特征比对 → 判定结果 → 控制门锁动作
每个环节都有专门的算法或硬件负责。摄像头负责“看”,人脸检测负责判断画面里有没有人脸以及人脸在哪里,预处理把人脸摆正、统一尺寸、调整光照影响,特征提取把人脸变成一串数字向量,比对环节计算当前特征和数据库里注册特征的相似度,最后根据相似度阈值决定是否开门。
一开始我打算用现成的云API快速验证,后来发现云端API的接口文档翻来覆去就那几个参数,对学习帮助不大。于是换成了本地部署模型的方式:用OpenCV做人脸检测,用FaceNet系列的轻量模型做特征提取,数据库用SQLite存特征向量。整个流程吃透了之后,再去看那些商业门禁机的宣传语,基本能猜出它们内部的大致结构。
1.3 技术栈选型的背后逻辑
选技术栈的时候,我给自己定了三个约束:只能在边缘设备上跑、必须实时、要能离线运行。
基于这三点,我排除了很多看着很炫但跑不动的方案。大尺寸的ResNet系列识别模型在树莓派上推理一次要好几秒,根本没法用。最终确定的技术栈是OpenCV做图像处理和人脸检测,特征提取用的是基于FaceNet思想训练的轻量化模型,跑在CPU上单次推理大约100毫秒左右。数据库用SQLite,因为它不需要单独起服务,文件型数据库在嵌入式设备上非常友好。
这套组合的优点是依赖少、部署简单、性能足够。缺点是如果门禁点位数特别多,比如一个园区几十个门,每台设备单独存一份特征库,管理起来会麻烦。不过对于单机门禁场景,这套方案是最务实的选择。
2. 硬件选型与电路设计
2.1 主控方案的取舍
主控是整个系统的“大脑”,决定了你的数据处理能力和开发效率。我前后对比了几种方案:
| 主控方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 树莓派4B | 生态成熟、资料多、性能够用 | 价格浮动大、功耗略高 | 中小型项目、学习验证 |
| Jetson Nano | GPU算力强、可以跑大模型 | 开发门槛高、散热麻烦 | 需要极强算力的复杂场景 |
| RK3588等国产板 | 性能功耗比好、接口丰富 | 资料相对少、上手成本高 | 产品化落地 |
| 香橙派/香蕉派 | 价格低、可玩性高 | 部分型号兼容性一般 | 低成本方案验证 |
最终我选了树莓派4B的4GB版本。原因很简单:遇到问题能搜到答案。树莓派的社区太庞大了,从系统烧录到GPIO控制,几乎每个坑都有人踩过。用OpenCV跑轻量化识别模型,CPU占用大约在40%左右,还有余量跑Web服务或者日志记录。
2.2 摄像头与识别模块如何选择
摄像头这块很多人会踩坑。树莓派官方CSI摄像头在树莓派上的兼容性确实好,但如果你是业余玩家或者想快速验证方案,我更推荐免驱USB摄像头,插上就能用。CSI接口一旦系统配置有变动,可能就要重新折腾驱动。
项目里我用的是一颗普通1080P免驱USB摄像头,30帧输出,价格几十块钱。识别的时候实际只需要取VGA分辨率(640x480),数据量小而且检测速度更快,清晰度完全够用。
网上流传的TX510人脸识别模块我也试过。它属于串口级别的成品模块,你通过串口协议给它发指令,它自己完成检测、特征提取和比对,然后把结果返回给主控。这种模块的优势是开发工作量极小,不需要懂任何算法,串口一接就能用。缺点是灵活度太低,模块内置的算法你改不了,光线适应性、识别距离、阈值调节都只能通过有限的参数控制,出了问题排查起来也很被动。
如果目的是快速做出一个demo,TX510这类模块是合适的;如果你想真正理解人脸识别门禁的原理,或者需要二次开发,一定选USB摄像头配合自研算法路线。做项目最重要的不是“看起来能用”,而是“问题来了能下手修”。
2.3 门锁控制与电气安全
门禁系统最终动作是开门,这就离不开继电器和电磁锁。我选用的是12V供电的电磁锁(也叫电插锁),通过一个5V触发的继电器模块控制。树莓派的GPIO引脚输出3.3V电压,驱动能力很弱,不能直接带动继电器线圈,所以中间需要加一个三极管或光耦做隔离驱动。
实际接线逻辑是:树莓派GPIO输出高电平 → 三极管导通 → 继电器线圈通电 → 常开触点闭合 → 电磁锁通电开锁。
这里必须强调一个安全问题。电磁锁是12V供电,树莓派和摄像头是5V供电,如果共用电源不规范,很容易造成电压不稳,严重的会烧主板。我的做法是使用一个12V 5A的开关电源给电磁锁供电,再用一个DC-DC降压模块把12V转成5V给树莓派供电。另外在继电器输出端并联一个反向续流二极管,防止电磁锁断开瞬间产生的反向电动势把继电器和GPIO击穿。
注意:涉及市电和锁具供电的操作,务必断开电源后再接线,接线前用万用表确认电压极性。如果完全没有电气经验,建议先用电工实验板做小电压验证。
3. 开发环境与基础配置
3.1 系统与基础依赖
系统我刷的是树莓派官方Raspberry Pi OS 64位版本。之前用兼容镜像遇到过不少莫名奇妙的坑,后来发现官方系统稳定性最好,软件源里的软件包也最全。
系统装好后第一步是更新软件源和系统包:
sudo apt update && sudo apt upgrade -y sudo apt install python3-pip python3-dev python3-venv git cmake build-essential我习惯把项目依赖装在独立的Python虚拟环境里,避免和系统Python环境互相污染:
python3 -m venv ~/face-door/env source ~/face-door/env/bin/activate3.2 安装OpenCV的经典痛点
OpenCV的安装是个老生常谈的问题,每次做视觉项目都能碰到。最简单的安装方式是直接用pip:
pip install opencv-python opencv-contrib-python在树莓派上直接用pip安装的OpenCV是预编译版本,能正常跑,但有几个细节要注意。第一,opencv-python和opencv-contrib-python不要混装,否则会出现符号冲突,报一堆莫名其妙的“undefined symbol”错误。第二,预编译版本不一定包含GStreamer支持,如果你需要读取RTSP视频流,可能会发现cv2.VideoCapture无法打开网络摄像头。
我的处理方式是先用pip装预编译版把流程跑通,后期确认项目稳定后再考虑源码编译。源码编译OpenCV在树莓派上非常耗时,我编译过一次,用了将近两个小时,性能提升并不明显,除非你需要特定的扩展模块,否则不建议新手一上来就编译源码。
3.3 核心依赖清单
项目主要依赖如下:
- Python 3.9+
- OpenCV 4.x
- NumPy(图像矩阵运算)
- dlib(可选,用于人脸关键点检测,配合活体检测)
- sqlite3(Python内置,无需额外安装)
- RPi.GPIO(树莓派GPIO控制)
- Flask(可选,用于搭建本地管理后台)
dlib的安装稍微麻烦一点,它需要cmake编译,安装时务必先确保系统里有build-essential和cmake。遇到编译进度卡住的情况,通常是因为内存不够,建议增加swap空间,或者用pip的预编译wheel包。
4. 人脸识别核心算法实现
4.1 人脸检测:Haar还是DNN
人脸检测是整个识别流程的入口。OpenCV提供了多种检测方案,最经典的是Haar Cascade级联分类器,当时因为它轻量、速度快被广泛应用。你只需要加载一个xml文件,几行代码就能实现人脸检测。
但我在实际测试中发现,Haar对侧脸、暗光、遮挡的漏检率偏高,稍微歪一下头就检测不到。而OpenCV的DNN人脸检测器在鲁棒性上明显更好,它是基于SSD框架训练的残差网络模型,对角度和光线变化的容忍度更高,在树莓派CPU上检测一帧大概耗时80毫秒,完全在可接受范围内。
两种方案的对比如下:
| 检测方式 | 模型体积 | 检测速度 | 鲁棒性 | 适用场景 |
|---|---|---|---|---|
| Haar Cascade | 约1MB | 极快 | 一般 | 正脸、光线均匀场景 |
| OpenCV DNN (SSD) | 约10MB | 较快 | 较强 | 角度变化、复杂光线 |
4.2 加载DNN检测器的核心代码
用OpenCV加载DNN人脸检测器需要两个文件:模型结构文件和训练好的权重文件,可以从OpenCV官方GitHub仓库下载。
import cv2 # 加载DNN人脸检测模型 net = cv2.dnn.readNetFromCaffe( "deploy.prototxt", "res10_300x300_ssd_iter_140000.caffemodel" ) def detect_faces(frame): h, w = frame.shape[:2] # 缩放至300x300输入网络 blob = cv2.dnn.blobFromImage( cv2.resize(frame, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0) ) net.setInput(blob) detections = net.forward() faces = [] for i in range(detections.shape[2]): confidence = detections[0, 0, i, 2] if confidence < 0.7: continue box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) x1, y1, x2, y2 = box.astype("int") # 保证边界不越界 x1, y1 = max(0, x1), max(0, y1) x2, y2 = min(w, x2), min(h, y2) faces.append((x1, y1, x2, y2, confidence)) return facesconfidence阈值设为0.7是我反复测试后的经验值。设太高会漏检,比如0.85以上在光线不好时经常检测不到人脸;设太低会出现大量误检框,比如把墙上的装饰画当成人脸,后面特征提取就会浪费算力。
4.3 特征提取:把人脸变成一组数字
人脸识别的本质不是“认脸”,而是“比对特征”。我们需要把一张人脸图像变成一组有区分度的数字向量,这个过程就是特征提取。
项目里我采用的是基于FaceNet思想训练的模型。FaceNet的核心思路是用卷积神经网络把一张人脸图像映射到一个128维的欧氏空间中,同一个人的不同照片在空间中的距离很近,不同人的照片距离很远。这样识别问题就转化成了向量距离计算问题。
模型加载和特征提取的代码大致如下:
from facenet_pytorch import InceptionResnetV1 import torch from PIL import Image import numpy as np # 加载预训练模型 model = InceptionResnetV1(pretrained='vggface2').eval() def get_embedding(face_img): # face_img是一张已经裁剪好并对齐的人脸BGR图像 rgb_img = cv2.cvtColor(face_img, cv2.COLOR_BGR2RGB) pil_img = Image.fromarray(rgb_img).resize((160, 160)) # 转换为tensor并归一化 face_tensor = torch.tensor(np.array(pil_img)).permute(2, 0, 1).float() face_tensor = (face_tensor - 127.5) / 128.0 face_tensor = face_tensor.unsqueeze(0) with torch.no_grad(): embedding = model(face_tensor).numpy().flatten() # 归一化,便于后续计算余弦相似度 embedding = embedding / np.linalg.norm(embedding) return embedding如果你不太想引入PyTorch这么大的依赖,也可以使用face_recognition库,它底层封装了dlib的模型,调用一行代码就能拿到128维特征向量,开发效率非常高。但face_recognition在树莓派上的推理速度稍慢,而且它内部封装的模型相对固定,后续想换更好的模型就不太灵活。
4.4 相似度计算与阈值设定
拿到特征向量后,比对环节用余弦相似度衡量两段特征向量的距离。余弦相似度的计算公式是:
cosine_similarity = (A · B) / (||A|| × ||B||)
其中A和B是两个人脸的特征向量,点积除以模长乘积。结果范围在-1到1之间,数值越大表示越相似。
我在上一节已经把特征向量归一化了,所以模长等于1,余弦相似度实际上就等于点积:
def cosine_similarity(embedding1, embedding2): return float(np.dot(embedding1, embedding2))关于阈值,我测试了多组数据:
| 阈值 | 误识率 | 拒识率 | 结论 |
|---|---|---|---|
| 0.75 | 极低 | 很高 | 熟人经常打不开门,不实用 |
| 0.65 | 低 | 中等 | 光线不好时识别率下降明显 |
| 0.55 | 中等 | 较低 | 整体体验好,安全性尚可 |
| 0.45 | 较高 | 极低 | 容易误开门,不适合门禁 |
最终我采用的阈值是0.55。在办公室环境下,这个阈值能让大多数人正常通行,同时对不是同一个人但长得比较像的情况有一定的拦截率。当然,门禁的安全级别和使用场景直接相关,如果用在机房等高安全区域,阈值应调高到0.65以上。
4.5 完整识别主流程代码
把检测、特征提取、比对串起来,就形成了门禁系统的核心识别逻辑:
import cv2 import sqlite3 import numpy as np def recognize(frame, known_embeddings, threshold=0.55): faces = detect_faces(frame) if len(faces) == 0: return None, "no_face" # 取最大人脸进行识别,避免多人场景下误选 faces.sort(key=lambda f: (f[2]-f[0])*(f[3]-f[1]), reverse=True) x1, y1, x2, y2, conf = faces[0] face_img = frame[y1:y2, x1:x2] embedding = get_embedding(face_img) best_id = None best_score = -1 for user_id, known_emb in known_embeddings.items(): score = cosine_similarity(embedding, known_emb) if score > best_score: best_score = score best_id = user_id if best_score >= threshold: return best_id, best_score return None, best_score实际测试中,单人识别全流程大约耗时300毫秒,也就是一秒钟能处理3帧左右,对于门禁场景已经绰绰有余。需要优化时,可以考虑跳帧检测,比如每3帧只做一次完整识别,能在不影响体验的前提下显著降低CPU占用。
5. 数据库设计与门禁控制逻辑
5.1 SQLite表结构与特征存储方式
人脸特征向量是一个128维的浮点数组,直接存成Python对象会占用大量空间,而且每次读取都要重新解析。SQLite里可以用BLOB类型保存二进制数据,存取效率都不错。
建表语句如下:
CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, department TEXT, face_embedding BLOB NOT NULL, photo_path TEXT, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS recognition_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, name TEXT, score REAL, result TEXT, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP );特征向量写入数据库时,用NumPy的tobytes()把数组转成二进制,读取时再通过np.frombuffer()恢复:
import numpy as np import sqlite3 def save_embedding(user_id, embedding): binary = embedding.astype(np.float32).tobytes() cursor.execute( "UPDATE users SET face_embedding=? WHERE id=?", (binary, user_id) ) conn.commit() def load_embeddings(): cursor.execute("SELECT id, name, face_embedding FROM users") rows = cursor.fetchall() known = {} for user_id, name, binary in rows: emb = np.frombuffer(binary, dtype=np.float32) known[user_id] = emb return known这里有一个容易踩的细节:数组默认是float64,转成二进制后体积翻倍,读取时类型对不上还会报错。我统一用float32存储,这样128维向量只占512字节,几百人的特征库才几百KB,SQLite文件依然很小。
5.2 注册新用户的人脸录入流程
正常使用门禁前,需要先把人员的照片录入系统生成特征向量。我设计了两种录入方式:一种是直接在采集页面用摄像头拍一张照片,另一种是上传照片文件。
拍照录入的核心过程是:实时显示摄像头画面 → 检测到人脸时自动抓拍 → 裁剪人脸区域 → 提取特征 → 保存到数据库 → 提示录入成功。
实操中发现,录入照片的质量直接决定识别效果。照片必须是正脸、光线均匀、没有遮挡。我录完第一批用户后发现识别率偏低,排查了很久才发现,原来有些人录入时是笑着的,有些是逆光的,还有人戴了帽子,特征向量差异很大,导致后面识别时频繁失败。
后来我在录入界面加了一道审查逻辑:拍照时如果检测不到人脸或者人脸置信度低于0.75,直接拒绝保存并提示重新拍摄。这看似简单的限制,把识别准确率提升了将近一倍。
5.3 活体检测:防止一张照片就开门
如果你只做到上面那一步,那这套门禁系统在安全上是不过关的。拿一张打印的A4纸照片放在摄像头前,很可能就被直接放行了。这是所有基于2D人脸识别的门禁系统都要面对的问题,也就是防照片攻击和防视频攻击。
基础方案是活体检测。常见手段包括:眨眼检测、张嘴检测、头部姿态变化检测、红外深度图检测、屏幕反光检测等。
我在项目上首先实现了基于dlib的人脸关键点检测,通过计算眼睛纵横比EAR(Eye Aspect Ratio)来判断是否有眨眼动作:
def eye_aspect_ratio(eye_points): # 计算上下眼睑距离 A = np.linalg.norm(eye_points[1] - eye_points[5]) B = np.linalg.norm(eye_points[2] - eye_points[4]) # 计算水平眼距 C = np.linalg.norm(eye_points[0] - eye_points[3]) return (A + B) / (2.0 * C)正常情况下,人眼EAR值在0.25左右,闭眼时降到0.1以下。识别时连续检测几帧,如果EAR值有明显从高到低的波动,说明画面里有眨眼动作。照片是静止的,EAR值恒定不变,直接判为攻击。
当然,这种2D活体检测对视频攻击(比如用手机录制一段眨眼视频)是拦不住的。更进一步可以结合红外模块或者结构光深度传感器,但那种方案成本和复杂度都会大幅上升。对普通办公环境,眨眼检测已经能把安全等级提升一个档次。
5.4 GPIO控制电子锁的执行代码
识别成功后的动作是开锁。树莓派的GPIO控制继电器,继电器驱动电磁锁。开锁逻辑需要加一个延时自动落锁,防止门打开了忘记关。
import RPi.GPIO as GPIO import time LOCK_PIN = 18 UNLOCK_SECONDS = 5 GPIO.setmode(GPIO.BCM) GPIO.setup(LOCK_PIN, GPIO.OUT) GPIO.output(LOCK_PIN, GPIO.LOW) def unlock_door(): # 给继电器一个高电平,触发开锁 GPIO.output(LOCK_PIN, GPIO.HIGH) time.sleep(UNLOCK_SECONDS) GPIO.output(LOCK_PIN, GPIO.LOW) def close_door(): GPIO.output(LOCK_PIN, GPIO.LOW)有一个细节需要注意:程序退出时一定要调用GPIO.cleanup(),否则GPIO引脚会保持最后的状态,下次程序启动时可能因为引脚状态不对导致继电器误动作。我在部署时还加了一个“看门狗”脚本,每隔几秒检查识别主进程是否还在,一旦发现进程死掉就自动重启,保证门禁系统不至于默默罢工。
6. 常见问题与排查技巧实录
6.1 摄像头打开失败或画面卡死
树莓派上使用USB摄像头最常碰到的问题是设备节点被占用。多个程序同时打开/dev/video0会导致后打开的程序报错。
排查步骤:
- 用lsusb确认摄像头被系统识别
- 用ls /dev/video*查看设备节点
- 用v4l2-ctl --list-devices查看摄像头详细信息
- 确认代码里打开的是正确的设备编号
另外OpenCV的VideoCapture在某些摄像头断开重连后不会自动恢复,需要在代码里增加重连机制。我的做法是循环检测cap.isOpened(),如果返回False,释放资源并休眠3秒后重新打开。
6.2 识别率白天高晚上低
这是所有2D视觉方案都逃不过的问题。光线对人脸识别的影响极其显著,同一张脸在自然光和昏暗灯光下提取的特征向量差异可能超过阈值范围。
我的解决方案分三步:第一,摄像头位置尽量避开逆光,门口加一盏补光灯;第二,OpenCV里用直方图均衡化和伽马校正做图像增强,改善暗部细节;第三,在特征提取前对裁剪好的人脸区域做标准化处理。
def preprocess_face(face_img): # 转为灰度后做直方图均衡化 gray = cv2.cvtColor(face_img, cv2.COLOR_BGR2GRAY) equalized = cv2.equalizeHist(gray) return cv2.cvtColor(equalized, cv2.COLOR_GRAY2BGR)虽然不是特别高级的做法,但对改善暗光识别率帮助很大。如果你的预算充足,直接选带红外补光灯的摄像头效果最好。
6.3 阈值怎么调都有人打不开门
遇到这种情况先别怀疑算法,大概率是注册照片的质量问题。我排查过很多次之后发现,绝大多数识别失败都是因为注册时采集的照片不标准。
解决思路是重新采集注册照片,并且按照下面标准执行:正脸,双眼睁开,面部无遮挡,光照均匀,不能逆光,不能低头仰头,表情自然。录制时取连续3帧照片各提取一组特征向量,取平均值存入数据库,识别稳定性会有明显提升。
6.4 OpenCV源码编译卡死
树莓派内存只有4GB,源码编译OpenCV时多个编译任务并行,很容易OOM。解决方式是减少并行编译线程数,同时扩大swap空间。
sudo nano /etc/dphys-swapfile # 设置 CONF_SWAPSIZE=2048 sudo systemctl restart dphys-swapfile然后编译时指定:
make -j2虽然编译时间变长了,但至少不会中途死掉。不过还是那句话,除非有特殊需求,不建议新手用源码编译OpenCV,社区里针对树莓派的换源方案和预编译wheel包已经很成熟了。
6.5 树莓派重启后服务不自动启动
门禁设备大概率不会有人守在旁边手动启动程序,所以必须配置开机自启。我用了systemd服务来做:
[Unit] Description=Face Door Recognition Service After=network.target [Service] ExecStart=/home/pi/face-door/env/bin/python /home/pi/face-door/main.py WorkingDirectory=/home/pi/face-door Restart=always User=pi [Install] WantedBy=multi-user.target这个配置比较关键的是Restart=always,它保证主程序崩溃退出后会被systemd自动拉起。我把这个服务文件放在/etc/systemd/system/face-door.service,然后:
sudo systemctl daemon-reload sudo systemctl enable face-door.service sudo systemctl start face-door.service部署完之后,TV断电重启也不会影响门禁使用,省去了每天手动启动的麻烦。
6.6 多特征匹配的优化方向
当录入人员超过几百人,每次识别都全表遍历一遍特征库,速度会逐渐变慢。普通门禁场景几百人问题不大,但如果考虑扩展,可以做两件事:一是按类聚中心把特征分成多个簇,先匹配最近的簇再精确匹配;二是用FAISS这类向量检索库建立索引。
对于树莓派这种算力有限的设备,更推荐第一个方案。编码量不大,但能把匹配时间从线性降到对数级别。这个优化我目前还没完全落地,只做了个原型验证,等数据量上来之后会补上。
项目扩展方向与个人体会
这个项目做到上线运行后,我在实际使用中感受最深的不是那些算法效果,而是“稳定压倒一切”。刚开始我做了一堆炫酷的功能,比如识别到人之后播放语音欢迎词、根据部门推送不同的问候语,后来发现这些功能在门禁场景里都是次要的。真正重要的是:设备不能死机、不能漏识别、不能延迟开锁、不能在断电后丢失数据。稳定性远比功能多重要。
后续我可以在这个框架上继续扩展的点包括:接入钉钉或企业微信的人员同步接口,实现人员离职后自动删除权限;增加多门联动的考勤统计;通过MQTT把开门事件上报到服务器做统一审计。这套设计的价值不在于代码本身,而在于它把图像处理、算法模型、嵌入式控制、数据存储这些知识真正串起来,让我能在一套系统里看到全貌。
最后再分享一个小技巧:如果你是第一次做类似项目,先别急着买一堆硬件,先用电脑的摄像头加虚拟机把识别流程跑通,确认算法没问题之后再买树莓派和电磁锁。软件不成熟的时候贸然接线,只会增加排查问题的难度。人识别开门这个事情的体验,真的只有自己亲手做完,才会发现它比想象中容易,也比想象中有趣。