電路學期末專題- Low-cost Facial Recognition Doorlock System
電路學期末專題- Low-cost Facial Recognition Doorlock System
功能介紹
- 透過AMB82開發版可連網的特性,可於同區域網內時實查看錄像。
- 電磁鎖解鎖(開門),可以透過以下兩種方法。
- 第一種方法為"臉部辨識解鎖"。AMB82本就是Realtek專門設計來跑相關辨識模型的開發版,透過跑臉部辨識模型,將實時影像與資料庫做2D圖像對比,如果比對正確,則LCD會顯示"Welcome Home!",電磁鎖會開啟3s,並於3s後重新上鎖,如比對錯誤,則不會作動。
- 第二種方法則是"密碼解鎖"。輸入密碼後,會進行密碼比對,若正確會於LCD顯示 "Welcome Home!",電磁鎖會開啟3s,並於3s後重新上鎖,如比對錯誤則會顯示"Wrong Password!",並於3s後可重新輸入。
- 電磁鎖開啟時外部綠色LED會亮,關閉時則紅色LED會亮。
- 以上兩種解鎖方法只能擇一進行,不可同時!
所需材料
Realtek AMB82-mini AI Camera:link
Arduino Uno-自備
電磁鎖:link
- 觸發電源大小:6V/0.6A
繼電器:link
杜邦線、跳線數條-自備
麵包板-自備
電磁鎖電源供應:link
AMB82 電源供應:行動電源 or 5V 1A 插座
DC母頭:link
LCD模組with i2c included:link
3x3 keypad:link
LED-自備
木板數片
電路連接
- 圖片待補
- Arduino
- LCD
- VCC->Arduino_5V
- GND->Arduino_GND
- SDA->Arduino_A4
- SDL->Arduino_A5
- Relay_Module
- Positive->Arduino_5V
- Negative->Arduino_GND
- S(訊號控制線)->Arduino_13
- COM->電源供應
- NumberPad
- 面相NumberPad,由左至右插在Arduino_5~Arduino_11
- LCD
- AMB82
- GND->Arduino_GND
- 8->Arduino_A0
- 3->Led_red
- 4->Led_green
- 電磁鎖
- 記得先試接,確認作動方向。
- GND與DC母頭連接,正極接在繼電器NO(Normal Open)接口。
程式編寫
需先設定AMB82環境
需安裝以下函式庫
程式碼
- AMB82
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
//鏡頭相關腳位定義
//設定臉部辨識解析度
//腳位定義
VideoSetting configVID(VIDEO_FHD, 30, VIDEO_H264, 0);
VideoSetting configJPEG(VIDEO_FHD, CAM_FPS, VIDEO_JPEG, 1);
VideoSetting configNN(NNWIDTH, NNHEIGHT, 10, VIDEO_RGB, 0);
NNFaceDetectionRecognition facerecog;
RTSP rtsp;
StreamIO videoStreamer(1, 1);
StreamIO videoStreamerFDFR(1, 1);
StreamIO videoStreamerRGBFD(1, 1);
char ssid[] = "HaHaPiYan"; //設定 wifi ssid
char pass[] = "00000000"; //設定 wifi password
int status = WL_IDLE_STATUS;
int resultSize = 0;
uint32_t img_addr = 0;
uint32_t img_len = 0;
String fileName;
long counter = 0;
//檔案初始化
AmebaFatFS fs;
void setup() {
pinMode(RED_LED, OUTPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(DOORLOCK, OUTPUT);
Serial.begin(115200);
//wifi連線
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass);
// wait 2 seconds for connection:
delay(2000);
}
//鏡頭初始化
Camera.configVideoChannel(CHANNELVID, configVID);
Camera.configVideoChannel(CHANNELJPEG, configJPEG);
Camera.configVideoChannel(CHANNELNN, configNN);
Camera.videoInit();
rtsp.configVideo(configVID);
rtsp.begin();
//臉部辨識模型設定
facerecog.configVideo(configNN);
facerecog.modelSelect(FACE_RECOGNITION, NA_MODEL, DEFAULT_SCRFD, DEFAULT_MOBILEFACENET);
facerecog.begin();
facerecog.setResultCallback(FRPostProcess);
videoStreamer.registerInput(Camera.getStream(CHANNELVID));
videoStreamer.registerOutput(rtsp);
if (videoStreamer.begin() != 0) {
Serial.println("StreamIO link start failed");
}
//開始影片串流
Camera.channelBegin(CHANNELVID);
Camera.channelBegin(CHANNELJPEG);
videoStreamerRGBFD.registerInput(Camera.getStream(CHANNELNN));
videoStreamerRGBFD.setStackSize();
videoStreamerRGBFD.setTaskPriority();
videoStreamerRGBFD.registerOutput(facerecog);
if (videoStreamerRGBFD.begin() != 0) {
Serial.println("StreamIO link start failed");
}
Camera.channelBegin(CHANNELNN);
OSD.configVideo(CHANNELVID, configVID);
OSD.begin();
digitalWrite(DOORLOCK, LOW);
}
void loop() {
if (Serial.available() > 0) {
String input = Serial.readString();
input.trim();
//基本指令
if (input.startsWith(String("REG="))) {
String name = input.substring(4);
facerecog.registerFace(name);
}
else if (input.startsWith(String("EXIT"))) {
facerecog.exitRegisterMode();
}
else if (input.startsWith(String("RESET"))) {
facerecog.resetRegisteredFace();
}
else if (input.startsWith(String("BACKUP"))) {
facerecog.backupRegisteredFace();
}
else if (input.startsWith(String("RESTORE"))) {
facerecog.restoreRegisteredFace();
}
}
delay(1000);
OSD.createBitmap(CHANNELVID,RECTTEXTLAYER0);
OSD.createBitmap(CHANNELVID,RECTTEXTLAYER1);
OSD.update(CHANNELVID, RECTTEXTLAYER0);
OSD.update(CHANNELVID, RECTTEXTLAYER1);
}
void FRPostProcess(std::vector<FaceRecognitionResult> results) {
uint16_t im_h = configVID.height();
uint16_t im_w = configVID.width();
printf("Total number of faces detected = %d\r\n", facerecog.getResultCount());
OSD.createBitmap(CHANNELVID, RECTTEXTLAYER0);
OSD.createBitmap(CHANNELVID, RECTTEXTLAYER1);
if (facerecog.getResultCount() > 0) {
if (1) {
if (facerecog.getResultCount() > 1) { // Door remain close when more than one face detecteD
digitalWrite(RED_LED, HIGH);
digitalWrite(GREEN_LED, LOW);
} else {
FaceRecognitionResult singleItem = results[0];
if (String(singleItem.name()) == String("unknown")) {
//臉部辨識結果錯誤,紅色警示開啟
digitalWrite(RED_LED, HIGH);
digitalWrite(GREEN_LED, LOW);
} else {
//臉部辨識結果正確,綠色通行燈開啟,並進行解鎖訊號傳輸工作
digitalWrite(RED_LED, LOW);
digitalWrite(GREEN_LED, HIGH);
fileName = String(singleItem.name());
digitalWrite(DOORLOCK, HIGH);
Serial.println("Door Opened!");
delay(3000);
digitalWrite(DOORLOCK,LOW);
Serial.println("Door Locked!");
}
}
}
}
for (uint32_t i = 0; i < facerecog.getResultCount(); i++) {
FaceRecognitionResult item = results[i];
int xmin = (int)(item.xMin() * im_w);
int xmax = (int)(item.xMax() * im_w);
int ymin = (int)(item.yMin() * im_h);
int ymax = (int)(item.yMax() * im_h);
uint32_t osd_color;
int osd_layer;
if (String(item.name()) == String("unknown")) {
osd_color = OSD_COLOR_RED;
osd_layer = RECTTEXTLAYER0;
} else {
osd_color = OSD_COLOR_GREEN;
osd_layer = RECTTEXTLAYER1;
}
printf("Face %d name %s:\t%d %d %d %d\n\r", i, item.name(), xmin, xmax, ymin, ymax);
OSD.drawRect(CHANNELVID, xmin, ymin, xmax, ymax, 3, osd_color, osd_layer);
char text_str[40];
snprintf(text_str, sizeof(text_str), "Face:%s", item.name());
OSD.drawText(CHANNELVID, xmin, ymin - OSD.getTextHeight(CHANNELVID), text_str, osd_color, osd_layer);
}
OSD.update(CHANNELVID, RECTTEXTLAYER0);
OSD.update(CHANNELVID, RECTTEXTLAYER1);
}- Arduino
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
const byte ROWS = 4; // 列數(橫的)
const byte COLS = 3; // 行數(直的)
int i=0, j=0;
//鍵盤上每一個按鍵的名稱
char keys[ROWS][COLS] = {
{'1','2','3'},
{'4','5','6'},
{'7','8','9'},
{'*','0','#'}
};
byte rowPins[ROWS] = {11, 10, 9, 8}; //定義列的腳位
byte colPins[COLS] = {7, 6, 5}; //定義行的腳位
String passcode = "1234"; // 預設密碼
String inputCode = ""; // 暫存用戶的按鍵字串
bool acceptKey = true; // 是否接受用戶按鍵輸入的boolean value
int value_A0;
//初始化鍵盤
Keypad customKeypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// LCD顯示器
LiquidCrystal_I2C lcd(0x27, 16, 2); // 設定模組位址0x27,以及16行, 2列的顯示形式
void clearRow(byte n) {
byte last = LCD_COLS - n;
lcd.setCursor(n, 1); // 移動到第2行,"PIN:"之後
for (byte i = 0; i < last; i++) {
lcd.print(" ");
}
lcd.setCursor(n, 1);
}
//重設LCD顯示文字和輸入狀態。
void resetLocker() {
lcd.clear();
lcd.print("Enter Password");
lcd.setCursor(0, 1);
lcd.print("PIN:");
lcd.cursor();
acceptKey = true;
inputCode = "";
}
// 比對用戶輸入的密碼
void checkPinCode() {
acceptKey = false; // 暫時不接受用戶按鍵輸入
clearRow(0); // 從第0個字元開始清除LCD畫面
lcd.noCursor();
lcd.setCursor(0, 1);
// 比對密碼
if (inputCode == passcode) {
lcd.print("Welcome Home!");
digitalWrite(13, HIGH);
delay(3000);
digitalWrite(13, LOW);
}
else {
lcd.print("Wrong Password!");
}
delay(3000);
resetLocker();
}
void setup() {
pinMode(13, OUTPUT);
pinMode(A0, INPUT);
digitalWrite(13, LOW);
Serial.begin(9600);
lcd.init();
lcd.backlight();
resetLocker();
}
void loop() {
value_A0=analogRead(A0); //以A0接收amb82開發板的訊號接收
//Serial.println(value_A0);
char key = customKeypad.getKey();
// 數字鍵盤的解鎖
if (acceptKey && key != NO_KEY) {
if (key == '*') { // 清除畫面
clearRow(4); // 從第4個字元開始清除
inputCode = "";
} else if (key == '#') {
checkPinCode(); // 比對輸入密碼
} else {
inputCode += key; // 儲存用戶的按鍵字元
lcd.print('*');
}
}
// 臉部辨識的解鎖
if(value_A0>900){
acceptKey = false;
lcd.clear();
lcd.noCursor();
lcd.print("Welcome Home!");
digitalWrite(13, HIGH);
delay(3000);
digitalWrite(13, LOW);
resetLocker();
}
}
遇到的問題及注意事項
- 一開始我們想使用Esp32來進行臉部辨識,但辨識度、流暢度及穩定度皆不佳。
- 解決方法: 改用AMB82
- 電腦讀取不到AMB82。
- 解決辦法: 安裝CH340接口的驅動程式(link )
- Arduino無法讀取到AMB82的訊號。
- 解決辦法: 兩塊開發板間GND要接GND,形成完整迴路。
- 程式編譯錯誤
- 可能原因
- 未正確安裝函式庫。
- 未正確設定開發環境。
- 可能原因
外觀設計
- 待補
成品展示
參考資料(References)
- Title: 電路學期末專題- Low-cost Facial Recognition Doorlock System
- Author: Shih Jiun Lin
- Created at : 2023-06-12 13:00:00
- Updated at : 2023-06-12 13:11:03
- Link: https://shih-jiun-lin.github.io/2023/06/12/電路學期末專題- Low-cost Facial Recognition Doorlock System/
- License: This work is licensed under CC BY-NC-SA 4.0.