1. 项目概述:从一块开发板到人脸识别应用

最近在捣鼓一个挺有意思的小玩意儿——虚谷号。这名字听起来有点玄乎,其实它是一块面向教育和创客领域的国产开源硬件,你可以把它理解成一个功能更强大、接口更丰富的“树莓派”类产品。我手头这块板子,自带了一个摄像头接口,于是我就琢磨着,能不能在上面跑一个实时的人脸识别应用?比如做个智能门禁的雏形,或者一个能打招呼的互动装置。

这个想法听起来简单,但实操起来,从环境搭建到模型部署,再到性能优化,每一步都有不少门道。网上关于虚谷号的资料相对零散,而人脸识别本身又是一个涉及操作系统、编程环境、计算机视觉库和深度学习模型的复杂技术栈。我花了差不多一周时间,踩了无数坑,终于把整个流程跑通并优化到了一个可用的状态。这篇文档,就是我这次折腾的完整记录,目标是把一个看似高大上的“人脸识别”项目,拆解成一步步可执行、可复现的操作,无论你是想学习计算机视觉入门,还是想在嵌入式设备上实现类似功能,相信都能从中找到参考。

2. 核心思路与方案选型

在虚谷号上做人脸识别,本质上是在资源受限的嵌入式设备上部署一个计算机视觉应用。我们需要一个完整的软件栈来支撑:操作系统、Python环境、视觉库、人脸检测与识别模型,以及最后的应用逻辑。

2.1 为什么选择虚谷号?

虚谷号通常预装了基于Linux的操作系统(如Ubuntu或定制发行版),这为我们提供了丰富的软件生态和命令行操作能力。其硬件配置一般包含多核ARM处理器、1GB或以上的内存,以及GPIO、摄像头等接口。这个配置跑轻量级的人脸识别是可行的,但和PC相比,资源依然紧张。因此, “轻量化” 是整个项目的核心设计原则。我们不能直接使用在服务器上运行的那些庞大模型,必须为嵌入式环境量身定制。

2.2 技术栈选型背后的考量

  1. 编程语言与主库:Python + OpenCV

    • Python :在AI和嵌入式开发领域生态最丰富,库安装和代码编写都最快捷。
    • OpenCV :计算机视觉的“瑞士军刀”。它提供了最基础也最高效的图像读取、显示、预处理(缩放、灰度化)功能,以及一些传统的人脸检测算法(如Haar级联分类器)。我们的流程会重度依赖它。
  2. 人脸检测方案:双引擎策略

    • 首选:OpenCV DNN + 轻量级检测模型 。这是性能与精度的平衡点。OpenCV的 dnn 模块可以直接加载和运行用Caffe、TensorFlow、ONNX等格式保存的深度学习模型。我们选择像 “MobileNet-SSD” 或专门为边缘计算优化的 “Ultra-Light-Fast-Generic-Face-Detector-1MB” 这样的模型。它们模型文件小(几MB),推理速度快,在虚谷号上能达到接近实时的检测速度。
    • 备选:OpenCV Haar Cascade 。这是一个非常经典的基于特征的方法,模型文件小,速度极快,但精度和抗干扰能力(如侧脸、遮挡、光照变化)远不如深度学习模型。它可以作为备用方案,或在检测到人脸后用于辅助关键点定位。
  3. 人脸识别方案:特征提取与比对

    • 人脸识别分为两步:一是从检测到的人脸框中提取出具有区分度的特征向量(一串数字);二是对比两个特征向量的相似度。
    • 我们同样采用轻量级深度学习模型来完成特征提取。 FaceNet MobileFaceNet 是经典选择,它们能将一张人脸图像映射到一个128维或512维的特征空间,同一个人的不同照片在这个空间里的距离会很近。我们可以使用预训练好的模型,直接调用进行特征提取。
    • 识别过程则简化为:1. 提取当前人脸特征;2. 与预先注册好的特征库中的每一个特征计算距离(如欧氏距离、余弦相似度);3. 如果最小距离小于某个阈值,则认为是同一个人。
  4. 应用框架与优化

    • 使用简单的循环来读取摄像头帧,并在每一帧上执行“检测->识别->显示”的流程。
    • 性能优化是关键:包括图像帧缩放、间隔帧检测、异步处理等技巧,后续会详细说明。

这个技术栈的选择,核心思想是 “在保证功能可用性的前提下,最大限度地降低计算和存储开销” ,以适应虚谷号的硬件条件。

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 准备人脸检测与识别模型

这是最关键的一步。我们需要下载预训练好的模型文件。

  1. 人脸检测模型 :以Ultra-Light-Fast-Face-Detector为例。我们需要其模型定义文件( .prototxt )和权重文件( .caffemodel )。你可以在GitHub上搜索该项目的发布页面下载。

    • 假设我们下载后得到: RFB-320.prototxt RFB-320.caffemodel
    • 将它们放在项目目录下的 models/ 文件夹中。
  2. 人脸识别模型 :这里我们使用一个轻量化的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()

核心逻辑与优化点解析

  1. 间隔检测 detect_interval = 2 意味着每2帧才运行一次耗资源的人脸检测。在检测间隔帧,程序依然会使用上一帧检测到的人脸位置进行识别和绘制,保证了视觉上的连续性,同时大幅降低了CPU负载。这是嵌入式设备上实现“实时”效果的关键技巧。
  2. 检测模型预处理 :注意,检测模型 blobFromImage 的参数与识别模型不同。 mean=(104.0, 177.0, 123.0) swapRB=False 是Caffe模型常用的预处理方式(BGR顺序,减去均值)。 务必与你使用的模型要求匹配 ,否则检测会失败。
  3. 识别与比对 :识别过程在检测到人脸框后立即进行。我们计算当前人脸特征与数据库中所有特征的欧氏距离,并找出最小值。 recog_threshold 是一个经验值,需要你根据实际模型和场景调整。可以先注册几个人脸,然后观察控制台打印的距离值,来确定一个合理的阈值。
  4. 交互与注册 :我们通过按键 R 来触发注册功能。这里实现了一个简易版本:将当前画面中检测到的第一个人脸注册为一个带时间戳的新用户。在一个更完善的系统中,你应该弹出输入框让用户输入姓名,或者与数据库联动。

5. 性能优化与调试技巧实录

在虚谷号上跑通只是第一步,跑得流畅、稳定才是目标。下面是我在调试过程中总结的几个关键点。

5.1 性能瓶颈分析与优化策略

  1. 最耗时的操作 :深度学习模型的前向推理( net.forward() )。检测和识别各需要一次。
  2. 优化策略
    • 降低输入分辨率 :将摄像头分辨率设为640x480甚至320x240。同时,在送入检测模型前, blobFromImage size 参数已经将图像缩放到300x300,这个缩放比例不宜过小,否则影响检测精度。
    • 间隔帧检测 :如前所述,这是提升帧率最有效的方法。你可以根据虚谷号的实际性能调整 detect_interval ,比如设为3或5。
    • 多线程/异步处理 :一个高级优化是将摄像头捕获、人脸检测、人脸识别、UI显示放在不同的线程中,用队列传递图像数据。但这会显著增加代码复杂度,在初期不建议使用。
    • 模型量化 :如果模型支持,可以尝试将FP32模型量化为INT8模型,能在几乎不损失精度的情况下提升速度。但这需要模型本身支持,且OpenCV的DNN模块对量化模型的支持度需要测试。

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接口。你可以很容易地将人脸识别结果与硬件动作联动。

  • 场景:智能门禁
    1. 识别到已注册的“家人”时,控制一个继电器模块(连接在GPIO上)吸合,模拟“开门”动作。
    2. 识别到“Unknown”时,可以控制一个蜂鸣器发出警报,或一个LED灯闪烁红光。
    3. 识别成功后,还可以通过语音合成模块(如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 )并根据按键在不同模式下执行不同的逻辑。虽然代码量会增加,但会让你的项目更像一个完整的应用。

整个项目从环境搭建到功能扩展,核心就是理解“在有限资源下做减法”的思路。选择对的工具,优化每一个步骤,最终在虚谷号这样的小设备上也能实现有趣且实用的人脸识别应用。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐