虚谷号嵌入式人脸识别实战:从OpenCV DNN到轻量化模型部署
1. 项目概述:从一块开发板到人脸识别应用
最近在捣鼓一个挺有意思的小玩意儿——虚谷号。这名字听起来有点玄乎,其实它是一块面向教育和创客领域的国产开源硬件,你可以把它理解成一个功能更强大、接口更丰富的“树莓派”类产品。我手头这块板子,自带了一个摄像头接口,于是我就琢磨着,能不能在上面跑一个实时的人脸识别应用?比如做个智能门禁的雏形,或者一个能打招呼的互动装置。
这个想法听起来简单,但实操起来,从环境搭建到模型部署,再到性能优化,每一步都有不少门道。网上关于虚谷号的资料相对零散,而人脸识别本身又是一个涉及操作系统、编程环境、计算机视觉库和深度学习模型的复杂技术栈。我花了差不多一周时间,踩了无数坑,终于把整个流程跑通并优化到了一个可用的状态。这篇文档,就是我这次折腾的完整记录,目标是把一个看似高大上的“人脸识别”项目,拆解成一步步可执行、可复现的操作,无论你是想学习计算机视觉入门,还是想在嵌入式设备上实现类似功能,相信都能从中找到参考。
2. 核心思路与方案选型
在虚谷号上做人脸识别,本质上是在资源受限的嵌入式设备上部署一个计算机视觉应用。我们需要一个完整的软件栈来支撑:操作系统、Python环境、视觉库、人脸检测与识别模型,以及最后的应用逻辑。
2.1 为什么选择虚谷号?
虚谷号通常预装了基于Linux的操作系统(如Ubuntu或定制发行版),这为我们提供了丰富的软件生态和命令行操作能力。其硬件配置一般包含多核ARM处理器、1GB或以上的内存,以及GPIO、摄像头等接口。这个配置跑轻量级的人脸识别是可行的,但和PC相比,资源依然紧张。因此, “轻量化” 是整个项目的核心设计原则。我们不能直接使用在服务器上运行的那些庞大模型,必须为嵌入式环境量身定制。
2.2 技术栈选型背后的考量
-
编程语言与主库:Python + OpenCV
- Python :在AI和嵌入式开发领域生态最丰富,库安装和代码编写都最快捷。
- OpenCV :计算机视觉的“瑞士军刀”。它提供了最基础也最高效的图像读取、显示、预处理(缩放、灰度化)功能,以及一些传统的人脸检测算法(如Haar级联分类器)。我们的流程会重度依赖它。
-
人脸检测方案:双引擎策略
- 首选:OpenCV DNN + 轻量级检测模型 。这是性能与精度的平衡点。OpenCV的
dnn模块可以直接加载和运行用Caffe、TensorFlow、ONNX等格式保存的深度学习模型。我们选择像 “MobileNet-SSD” 或专门为边缘计算优化的 “Ultra-Light-Fast-Generic-Face-Detector-1MB” 这样的模型。它们模型文件小(几MB),推理速度快,在虚谷号上能达到接近实时的检测速度。 - 备选:OpenCV Haar Cascade 。这是一个非常经典的基于特征的方法,模型文件小,速度极快,但精度和抗干扰能力(如侧脸、遮挡、光照变化)远不如深度学习模型。它可以作为备用方案,或在检测到人脸后用于辅助关键点定位。
- 首选:OpenCV DNN + 轻量级检测模型 。这是性能与精度的平衡点。OpenCV的
-
人脸识别方案:特征提取与比对
- 人脸识别分为两步:一是从检测到的人脸框中提取出具有区分度的特征向量(一串数字);二是对比两个特征向量的相似度。
- 我们同样采用轻量级深度学习模型来完成特征提取。 FaceNet 或 MobileFaceNet 是经典选择,它们能将一张人脸图像映射到一个128维或512维的特征空间,同一个人的不同照片在这个空间里的距离会很近。我们可以使用预训练好的模型,直接调用进行特征提取。
- 识别过程则简化为:1. 提取当前人脸特征;2. 与预先注册好的特征库中的每一个特征计算距离(如欧氏距离、余弦相似度);3. 如果最小距离小于某个阈值,则认为是同一个人。
-
应用框架与优化
- 使用简单的循环来读取摄像头帧,并在每一帧上执行“检测->识别->显示”的流程。
- 性能优化是关键:包括图像帧缩放、间隔帧检测、异步处理等技巧,后续会详细说明。
这个技术栈的选择,核心思想是 “在保证功能可用性的前提下,最大限度地降低计算和存储开销” ,以适应虚谷号的硬件条件。
3. 环境准备与依赖安装
在开始写代码之前,我们需要一个干净、健全的软件环境。假设你的虚谷号已经安装了Ubuntu 18.04/20.04或类似系统,并可以连接网络。
3.1 系统更新与基础工具
首先,打开终端,更新软件包列表并安装一些必要的编译工具和Python环境管理工具。
sudo apt update
sudo apt upgrade -y
sudo apt install -y build-essential cmake pkg-config
sudo apt install -y python3-dev python3-pip python3-venv
使用虚拟环境是一个好习惯,它能避免项目间的依赖冲突。
cd ~
mkdir face_recog_project
cd face_recog_project
python3 -m venv venv
source venv/bin/activate
激活虚拟环境后,你的命令行提示符前会出现 (venv) 字样。
3.2 安装OpenCV
在嵌入式设备上从源码编译OpenCV非常耗时,且容易出错。最稳妥的方法是安装预编译的版本。OpenCV-Python包( opencv-python 和 opencv-contrib-python )是官方预编译的轮子,通常兼容性很好。
pip install opencv-python==4.5.5.64
# 如果你需要一些额外的贡献模块(某些版本的SIFT/SURF等),可以也安装contrib版,但主体版本已足够。
# pip install opencv-contrib-python==4.5.5.64
注意 :直接
pip install opencv-python可能会安装最新版,但最新版有时存在与系统底层库的兼容性问题。指定一个经过广泛测试的版本(如4.5.5)更为稳妥。如果安装失败,可能需要先安装一些系统依赖:sudo apt install -y libsm6 libxrender1 libxext6 libgl1-mesa-glx。
3.3 安装其他Python库
我们需要一些辅助库来处理模型、数学运算和图像处理。
pip install numpy
pip install scikit-learn # 用于可选的更高级的相似度计算或分类器
pip install pillow # 图像处理库,有时比OpenCV的Python接口更易用
3.4 准备人脸检测与识别模型
这是最关键的一步。我们需要下载预训练好的模型文件。
-
人脸检测模型 :以Ultra-Light-Fast-Face-Detector为例。我们需要其模型定义文件(
.prototxt)和权重文件(.caffemodel)。你可以在GitHub上搜索该项目的发布页面下载。- 假设我们下载后得到:
RFB-320.prototxt和RFB-320.caffemodel。 - 将它们放在项目目录下的
models/文件夹中。
- 假设我们下载后得到:
-
人脸识别模型 :这里我们使用一个轻量化的FaceNet变体,或者直接使用OpenCV的FaceRecognizerSF模块(OpenCV 4.5.1+)。为了更通用,我们选择下载一个ONNX格式的轻量人脸识别模型,例如来自
insightface项目的arcface_r100_v1转换后的ONNX模型。- 下载得到的模型文件,例如:
face_recognition.onnx。 - 同样放入
models/文件夹。
- 下载得到的模型文件,例如:
你可以使用 wget 命令在虚谷号上直接下载:
mkdir -p ~/face_recog_project/models
cd ~/face_recog_project/models
# 示例下载命令,实际URL需替换为有效的模型文件直链
# wget -O RFB-320.prototxt [模型prototxt文件URL]
# wget -O RFB-320.caffemodel [模型权重文件URL]
# wget -O face_recognition.onnx [ONNX模型文件URL]
由于模型文件可能较大,如果虚谷号下载慢,也可以在PC上下载后通过SFTP工具(如FileZilla)传输到虚谷号上。
4. 核心代码实现与分步解析
环境就绪,模型到位,现在开始编写核心代码。我们将创建一个名为 face_recog_虚谷号.py 的脚本。
4.1 初始化与模型加载
import cv2
import numpy as np
import os
from datetime import datetime
# 初始化摄像头
# 虚谷号的摄像头通常是 /dev/video0,如果外接USB摄像头也可能是 /dev/video1
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("错误:无法打开摄像头。请检查连接。")
exit()
# 设置摄像头分辨率,降低分辨率可以显著提升处理速度
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
# 定义路径
model_dir = 'models'
det_prototxt = os.path.join(model_dir, 'RFB-320.prototxt')
det_model = os.path.join(model_dir, 'RFB-320.caffemodel')
recog_model = os.path.join(model_dir, 'face_recognition.onnx')
# 加载人脸检测模型 (Caffe)
det_net = cv2.dnn.readNetFromCaffe(det_prototxt, det_model)
# 加载人脸识别模型 (ONNX)
recog_net = cv2.dnn.readNetFromONNX(recog_model)
# 设置计算后端和目标(可选,使用CPU)
# 虚谷号通常没有独立的GPU,所以使用CPU
det_net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
det_net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)
recog_net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
recog_net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)
# 定义一些参数
conf_threshold = 0.7 # 人脸检测置信度阈值
recog_threshold = 0.6 # 人脸识别相似度阈值(具体阈值需根据模型和距离度量方式调整)
known_face_encodings = [] # 存储已知人脸特征向量的列表
known_face_names = [] # 存储对应人名的列表
print("模型加载完毕,开始摄像头捕捉...")
代码解析 :
- 我们使用OpenCV的
VideoCapture打开默认摄像头(索引0)。 set函数将分辨率设为640x480,这是一个在精度和速度间很好的平衡点。320x240更快,但可能影响识别精度。cv2.dnn.readNetFrom...系列函数是加载深度学习模型的关键。我们分别加载了Caffe格式的检测模型和ONNX格式的识别模型。setPreferableBackend/Target指定使用OpenCV后端和CPU进行计算,这是虚谷号上的标准配置。conf_threshold用于过滤掉检测结果中置信度低的人脸框,避免误检。known_face_encodings和known_face_names是我们的人脸数据库,将在“注册”阶段填充。
4.2 人脸注册功能实现
在识别之前,系统需要知道“谁是谁”。我们需要一个注册新面孔的功能。
def register_new_face(frame, face_box, name):
"""
注册一张新人脸。
:param frame: 原始图像帧
:param face_box: 人脸边框 (x, y, w, h)
:param name: 该人脸对应的名字
"""
x, y, w, h = face_box
# 确保边框在图像范围内
x, y, w, h = max(0, x), max(0, y), w, h
face_roi = frame[y:y+h, x:x+w]
if face_roi.size == 0:
print("注册失败:人脸区域无效。")
return False
# 将人脸区域调整为识别模型所需的输入尺寸,例如112x112
face_blob = cv2.dnn.blobFromImage(face_roi, scalefactor=1.0/127.5, size=(112, 112),
mean=(127.5, 127.5, 127.5), swapRB=True)
# 提取特征
recog_net.setInput(face_blob)
encoding = recog_net.forward()
# 通常forward()输出是一个多维数组,我们需要将其扁平化为一维向量
encoding = encoding.flatten()
# 添加到已知数据库
known_face_encodings.append(encoding)
known_face_names.append(name)
print(f"成功注册: {name}")
return True
# 示例:如何调用注册函数(通常在另一个模式或通过按键触发)
# 假设我们有一个检测到的人脸框 `box` 和用户输入的名字 `input_name`
# register_new_face(current_frame, box, input_name)
实操要点 :
cv2.dnn.blobFromImage是预处理函数,它将图像转换为深度学习模型需要的输入格式(blob)。参数scalefactor、mean和swapRB必须与模型训练时的预处理方式严格一致,否则特征提取会出错。这里(127.5, 127.5, 127.5)的均值和1.0/127.5的缩放因子,对应的是将像素值从[0,255]归一化到[-1,1],这是很多模型的常见做法。- 模型所需的输入尺寸
(112, 112)需要根据你下载的具体模型来确定,常见的有112x112或96x96。 - 提取出的
encoding就是这个人脸的“数字指纹”。我们将其保存起来。
4.3 主循环:检测、识别与显示
这是程序的核心循环,不断从摄像头抓取帧,进行处理。
frame_count = 0
detect_interval = 2 # 每2帧做一次人脸检测,以提升性能
while True:
ret, frame = cap.read()
if not ret:
print("无法获取帧。退出中...")
break
# 性能优化:只在特定间隔帧进行检测
if frame_count % detect_interval == 0:
# 创建一个用于检测的blob,检测模型通常需要300x300的输入
h, w = frame.shape[:2]
blob = cv2.dnn.blobFromImage(frame, scalefactor=1.0, size=(300, 300),
mean=(104.0, 177.0, 123.0), swapRB=False)
det_net.setInput(blob)
detections = det_net.forward()
current_face_locations = []
current_face_encodings = []
current_face_names = []
# 遍历所有检测结果
for i in range(detections.shape[2]):
confidence = detections[0, 0, i, 2]
if confidence > conf_threshold:
# 将检测框坐标从归一化值转换回原图坐标
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
(startX, startY, endX, endY) = box.astype("int")
# 确保坐标不超出图像边界
startX, startY = max(0, startX), max(0, startY)
endX, endY = min(w-1, endX), min(h-1, endY)
face_location = (startX, startY, endX-startX, endY-starty)
current_face_locations.append(face_location)
# --- 对每个检测到的人脸进行识别 ---
face_roi = frame[startY:endY, startX:endX]
if face_roi.size == 0:
continue
# 提取人脸特征
face_blob_for_recog = cv2.dnn.blobFromImage(face_roi, scalefactor=1.0/127.5,
size=(112, 112),
mean=(127.5, 127.5, 127.5), swapRB=True)
recog_net.setInput(face_blob_for_recog)
face_encoding = recog_net.forward().flatten()
# 与已知人脸库比对
name = "Unknown"
if len(known_face_encodings) > 0:
# 计算当前人脸特征与库中所有特征的欧氏距离
distances = np.linalg.norm(known_face_encodings - face_encoding, axis=1)
min_distance_idx = np.argmin(distances)
min_distance = distances[min_distance_idx]
# 如果最小距离小于阈值,则认为是已知人脸
if min_distance < recog_threshold:
name = known_face_names[min_distance_idx]
# 可选:在图像上显示距离值,用于调试阈值
# name = f"{name}({min_distance:.2f})"
current_face_names.append(name)
current_face_encodings.append(face_encoding) # 可以保存用于后续注册
frame_count += 1
# --- 绘制结果到当前帧 ---
for (loc, name) in zip(current_face_locations, current_face_names):
startX, startY, boxW, boxH = loc
endX, endY = startX + boxW, startY + boxH
# 绘制人脸框
cv2.rectangle(frame, (startX, startY), (endX, endY), (0, 255, 0), 2)
# 绘制姓名标签背景
cv2.rectangle(frame, (startX, endY - 20), (endX, endY), (0, 255, 0), cv2.FILLED)
font = cv2.FONT_HERSHEY_DUPLEX
cv2.putText(frame, name, (startX + 6, endY - 6), font, 0.5, (255, 255, 255), 1)
# 显示帧率(简易计算)
fps = cap.get(cv2.CAP_PROP_FPS) if frame_count % 30 == 0 else 0 # 简易显示
cv2.putText(frame, f"FPS: {fps:.1f}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
# 显示图像
cv2.imshow('虚谷号人脸识别 - 按R注册新人脸,Q退出', frame)
# 键盘交互
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
elif key == ord('r'):
# 注册模式:这里需要你实现一个交互逻辑,例如选择当前画面中的一个人脸框进行注册
# 简单示例:注册第一个检测到的人脸为“New_User_XXX”
if current_face_locations:
# 这里需要获取一个名字,可以通过命令行输入或固定值
# 为简单演示,我们使用时间戳作为名字
new_name = f"User_{datetime.now().strftime('%H%M%S')}"
register_new_face(frame, current_face_locations[0], new_name)
else:
print("当前未检测到人脸,无法注册。")
# 释放资源
cap.release()
cv2.destroyAllWindows()
核心逻辑与优化点解析 :
- 间隔检测 :
detect_interval = 2意味着每2帧才运行一次耗资源的人脸检测。在检测间隔帧,程序依然会使用上一帧检测到的人脸位置进行识别和绘制,保证了视觉上的连续性,同时大幅降低了CPU负载。这是嵌入式设备上实现“实时”效果的关键技巧。 - 检测模型预处理 :注意,检测模型
blobFromImage的参数与识别模型不同。mean=(104.0, 177.0, 123.0)和swapRB=False是Caffe模型常用的预处理方式(BGR顺序,减去均值)。 务必与你使用的模型要求匹配 ,否则检测会失败。 - 识别与比对 :识别过程在检测到人脸框后立即进行。我们计算当前人脸特征与数据库中所有特征的欧氏距离,并找出最小值。
recog_threshold是一个经验值,需要你根据实际模型和场景调整。可以先注册几个人脸,然后观察控制台打印的距离值,来确定一个合理的阈值。 - 交互与注册 :我们通过按键
R来触发注册功能。这里实现了一个简易版本:将当前画面中检测到的第一个人脸注册为一个带时间戳的新用户。在一个更完善的系统中,你应该弹出输入框让用户输入姓名,或者与数据库联动。
5. 性能优化与调试技巧实录
在虚谷号上跑通只是第一步,跑得流畅、稳定才是目标。下面是我在调试过程中总结的几个关键点。
5.1 性能瓶颈分析与优化策略
- 最耗时的操作 :深度学习模型的前向推理(
net.forward())。检测和识别各需要一次。 - 优化策略 :
- 降低输入分辨率 :将摄像头分辨率设为640x480甚至320x240。同时,在送入检测模型前,
blobFromImage的size参数已经将图像缩放到300x300,这个缩放比例不宜过小,否则影响检测精度。 - 间隔帧检测 :如前所述,这是提升帧率最有效的方法。你可以根据虚谷号的实际性能调整
detect_interval,比如设为3或5。 - 多线程/异步处理 :一个高级优化是将摄像头捕获、人脸检测、人脸识别、UI显示放在不同的线程中,用队列传递图像数据。但这会显著增加代码复杂度,在初期不建议使用。
- 模型量化 :如果模型支持,可以尝试将FP32模型量化为INT8模型,能在几乎不损失精度的情况下提升速度。但这需要模型本身支持,且OpenCV的DNN模块对量化模型的支持度需要测试。
- 降低输入分辨率 :将摄像头分辨率设为640x480甚至320x240。同时,在送入检测模型前,
5.2 常见问题与排查方法
| 问题现象 | 可能原因 | 排查与解决方法 |
|---|---|---|
摄像头打不开 ( cap.isOpened() 返回 False) |
1. 摄像头设备号错误。 2. 摄像头被其他进程占用。 3. 虚谷号系统无摄像头驱动或权限问题。 |
1. 尝试 VideoCapture(1) 或 VideoCapture(-1) 。 2. 关闭可能占用摄像头的其他程序。 3. 运行 ls /dev/video* 查看设备节点。使用 sudo 运行脚本或添加用户到 video 组 ( sudo usermod -a -G video $USER ,需注销重登)。 |
| 人脸检测框乱飞或一个都没有 | 1. 检测模型加载失败或路径错误。 2. 模型预处理参数 ( mean , scalefactor , swapRB ) 错误。 3. 置信度阈值 conf_threshold 设置过高或过低。 |
1. 检查模型文件路径和权限,确认 readNet 未报错。 2. 这是最常见原因! 务必核对模型官方文档或训练代码中的预处理方式。可以尝试用模型自带的示例图片测试。 3. 逐步调整阈值,并打印 detections 中的 confidence 值观察。 |
| 人脸识别结果全是“Unknown” | 1. 识别模型加载失败。 2. 识别模型预处理参数错误。 3. 人脸对齐问题(识别模型要求正面、对齐的人脸)。 4. 特征比对阈值 recog_threshold 设置不当。 5. 注册时的人脸图像质量差(模糊、过暗、侧脸)。 |
1. 同上,检查模型路径。 2. 同上,核对预处理参数,尤其是输入尺寸和归一化方式。 3. 检测到的人脸框直接裁剪后送入识别模型,可能包含过多背景或姿态不正。可以考虑加入简单的人脸对齐(基于关键点),但会进一步增加计算量。初期确保注册和识别时人脸正对摄像头、光照良好。 4. 注册几个人脸后,打印出比对时的 min_distance 值。观察同一个人不同照片的距离(应较小),不同人之间的距离(应较大)。根据这个分布设定阈值。 5. 确保注册时捕捉到清晰、正面的人脸图像。 |
| 程序运行卡顿,帧率极低 | 1. 未进行性能优化(全分辨率、每帧检测)。 2. 虚谷号CPU占用过高,可能后台有其他任务。 3. 模型过于复杂。 |
1. 实施 “降低分辨率” 和 “间隔帧检测” 优化。 2. 使用 htop 命令查看CPU使用情况,关闭不必要的进程。 3. 尝试更轻量的模型,如将检测模型换成更小的版本。 |
| 内存占用不断增长直至崩溃 | 内存泄漏。可能由于循环中不断创建大型对象(如大数组)而未释放。 | 检查代码,确保在循环内部没有不必要的全局变量累积。特别是 current_face_encodings 列表,如果在主循环中不断 append 而不清理,会导致内存增长。我们的示例代码中,这个列表在每次检测循环开始时被重新创建,是正确做法。 |
5.3 模型选择与调参心得
- 检测模型 :
Ultra-Light-Fast-Face-Detector在速度和精度上对虚谷号非常友好。如果追求极速,可以尝试OpenCV Haar Cascade(cv2.CascadeClassifier),但要做好心理准备,它对侧脸和复杂背景的误检率会高很多。 - 识别模型 :ONNX格式的模型通用性好。除了
insightface的模型,也可以尝试MobileFaceNet的ONNX版本。关键是要找到 输入尺寸小(如112x112) 且 参数量少 的模型。 - 阈值调参 :
conf_threshold和recog_threshold不是固定值。- 调高
conf_threshold可以减少误检(将不是人脸的东西框出来),但可能漏检一些模糊或侧脸的人脸。建议从0.7开始,根据实际画面调整。 recog_threshold直接影响识别严格度。阈值设得太低,谁都能被认成已注册用户(易误识);设得太高,同一个人稍微换个角度就认不出了(易拒识)。 最佳实践是:收集10-20张已注册用户的不同角度照片和若干非注册用户照片,分别计算特征距离,绘制分布图,选取一个使误识率和拒识率平衡的阈值。
- 调高
6. 功能扩展与项目进阶思路
基础版本跑起来后,你可以考虑以下几个方向进行扩展,让项目更实用、更健壮。
6.1 持久化存储人脸数据库
目前人脸特征和名字存在内存里,程序退出就丢失。我们需要将其保存到文件。
import pickle
def save_face_database(encodings, names, filename='face_database.pkl'):
"""保存人脸数据库到文件"""
data = {'encodings': encodings, 'names': names}
with open(filename, 'wb') as f:
pickle.dump(data, f)
print(f"数据库已保存至 {filename}")
def load_face_database(filename='face_database.pkl'):
"""从文件加载人脸数据库"""
if os.path.exists(filename):
with open(filename, 'rb') as f:
data = pickle.load(f)
print(f"从 {filename} 加载了 {len(data['names'])} 个人脸数据")
return data['encodings'], data['names']
else:
print(f"数据库文件 {filename} 不存在,将创建新数据库。")
return [], []
# 在程序初始化部分加载数据库
known_face_encodings, known_face_names = load_face_database()
# 在注册新人脸后,或程序退出前(例如捕获到'q'按键时),保存数据库
# save_face_database(known_face_encodings, known_face_names)
6.2 集成GPIO控制与硬件联动
虚谷号强大的地方在于其丰富的GPIO接口。你可以很容易地将人脸识别结果与硬件动作联动。
- 场景:智能门禁
- 识别到已注册的“家人”时,控制一个继电器模块(连接在GPIO上)吸合,模拟“开门”动作。
- 识别到“Unknown”时,可以控制一个蜂鸣器发出警报,或一个LED灯闪烁红光。
- 识别成功后,还可以通过语音合成模块(如USB声卡+语音库)播报“欢迎回家,XXX”。
# 示例:使用RPi.GPIO库(需安装)控制GPIO
# 注意:虚谷号的GPIO引脚编号可能与树莓派不同,需查阅虚谷号文档。
import RPi.GPIO as GPIO
import time
RELAY_PIN = 17 # 假设继电器接在GPIO17上
BUZZER_PIN = 27 # 假设蜂鸣器接在GPIO27上
def setup_gpio():
GPIO.setmode(GPIO.BCM) # 使用BCM编号
GPIO.setup(RELAY_PIN, GPIO.OUT, initial=GPIO.HIGH) # 继电器常闭
GPIO.setup(BUZZER_PIN, GPIO.OUT, initial=GPIO.LOW)
def unlock_door(seconds=5):
"""开门(继电器吸合)"""
GPIO.output(RELAY_PIN, GPIO.LOW) # 吸合继电器
print("门已解锁")
time.sleep(seconds)
GPIO.output(RELAY_PIN, GPIO.HIGH) # 释放继电器
print("门已锁定")
def trigger_alarm():
"""触发警报(蜂鸣器响)"""
for _ in range(3):
GPIO.output(BUZZER_PIN, GPIO.HIGH)
time.sleep(0.5)
GPIO.output(BUZZER_PIN, GPIO.LOW)
time.sleep(0.5)
# 在主循环的识别部分加入判断
# if name != "Unknown":
# unlock_door(3) # 识别成功,开门3秒
# else:
# trigger_alarm() # 识别失败,触发警报
硬件连接注意事项 :GPIO引脚驱动能力有限,不能直接驱动大功率器件(如电机、大功率灯)。务必使用继电器模块或晶体管/MOSFET进行隔离和放大控制。连接时注意共地。
6.3 增加简单的UI交互模式
通过键盘按键切换不同模式,提升用户体验。
- 模式1:实时识别模式 (默认)。显示识别结果。
- 模式2:注册模式 。按
R键后,进入注册模式,屏幕提示“请正对摄像头,按空格键拍照注册”,按空格后捕捉当前人脸,并弹出输入框(可使用input()在终端输入)让用户输入姓名。 - 模式3:管理模式 。按
L键列出所有已注册用户,按D键选择删除某个用户。
这需要重构代码,引入一个状态变量(如 current_mode )并根据按键在不同模式下执行不同的逻辑。虽然代码量会增加,但会让你的项目更像一个完整的应用。
整个项目从环境搭建到功能扩展,核心就是理解“在有限资源下做减法”的思路。选择对的工具,优化每一个步骤,最终在虚谷号这样的小设备上也能实现有趣且实用的人脸识别应用。
更多推荐


所有评论(0)