人脸识别卡壳之后:RK3588门禁项目中的指纹备选方案复盘

📅 发布时间:2026/9/10 19:10:02
人脸识别卡壳之后:RK3588门禁项目中的指纹备选方案复盘
1. 立项时我为什么铁了心要上人脸识别1.1 客户一句话项目方向就定了这个项目其实来得挺突然。园区有个角落门禁原来用刷卡但客户那边的管理人员吐槽了一堆问题有人忘带卡、有人卡丢了补办麻烦、还有人借着“帮同事刷卡”的名义随便放人进来。需求会上客户负责人随口说了一句“现在小区单元门都刷脸了咱们这个能不能也搞刷脸多酷。”就是这句话把项目定调了。我当时在需求书里写了一行主识别方案为人脸识别终端本地边缘计算不依赖云端。理由是园区网络环境一般办公区到机房跨了好几栋楼实时请求云端一旦网络抖动门禁就废了。人脸识别模型必须跑在设备本地这一点直接决定了后面所有硬件选型和算法选型的方向。但说实话我之所以敢拍板做人脸识别一是因为手头刚好有RK3588的开发板在做别的边缘计算项目二是当时觉得开源人脸识别方案已经挺成熟了。后来证明这两点都只对了一半。“模型能跑”和“现场能用”之间的距离远比我预想的要长。1.2 为什么选了RK3588而不是树莓派这块板子是整个项目的地基选型时对比过几个方向简单列一下当时的考量方案算力表现接口丰富度坑的程度Raspberry Pi 4纯CPU推理人脸检测能到几帧识别就吃力了常规USB/GPIO勉强够用社区资料多但算力是瓶颈RK3588开发板6 TOPS NPU理论能跑30fps级别的人脸识别MIPI-CSI/串口/USB/GPIO非常全RKNN模型转换有点折腾低功耗PC 显卡算力最强接口完善功耗、体积、成本都超出门禁场景预期最后选了RK3588核心就两个理由。第一它自带6 TOPS的NPU算力人脸识别这类模型在端侧跑有足够的性能余量而且NPU推理时CPU负载很低后面想加其他业务逻辑也不用担心抢资源。第二接口实在太全了MIPI-CSI可以接摄像头串口和GPIO可以接各种外设。我当时留了个心眼尽管项目只提人脸我还是在硬件上把一组串口和几个GPIO预留出来了——我见过太多项目做到一半需求方突然说“能不能再加个指纹”“能不能再接个按钮”没有预留接口就得重新画板子周期完全不可控。这个决定后来帮了大忙。1.3 指纹为什么被我放进了备选位按说指纹识别技术成熟度高、成本低、误识率好控制为什么一开始没做成主方案两个原因。第一是客户主观偏好他们已经预设了“刷脸”这个形态产品汇报的时候人脸识别的演示效果确实比指纹更有冲击力。第二是场景适配上的潜在问题门禁这种使用频率很高的设备指纹头容易积灰、沾水、沾油碰上手上带水或者戴手套的用户体验会非常差。所以从需求文档上指纹只作为后续扩展项甚至一开始都没写进排期。但这里我想多说一句也是这次项目让我彻底想明白的一件事所谓“主方案”和“备选方案”本质上是同一个目标的两种实现路径而不是两件事。识别方式只是交互差异真正的目标永远是“让合法的人方便地开门同时把不合法的人挡在外面”。当时我在意的是路径A忽略了路径B可能在工程上更稳。后来事实也证明把人脸方案逼到绝路的那些环境因素恰恰是指纹方案完全不受影响的。2. 人脸识别调了大半个月卡在哪几个真实瓶颈上2.1 模型部署的坎ONNX转RKNN没那么顺利先说模型选型。当时我们先用的是MobileFaceNet结构的人脸识别模型加上RetinaFace做人脸检测PyTorch下训练好的模型导出ONNX非常顺畅在PC上用CPU推理检测加识别整体能跑到十几帧效果看着也挺好。我当时天真地以为转到RK3588的NPU上也差不多就是这个性能。结果栽在了模型转换上。RK3588跑模型要用瑞芯微的RKNN格式转换工具是RKNN-Toolkit2。第一次转ONNX到RKNN直接报了一堆warning说某些算子不支持NPU加速自动fallback到CPU。什么概念呢人脸检测网络里那几层如果被CPU接管整个推理链路的数据就要在CPU和NPU之间来回搬运速度直接掉一个量级。实测下来检测加识别的端到端帧率大概只有6到8fps跟预览要求的流畅度差得很远。后来我查了RKNN社区的讨论发现transpose、reshape、slice这几类算子最容易触发fallback。最稳妥的办法不是去改RKNN的底层支持而是回头改模型结构把这些算子的使用降到最低。我当时换了另一个轻量检测模型并且把识别模型的结构稍微调整了一下总算把检测能跑到15fps左右识别链路也能稳定在10fps以上。这个性能做门禁勉强够了但代价是整整一周多的时间都耗在“模型转换-刷板子-跑测试”这个循环里后面还有更麻烦的坎在等着。2.2 光线和角度实验室里满分现场直接不及格模型转换的问题解决后我拿着板子挂了块屏幕在办公室搭了个测试环境。办公室灯光均匀人坐在摄像头正前方识别率很高那一刻我觉得项目快完了。结果设备装到园区门禁现场第一条就被打脸了。现场的问题是逆光。门禁设备装在门边的墙上正对着室外方向白天太阳从人背后照过来人脸区域在画面里整个是暗的。我一开始把摄像头宽动态开了检测框确实能稳定锁住人脸了但识别模型的输入是宽动态调过亮度的画面跟训练数据里的正常亮度人脸分布差太远了识别置信度掉得很厉害。到了晚上更麻烦补光灯开太强人脸过曝开太弱又看不清只能一点一点找平衡。还有一个容易被忽略的问题是安装高度。门禁设备按标准装在1.2米高度左右但实际用的人身高差异很大一米九的人要低头一米六的人要微仰头。人脸检测对这些角度变化还算能容忍但识别模型在俯角仰角稍大一点时特征提取的稳定性明显下降。后来我们紧急调了一批现场照片回服务器重新跑评测发现极端角度下的识别通过率只有不到70%这个数据在门禁场景是不可接受的。2.3 活体检测的误拦截防攻击和便民性的矛盾人脸门禁不能只做识别还得防照片、视频攻击。开始我觉得这就是加个活体模型的事结果它成了压垮整个方案的最后几根稻草之一。我们接了一个静默活体检测模型不需要用户配合做动作理论上对着摄像头正常站着就能判断是不是真人。但这个模型在现场的误拦截率非常高真实用户站在原地等门开系统却提示“请面对摄像头”甚至直接不响应。原因部分来自光线和角度部分来自模型本身对真实场景的泛化能力不足。后来测试了需要用户配合的眨眼、张嘴的动作活体误拦截率降下来了但用户明显很烦门禁本来就是图个“无感通行”每次要配合做动作反而比刷卡还慢。到了这个阶段整个项目组士气已经很低了。模型层的调整空间越来越小现场环境的限制又没法通过参数彻底解决。我意识到要继续打通人脸识别方案可能得从补光设计、安装位置、摄像头选型这些硬件层面重新来过但项目周期上已经不允许我再花一个月去试错。2.4 团队里吵了一架之后决定先接个指纹模块人脸方案僵住的时候团队内部有了分歧。一部分人觉得应该继续硬啃把现场光线问题通过补光方案解决活体误拦再慢慢调另一部分人觉得得先给客户一个有感知的交付成果哪怕不是刷脸是刷指纹也行。我的决定是两件事并行。模型的事情继续优化但同时把硬件上预留的串口利用起来接一个指纹模块先把链路通了再说。现在回头看这个决定才是项目真正的转机。因为主方案的人脸识别目标太“重”了每个人都希望它一次成功反而被压得喘不过气而指纹模块当时没人对它抱期待目标就是个“能用的备胎”心理压力小反而一切进展都特别顺。3. 备选指纹方案为什么会被“顺手”调通3.1 ZW101模块的接入不过是四根线的事指纹模块用的是ZW101这是很常见的一款光学指纹识别模块。当初选它的原因很简单有串口有成熟的指令集工作温度范围宽到能覆盖北方冬天室外机柜的环境资料也好找。项目里很多设备外设的坑都是因为资料不完整、指令说明含糊ZW101在这点上明显好于同价位的很多方案。硬件连接方面核心就四根线VCC、GND、TX、RX接到RK3588开发板的UART串口上。但这里有个特别容易踩的坑——电平匹配。ZW101模块的逻辑电平是3.3V而很多开发板的调试串口默认暴露的是RS232电平或者5V电平直接接上去轻则乱码重则烧模块。接线之前一定要确认串口电平最好用万用表量一下别省这个步骤。另外一个坑是供电ZW101指纹模块工作时峰值电流不算低如果用了那种劣质USB转串口模块同时做供电和数据通信一录入指纹模块就掉线看起来像模块坏了实际上是供电不足。后来我单独接了一路3.3V/500mA以上的供电问题立刻消失。3.2 指令协议其实比想象中简单ZW101的通信协议是典型的串口帧格式简洁程度远超我的预期。基础帧结构大概是帧头、设备地址、包标识、指令、参数区、校验和。发送一条“查询模块状态”的指令模块返回一帧状态数据整个过程干脆利落。常用指令也就那么几条录入指纹、搜索指纹、比对指纹、删除指定指纹ID、清空指纹库。最开始我拿一个串口调试助手手动发指令测试先录入第一枚指纹模块大概在0.6秒内完成特征提取并反馈了一个指纹ID然后再拿同一根手指去比对上返回了“匹配成功”的确认码。这个流程走通的时候说实话我是有点吃惊的——就这么十分钟一个能用的指纹识别闭环就有了。后面我顺手写了段Python脚本通过pyserial走串口把整个流程自动化录入、搜索、比对、删除一把梭。整段脚本两百来行没有复杂的依赖在开发板上直接跑识别反馈延迟比预期还低。这跟人脸识别那边又是模型转换、又是算子兼容性、又是现场光线调试的复杂度形成了鲜明对比。3.3 意外的是小目标反而容易出成果这个“顺手调通”的过程让我反思了很久。为什么Z101这边十分钟就能通因为目标足够小心理预期足够低。没人指纹模块必须在多复杂的环境里工作只要“手指放上去门能开”就行。反观人脸识别那边我心里一直在期待一个“完美的人脸识别门禁系统”这个庞大的目标带来的隐性压力让每次小失败看起来都难以接受反而忘了先把最基础的通路跑通。指纹模块的另一个好处是对环境几乎无感。它不关心光照是强是弱不关心你站的位置是靠左还是靠右也不关心窗外是不是逆光。它只关心手指传感器上有没有一个清晰的指纹。这让我意识到人脸识别门禁现场的硬件层复杂度指指纹识别压根不存在。3.4 参数调优安全等级、拒真率与认假率如何平衡ZW101在出厂默认参数下识别速度不算快大概0.9到1秒左右才出结果而且对指纹质量的挑剔程度偏高手指稍微有点脏就有可能拒真。好在模块支持调安全等级和比对阈值我在门禁场景下把安全等级从默认的3级调到了1到2级之间识别速度提升到0.4到0.6秒拒真率明显下降实测下来体验好了很多。这里顺便说明一下指纹识别的两个核心指标很多人容易混淆**拒真率FRR**是“合法用户被系统拒绝”的概率**认假率FAR**是“非法用户被系统接受”的概率。这两个指标是互相矛盾的安全等级越高认假率越低但拒真率越高。门禁场景最怕的不是一万个人里混进一个非法用户而是合法用户频繁被拦在门外引发投诉。所以在可接受的认假率范围内把拒真率调低更符合实际运营需求。我当时调完后专门做了小规模测试几十个人重复按手指几十次认假率没有出现肉眼可见的问题拒真率大概到了理想状态。3.5 多人指纹库的管理方式指纹模块内置Flash可以存多枚指纹模板但只有指纹ID是不够的门禁系统要知道“这个指纹是谁”。所以我在项目里加了一张简单的映射表用SQLite数据库存用户ID和指纹ID的对应关系指纹比对成功后先拿到指纹ID再到数据库里查对应的是哪个用户。用户离职或者需要删除权限时只要删掉数据库里的映射关系再把模块里对应的指纹模板也删掉就行。建表的时候我给指纹ID加了唯一索引避免同一个用户重复录入多枚指纹后搞混。这其实跟热词里提到的“设备指纹识别库”有概念上的混用我后面专门说一下。多人指纹库管理上还有一个容易忽略的点录入指纹时要对同一根手指多次采样ZW101在这时候会比较挑剔如果用户按太快或者挪动了手指两次采样可能生成完全不同的模板导致后续比对失败。我的建议是每个手指录入时提示用户“按稳等待提示后再抬起”宁可多录几秒也不要录出废模板。3.6 从“能通”到“能用”串口线程和稳定性的细节脚本流程能跑通之后离实际部署还有一段距离。门禁设备不是一次性的人机交互而是长期待机、随时响应。串口通信这块需要变成一个常驻的后台服务处理指令超时、模块无响应、串口占用冲突这些异常情况。我当时的实现方式是起了一个串口服务线程带着超时重发机制。发送指令后如果一秒内没收到有效应答帧就重发一次连续重发三次还没响应就把模块标记为“离线”同时给门禁控制逻辑返回一个错误状态。这个超时机制在人脸识别那边的体验优化上也有启发系统能力不足时要学会“快速失败”而不是一直转圈让用户干等。4. 人脸没成、指纹先行的复盘问题到底出在哪里4.1 主方案失败的技术归因项目收尾复盘时我们对人脸识别这条线做了比较客观的归因结论很直接问题不是出在RK3588算力不够也不是出在模型本身而是出在“现场工程化”这件事被严重低估了。人脸识别从“模型能跑”到“现场可用”中间隔着的是一条完整的坑链光路设计、摄像头选型、补光策略、安装高度与角度、活体检测策略、误报率指标、红外夜视与宽动态调试、现场数据回灌评测……每一项单独拿出来都不算高难度但组合在一起工程量就非常可观了。这些事在项目排期的时候完全没被当作一个独立的工作项而是被笼统地归在“调试”里导致真正出问题的时候没有时间余量去系统性地解决。指纹识别之所以能顺利落地恰恰是因为它的工程链路短只要串口硬件没接错协议按帧格式组织识别过程是模块内部完成的系统的外部变量非常少。它不需要考虑光、角度、遮挡这些复杂因素所以预期管理一旦摆正效率反而高。4.2 双模态冗余不是浪费时间这个项目给我的一个非常重要的教训是双模态方案不是“浪费成本”而是“买保险”。我见过很多门禁项目立项时只关注一种识别方式到现场出了问题才开始想备选方案结果硬件上没留接口软件上没留架构临时要加识别方式等于重新开发。所以我的建议是哪怕需求文档里只写了一种识别方式硬件设计和软件架构也要按“可扩展多模态”来规划。最简单的做法就是留出至少一组空闲串口和几个GPIO控制引脚软件层把“采集识别结果”和“执行开门动作”解耦识别模块可以随时替换和增加。这次项目里就是因为预留了串口和GPIO我才能在决定接指纹模块的当天下午就完成硬件连接两天内跑通完整流程。如果这些都没预留等重新打板、重新接线这个项目大概率就要延期了。4.3 对“设备指纹识别库”的一点澄清搜索热词里有一个“设备指纹识别库”很多人会跟指纹识别模块混淆我在项目总结时正好讨论过这事。设备指纹识别库在安全行业通常不是指物理指纹传感器而是指通过网络和设备软硬件特征生成一串唯一标识用来识别一台设备的能力。它常被用于风控、反欺诈、防批量注册等场景识别对象是设备不是人。本文场景里的指纹识别库准确的描述是“物理指纹模板库”管理的是指纹ID和用户信息之间的映射关系。虽然都叫“指纹”但两者完全不是一回事。如果你做的是后者数据模型用简单的SQLite表就能搞定如果你要做的是前者那要处理的问题就完全不一样了至少要涉及设备信息采集、特征哈希、唯一性算法这些事情。这两个概念在项目文档里一定要分清楚否则后续交接的时候非常容易出歧义。4.4 关于“rust 人脸识别”这个技术方向的一点观察热词里还有个“rust 人脸识别”这个方向我在这个项目之后也观察过一段时间。Rust写人脸识别推理的优势在于内存安全和性能尤其是在嵌入式边缘设备上Rust的内存管理特性可以避免很多C侧的内存泄漏问题。但人脸识别核心逻辑其实还是依赖ONNX Runtime、TensorRT或者RKNN这类推理框架Rust更多是作为“胶水层”去调用推理引擎同时负责业务逻辑、IO、多线程调度这些部分。如果你对Rust感兴趣可以从rust-rknn这类社区项目开始但要有心理准备目前Rust生态在NPU推理这块的成熟度还比不上Python和C很多底层API还是需要自己封装C接口。稳妥的做法是算法模型用Python快速验证工程落地再考虑用Rust重写性能关键路径。这个项目里我没用Rust因为时间不允许但如果后续要做量产版本性能敏感的串口采集和识别调度逻辑用Rust重写是一个合理的方向。5. 如果重来一次我会怎么做5.1 排期上把“现场未知问题”当成一等公民回看整个项目最大的时间黑洞不是某一个技术难点而是“每个环节都预留得太少”。模型转换我以为三天能完实际上花了一周多现场光线调试我以为一天能解决结果拖了快两周。如果再让我排一次计划我会把人脸识别相关的工作拆成三个阶段时间比例大约是1比1比1模型验证调优、整机实验室测试、现场环境迭代。前两个阶段可能各两周现场迭代至少要留出两周以上因为现场出现的未知问题会反复拉扯前两段的方案。5.2 “先交付一个能用的版本”比“追求完美方案”重要得多给后来者一个最直接的实践建议不管需求方怎么强调人脸识别的“酷”如果你项目周期有限请先把指纹识别这条链路打通作为发布版本的基础功能。指纹模块成本低、方案成熟、调试周期短能给团队建立信心也能给客户一个确定性的交付预期。人脸识别作为一个增量功能去迭代会从容很多。具体的最小可行路径我总结成这样先确认主控板有一组空闲的3.3V TTL串口连接ZW101等指纹模块。写一个串口调试脚本用命令行完成“录入指纹-搜索指纹-比对指纹”三个核心动作。建立SQLite映射表把指纹ID和用户ID绑定做好增删改查。门禁控制逻辑只依赖“比对成功的指纹ID和用户ID”不关心识别方式是哪种。在这个稳定底座上再逐步迭代人脸检测、人脸识别、活体检测等功能。5.3 最后再分享两个小细节一是关于指纹模块的固件版本同一型号不同批次之间指令集和校验和计算方式可能有细微差别拿到模块后第一件事就是查询固件版本和模块状态别照着旧文档直接批量开发。二是现场部署指纹模块时如果门禁设备外壳是金属的要注意指纹传感器周围不能完全被金属包围否则会造成电容/射频信号干扰指纹采集质量会莫名下降。我们第一次装进去就遇到识别率突然变差排查了半天才发现是外壳压迫了传感器附近的结构。这个项目结尾的时候客户来验收看到设备上指纹识别稳稳地工作其实也挺满意的。人脸识别虽然没在主方案上顺利跑通但指纹方案先行交付反而让客户觉得我们“多做了功能”。后来我听说他们园区另一栋楼要立项做新的门禁需求量说明就已经主动写了“支持人脸识别指纹识别”还加了一句“两种方式并行互为备份”。从商业化角度看这大概就是意外收获。