電路學期末專題- Low-cost Facial Recognition Doorlock System

Shih Jiun Lin Lv4

電路學期末專題- Low-cost Facial Recognition Doorlock System

功能介紹

  1. 透過AMB82開發版可連網的特性,可於同區域網內時實查看錄像。
  2. 電磁鎖解鎖(開門),可以透過以下兩種方法。
    • 第一種方法為"臉部辨識解鎖"。AMB82本就是Realtek專門設計來跑相關辨識模型的開發版,透過跑臉部辨識模型,將實時影像與資料庫做2D圖像對比,如果比對正確,則LCD會顯示"Welcome Home!",電磁鎖會開啟3s,並於3s後重新上鎖,如比對錯誤,則不會作動。
    • 第二種方法則是"密碼解鎖"。輸入密碼後,會進行密碼比對,若正確會於LCD顯示 "Welcome Home!",電磁鎖會開啟3s,並於3s後重新上鎖,如比對錯誤則會顯示"Wrong Password!",並於3s後可重新輸入。
    • 電磁鎖開啟時外部綠色LED會亮,關閉時則紅色LED會亮。
    • 以上兩種解鎖方法只能擇一進行,不可同時!

所需材料

  1. Realtek AMB82-mini AI Camera:link

  2. Arduino Uno-自備

  3. 電磁鎖:link

    • 觸發電源大小:6V/0.6A

  4. 繼電器:link

  5. 杜邦線、跳線數條-自備

  6. 麵包板-自備

  7. 電磁鎖電源供應:link

  8. AMB82 電源供應:行動電源 or 5V 1A 插座

  9. DC母頭:link

  10. LCD模組with i2c included:link

  11. 3x3 keypad:link

  12. LED-自備

    Imgur
  13. 木板數片

電路連接

  • 圖片待補
  1. 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
  2. AMB82
    • GND->Arduino_GND
    • 8->Arduino_A0
    • 3->Led_red
    • 4->Led_green
  3. 電磁鎖
    • 記得先試接,確認作動方向。
    • 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
    #include "WiFi.h"
    #include "StreamIO.h"
    #include "VideoStream.h"
    #include "RTSP.h"
    #include "NNFaceDetectionRecognition.h"
    #include "VideoStreamOverlay.h"
    #include "AmebaFatFS.h"

    //鏡頭相關腳位定義
    #define CHANNELVID 0
    #define CHANNELJPEG 1
    #define CHANNELNN 3

    //設定臉部辨識解析度
    #define NNWIDTH 576
    #define NNHEIGHT 320

    #define RECTTEXTLAYER0 OSDLAYER0
    #define RECTTEXTLAYER1 OSDLAYER1

    //腳位定義
    #define RED_LED 3
    #define GREEN_LED 4
    #define DOORLOCK 8

    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
    #include "Keypad.h"
    #include <Wire.h>
    #include <LiquidCrystal_I2C.h>

    const byte ROWS = 4; // 列數(橫的)
    const byte COLS = 3; // 行數(直的)

    int i=0, j=0;
    #define LCD_ROWS 2 // LCD顯示器的列數
    #define LCD_COLS 16 // LCD顯示器的行數

    //鍵盤上每一個按鍵的名稱
    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();
    }
    }

遇到的問題及注意事項

  1. 一開始我們想使用Esp32來進行臉部辨識,但辨識度、流暢度及穩定度皆不佳。
    • 解決方法: 改用AMB82
  2. 電腦讀取不到AMB82。
    • 解決辦法: 安裝CH340接口的驅動程式(link )
  3. Arduino無法讀取到AMB82的訊號。
    • 解決辦法: 兩塊開發板間GND要接GND,形成完整迴路。
  4. 程式編譯錯誤
    • 可能原因
      1. 未正確安裝函式庫。
      2. 未正確設定開發環境。

外觀設計

  • 待補

成品展示

參考資料(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.