从零构建智能门禁:基于树莓派与MQTT的软硬件一体化实战

从零构建智能门禁:基于树莓派与MQTT的软硬件一体化实战 1. 项目概述一个从零到一的智能门禁实战最近我完成了一个自己家用的智能门禁系统项目。这个系统的核心目标很简单让我能“刷脸”开门同时保证在没电、断网等极端情况下门锁依然能用钥匙手动操作安全可靠永远是第一位的。整个系统由两个核心模块组成一个是装在门上的“锁控模块”负责物理上转动锁舌另一个是装在门内侧墙上的“控制模块”负责“看脸”并下达开门指令。它们之间通过家里的Wi-Fi用MQTT协议“悄悄”对话。选择这个架构主要是基于几个现实的考量。首先模块化设计能让故障排查和升级变得简单锁控部分坏了不影响识别反之亦然。其次无线连接避免了在门上打孔穿线的麻烦尤其是对于已经装修好的房子。最后MQTT这种轻量级的发布/订阅消息协议非常适合这种设备间需要异步、可靠通信的场景它比简单的HTTP轮询要高效和稳定得多。这个项目涉及了嵌入式开发、物联网通信、计算机视觉和简单的机械结构算是一个软硬件结合的典型小系统。无论你是对物联网感兴趣的开发者还是想给自家大门升级点智能功能的DIY爱好者相信这个从硬件选型、电路设计到代码调试的全过程记录都能给你带来一些实实在在的参考。2. 系统整体设计与核心思路拆解2.1 为什么选择“主从分离”的架构在设计之初我面临一个选择是把所有功能摄像头、识别算法、锁控驱动都集成到门锁上一个设备里还是拆分开我最终选择了后者即“控制模块”主与“锁控模块”从分离的架构。这背后有几个关键原因功耗与供电的平衡锁控模块需要驱动一个扭矩不小的舵机来转动锁舌瞬间电流可能超过2A。如果和需要持续运行面部识别算法的树莓派放在一起对电池供电来说是巨大的挑战。分离后锁控模块可以优化为低功耗待机仅在收到指令时瞬间大电流工作而树莓派则可以常插电源专心处理计算密集型任务。部署的灵活性控制模块含摄像头需要良好的视野和网络连接通常放在门内墙上更合适。锁控模块则需要紧密安装在门锁机械结构上。两者分离安装位置可以各自最优中间仅通过无线连接非常灵活。故障隔离与维护任何一个模块出问题不会导致整个系统完全瘫痪。例如网络故障导致控制模块无法识别你依然可以通过物理按钮集成在锁控模块上或钥匙开门。这种“降级可用”的能力对于安防设备至关重要。2.2 通信协议选型为什么是MQTT模块间通信有多种选择比如HTTP REST API、WebSocket或者更底层的TCP/UDP自定义协议。我选择MQTT主要看中它的几个特性轻量级与低带宽MQTT协议头很小特别适合在微控制器如Particle Argon和单板计算机如树莓派这种资源受限的环境下运行对网络带宽要求极低。发布/订阅模式这种模式完美契合本系统的信息流。锁控模块可以定期发布Publish自己的状态如“门已关”、“锁已上”到door/status主题。控制模块只需要订阅Subscribe这个主题就能实时获知状态而不需要不停地去问轮询。当控制模块识别到授权人脸并检测到按钮按下时它只需向door/control/unlock主题发布一条消息锁控模块订阅了该主题就会自动接收并执行。这种解耦让系统扩展变得非常容易未来如果想加个手机App来查看门锁状态只需要让App订阅door/status主题即可。服务质量QoSMQTT支持三种QoS等级。对于“开门”这种关键指令我使用了QoS 1至少送达一次确保消息不会因为网络波动而丢失。对于状态更新使用QoS 0最多一次以节省资源。本地代理Broker我在树莓派上本地部署了Mosquitto作为MQTT代理。这意味着所有通信都在局域网内完成识别和开门的响应速度极快毫秒级且不依赖外网。只有锁控模块的状态会同时同步到Particle云方便我远程查看比如确认出门后门是否自动锁好但核心控制逻辑完全本地化保证了即使互联网中断家门口的刷脸开门功能依然正常。2.3 核心组件选型背后的考量锁控主控Particle Argon理由我需要一个集成了Wi-Fi和蓝牙、电池管理友好、且开发体验接近Arduino的物联网开发板。Particle Argon完美符合。它基于Nordic nRF52840和Realtek RTL8721DM芯片双核架构能很好地处理网络任务和用户代码。其Device OS提供了稳定的无线连接管理和OTA空中升级功能这对于部署后需要更新固件来说太方便了。相比直接使用ESP32Particle的云端管理工具和更抽象的API让设备管理更省心。注意Particle的某些型号在国内直接使用可能需要关注网络连通性确保其能稳定连接到Particle云用于远程管理但本项目核心的MQTT通信在局域网不影响主体功能。控制主控Raspberry Pi 4B理由面部识别需要一定的计算能力。树莓派4B的Cortex-A72四核处理器和足够的内存我用的4GB版能够流畅运行OpenCV进行实时人脸检测和特征编码。其丰富的GPIO、USB接口和庞大的社区支持也让摄像头接入、调试和扩展变得非常容易。替代方案如果对成本更敏感或功耗要求更严苛可以考虑使用带NPU神经网络处理单元的嵌入式平台如瑞芯微RK3566或华为昇腾Atlas 200 DK它们在人脸识别任务上能效比更高但软件生态和开发复杂度会相应增加。执行器高扭矩金属齿轮反馈舵机理由普通的塑料齿轮舵机在长期、频繁的锁舌转动操作下极易扫齿损坏。金属齿轮更耐用。“反馈”功能是关键这种舵机内部集成了电位器可以实时回传当前角度位置。这让我能精确知道锁舌是处于“已锁”还是“已开”状态而不仅仅是发送了指令就假设动作完成实现了闭环控制安全性大增。参数计算选择舵机扭矩时需要测量转动你家门锁锁舌所需的扭矩。用一个弹簧秤钩住锁舌拉手垂直拉动直至锁舌开始转动读出此时的力F乘以力臂长度L即锁舌到转动轴心的距离粗略得到扭矩 T F * L。然后选择舵机标称扭矩为此值1.5倍以上的型号以留出余量应对阻力变化。视觉模块Raspberry Pi Camera Module V1理由官方摄像头模块兼容性最好通过CSI接口直接连接带宽和延迟都比USB摄像头有优势。V1版虽然像素不高500万但对于人脸识别任务已经足够且价格更实惠。关键在于其驱动和OpenCV的支持非常成熟。3. 硬件设计与搭建实战3.1 锁控模块的电路设计绕开那些“坑”锁控模块的电路是本次硬件设计的难点核心矛盾在于如何用一块锂电池同时稳定地给微控制器3.3V和舵机5V/瞬间大电流供电。最初的错误方案与问题我最初尝试直接用Particle Argon板载的3.3V线性稳压器给整个系统供电包括通过一个电平转换模块驱动舵机。结果一发送舵机转动指令Argon就重启。用示波器观察电源轨发现舵机启动瞬间电池电压被拉低至3V以下导致Argon欠压复位。根本原因分析舵机尤其是金属齿轮舵机在收到位置指令开始转动时转子从静止到启动需要很大的启动电流堵转电流远超其标称的1-2A工作电流。锂电池本身有内阻导线也有电阻在大电流冲击下会产生很大的压降。最终的可靠方案电源分离采用两路独立的电源轨。锂电池正负极同时接入两个地方一路直接接入Argon的LiPo引脚为其内部稳压电路供电。另一路接入一个DC-DC升压模块将电池电压3.7V-4.2V稳定升至5V专门为舵机供电。我选用了一款最大输出电流3A的升压模块。共地至关重要必须将Argon的GND、升压模块的GND输入和输出、舵机的GND全部连接在一起建立一个统一的“参考地”。否则信号电平会混乱舵机控制将不可预测。“软启动”策略硬件上我在舵机的电源正负极之间并联了一个4700μF的电解电容用于缓冲瞬间的大电流需求就像一个小水库在舵机突然“口渴”时先放点水。但实测发现仅靠电容对于某些极端情况仍不足够。软件补救在代码中我没有让舵机直接从A点跳到B点而是实现了渐进式移动。例如从锁止位置角度0到开启位置角度90我让代码每20毫秒将目标角度改变1度大约1.8秒完成整个动作。这极大地平滑了电流需求彻底解决了重启问题。这比单纯增加电容容量更有效、更经济。重要提示在连接电池时务必确认极性正确。建议先不接舵机用万用表测量各电源轨电压正常后再连接舵机进行测试。调试时可以在电源线上串联一个电流表直观观察舵机工作时的电流变化。3.2 机械结构安装精度与可靠性的平衡锁控模块的机械安装目标只有一个将舵机的旋转运动精准、可靠地转换为门锁锁舌的转动。我的具体做法制作联轴器我使用了一截宽度刚好等于门锁旋钮厚度的铝型材槽。将舵机摆臂舵盘用环氧树脂胶牢固地粘在铝槽一端并辅以一颗小螺丝从侧面锁紧防止扭动脱落。对齐轴心这是最关键的一步。将装有舵机的整个盒子临时固定在门内侧把铝槽的另一端套在门锁的旋钮上。然后手动缓慢地旋转门锁旋钮观察舵机盒是否随之发生不必要的位移或扭曲。如果会说明舵机轴心与锁舌旋转轴心没有对准。需要细微调整盒子位置直到手动旋转锁舌时整个舵机盒几乎保持静止只有舵机输出轴在转动。这个步骤需要耐心对准后门锁操作会非常顺滑舵机负载最小。最终固定对准后用自攻螺丝将舵机盒我用的是自制木盒倾斜着斜打拧入门板固定。倾斜的螺丝能提供更好的抗拉拔力。保留手动功能确保在安装后你的手仍然可以方便地接触到门锁旋钮或原有的钥匙孔。在盒子上开一个足够大的孔。这是安全底线确保电子系统失效时物理钥匙依然可用。控制模块的安装就简单多了找一个现成的树莓派塑料外壳或3D打印一个固定在门内侧的墙壁上确保摄像头视野能覆盖门口通常站立的位置并稍微向下倾斜以避免仰拍失真。将开门按钮一个常开式自复位按钮引线接入树莓派GPIO并安装在方便的位置。4. 软件实现状态机与面部识别的融合4.1 锁控模块固件状态机让逻辑一目了然对于锁控模块这种需要根据多种输入按钮、网络指令、门磁信号和当前状态来决定行为的系统状态机是最清晰的设计模式。它让程序流程像流程图一样直观避免了复杂的if-else嵌套。我实现的状态机包含以下几个状态INIT初始化硬件从EEPROM读取保存的锁止/开启角度值。WAIT核心待机状态。在此状态下程序循环检查门磁传感器状态门开/关。本地按钮事件短按、长按。来自Particle云函数的远程调用通过Particle.function绑定。来自MQTT的lock或unlock消息。通过舵机反馈电位器读出的实际锁位置。 根据这些输入并结合一个“期望锁状态”desiredLockState变量来决定下一个状态。例如如果desiredLockState为“开锁”而实际位置是“锁止”且门是关着的则切换到UNLOCKING状态。LOCKING/UNLOCKING驱动舵机缓慢向目标位置移动。每次循环只移动一小步如前文所述的1度/20ms并实时读取反馈位置直到进入目标容差范围。到达后更新实际锁状态并返回WAIT状态。CONFIG配置模式。通过长按按钮进入用于现场校准锁止和开启时舵机的具体角度值并保存到EEPROM。这解决了不同门锁、不同安装位置导致的机械差异。ERROR故障状态。当检测到异常如舵机卡住超过时限时进入。在此状态下停止一切驱动仅通过LED闪烁报错代码等待人工干预如再次长按按钮复位。中断处理为了及时响应按钮和门磁信号我将它们的检测放在了定时器中断服务函数中。这里实现了软件消抖连续多次采样如10ms一次信号都稳定才认为是一个有效的状态变化。同时通过计时区分了“短按”用于触发开/关门和“长按”用于进入配置模式。4.2 控制模块程序OpenCV人脸识别的本地化集成树莓派上的Python程序是系统的大脑它需要连续完成“捕捉图像 - 检测人脸 - 识别身份 - 触发开门”这一系列动作。1. 环境搭建与依赖 在树莓派上安装OpenCV是一个稍微耗时的过程因为需要从源码编译以启用所有优化尤其是NEON指令集。我遵循的步骤大致如下# 更新系统并安装编译工具和依赖 sudo apt update sudo apt upgrade -y sudo apt install build-essential cmake git pkg-config libjpeg-dev libtiff-dev libpng-dev libavcodec-dev libavformat-dev libswscale-dev libv4l-dev libxvidcore-dev libx264-dev libgtk2.0-dev libatlas-base-dev gfortran python3-dev python3-numpy -y # 克隆OpenCV和contrib模块包含人脸识别算法 git clone https://github.com/opencv/opencv.git git clone https://github.com/opencv/opencv_contrib.git cd opencv mkdir build cd build # 配置CMake关键是指定contrib路径和优化选项 cmake -D CMAKE_BUILD_TYPERELEASE \ -D CMAKE_INSTALL_PREFIX/usr/local \ -D OPENCV_EXTRA_MODULES_PATH../../opencv_contrib/modules \ -D ENABLE_NEONON \ -D BUILD_TESTSOFF \ -D WITH_LIBV4LON \ -D PYTHON3_EXECUTABLE$(which python3) \ -D PYTHON3_INCLUDE_DIR$(python3 -c from sysconfig import get_paths; print(get_paths()[include])) \ -D PYTHON3_PACKAGES_PATH$(python3 -c from sysconfig import get_paths; print(get_paths()[purelib])) \ .. # 编译并安装-j4表示用4个核并行编译根据你的Pi型号调整 make -j4 sudo make install这个过程在树莓派4B上可能需要数小时。完成后可以通过python3 -c import cv2; print(cv2.__version__)验证。2. 人脸识别流程的核心代码逻辑 我的程序主要使用了OpenCV的dnn模块加载基于ResNet的人脸检测器以及face_recognition库它封装了dlib的深度学习模型来进行人脸特征编码和比对。import cv2 import face_recognition import paho.mqtt.client as mqtt # 初始化 known_face_encodings [] # 加载已注册用户的人脸编码 known_face_names [] # 对应的用户名 camera cv2.VideoCapture(0) # 对于CSI摄像头可能是 cv2.VideoCapture(0, cv2.CAP_V4L2) # MQTT客户端初始化 mqtt_client mqtt.Client() mqtt_client.connect(localhost, 1883) # 连接到本地Mosquitto代理 def detect_and_identify(): while True: ret, frame camera.read() if not ret: break # 1. 人脸检测将BGR帧转换为RGBface_recognition所需 rgb_frame frame[:, :, ::-1] face_locations face_recognition.face_locations(rgb_frame, modelhog) # 使用HOG模型CPU友好 # 对于树莓派4B也可以尝试 modelcnn 更准确但更慢 if face_locations: # 2. 特征编码 face_encodings face_recognition.face_encodings(rgb_frame, face_locations) for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings): # 3. 与已知人脸比对 matches face_recognition.compare_faces(known_face_encodings, face_encoding, tolerance0.5) name Unknown if True in matches: first_match_index matches.index(True) name known_face_names[first_match_index] # 4. 识别为授权用户且检测到开门按钮被按下需连接GPIO读取 if check_open_button_pressed(): # 此函数需自己实现读取GPIO # 发布MQTT开门指令 mqtt_client.publish(door/control/unlock, 1, qos1) print(fAccess granted for {name}. Door unlock signal sent.) else: print(fAuthorized user {name} detected, awaiting button press.) else: print(Unauthorized face detected.) # 降低循环频率节省CPU time.sleep(0.1)3. 用户注册功能 我单独写了一个注册脚本。运行后它会提示新用户站在摄像头前自动捕捉多张比如20张不同角度和表情的图片为每张图片生成编码然后取这些编码的平均值作为该用户的最终特征向量存入一个文件如known_faces.dat。主识别程序启动时会加载这个文件。一个关键的坑与规避在开发中发现OpenCV的VideoCapture和某些图像处理库在多次release()和重新initialize()后有时会无法正确释放摄像头设备导致程序崩溃。我的临时解决方案比较“粗暴”但有效在完成用户注册流程后自动重启树莓派。这样能确保主识别程序启动时摄像头处于一个绝对干净的状态。在生产环境中可以考虑用更优雅的方式如使用单独的守护进程管理摄像头资源或者使用subprocess调用独立的注册程序。5. 系统集成、调试与问题排查实录5.1 MQTT通信调试确保消息不“丢”集成测试时最常遇到的问题就是模块间“失联”。一套清晰的调试流程至关重要。首先验证MQTT代理在树莓派上使用mosquitto自带的客户端工具测试。# 终端1订阅一个测试主题 mosquitto_sub -h localhost -t test/status -v # 终端2发布一条消息到同一主题 mosquitto_pub -h localhost -t test/status -m Hello MQTT如果在终端1能看到test/status Hello MQTT说明代理工作正常。验证锁控模块的连接与发布在Particle Argon的代码中在连接MQTT成功后定期比如每10秒发布一条状态消息到door/debug主题。在电脑上使用MQTT客户端工具如MQTT Explorer订阅这个主题看是否能收到。如果收不到检查Argon的Wi-Fi连接、MQTT服务器地址和端口是否正确以及防火墙设置树莓派默认的ufw防火墙可能需要开放1883端口。验证控制模块的订阅与发布在树莓派的Python脚本中增加一个调试回调打印所有收到的MQTT消息。同时手动用mosquitto_pub发布一条开门指令看树莓派脚本是否能收到并打印。再用树莓派脚本发布一条消息看电脑上的客户端是否能收到。双向验证能快速定位问题是出在发送方、接收方还是网络。使用遗嘱消息Last Will在Argon连接MQTT代理时设置一个遗嘱消息如door/status/online主题内容为0。这样如果Argon异常掉线如电池耗尽代理会自动发布这条消息告知系统锁控模块已离线控制模块可以据此发出警报。5.2 面部识别效果优化光线与角度是敌人在实际部署中识别率受环境影响很大。我遇到了白天逆光识别失败、夜晚光线不足识别慢等问题。解决逆光/背光人脸变暗与背景对比度低。尝试调整摄像头位置避免正对窗户或光源。在软件上可以尝试在识别前对图像进行直方图均衡化或CLAHE限制对比度自适应直方图均衡来增强面部细节。# 在将帧转换为RGB之前可以先进行预处理 gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8,8)) gray clahe.apply(gray) # 再将灰度图转回BGR进行后续处理注意face_recognition需要彩色图但检测可以用灰度 frame cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)解决光线不足考虑增加一个低照度、无红曝的IR补光灯并搭配树莓派官方的红外摄像头NoIR版本。这样在完全黑暗的环境下也能清晰捕捉人脸图像且补光不刺眼。OpenCV处理红外图像与人脸识别流程兼容。多角度注册在注册用户时务必引导用户左右转头、稍微抬头低头采集不同角度的照片。这样训练出的特征模型更健壮能应对日常开门时并非完全正对摄像头的情况。设置置信度阈值与活体检测可选compare_faces函数的tolerance参数很关键。默认0.6比较宽松可以调到0.5甚至0.4以提高安全性但可能增加拒识率把本人认成陌生人。对于高安全场景可以引入简单的活体检测如要求用户眨眼或转头通过连续帧分析防止用照片欺骗。5.3 功耗管理与续航实战锁控模块由电池供电续航是必须考虑的问题。测量待机电流使用万用表串联在电池和板子之间测量Argon在深度睡眠模式下的电流。Particle Argon的深度睡眠电流可以低至几十微安。确保代码中在WAIT状态长时间无操作时调用了System.sleep()或类似的低功耗函数。优化状态机循环在loop()中如果没有状态变更不要使用delay(100)这样的忙等待这会阻止CPU进入低功耗模式。应该使用非阻塞的定时器或者直接利用Particle.process()和delay(1)的组合让系统有机会处理后台任务并进入空闲状态。舵机电源管理如果舵机支持可以在非动作期间切断其电源通过一个MOSFET控制舵机VCC的通断。我的舵机不支持所以主要依靠软件上减少不必要的微小震动确保PWM信号在非动作期间稳定在一个固定值而不是悬空。电池选型与估算我使用了一块3000mAh的18650锂电池。假设平均每天开门20次每次舵机工作约3秒开关电流2A则每天耗电约 (20 * 2A * 3s / 3600s/h) ≈ 0.033 Ah。Argon待机电流按100μA算一天约0.0024 Ah。加上其他损耗总日耗电约0.04 Ah。3000mAh电池理论可续航75天。实际测试中我做到了约两个半月充一次电与估算基本吻合。5.4 常见问题速查表问题现象可能原因排查步骤与解决方案树莓派摄像头无画面1. 摄像头排线未插紧或损坏。2. 摄像头接口未在raspi-config中启用。3. OpenCV使用的摄像头索引错误。1. 重新拔插排线检查金手指。2. 运行sudo raspi-config在Interface Options中启用Camera。3. 尝试cv2.VideoCapture(0)或cv2.VideoCapture(-1)对于CSI摄像头有时需要指定后端cv2.VideoCapture(0, cv2.CAP_V4L2)。人脸检测框乱跳或无法检测1. 环境光线过暗或过曝。2.face_locations函数使用的模型HOG/CNN不合适或参数需调整。3. 摄像头焦距未对准。1. 改善光照避免强光直射镜头或背景过亮。2. HOG更快但精度稍低CNN更准但耗资源。在树莓派4B上HOG模型实时性更好。可调整number_of_times_to_upsample参数默认1尝试0或2。3. 手动调整摄像头镜头对焦环确保人脸清晰。识别为“Unknown”或识别错误率高1. 注册时样本太少或角度单一。2.tolerance阈值设置不合理。3. 当前人脸与注册时外貌变化大如戴眼镜、帽子。1. 重新注册采集更多20、更多样化的照片。2. 调整tolerance值默认0.6降低如0.5更严格提高如0.7更宽松。3. 建议注册时包含戴/不戴眼镜等不同装扮。或设置多个特征模板。舵机转动不顺畅或卡顿1. 机械结构未对准阻力过大。2. 电源电压不足或电流不够。3. 舵机内部齿轮损坏。1.断电手动转动门锁检查是否顺滑。重新调整舵机与锁舌的轴心对齐。2. 用万用表测量舵机供电电压在动作时是否跌落到4.5V以下。检查电池电量升级电源线径确保连接牢固。3. 更换舵机。Particle Argon频繁重启1. 舵机启动瞬间导致电源电压跌落。2. 代码中有内存泄漏或看门狗复位。1.这是最常见原因。实施“软启动”代码渐进式移动并在舵机电源端并联大容量电容如1000μF以上。2. 检查代码中是否有大型局部变量导致栈溢出或使用Particle.process()及时处理网络任务。MQTT消息收不到1. 网络IP冲突或Wi-Fi信号弱。2. 主题Topic订阅/发布拼写不一致。3. 防火墙阻止了1883端口。4. MQTT客户端ID冲突。1. 检查设备能否ping通MQTT代理。增强Wi-Fi信号或使用有线网络树莓派。2.仔细核对主题字符串包括大小写。建议在代码中使用常量定义主题名。3. 在树莓派上运行sudo ufw allow 1883。4. 确保每个MQTT客户端都有唯一的Client ID。按钮按下无反应1. GPIO引脚配置错误输入/输出模式。2. 内部上拉/下拉电阻未启用引脚悬空。3. 消抖逻辑过于严格或代码有bug。1. 确认代码中设置为输入模式pinMode(BUTTON_PIN, INPUT_PULLUP)。2. 使用内部上拉电阻并将按钮接在引脚和GND之间。3. 简化测试先去掉消抖逻辑看是否能检测到电平变化。用digitalRead和打印语句调试。这个项目从构思到稳定运行前后花了近一个月的业余时间。最大的体会是物联网项目永远是“三分软件七分调试”硬件上的一个小问题比如电源干扰可能让你在软件层面排查好几天。务必准备好万用表、逻辑分析仪或至少一个串口调试器这些基础工具它们能帮你快速定位是电源问题、信号问题还是代码逻辑问题。另外日志是关键。无论是在Argon上通过Serial.print输出日志到云端控制台还是在树莓派上将关键事件写入文件详尽的日志都能在出现问题时让你快速回溯系统状态。最后关于安全性再多说一句。这个DIY系统适合家庭等对物理安全要求不是极端严苛的场景。如果你需要商用或用于更高安全级别的环境务必考虑增加更多安全层比如使用加密的MQTTMQTTS、定期更换人脸特征库、增加刷卡或密码作为备用验证、以及将核心识别模型放在更安全的服务器端而非边缘设备上。安全是一个持续的过程而非一劳永逸的功能。