人脸识别卡壳之后:RK3588门禁项目中的指纹备选方案复盘
2026/9/10 19:09:43 网站建设 项目流程

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 “先交付一个能用的版本”,比“追求完美方案”重要得多

给后来者一个最直接的实践建议:不管需求方怎么强调人脸识别的“酷”,如果你项目周期有限,请先把指纹识别这条链路打通,作为发布版本的基础功能。指纹模块成本低、方案成熟、调试周期短,能给团队建立信心,也能给客户一个确定性的交付预期。人脸识别作为一个增量功能去迭代,会从容很多。

具体的最小可行路径我总结成这样:

  1. 先确认主控板有一组空闲的3.3V TTL串口,连接ZW101等指纹模块。
  2. 写一个串口调试脚本,用命令行完成“录入指纹-搜索指纹-比对指纹”三个核心动作。
  3. 建立SQLite映射表,把指纹ID和用户ID绑定,做好增删改查。
  4. 门禁控制逻辑只依赖“比对成功的指纹ID和用户ID”,不关心识别方式是哪种。
  5. 在这个稳定底座上,再逐步迭代人脸检测、人脸识别、活体检测等功能。

5.3 最后再分享两个小细节

一是关于指纹模块的固件版本,同一型号不同批次之间,指令集和校验和计算方式可能有细微差别,拿到模块后第一件事就是查询固件版本和模块状态,别照着旧文档直接批量开发。二是现场部署指纹模块时,如果门禁设备外壳是金属的,要注意指纹传感器周围不能完全被金属包围,否则会造成电容/射频信号干扰,指纹采集质量会莫名下降。我们第一次装进去就遇到识别率突然变差,排查了半天才发现是外壳压迫了传感器附近的结构。

这个项目结尾的时候,客户来验收,看到设备上指纹识别稳稳地工作,其实也挺满意的。人脸识别虽然没在主方案上顺利跑通,但指纹方案先行交付,反而让客户觉得我们“多做了功能”。后来我听说他们园区另一栋楼要立项做新的门禁,需求量说明就已经主动写了“支持人脸识别+指纹识别”,还加了一句“两种方式并行,互为备份”。从商业化角度看,这大概就是意外收获。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询