基于树莓派4B与STM32的智能门禁系统项目(代码开源)
前言:本文为手把手教学嵌入式经典项目——智能门禁项目,本次项目采用 树莓派4B 与 STM32F103C8T6 进行联合开发。项目充分发挥各自 CPU 的优势与长处,将人脸识别的大计算量任务给树莓派4B,将门禁系统的控制部分交给 STM32 进行处理。该项目算是嵌入式人工智能(Embedded Artificial Intelligence)的入门项目,该代码框架可以在 STM32 部分可以根据自己的需求增加:密码锁,指纹锁等进行丰富。希望这篇博客能帮助大家助力自己的毕设亦或是项目!(文末有代码开源!)
硬件实物图:
效果图:
一、项目概述
1.1 智能门禁系统
智能门禁系统是一种现代化的安全管理系统,采用微机自动识别技术和现代安全管理措施,实现对各种门禁设备的集中管理和安全控制。该系统可以管理各种门禁设备,如密码门禁系统、非接触卡门禁系统等,并可以提供事后的查询报表。智能门禁系统广泛应用于各种场所,如住宅小区、办公室、实验室、仓库、银行等,旨在提高安全性和管理效率。该系统不仅可以保障人们的安全和财产安全,还可以提高场所的管理水平和效率。总之,智能门禁系统是一种高效、安全、灵活的安全管理系统,在现代化社会中扮演着越来越重要的角色。
市面上智能门禁系统分为 2 大类,分别是:(1)本地部署;(2)服务商运营
(1)本地部署:顾名思义,智能门禁系统的所有识别与判断都在本地机器上完成。优势:综合成本较低;劣势:性能过于依赖本地机器的性能
(2)服务商运营:该方案则是借助网络技术将本地检测到的目标数据发送至云端,由云端进行识别判断,再将结果返回至本地机器。优势:运营维护方便,整体性能很强;劣势:需要长期缴纳服务费用
1.2 项目概述
本项目的智能门禁系统目前仅为大家提供人脸识别判断开门的功能,至于密码锁,指纹锁等部分功能的实现就交给感兴趣的读者朋友了(这部分仅需要在下位机STM32进行改动即可)。
本项目通过树莓派4B的摄像头进行视频读取,使用传统的人脸识别技术(LBP人脸特征进行识别),将识别到的人脸数据结果通过树莓派4B的 UART 串口发送至 STM32。STM32 则通过 I2C 协议将树莓派4B发送过来的数据解码后显示在 OLED 屏幕上,并根据实际条件判断是否驱动 SG90 舵机进行开门操作!
二、人脸识别
2.1 人脸识别概述
人脸识别指识别并理解一张脸。人脸识别是一项热门的计算机技术研究领域, 它属于生物特征识别技术,是对生物体(一般特指人)本身的生物特征来区分生物体个体。人脸识别技术在安防监控、身份认证等众多领域都有着重要的作用。
人脸识别技术可以分为以下几类:
1、3D人脸识别:这种技术主要处理3D数据,利用了人体的立体特征。然而,这种技术的开发难度较大。
2、2D人脸识别:这种技术以2D图像为基础,虽然数据模糊,但可以使用图片和视频进行破解,因此存在一定的安全隐患。
3、2D+人脸识别:这种技术处理方式相对简单,只需要将3D数据分为RGB和深度数据。这种方法的实现速度较快。
4、基于几何特征的方法:这种方法利用人脸的各个部件(如眼睛、鼻子、嘴巴、下巴等)的形状、大小和结构关系作为识别的特征。
5、卷积神经网络(CNN):这是一种深度学习算法,通过学习直接对图像进行分类。CNN通常是一个包含多个卷积层和池化层的神经网络。
6、局部特征分析方法(Local Face Analysis):这种方法通过寻找具有局部性和拓扑性的表达方式,对模式分析和分割非常有效。
★本项目采用的就是比较传统的局部特征分析的方式,借助的特征模型为BLP人脸特征。
2.2 BLP人脸特征
BLP(基于局部二值模式)是一种图像局部特征描述符,可用于人脸识别。它属于图像处理和计算机视觉领域。简单来说,LBP 是一种在纹理识别以及人脸识别中经常使用的特征,其提取过程如图所示。
首先将输入图像灰度化之后,对于每一个像素点,在 3 × 3 的窗口内,比较其与周围 8 个像 素点的大小。根据大小比较情况对周围的 8 个像素点进行二值化编码,比中心点小的编码为 0,比中心点大的编码为 1。这样中心点的像素就可以用一个 8 位 2 进制数来进行描述。
对图像中的每一个点都进行上述操作后,就可以将一张灰度图重新编码为一张 LBP 特征图。
将这个特征图分成小块(通常是 8 × 8 的小块),计算每个小块中特征点的统计直方图,然 后将所有小块的直方图拼接起来,就可以形成一个能够描述输入人脸图像的特征向量。
特征维度:8*8*256 = 16384
作者概述:BLP 方法就是将人脸图像通过BLP法则转换为BLP专属的特征向量,然后将待识别的人脸特征向量与已知的人脸特征向量进行匹配(BLP类型的),计算它们之间的距离或相似度。根据匹配结果进行识别决策,如设定阈值判断是否为同一人脸等。
三、树莓派4B的人脸识别
作者将通过BLP方法实现人脸识别技术,读者朋友可以结合着下方 BLP 方法的人脸识别技术代码进行学习!
3.1 人脸收集
智能门禁系统的第一步肯定是需要对可以通行用户的人脸数据进行采集,为了方便起见,作者直接采用使用以下 Python 代码进行收集人脸信息;
import cv2
import os
if __name__ == "__main__":
str_face_id = ""
index_photo=0
# 加载训练好的人脸检测器
faceCascade = cv2.CascadeClassifier('haarcascade_frontalface_alt.xml')
# 打开摄像头
cap = cv2.VideoCapture(0)
while True:
# 判断人脸id 是否为空,空的话创建face_id
if str_face_id.strip()=="":
str_face_id = input('Enter your face ID:')
index_photo=0
if not os.path.exists(str_face_id):
os.makedirs(str_face_id)
# 读取一帧图像
success, img = cap.read()
if not success:
continue
# 转换为灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 进行人脸检测
faces = faceCascade.detectMultiScale(gray,scaleFactor=1.1,minNeighbors=5,minSize=(50, 50),flags=cv2.CASCADE_SCALE_IMAGE)
# 画框
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 3)
# 显示检测结果
cv2.imshow("FACE",img)
# 读取按键键值
key = cv2.waitKey(1) & 0xFF
# 按键"c" 进行人脸采集
if key == ord('c'):
# 保存人脸
for (x, y, w, h) in faces:
roi = img[y:y+h,x:x+w]
cv2.imwrite("%s/%d.jpg"%(str_face_id,index_photo),roi)
index_photo = index_photo+1
key = 0
# 按键"x" 切换 人脸_id
elif key == ord('x'):
str_face_id = ""
key = 0
# 按键 "q" 退出
elif key == ord('q'):
break
cap.release()
上述代码主要借助 OpneCV 自带的人脸检测器 haarcascade_frontalface_alt.xml 进行检测视频流中出现的人脸信息!
在终端输入:python3 face_collect.py
随后在 Enter your face ID: 之后输入你即将收集人脸ID(字母格式),输入完毕回车后将成功打开摄像头,进行人脸收集。通过按 “c” 按键可以进行保存图片;
通过按键 “x” 可以切换ID进行收集下一个目标人脸的有效信息,按 Ctrl+c 即可退出收集程序!
如上图所示为作者本人收集到的刘德华先生与李连杰先生的人脸数据信息
3.2 构建人脸特征库
拥有收集好的人脸数据信息接下来我们就可以进行制作基于 BLP 的人脸特征库!
通过已有的人脸数据集库制作基于 BLP 的人脸特征库的 Python 代码如下:
在终端输入代码:python3 train_model_LBP.py
import cv2
import os
import numpy as np
# 获取所有文件(人脸id)
def get_face_list(path):
for root,dirs,files in os.walk(path):
if root == path:
return dirs
if __name__ == "__main__":
# 创建人脸识别器
recognizer = cv2.face.LBPHFaceRecognizer_create()
# 用来存放人脸id的字典
# 构建人脸编号 和 人脸id 的关系
dic_face = {}
# 人脸存储路径
base_path = "../face-collect/"
# 获取人脸id
face_ids = get_face_list(base_path)
print(face_ids)
# 用来存放人脸数据与id号的列表
faceSamples=[]
ids = []
# 遍历人脸id命名的文件夹
for i, face_id in enumerate(face_ids):
# 人脸字典更新
dic_face[i] = face_id
# 获取人脸图片存放路径
path_img_face = os.path.join(base_path,face_id)
for face_img in os.listdir(path_img_face):
# 读取以.jpg为后缀的文件
if face_img.endswith(".jpg"):
file_face_img = os.path.join(path_img_face,face_img)
# 读取图像并转换为灰度图
img = cv2.imread(file_face_img)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 保存图像和人脸ID
faceSamples.append(img)
ids.append(i)
print(dic_face)
# 进行模型训练
recognizer.train(faceSamples, np.array(ids))
# 模型保存
recognizer.save('trainer.yml')
# 进行字典保存
with open("face_list.txt",'w') as f:
for face_id in dic_face:
f.write("%d %s\n"%(face_id,dic_face[face_id]))
代码运行成功之后将在当前文件夹下生成 trainer.yml 的文件,该文件存放了人脸库数据集转换成的 LBP 人脸特征!
3.3 人脸识别
通过上述步骤,我们成功得到了收集到人脸数据的 LBP 人脸特征,接下来我们就可以通过摄像头采集到的人脸 LBP 特征向量与数据库中存在的人脸 LBP 特征向量进行对比,计算距离得分来判断目标是否为可进入人员!
我们需要先制作目标人脸ID的字典,在 face_list.txt 文件中进行如下标注:
在终端输入代码:python3 face_recognize_LBP.py
face_recognize_LBP.py 代码:
import cv2
import os
import numpy as np
import serial
ser = serial.Serial('/dev/ttyAMA0',115200)
def read_dic_face(file_list):
data = np.loadtxt(file_list,dtype='str')
dic_face = {}
for i in range(len(data)):
dic_face[int(data[i][0])] = data[i][1]
return dic_face
if __name__ == "__main__":
# 加载人脸字典
dic_face = read_dic_face("face_list.txt")
print(dic_face)
# 加载Opencv人脸检测器
faceCascade = cv2.CascadeClassifier('../face-collect/haarcascade_frontalface_alt.xml')
# 加载训练好的人脸识别器
recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.read('trainer.yml')
# 打开摄像头
cap = cv2.VideoCapture(0)
while True:
# 读取一帧图像
success, img = cap.read()
if not success:
continue
# 转换为灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 进行人脸检测
faces = faceCascade.detectMultiScale(gray,scaleFactor=1.1,minNeighbors=5,minSize=(50, 50),flags=cv2.CASCADE_SCALE_IMAGE)
ser.write(str("idea").encode())
# 遍历检测到的人脸
for (x, y, w, h) in faces:
# 画框
cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 3)
# 进行人脸识别
id_face, confidence = recognizer.predict(gray[y:y+h,x:x+w])
print(confidence)
# 检测可信度,这里是通过计算距离来计算可信度,confidence越小说明越近似
if (confidence
上述代码通过 OpenCV 的人脸检测器在视频流截取到人脸信息,随后进行人脸识别(与已存的人脸数据特征进行可信度计算,距离越小说明两者越相似)
★上述代码涉及到树莓派4B与STM32之间的UART串口通信,这方面知识薄弱或者不太懂的朋友可以借鉴作者这篇博客:http://t.csdn.cn/mcMra
上述代码中主要用2个串口通信信息进行发送,分别为:ser.write(str("idea").encode()) 和 ser.write(str(id_face).encode())。
其中,ser.write(str("idea").encode()) 代码是在视频中未检测到人脸,即发送 “idea” 字符串给STM32(这部分很重要,否则STM32可能会出现延迟反复检测人脸的现象)。
ser.write(str(id_face).encode()) 则是正常根据检测到的人脸ID数字进行发送数据,STM32那边正常解码即可!
四、树莓派4B与STM32的智能门禁
4.1 CubeMX配置
1、RCC配置外部高速晶振(精度更高)——HSE;
2、SYS配置:Debug设置成Serial Wire(否则可能导致芯片自锁);
3、TIM2配置:使用TIM2的Channel1产生PWM信号(控制SG90);
数据参数意义:
此时产生PWM波形频率:72M / (719 +1)/ (1999+1) = 50Hz
定时器周期:1/50 = 20ms
4、USART1配置:设置UART1串口;波特率:115200;开启UART串口中断;
5、I2C配置:
6、时钟树配置
7、工程配置
4.2 STM32代码
4.2.1 OLED代码
OLED部分的详解可以参考博客:http://t.csdn.cn/GCWyc
OLED模块主要是方便显示树莓派4B发送给STM32的数据信息!根据树莓派4B发送过来数据在OLED屏幕上显示出当前智能门禁系统的运行状态(即当前识别人物信息ID等)!主要涉及:字符串显示函数与汉字显示函数如下:
字符串显示函数:
// Parameters : x,y -- 起始点坐标(x:0~127, y:0~7); ch[] -- 要显示的字符串; TextSize -- 字符大小(1:6*8 ; 2:8*16)
// Description : 显示codetab.h中的ASCII字符,有6*8和8*16可选择
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
{
unsigned char c = 0,i = 0,j = 0;
switch(TextSize)
{
case 1:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x > 126)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i 120)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i




















