从零打造智能门禁:树莓派4B + 人脸识别实战全记录
最近在折腾一个让我既兴奋又头疼的小项目——用一块树莓派4B,搭一套能“认脸开门”的智能门禁系统。听起来像是科技公司的产品?其实成本不到500块,代码全是自己写的,硬件也是淘宝拼凑的。今天就来分享下这个DIY全过程,不讲空话套话,只聊真实踩过的坑、调过的参数、跑得动的代码。
为什么是树莓派4B?
市面上做嵌入式开发的板子不少,Nano、ESP32也能跑基础逻辑,但要做实时人脸识别+网络服务+远程访问这一整套闭环,还得靠性能在线的平台。
我最终选了Raspberry Pi 4 Model B(8GB内存版),原因很实际:
| 关键指标 | 树莓派4B表现 | 实际意义 |
|---|---|---|
| CPU | 四核Cortex-A72 @1.5GHz | 能跑轻量级深度学习模型 |
| 内存 | 最高8GB LPDDR4 | 多任务不卡顿,缓存视频流更稳 |
| 视频输入 | CSI-2接口 + 支持USB摄像头 | 可接官方摄像头,低延迟采集 |
| 网络 | 千兆以太网 + 双频Wi-Fi | 远程推流不断连 |
| GPIO | 40针通用引脚 | 控制继电器毫无压力 |
坦白说,如果你打算做个“按按钮开灯”级别的小玩具,树莓派有点杀鸡用牛刀。但一旦涉及图像处理+联网+本地决策,它几乎是目前性价比最高的选择。
更重要的是——社区资源太丰富了。OpenCV、PyTorch都能装,GitHub上随便一搜就是成堆的参考项目。省下的时间,够你多调试三轮算法。
核心技术拆解:人脸怎么“看懂”你是谁?
整个系统的灵魂当然是人脸识别。很多人一听就觉得高深莫测,其实现在开源工具链已经非常成熟,关键在于如何让它在树莓派这种边缘设备上跑得快、认得准。
我的技术路线:MTCNN + FaceNet 组合拳
没有用什么商业SDK,全部基于facenet_pytorch库实现,两个核心组件:
- MTCNN:负责从画面中“找脸”
- InceptionResnetV1(FaceNet变体):把脸变成一串数字特征向量
这套组合的好处是:
- 模型小(<100MB),适合部署
- 准确率高,在LFW数据集上能达到99%+
- PyTorch支持良好,推理过程可控性强
实战代码优化版(亲测可用)
下面这段是我最终定稿的人脸识别函数,比原生示例更稳定,加了置信度过滤和异常处理:
import cv2 import torch import numpy as np from facenet_pytorch import MTCNN, InceptionResnetV1 # 自动检测是否可用CUDA(虽然Pi没有GPU,但保持结构统一) device = 'cpu' # Raspberry Pi无独立GPU mtcnn = MTCNN(keep_all=True, thresholds=[0.6, 0.7, 0.7], device=device) resnet = InceptionResnetV1(pretrained='vggface2').eval().to(device) # 加载已注册用户的特征库 registered_embeddings = { "张三": np.load("embeddings/zhangsan.npy"), "李四": np.load("embeddings/lisi.npy") } def recognize_face(frame): """ 输入BGR图像帧,返回是否匹配成功及姓名 """ boxes, probs = mtcnn.detect(frame) if boxes is None: return False, None for box, prob in zip(boxes, probs): if prob < 0.8: # 置信度太低直接跳过 continue x1, y1, x2, y2 = [int(b) for b in box] face_img = frame[y1:y2, x1:x2] try: face_tensor = mtcnn.preprocess(frame, box).unsqueeze(0).to(device) with torch.no_grad(): embedding = resnet(face_tensor).cpu().numpy() except Exception as e: print(f"特征提取失败: {e}") continue # 计算欧氏距离(越小越相似) min_distance = float('inf') matched_name = "未知" for name, saved_emb in registered_embeddings.items(): dist = np.linalg.norm(embedding - saved_emb) if dist < min_distance: min_distance = dist # 设定阈值(根据训练数据调整,一般0.8~1.0之间) if min_distance < 0.9: matched_name = list(registered_embeddings.keys())[ list(np.linalg.norm(embedding - v) for v in registered_embeddings.values()).index(min_distance) ] # 绘制框和标签 color = (0, 255, 0) if matched_name != "未知" else (0, 0, 255) cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2) cv2.putText(frame, f"{matched_name} ({min_distance:.2f})", (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2) if matched_name != "未知": return True, matched_name return False, None💡经验贴士:
- 阈值0.9是我通过测试几百张正脸/侧脸样本调出来的平衡点,太低会误识别,太高则熟人也进不来。
- 摄像头尽量固定位置,避免逆光或强阴影影响识别稳定性。
- 建议每人注册至少3张不同光照条件下的照片生成特征向量取平均。
如何让“识别结果”真正控制门锁?GPIO实战详解
识别出来了,接下来就得动手——怎么让软件判断变成物理世界的“咔哒”一声开门?
答案就是:GPIO + 继电器模块
硬件连接要点
我用的是常见的5V继电器模块(某宝十几块钱),接线如下:
| 树莓派GPIO | 接继电器端口 | 说明 |
|---|---|---|
| Pin 18 (BCM 18) | IN | 控制信号输入 |
| Pin 6 (GND) | GND | 共地 |
| 继电器COM | 12V电源正极 | 主回路接入点 |
| 继电器NO | 电磁锁正极 | 常开触点,通电闭合 |
| 电磁锁负极 | 接12V电源负极 | 构成完整回路 |
⚠️重要提醒:电磁锁工作电压通常是12V,绝对不能直接接到树莓派!必须通过继电器隔离,否则轻则烧IO口,重则主板报废。
控制代码封装成可靠函数
别小看这一步,我第一次写完就忘了清理GPIO,重启后发现继电器一直吸合……差点把锁烧了。
改进后的安全版本:
import RPi.GPIO as GPIO import time RELAY_PIN = 18 GPIO.setmode(GPIO.BCM) GPIO.setup(RELAY_PIN, GPIO.OUT) GPIO.output(RELAY_PIN, GPIO.LOW) # 初始状态断开 def unlock_door(duration=3): """ 打开门锁 duration 秒后自动关闭 """ try: GPIO.output(RELAY_PIN, GPIO.HIGH) print(f"[{time.strftime('%H:%M:%S')}] 门已开启") time.sleep(duration) GPIO.output(RELAY_PIN, GPIO.LOW) print(f"[{time.strftime('%H:%M:%S')}] 门已关闭") except Exception as e: print(f"开门过程中出错: {e}") finally: pass # 不在此处 cleanup,防止其他线程受影响 # 使用示例 if __name__ == "__main__": try: unlock_door(5) finally: GPIO.cleanup() # 程序退出时释放资源这个duration=3很关键——没人希望门一直开着。3秒足够进出,又能防尾随。
能不能不在现场也能开门?远程访问怎么搞?
设想一下:朋友来访你不在家,能不能手机一点,“滴”,门开了?
完全可以。我在树莓派上起了一个轻量级 Web 服务,前端用 HTML + JS 做了个简易面板,功能包括:
- 查看实时画面(MJPG-Streamer 推流)
- 点击按钮远程开门
- 查询最近10条出入记录
- 异常报警微信通知(后面讲)
Flask 服务最小可行实现
考虑到安全性,这里只暴露必要接口,并加入简单 token 验证:
from flask import Flask, jsonify, request import threading import logging app = Flask(__name__) SECRET_TOKEN = "your_very_secret_token_here" # 替换为你自己的密钥 is_locked = True @app.route('/status') def get_status(): return jsonify({ "locked": is_locked, "timestamp": time.time() }) @app.route('/unlock', methods=['POST']) def remote_unlock(): token = request.headers.get('X-Auth-Token') if token != SECRET_TOKEN: return jsonify({"success": False, "message": "认证失败"}), 403 global is_locked unlock_door(3) is_locked = False return jsonify({"success": True, "message": "门已开启"}) # 启动Web服务(单独线程) def start_web_server(): logging.basicConfig(level=logging.WARNING) app.run(host='0.0.0.0', port=8080, threaded=True) web_thread = threading.Thread(target=start_web_server) web_thread.daemon = True web_thread.start()然后通过 Nginx 反向代理 + Let’s Encrypt 证书实现 HTTPS,再配合花生壳之类的动态DNS,外网就能安全访问了。
🔐安全建议:
- 不要用默认端口80/443,容易被扫描
- 加IP白名单或登录页面更好
- Token定期更换
完整系统是如何运转的?
把所有模块串起来,整个流程就像一条流水线:
[摄像头] → [人脸检测] → [特征提取] → [比对数据库] → ↓(匹配成功) ↓(失败多次) [触发GPIO开锁] [记录日志 + 可选报警] ↓ [更新状态 → Web界面可见]主循环伪代码大致如下:
cap = cv2.VideoCapture(0) # 或使用 picamera2 while True: ret, frame = cap.read() if not ret: continue success, name = recognize_face(frame) if success: log_access(name, success=True) unlock_door(3) send_welcome_message(name) # 如语音播放“欢迎回家” # 显示画面(调试用) cv2.imshow('Door Access System', frame) if cv2.waitKey(1) == ord('q'): break cap.release() cv2.destroyAllWindows()实际部署中的那些“坑”
理论美好,落地才见真章。以下是几个血泪教训:
1. 电源问题最致命
一开始图省事,用同一个5V电源给树莓派和继电器供电,结果一启动继电器,树莓派直接重启——电压跌落严重!
✅ 解决方案:树莓派用专用5V/3A适配器,继电器和电磁锁另接12V/2A稳压电源,共地但不共电。
2. 摄像头角度决定识别率
装太高,拍到头顶;装太低,下巴都看不见。反复调试后发现:
✅最佳安装高度:1.6米左右,俯角10°~15°,人脸基本正对镜头。
3. 特征库要多样化
只用一张正面照注册?那遇到戴帽子、眼镜、晚上光线差的情况,基本就歇菜了。
✅ 建议每人录入5张以上:白天/夜晚、戴镜/不戴镜、微笑/自然表情。
4. 日志一定要持久化
系统运行几天后想查谁什么时候进来过?没做日志等于瞎子。
✅ 用 SQLite 存每条识别事件:
CREATE TABLE access_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, success BOOLEAN, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP );总结:这不是玩具,而是可用的解决方案
做完这套系统我才意识到,真正的智能不是炫技,而是解决问题。
它帮我解决了几个实实在在的问题:
- 不用带钥匙,刷脸进门,下雨天单手拎包也能进
- 临时访客来了,手机一点就能放行
- 有人半夜强行撬门?连续识别失败立刻发微信告警
- 所有出入都有记录,再也不用猜“到底是谁忘关门”
未来我还计划升级的方向:
- 接入 Home Assistant,实现“回家模式”自动开灯拉窗帘
- 用 TensorFlow Lite + Coral USB Accelerator 加速推理
- 添加语音提示:“您好,张三,门已为您打开”
- 结合温湿度传感器,做一个多功能门口机
如果你也在考虑做个属于自己的智能门禁,不妨从这块小小的树莓派开始。
代码我已经整理好放在 GitHub,硬件清单也不超过500元。
技术不该只是大厂的专利,每个人都可以亲手构建自己想要的生活方式。
👉 想要完整项目代码或遇到具体问题?欢迎留言交流,一起完善这个“家门口的AI守卫”。