設計與實現(xiàn))
做這個項目之前我一直在糾結(jié)一個問題為什么智能化喊了這么多年公司門口、小區(qū)樓道用的還是那張一刷就過的IC卡卡丟了要補辦卡被復制了毫無感知訪客來了還得保安親自下來接。后來我把目光放到人臉識別門禁上干脆自己從零擼了一套完整的智能門禁系統(tǒng)。項目做到一半我才發(fā)現(xiàn)這東西比想象中有意思也比想象中坑多。這個項目表面上看是一個“攝像頭單片機開鎖”的小玩意真正落地時你會碰到人臉檢測、特征提取、相似度比對、數(shù)據(jù)庫存儲、GPIO控制、硬件選型、光線干擾、活體檢測等一系列問題。我盡量把整個設計和實操過程都攤開來講適合剛?cè)腴T嵌入式AI的開發(fā)者、想做畢業(yè)設計或課程項目的學生也適合想在公司內(nèi)部搞一套低成本門禁方案的運維同學參考。1. 項目設計與整體思路1.1 為什么選擇離線本地識別方案人臉識別門禁市面上成品很多基本分兩類一類是云端識別攝像頭把照片傳到云端識別完返回結(jié)果另一類是本地離線識別所有算法都在門口那臺設備上跑。我選擇離線方案原因很直接。公司或小區(qū)場景下人員進出都是隱私數(shù)據(jù)人臉照片屬于生物特征信息往第三方云服務器傳本身就存在合規(guī)風險。再一個是延遲問題云端識別無論如何都要經(jīng)過網(wǎng)絡往返高峰期排隊反而比刷卡還慢。離線識別的延遲基本可以控制在幾百毫秒以內(nèi)斷網(wǎng)也不影響使用這對門禁這種可靠性要求高的場景太關(guān)鍵了。另外成本上也有優(yōu)勢離線方案不需要買云服務不需要按調(diào)用次數(shù)付費一臺終端設備就能搞定所有事情。對于幾十人到幾百人的中小型團隊這筆賬算下來非常劃算。1.2 整體識別流程與核心模塊劃分整套系統(tǒng)的數(shù)據(jù)流可以拆成下面這幾個環(huán)節(jié)攝像頭采集畫面 → 人臉檢測 → 人臉對齊與預處理 → 特征提取 → 特征比對 → 判定結(jié)果 → 控制門鎖動作每個環(huán)節(jié)都有專門的算法或硬件負責。攝像頭負責“看”人臉檢測負責判斷畫面里有沒有人臉以及人臉在哪里預處理把人臉擺正、統(tǒng)一尺寸、調(diào)整光照影響特征提取把人臉變成一串數(shù)字向量比對環(huán)節(jié)計算當前特征和數(shù)據(jù)庫里注冊特征的相似度最后根據(jù)相似度閾值決定是否開門。一開始我打算用現(xiàn)成的云API快速驗證后來發(fā)現(xiàn)云端API的接口文檔翻來覆去就那幾個參數(shù)對學習幫助不大。于是換成了本地部署模型的方式用OpenCV做人臉檢測用FaceNet系列的輕量模型做特征提取數(shù)據(jù)庫用SQLite存特征向量。整個流程吃透了之后再去看那些商業(yè)門禁機的宣傳語基本能猜出它們內(nèi)部的大致結(jié)構(gòu)。1.3 技術(shù)棧選型的背后邏輯選技術(shù)棧的時候我給自己定了三個約束只能在邊緣設備上跑、必須實時、要能離線運行?;谶@三點我排除了很多看著很炫但跑不動的方案。大尺寸的ResNet系列識別模型在樹莓派上推理一次要好幾秒根本沒法用。最終確定的技術(shù)棧是OpenCV做圖像處理和人臉檢測特征提取用的是基于FaceNet思想訓練的輕量化模型跑在CPU上單次推理大約100毫秒左右。數(shù)據(jù)庫用SQLite因為它不需要單獨起服務文件型數(shù)據(jù)庫在嵌入式設備上非常友好。這套組合的優(yōu)點是依賴少、部署簡單、性能足夠。缺點是如果門禁點位數(shù)特別多比如一個園區(qū)幾十個門每臺設備單獨存一份特征庫管理起來會麻煩。不過對于單機門禁場景這套方案是最務實的選擇。2. 硬件選型與電路設計2.1 主控方案的取舍主控是整個系統(tǒng)的“大腦”決定了你的數(shù)據(jù)處理能力和開發(fā)效率。我前后對比了幾種方案主控方案優(yōu)點缺點適用場景樹莓派4B生態(tài)成熟、資料多、性能夠用價格浮動大、功耗略高中小型項目、學習驗證Jetson NanoGPU算力強、可以跑大模型開發(fā)門檻高、散熱麻煩需要極強算力的復雜場景RK3588等國產(chǎn)板性能功耗比好、接口豐富資料相對少、上手成本高產(chǎn)品化落地香橙派/香蕉派價格低、可玩性高部分型號兼容性一般低成本方案驗證最終我選了樹莓派4B的4GB版本。原因很簡單遇到問題能搜到答案。樹莓派的社區(qū)太龐大了從系統(tǒng)燒錄到GPIO控制幾乎每個坑都有人踩過。用OpenCV跑輕量化識別模型CPU占用大約在40%左右還有余量跑Web服務或者日志記錄。2.2 攝像頭與識別模塊如何選擇攝像頭這塊很多人會踩坑。樹莓派官方CSI攝像頭在樹莓派上的兼容性確實好但如果你是業(yè)余玩家或者想快速驗證方案我更推薦免驅(qū)USB攝像頭插上就能用。CSI接口一旦系統(tǒng)配置有變動可能就要重新折騰驅(qū)動。項目里我用的是一顆普通1080P免驅(qū)USB攝像頭30幀輸出價格幾十塊錢。識別的時候?qū)嶋H只需要取VGA分辨率640x480數(shù)據(jù)量小而且檢測速度更快清晰度完全夠用。網(wǎng)上流傳的TX510人臉識別模塊我也試過。它屬于串口級別的成品模塊你通過串口協(xié)議給它發(fā)指令它自己完成檢測、特征提取和比對然后把結(jié)果返回給主控。這種模塊的優(yōu)勢是開發(fā)工作量極小不需要懂任何算法串口一接就能用。缺點是靈活度太低模塊內(nèi)置的算法你改不了光線適應性、識別距離、閾值調(diào)節(jié)都只能通過有限的參數(shù)控制出了問題排查起來也很被動。如果目的是快速做出一個demoTX510這類模塊是合適的如果你想真正理解人臉識別門禁的原理或者需要二次開發(fā)一定選USB攝像頭配合自研算法路線。做項目最重要的不是“看起來能用”而是“問題來了能下手修”。2.3 門鎖控制與電氣安全門禁系統(tǒng)最終動作是開門這就離不開繼電器和電磁鎖。我選用的是12V供電的電磁鎖也叫電插鎖通過一個5V觸發(fā)的繼電器模塊控制。樹莓派的GPIO引腳輸出3.3V電壓驅(qū)動能力很弱不能直接帶動繼電器線圈所以中間需要加一個三極管或光耦做隔離驅(qū)動。實際接線邏輯是樹莓派GPIO輸出高電平 → 三極管導通 → 繼電器線圈通電 → 常開觸點閉合 → 電磁鎖通電開鎖。這里必須強調(diào)一個安全問題。電磁鎖是12V供電樹莓派和攝像頭是5V供電如果共用電源不規(guī)范很容易造成電壓不穩(wěn)嚴重的會燒主板。我的做法是使用一個12V 5A的開關(guān)電源給電磁鎖供電再用一個DC-DC降壓模塊把12V轉(zhuǎn)成5V給樹莓派供電。另外在繼電器輸出端并聯(lián)一個反向續(xù)流二極管防止電磁鎖斷開瞬間產(chǎn)生的反向電動勢把繼電器和GPIO擊穿。注意涉及市電和鎖具供電的操作務必斷開電源后再接線接線前用萬用表確認電壓極性。如果完全沒有電氣經(jīng)驗建議先用電工實驗板做小電壓驗證。3. 開發(fā)環(huán)境與基礎配置3.1 系統(tǒng)與基礎依賴系統(tǒng)我刷的是樹莓派官方Raspberry Pi OS 64位版本。之前用兼容鏡像遇到過不少莫名奇妙的坑后來發(fā)現(xiàn)官方系統(tǒng)穩(wěn)定性最好軟件源里的軟件包也最全。系統(tǒng)裝好后第一步是更新軟件源和系統(tǒng)包sudo apt update sudo apt upgrade -y sudo apt install python3-pip python3-dev python3-venv git cmake build-essential我習慣把項目依賴裝在獨立的Python虛擬環(huán)境里避免和系統(tǒng)Python環(huán)境互相污染python3 -m venv ~/face-door/env source ~/face-door/env/bin/activate3.2 安裝OpenCV的經(jīng)典痛點OpenCV的安裝是個老生常談的問題每次做視覺項目都能碰到。最簡單的安裝方式是直接用pippip install opencv-python opencv-contrib-python在樹莓派上直接用pip安裝的OpenCV是預編譯版本能正常跑但有幾個細節(jié)要注意。第一opencv-python和opencv-contrib-python不要混裝否則會出現(xiàn)符號沖突報一堆莫名其妙的“undefined symbol”錯誤。第二預編譯版本不一定包含GStreamer支持如果你需要讀取RTSP視頻流可能會發(fā)現(xiàn)cv2.VideoCapture無法打開網(wǎng)絡攝像頭。我的處理方式是先用pip裝預編譯版把流程跑通后期確認項目穩(wěn)定后再考慮源碼編譯。源碼編譯OpenCV在樹莓派上非常耗時我編譯過一次用了將近兩個小時性能提升并不明顯除非你需要特定的擴展模塊否則不建議新手一上來就編譯源碼。3.3 核心依賴清單項目主要依賴如下Python 3.9OpenCV 4.xNumPy圖像矩陣運算dlib可選用于人臉關(guān)鍵點檢測配合活體檢測sqlite3Python內(nèi)置無需額外安裝RPi.GPIO樹莓派GPIO控制Flask可選用于搭建本地管理后臺dlib的安裝稍微麻煩一點它需要cmake編譯安裝時務必先確保系統(tǒng)里有build-essential和cmake。遇到編譯進度卡住的情況通常是因為內(nèi)存不夠建議增加swap空間或者用pip的預編譯wheel包。4. 人臉識別核心算法實現(xiàn)4.1 人臉檢測Haar還是DNN人臉檢測是整個識別流程的入口。OpenCV提供了多種檢測方案最經(jīng)典的是Haar Cascade級聯(lián)分類器當時因為它輕量、速度快被廣泛應用。你只需要加載一個xml文件幾行代碼就能實現(xiàn)人臉檢測。但我在實際測試中發(fā)現(xiàn)Haar對側(cè)臉、暗光、遮擋的漏檢率偏高稍微歪一下頭就檢測不到。而OpenCV的DNN人臉檢測器在魯棒性上明顯更好它是基于SSD框架訓練的殘差網(wǎng)絡模型對角度和光線變化的容忍度更高在樹莓派CPU上檢測一幀大概耗時80毫秒完全在可接受范圍內(nèi)。兩種方案的對比如下檢測方式模型體積檢測速度魯棒性適用場景Haar Cascade約1MB極快一般正臉、光線均勻場景OpenCV DNN (SSD)約10MB較快較強角度變化、復雜光線4.2 加載DNN檢測器的核心代碼用OpenCV加載DNN人臉檢測器需要兩個文件模型結(jié)構(gòu)文件和訓練好的權(quán)重文件可以從OpenCV官方GitHub倉庫下載。import cv2 # 加載DNN人臉檢測模型 net cv2.dnn.readNetFromCaffe( deploy.prototxt, res10_300x300_ssd_iter_140000.caffemodel ) def detect_faces(frame): h, w frame.shape[:2] # 縮放至300x300輸入網(wǎng)絡 blob cv2.dnn.blobFromImage( cv2.resize(frame, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0) ) net.setInput(blob) detections net.forward() faces [] for i in range(detections.shape[2]): confidence detections[0, 0, i, 2] if confidence 0.7: continue box detections[0, 0, i, 3:7] * np.array([w, h, w, h]) x1, y1, x2, y2 box.astype(int) # 保證邊界不越界 x1, y1 max(0, x1), max(0, y1) x2, y2 min(w, x2), min(h, y2) faces.append((x1, y1, x2, y2, confidence)) return facesconfidence閾值設為0.7是我反復測試后的經(jīng)驗值。設太高會漏檢比如0.85以上在光線不好時經(jīng)常檢測不到人臉設太低會出現(xiàn)大量誤檢框比如把墻上的裝飾畫當成人臉后面特征提取就會浪費算力。4.3 特征提取把人臉變成一組數(shù)字人臉識別的本質(zhì)不是“認臉”而是“比對特征”。我們需要把一張人臉圖像變成一組有區(qū)分度的數(shù)字向量這個過程就是特征提取。項目里我采用的是基于FaceNet思想訓練的模型。FaceNet的核心思路是用卷積神經(jīng)網(wǎng)絡把一張人臉圖像映射到一個128維的歐氏空間中同一個人的不同照片在空間中的距離很近不同人的照片距離很遠。這樣識別問題就轉(zhuǎn)化成了向量距離計算問題。模型加載和特征提取的代碼大致如下from facenet_pytorch import InceptionResnetV1 import torch from PIL import Image import numpy as np # 加載預訓練模型 model InceptionResnetV1(pretrainedvggface2).eval() def get_embedding(face_img): # face_img是一張已經(jīng)裁剪好并對齊的人臉BGR圖像 rgb_img cv2.cvtColor(face_img, cv2.COLOR_BGR2RGB) pil_img Image.fromarray(rgb_img).resize((160, 160)) # 轉(zhuǎn)換為tensor并歸一化 face_tensor torch.tensor(np.array(pil_img)).permute(2, 0, 1).float() face_tensor (face_tensor - 127.5) / 128.0 face_tensor face_tensor.unsqueeze(0) with torch.no_grad(): embedding model(face_tensor).numpy().flatten() # 歸一化便于后續(xù)計算余弦相似度 embedding embedding / np.linalg.norm(embedding) return embedding如果你不太想引入PyTorch這么大的依賴也可以使用face_recognition庫它底層封裝了dlib的模型調(diào)用一行代碼就能拿到128維特征向量開發(fā)效率非常高。但face_recognition在樹莓派上的推理速度稍慢而且它內(nèi)部封裝的模型相對固定后續(xù)想換更好的模型就不太靈活。4.4 相似度計算與閾值設定拿到特征向量后比對環(huán)節(jié)用余弦相似度衡量兩段特征向量的距離。余弦相似度的計算公式是cosine_similarity (A · B) / (||A|| × ||B||)其中A和B是兩個人臉的特征向量點積除以模長乘積。結(jié)果范圍在-1到1之間數(shù)值越大表示越相似。我在上一節(jié)已經(jīng)把特征向量歸一化了所以模長等于1余弦相似度實際上就等于點積def cosine_similarity(embedding1, embedding2): return float(np.dot(embedding1, embedding2))關(guān)于閾值我測試了多組數(shù)據(jù)閾值誤識率拒識率結(jié)論0.75極低很高熟人經(jīng)常打不開門不實用0.65低中等光線不好時識別率下降明顯0.55中等較低整體體驗好安全性尚可0.45較高極低容易誤開門不適合門禁最終我采用的閾值是0.55。在辦公室環(huán)境下這個閾值能讓大多數(shù)人正常通行同時對不是同一個人但長得比較像的情況有一定的攔截率。當然門禁的安全級別和使用場景直接相關(guān)如果用在機房等高安全區(qū)域閾值應調(diào)高到0.65以上。4.5 完整識別主流程代碼把檢測、特征提取、比對串起來就形成了門禁系統(tǒng)的核心識別邏輯import cv2 import sqlite3 import numpy as np def recognize(frame, known_embeddings, threshold0.55): faces detect_faces(frame) if len(faces) 0: return None, no_face # 取最大人臉進行識別避免多人場景下誤選 faces.sort(keylambda f: (f[2]-f[0])*(f[3]-f[1]), reverseTrue) x1, y1, x2, y2, conf faces[0] face_img frame[y1:y2, x1:x2] embedding get_embedding(face_img) best_id None best_score -1 for user_id, known_emb in known_embeddings.items(): score cosine_similarity(embedding, known_emb) if score best_score: best_score score best_id user_id if best_score threshold: return best_id, best_score return None, best_score實際測試中單人識別全流程大約耗時300毫秒也就是一秒鐘能處理3幀左右對于門禁場景已經(jīng)綽綽有余。需要優(yōu)化時可以考慮跳幀檢測比如每3幀只做一次完整識別能在不影響體驗的前提下顯著降低CPU占用。5. 數(shù)據(jù)庫設計與門禁控制邏輯5.1 SQLite表結(jié)構(gòu)與特征存儲方式人臉特征向量是一個128維的浮點數(shù)組直接存成Python對象會占用大量空間而且每次讀取都要重新解析。SQLite里可以用BLOB類型保存二進制數(shù)據(jù)存取效率都不錯。建表語句如下CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, department TEXT, face_embedding BLOB NOT NULL, photo_path TEXT, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS recognition_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, name TEXT, score REAL, result TEXT, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP );特征向量寫入數(shù)據(jù)庫時用NumPy的tobytes()把數(shù)組轉(zhuǎn)成二進制讀取時再通過np.frombuffer()恢復import numpy as np import sqlite3 def save_embedding(user_id, embedding): binary embedding.astype(np.float32).tobytes() cursor.execute( UPDATE users SET face_embedding? WHERE id?, (binary, user_id) ) conn.commit() def load_embeddings(): cursor.execute(SELECT id, name, face_embedding FROM users) rows cursor.fetchall() known {} for user_id, name, binary in rows: emb np.frombuffer(binary, dtypenp.float32) known[user_id] emb return known這里有一個容易踩的細節(jié)數(shù)組默認是float64轉(zhuǎn)成二進制后體積翻倍讀取時類型對不上還會報錯。我統(tǒng)一用float32存儲這樣128維向量只占512字節(jié)幾百人的特征庫才幾百KBSQLite文件依然很小。5.2 注冊新用戶的人臉錄入流程正常使用門禁前需要先把人員的照片錄入系統(tǒng)生成特征向量。我設計了兩種錄入方式一種是直接在采集頁面用攝像頭拍一張照片另一種是上傳照片文件。拍照錄入的核心過程是實時顯示攝像頭畫面 → 檢測到人臉時自動抓拍 → 裁剪人臉區(qū)域 → 提取特征 → 保存到數(shù)據(jù)庫 → 提示錄入成功。實操中發(fā)現(xiàn)錄入照片的質(zhì)量直接決定識別效果。照片必須是正臉、光線均勻、沒有遮擋。我錄完第一批用戶后發(fā)現(xiàn)識別率偏低排查了很久才發(fā)現(xiàn)原來有些人錄入時是笑著的有些是逆光的還有人戴了帽子特征向量差異很大導致后面識別時頻繁失敗。后來我在錄入界面加了一道審查邏輯拍照時如果檢測不到人臉或者人臉置信度低于0.75直接拒絕保存并提示重新拍攝。這看似簡單的限制把識別準確率提升了將近一倍。5.3 活體檢測防止一張照片就開門如果你只做到上面那一步那這套門禁系統(tǒng)在安全上是不過關(guān)的。拿一張打印的A4紙照片放在攝像頭前很可能就被直接放行了。這是所有基于2D人臉識別的門禁系統(tǒng)都要面對的問題也就是防照片攻擊和防視頻攻擊?;A方案是活體檢測。常見手段包括眨眼檢測、張嘴檢測、頭部姿態(tài)變化檢測、紅外深度圖檢測、屏幕反光檢測等。我在項目上首先實現(xiàn)了基于dlib的人臉關(guān)鍵點檢測通過計算眼睛縱橫比EAREye Aspect Ratio來判斷是否有眨眼動作def eye_aspect_ratio(eye_points): # 計算上下眼瞼距離 A np.linalg.norm(eye_points[1] - eye_points[5]) B np.linalg.norm(eye_points[2] - eye_points[4]) # 計算水平眼距 C np.linalg.norm(eye_points[0] - eye_points[3]) return (A B) / (2.0 * C)正常情況下人眼EAR值在0.25左右閉眼時降到0.1以下。識別時連續(xù)檢測幾幀如果EAR值有明顯從高到低的波動說明畫面里有眨眼動作。照片是靜止的EAR值恒定不變直接判為攻擊。當然這種2D活體檢測對視頻攻擊比如用手機錄制一段眨眼視頻是攔不住的。更進一步可以結(jié)合紅外模塊或者結(jié)構(gòu)光深度傳感器但那種方案成本和復雜度都會大幅上升。對普通辦公環(huán)境眨眼檢測已經(jīng)能把安全等級提升一個檔次。5.4 GPIO控制電子鎖的執(zhí)行代碼識別成功后的動作是開鎖。樹莓派的GPIO控制繼電器繼電器驅(qū)動電磁鎖。開鎖邏輯需要加一個延時自動落鎖防止門打開了忘記關(guān)。import RPi.GPIO as GPIO import time LOCK_PIN 18 UNLOCK_SECONDS 5 GPIO.setmode(GPIO.BCM) GPIO.setup(LOCK_PIN, GPIO.OUT) GPIO.output(LOCK_PIN, GPIO.LOW) def unlock_door(): # 給繼電器一個高電平觸發(fā)開鎖 GPIO.output(LOCK_PIN, GPIO.HIGH) time.sleep(UNLOCK_SECONDS) GPIO.output(LOCK_PIN, GPIO.LOW) def close_door(): GPIO.output(LOCK_PIN, GPIO.LOW)有一個細節(jié)需要注意程序退出時一定要調(diào)用GPIO.cleanup()否則GPIO引腳會保持最后的狀態(tài)下次程序啟動時可能因為引腳狀態(tài)不對導致繼電器誤動作。我在部署時還加了一個“看門狗”腳本每隔幾秒檢查識別主進程是否還在一旦發(fā)現(xiàn)進程死掉就自動重啟保證門禁系統(tǒng)不至于默默罷工。6. 常見問題與排查技巧實錄6.1 攝像頭打開失敗或畫面卡死樹莓派上使用USB攝像頭最常碰到的問題是設備節(jié)點被占用。多個程序同時打開/dev/video0會導致后打開的程序報錯。排查步驟用lsusb確認攝像頭被系統(tǒng)識別用ls /dev/video*查看設備節(jié)點用v4l2-ctl --list-devices查看攝像頭詳細信息確認代碼里打開的是正確的設備編號另外OpenCV的VideoCapture在某些攝像頭斷開重連后不會自動恢復需要在代碼里增加重連機制。我的做法是循環(huán)檢測cap.isOpened()如果返回False釋放資源并休眠3秒后重新打開。6.2 識別率白天高晚上低這是所有2D視覺方案都逃不過的問題。光線對人臉識別的影響極其顯著同一張臉在自然光和昏暗燈光下提取的特征向量差異可能超過閾值范圍。我的解決方案分三步第一攝像頭位置盡量避開逆光門口加一盞補光燈第二OpenCV里用直方圖均衡化和伽馬校正做圖像增強改善暗部細節(jié)第三在特征提取前對裁剪好的人臉區(qū)域做標準化處理。def preprocess_face(face_img): # 轉(zhuǎn)為灰度后做直方圖均衡化 gray cv2.cvtColor(face_img, cv2.COLOR_BGR2GRAY) equalized cv2.equalizeHist(gray) return cv2.cvtColor(equalized, cv2.COLOR_GRAY2BGR)雖然不是特別高級的做法但對改善暗光識別率幫助很大。如果你的預算充足直接選帶紅外補光燈的攝像頭效果最好。6.3 閾值怎么調(diào)都有人打不開門遇到這種情況先別懷疑算法大概率是注冊照片的質(zhì)量問題。我排查過很多次之后發(fā)現(xiàn)絕大多數(shù)識別失敗都是因為注冊時采集的照片不標準。解決思路是重新采集注冊照片并且按照下面標準執(zhí)行正臉雙眼睜開面部無遮擋光照均勻不能逆光不能低頭仰頭表情自然。錄制時取連續(xù)3幀照片各提取一組特征向量取平均值存入數(shù)據(jù)庫識別穩(wěn)定性會有明顯提升。6.4 OpenCV源碼編譯卡死樹莓派內(nèi)存只有4GB源碼編譯OpenCV時多個編譯任務并行很容易OOM。解決方式是減少并行編譯線程數(shù)同時擴大swap空間。sudo nano /etc/dphys-swapfile # 設置 CONF_SWAPSIZE2048 sudo systemctl restart dphys-swapfile然后編譯時指定make -j2雖然編譯時間變長了但至少不會中途死掉。不過還是那句話除非有特殊需求不建議新手用源碼編譯OpenCV社區(qū)里針對樹莓派的換源方案和預編譯wheel包已經(jīng)很成熟了。6.5 樹莓派重啟后服務不自動啟動門禁設備大概率不會有人守在旁邊手動啟動程序所以必須配置開機自啟。我用了systemd服務來做[Unit] DescriptionFace Door Recognition Service Afternetwork.target [Service] ExecStart/home/pi/face-door/env/bin/python /home/pi/face-door/main.py WorkingDirectory/home/pi/face-door Restartalways Userpi [Install] WantedBymulti-user.target這個配置比較關(guān)鍵的是Restartalways它保證主程序崩潰退出后會被systemd自動拉起。我把這個服務文件放在/etc/systemd/system/face-door.service然后sudo systemctl daemon-reload sudo systemctl enable face-door.service sudo systemctl start face-door.service部署完之后TV斷電重啟也不會影響門禁使用省去了每天手動啟動的麻煩。6.6 多特征匹配的優(yōu)化方向當錄入人員超過幾百人每次識別都全表遍歷一遍特征庫速度會逐漸變慢。普通門禁場景幾百人問題不大但如果考慮擴展可以做兩件事一是按類聚中心把特征分成多個簇先匹配最近的簇再精確匹配二是用FAISS這類向量檢索庫建立索引。對于樹莓派這種算力有限的設備更推薦第一個方案。編碼量不大但能把匹配時間從線性降到對數(shù)級別。這個優(yōu)化我目前還沒完全落地只做了個原型驗證等數(shù)據(jù)量上來之后會補上。項目擴展方向與個人體會這個項目做到上線運行后我在實際使用中感受最深的不是那些算法效果而是“穩(wěn)定壓倒一切”。剛開始我做了一堆炫酷的功能比如識別到人之后播放語音歡迎詞、根據(jù)部門推送不同的問候語后來發(fā)現(xiàn)這些功能在門禁場景里都是次要的。真正重要的是設備不能死機、不能漏識別、不能延遲開鎖、不能在斷電后丟失數(shù)據(jù)。穩(wěn)定性遠比功能多重要。后續(xù)我可以在這個框架上繼續(xù)擴展的點包括接入釘釘或企業(yè)微信的人員同步接口實現(xiàn)人員離職后自動刪除權(quán)限增加多門聯(lián)動的考勤統(tǒng)計通過MQTT把開門事件上報到服務器做統(tǒng)一審計。這套設計的價值不在于代碼本身而在于它把圖像處理、算法模型、嵌入式控制、數(shù)據(jù)存儲這些知識真正串起來讓我能在一套系統(tǒng)里看到全貌。最后再分享一個小技巧如果你是第一次做類似項目先別急著買一堆硬件先用電腦的攝像頭加虛擬機把識別流程跑通確認算法沒問題之后再買樹莓派和電磁鎖。軟件不成熟的時候貿(mào)然接線只會增加排查問題的難度。人識別開門這個事情的體驗真的只有自己親手做完才會發(fā)現(xiàn)它比想象中容易也比想象中有趣。