Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

MengFanjun的博客

总体介绍

项目名称:基于RetinaFace+Jetson Nano的智能门锁系统 项目时间:2024年4月-2024年6月 项目平台:PC、Linux 项目语言:Python、C 项目软件:Pycharm、阿里云、Arduino、VScode

1.项目背景

在科技发展的推动下,门锁系统的演进日新月异。从原始、简单的机械构造,门锁技术已迈向了一个全新的智能化阶段,这一转变涵盖了生物识别、通讯技术、机械工程以及计算机科学的深度融合与应用。门锁系统的初期阶段一般使用机械门锁,但由于其仅依赖于钥匙这一验证方式,容易被盗用,安全性不足。所以,改进后的门锁就诞生了,它有电子锁,也有电子卡,可以用密码和磁卡进行识别。随着网络、物联网、计算机和生物特征识别等技术的不断进步,门禁系统越来越具有安全性、可靠性、便利性和便于管理的特点。 # 2.研究目的 人脸识别技术凭借普通摄像头即可轻松采集信息,为用户提供了便捷的基础信息获取途径,同时非接触的识别过程确保了良好的用户体验。并且,人脸识别技术的实施并无需大量特殊装置,只需对现有计算机系统进行功能升级即可实现,不仅可以保证使用者的资金投入,而且可以扩充装置的功能,更能满足使用者的安全性要求。因此,在门锁系统中引入人脸识别技术,是一项很有实用价值的研究课题。 # 3.项目架构 ## 3.1 Nvidia Jetson Nano Nvidia Jetson Nano 是一款由 Nvidia推出的小型嵌入式计算机,如图2.1所示,其旨在提供高性能的人工智能计算能力。Jetson Nano采用了Nvidia的Tegra系列系统级芯片,这款设备配备了四核 ARM Cortex-A57 处理器和 Nvidia Maxwell 架构的 GPU,带有 128 个 CUDA 核心,同时还搭载了 4GB LPDDR4 内存。提供了强大的计算能力,支持各种人工智能算法和深度学习模型的部署和运行。并且有丰富的连接接口,包括4个USB 3.0端口、1个Gigabit Ethernet端口、1个 HDMI输出端口、1个DisplayPort输出端口、1个MIPI CSI摄像头接口和1个MIPI DSI显示器接口等,与此同时,Jetson Nano采用了低功耗设计,功耗仅为5-10瓦,适合于嵌入式应用和便携式设备。

img

3.2 工作逻辑

智能门锁的工作逻辑如图2.3所示,系统是由Jetson Nano作为主控芯片,PyQt5界面作为用户端窗口,系统启动后,为用户展示登录界面,用户在登录后,可以进行检测视频、检测照片、人脸编码、录入人脸的功能,启动程序后,选择检测视频模式,程序调用Retinaface 和 FaceNet 开始实时对人脸进行检测,当检测到的人脸信息与在数据库中的人脸信息匹配后,把检测结果到Esp8266,假如 Esp8266收到了身份确认的消息,则开门。反之,假如Esp8266没有收到确认身份的消息,则不开门,开门的信息会以日志形式回传到阿里云平台,用户可以远程监测门锁开启的时间。 用户在选择“检测图片”模式后,会弹出文件选择窗口,供用户选取测试图片,在系 统目录下/img文件夹内有若干个图片可供测试,此功能用于测试模型能否工作。 用户在首次启动本系统时,要先选择“录入人脸信息”模式,点击此按钮会弹出新窗 口,可以进行拍照、命名、保存,在人脸信息成功保存后,退出当前窗口,再点击“编码人脸信息”,系统则会自动开始编码,当编码完成之后,用户便可选择“检测视频”模式,查看系统能否正常工作。 项目的整体情况如下图所示,屏幕上显示的用户的登陆与注册界面,在用户登陆进入系统之后,即可开始执行人脸检测模式或人脸录入模式。Jetson Nano连接了Esp8266,假如人脸数据在数据中并且识别通过的话,Jetson Nano会向Esp8266发送指令,Esp8266收到指令后,控制舵机的开启。

img

下面是系统的工作流程图

img

摄像头拍摄到图像之后,先对图片进行预处理,统一尺寸,目的是保持宽高比、统一输入尺寸、减少边缘效应。在图像输入进MobileNet0.25后,借助FPN金字塔从MobileNet中提取出3个关键特征层,再利用1x1卷积和上采样进行特征融合,提高了Retinaface对目标的检测能力。因为MobileNet网络架构简单,参数量少,那么准确率相比于resnet必然会有一定下降,本课题引入SSH模块进一步增强特征,利用3x3卷积的堆叠来模拟5x5或7x7的卷积。这样既保证了计算效率,也丰富了特征。 下图是整个算法的工作流程, box是人脸框 conf是置信度 landms是人脸关键点

img

4.主要算法

4.1 Retinaface 算法

Retinaface 算法在 WiderFace 数据集上取得了显著的 AP 提升,这一结果显示了其在人脸识别领域的出色性能。Retinaface的人脸检测方式如图2.4所示,该算法的源代码已在insightface 平台上公开,原模型基于mxnet框架开发,但目前在社区中,基于其他框架的复现版本也颇受欢迎。Retinaface的核心创新在于在原有的检测网络RetinaNet基础上,巧妙地融合了三个层级的SSH检测模组,这一设计显著提升了人脸侦测的准确度。研究人员给出了两个不同的网络模型,前者是为了提高检测的速度,后者是为了提高检测的准确性。

img
Retinaface 网络对待检测的图片:首先,采用 MobileNet0.25 进行主干特征网络提取。接着,结合使用了 FPN(特征金字塔网络)和 SSH来进一步增强特征提取的效果。随后,模型使用 ClassHead、BoxHead 和 LandmarkHead 模型从这一部分特征中得到预测后的结果。最后,本文对预测结果进行解码(decode)并利用 NMS(非极大抑制)算法来消除重复检测,从而得出最终的结果。 

Mobilenet基于深度可分离卷积,它将标准卷积拆成了两个操作,深度卷积逐点卷积,深度卷积和标准卷积的不同之处在于,标准卷积是卷积核在所有通道上,深度卷积对每个不同的输入通道采用不同的卷积。==深度卷积减少计算量。==

Mobilenet网络如下表所示,一共28层,第一层是标准卷积,剩下的就是Depth wise Separable Convolution深度可分离卷积

img

卷积核的通道数由输入数据的通道数决定,输出数据的通道数由卷积核的个数决定。 1x1的卷积,相当于把原输入数据的通道数进行了降维,作用就是对计算进行了化简。 通道数越来越多代表提取的特征越来越多,图像的细节就更丰富。

img

4.2 FaceNet算法

FaceNet的核心策略在于利用卷积神经网络来学习人脸图像在欧式空间中的特征表示。这种方法的设计初衷在于通过训练,使得在欧式空间中,两张属于同一人的人脸图像所对应的特征向量之间的距离尽可能小。换言之,如果两幅人脸图像的特征向量在欧式空间中的距离越近,那么这两幅图像属于同一人的可能性就越大。这种策略为人脸识别任务提供了一种高效且准确的方法,使得在海量的人脸数据中快速、准确地识别出目标个体成为可能。此算法的实现过程:首先计算人脸特征的特征向量,然后通过比较距离并设置阈值,即可确定两张人脸照片是否来自同一人。在成功构建人脸图像特征提取模型后,人脸验证过程实质上转化为了一个比较过程:比较两幅图像特征向量的相似度与预设阈值的大小。

对于人脸识别任务,问题进一步转化为在已提取的特征向量集中进行K近邻分类的问题,通过寻找与待识别图像特征最相近的 K个已知类别特征,来确定其所属身份。此外,对于人脸聚类任务,本文可以采用k-means聚类算法对提取到的人脸特征集进行分组,从而实现将相似的人脸图像聚类到同一个类别中的目的。这样,不同的人脸图像处理任务就可以通过这些高效且准确的方法得到妥善解决。

工作流程:

FaceNet,只负责识别人脸,获取到含有人脸的图片时,图像会输入到FaceNet的主干特征网络MobileNet,随后得到一个特征层,为了方便后续处理,我们对其进行平均池化,对平铺后的特征层进行一个128神经元的全连接,相当于用长度为128的特征向量来代替输入的图片。最后是比较所识别到人脸的特征向量和数据库中人脸的特征向量,如果其相对误差小于所设置的门限值,则可以判断其在数据库中。

4.3 PyQt界面

通过PyQt5,本文创建了三个基本页面,分别是登录界面、主窗口、录入界面。程序启动后,会展示出登录界面,登录界面有两个基本功能,一个是登录,一个是注册,用户注册后的信息会存储到csv的表格中。登陆界面如下图所示

img

当用户输入帐号密码后,会进入到人脸检测系统的主页面。主界面如图 2.12所示。 主界面有四个功能,分别是检测视频、检测图片、编码人脸信息、录入人脸。检测视频的功能是开启摄像头,利用摄像头进行实时检测,检测图片的主要功能是测试算法能否正常运行。点击“检测图片”按钮后,会弹出选择人脸图片的窗口,选择好人脸图片后,执行算法显示检测信息。编码人脸信息的主要功能是对录入的人脸信息进行编码,把人脸信息存储到数据库中。录入人脸会打开新的窗口,启动Jetson Nano的摄像头进行拍摄功能。

img

4.4 MG996R舵机

在这一课题中,舵机由 Esp8266 控制,在检测视频的模式下,当确认身份信息后,Jetson Nano 会将确认信息发送给Esp8266,Esp8266收到确认信息后,控制舵机的开启与关闭。舵机的启动结果会通过mqtt协议上传到阿里云,用户可以远程监控门锁的开启情况。

img

# 5.项目核心文件与代码 ## 5.1 项目文件目录 文件目录如下所示

img

## 5.2 项目部分代码 ### 5.2.1 Main.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#coding=utf-8
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QFileDialog
import time
import cv2
import csv
import numpy as np
from retinaface import Retinaface
import extract_name
import compare
from SelectPic import pick_video
from SelectPic import pick_pic
from SelectPic import encode_pic
import sys
from PyQt5 import QtCore,QtWidgets
from PyQt5.QtWidgets import *
import predict_pic
import predict_video
from LoginWindow import Ui_loginWindow
from SelectPic import FilePicker
import serial

class LoginWindow(QMainWindow,Ui_loginWindow):
def __init__(self):
super().__init__()
# 继承外部ui
self.setupUi(self)
# 隐藏外部多余窗口 他们要一起写上
self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
# 主窗体阴影
self.shadow = QtWidgets.QGraphicsDropShadowEffect(self)
self.shadow.setOffset(0,0)
self.shadow.setBlurRadius(15)
self.shadow.setColor(QtCore.Qt.blue)
self.frame_1.setGraphicsEffect(self.shadow)
self.frame_2.setGraphicsEffect(self.shadow)
# 主按钮绑定事件
self.stackedWidget_login.setCurrentIndex(1)
self.pushButton_login.clicked.connect(lambda :self.stackedWidget_login.setCurrentIndex(1))
self.pushButton_register.clicked.connect(lambda :self.stackedWidget_login.setCurrentIndex(0))
self.pushButton_sure.clicked.connect(self.verify_login)
self.pushButton_r_register.clicked.connect(self.register_submit)

def register_submit(self):
account = self.lineEdit_r_account.text()
pwd1 = self.lineEdit_r_pwd.text()
pwd2 = self.lineEdit_r_pwd2.text()

# 检查密码是否一致
if pwd1 != pwd2:
self.label_message.setText('两次输入的密码不一样')
return

# 将账户和密码写入 CSV 文件
csv_file = 'user_accounts.csv'
with open(csv_file, 'a', newline='') as file:
writer = csv.writer(file)
writer.writerow([account, pwd1])

self.label_message.setText('注册成功')



# 自定义内部ui
self.setup_ui()

def verify_login(self):
account = self.lineEdit_account.text()
pwd = self.lineEdit_pwd.text()

# 从 CSV 文件中读取账户和密码信息
csv_file = 'user_accounts.csv'
with open(csv_file, 'r', newline='') as file:
reader = csv.reader(file)
for row in reader:
if row[0] == account and row[1] == pwd:
print('登录成功')
try:
self.main = FilePicker()
self.main.show()
self.close()
except Exception as e:
print(e)
return

# 如果账户或密码错误,则显示错误消息
self.label_message.setText('账号或密码错误,请重试')

def setup_ui(self):
pass


# 主界面类
class MianWindow(QMainWindow,FilePicker):
def __init__(self):
super().__init__()
self.ui = FilePicker()



if __name__ == '__main__':
app = QApplication(sys.argv)
window = LoginWindow()
window.show()
sys.exit(app.exec_())
predict_pic.detect_pic(pick_pic())
# predict_video.detect_video(pick_video())

5.2.2 Esp8266.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include <Servo.h>

#include <ArduinoMqttClient.h>
#include <ESP8266WiFi.h>

// 舵机控制引脚
const int servoPin = D5;
Servo servo;

// 串口通信参数
const int baudRate = 9600;

// 定义舵机初始角度和目标角度
const int initialAngle = 90;
const int targetAngle = 180;

// 定义舵机转动速度
const int servoSpeed = 1; // 舵机转动速度,可以根据需要调整

bool servoMoving = false;

char ssid[] = "meng"; // your network SSID (name)
char pass[] = "25197758"; // your network password (use for WPA, or use as key for WEP)


WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);

const char broker[] = "iot-06z00eaojla1tr3.mqtt.iothub.aliyuncs.com";
int port = 1883;

const char inTopic[] = "/sys/k15pz2oiJaP/Cloud/thing/service/property/set";
const char outTopic[] = "/sys/k15pz2oiJaP/MG996R/thing/service/property/set";

const long interval = 10000;
unsigned long previousMillis = 0;

int count = 0;

void setup() {

// 初始化舵机引脚
servo.attach(servoPin);

// 初始化串口通信
Serial.begin(baudRate);

// 将舵机移动到初始位置
servo.write(initialAngle);
//Initialize serial and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}

// attempt to connect to WiFi network:
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(ssid);
while (WiFi.begin(ssid, pass) != WL_CONNECTED) {
// failed, retry
Serial.print(".");
delay(5000);
}

Serial.println("You're connected to the network");
Serial.println();

mqttClient.setId("k15pz2oiJaP.MG996R|securemode=2,signmethod=hmacsha256,timestamp=1713862406063|");
mqttClient.setUsernamePassword("MG996R&k15pz2oiJaP","39ab0c4c3dc156df441d38bf1578371726899ca7db789739beb141fce70aa6cf");

Serial.print("Attempting to connect to the MQTT broker: ");
Serial.println(broker);

if (!mqttClient.connect(broker, port)) {
Serial.print("MQTT connection failed! Error code = ");
Serial.println(mqttClient.connectError());

while (1);
}

Serial.println("You're connected to the MQTT broker!");
Serial.println();

// set the message receive callback
mqttClient.onMessage(onMqttMessage);

Serial.print("Subscribing to topic: ");
Serial.println(inTopic);
Serial.println();

int subscribeQos = 1;

mqttClient.subscribe(inTopic, subscribeQos);


Serial.print("Waiting for messages on topic: ");
Serial.println(inTopic);
Serial.println();
}

void loop() {

int command = Serial.parseInt();
servo.write(90);
mqttClient.poll();
String payload;

if (command == 1 && !servoMoving) {
//servoMoving = true;
//moveServo();
servo.write(targetAngle);
delay(5000);
servo.write(90);//停止了
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {

previousMillis = currentMillis;
payload += "Start!";
//payload += "hello world!";
//payload += " ";
//payload += count;

Serial.print("Sending message to topic: ");
Serial.println(outTopic);
Serial.println(payload);



bool retained = false;
int qos = 1;
bool dup = false;

mqttClient.beginMessage(outTopic, payload.length(), retained, qos, dup);
mqttClient.print(payload);
mqttClient.endMessage();

Serial.println();

count++;
}


}


// 检查舵机是否在运动中,如果是,则更新舵机角度
if (servoMoving) {
if (servo.read() == targetAngle) {
// 舵机已经到达目标位置,复位并停止运动
servoMoving = false;
servo.write(initialAngle);
}
}

//unsigned long currentMillis = millis();

// 读取串口输入


// if (currentMillis - previousMillis >= interval) {

// previousMillis = currentMillis;
// payload += "hello world!";
// //payload += " ";
// //payload += count;

// Serial.print("Sending message to topic: ");
// Serial.println(outTopic);
// Serial.println(payload);



// bool retained = false;
// int qos = 1;
// bool dup = false;

// mqttClient.beginMessage(outTopic, payload.length(), retained, qos, dup);
// mqttClient.print(payload);
// mqttClient.endMessage();

// Serial.println();

// count++;
// }
}

void onMqttMessage(int messageSize) {
// we received a message, print out the topic and contents
Serial.print("Received a message with topic '");
Serial.print(mqttClient.messageTopic());
Serial.print("', duplicate = ");
Serial.print(mqttClient.messageDup() ? "true" : "false");
Serial.print(", QoS = ");
Serial.print(mqttClient.messageQoS());
Serial.print(", retained = ");
Serial.print(mqttClient.messageRetain() ? "true" : "false");
Serial.print("', length ");
Serial.print(messageSize);
Serial.println(" bytes:");

// use the Stream interface to print the contents
while (mqttClient.available()) {
Serial.print((char)mqttClient.read());
}
Serial.println();

Serial.println();
}

6.总结

项目难点:在特征提取网络的选择上,有两个选择,可以选择resnet和MobileNet,但是因为resnet的参数多,所以选择MobileNet,但是运行速度快,就意味着一定的精度损失,所以引入了例如SSH这样的特征提取模块,提高精确度。 在PC端能跑起来,但是在开发板不能运行,或者运行卡顿,找到原因是因为算力不足,所以需要引入tensorrt加速。 linux配置环境很复杂,需要重新编译几个库,在运行软件的时候,使用strace跟踪程序运行进程,检查在哪里卡住了。 # 7.参考文献 [1] 李浩轩.手背静脉身份识别方法门锁系统研究[J].冶金设备,2023(S1):14-17+117. [2] 贺云飞,甘雨,肖国锐.基于ZigBee的智能门锁系统设计[J].电子设计工程,2023,31(16):6-10. [3] 谷月. 虹膜识别“解锁”智能门锁[N]. 中国电子报,2023-04-21(006). [4] J. B. Jin, H. K. Hee, S. S. Soo. Door-Lock System to Detect and Transmit in Real Time according to External Shock Sensitivity[J]. Journal of the Korea Convergence Society, 2018, 9(7): 9-16.
[5] P.Jayasri ,C.Pradeepthi,B.Madhavi, et al.SMART DOOR LOCK SYSTEM INTEGRATED WITH RASPBERRY PI USING IoT[J].Journal of Critical Reviews,2020,7(16):2244-2250. [6] M. Shanthini, G. Vidya and R. Arun, "IoT Enhanced Smart Door Locking System," 2020 Third International Conference on Smart Systems and Inventive Technology (ICSSIT), Tirunelveli, India, 2020, pp. 92-96. [7] Y. Sun, X. Wang and X. Tang, "Deep Learning Face Representation from Predicting 10,000 Classes," 2014 IEEE Conference on Computer Vision and Pattern Recognition, Columbus, OH, USA, 2014, pp. 1891-1898. [8] X. Deng and P. L. Dragotti, "Deep Convolutional Neural Network for Multi-Modal Image Restoration and Fusion," in IEEE Transactions on Pattern Analysis and Machine Intelligence, vol. 43, no. 10, pp. 3333 3348, 1 Oct. 2021. [9] Liu, W., Anguelov, D., Erhan, D., Szegedy, C., Reed, S.E., Fu, C., & Berg, A.C. (2015). SSD: Single Shot MultiBox Detector. European Conference on Computer Vision. [10] J. Deng, J. Guo, N. Xue and S. Zafeiriou, "ArcFace: Additive Angular Margin Loss for Deep Face Recognition," 2019 IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), Long Beach, CA, USA, 2019, pp. 4685-4694. [11]Bubbliiiing. 聪明的人脸识别3——Pytorch 搭建自己的Facenet人脸识别平台[EB/OL]. (2020-11-24)[2024-04-13]. https://blog.csdn.net/weixin_44791964/article/details/108220265. [12]Bubbliiiing. 聪明的人脸识别4——Pytorch 利用Retinaface+Facenet搭建人脸识别平台[EB/OL]. (2020-12-13)[2024-11-12]. https://blog.csdn.net/weixin_44791964/article/details/111130326.

评论