基于 RA8P1 的人脸门禁系统
# 基于 RA8P1 的人脸门禁系统## 写在前面
我们是全国大学生电子设计竞赛信息科技前沿专题赛的参赛队伍。本文是参赛作品中"人脸门禁"部分整理出的学习例程:一个基于瑞萨 RA8P1 的端侧人脸门禁系统,摄像头实时采集 → 板载 NPU 完成人脸检测与特征提取 → 与 SD 卡上的人脸库比对,识别结果作为门禁判据。
## 一、人脸门禁系统概览
系统整体是"检测 → 对齐 → 特征 → 比对 → 门禁决策"五段式端侧推理管线,全部在板端完成,不依赖上位机与网络:
| 环节 | 处理 | 说明 |
| --- | --- | --- |
| 图像采集 | OV5640 → CEU DMA | 480×640 RGB565,614,400 字节/帧 |
| 人脸检测 | SCRFD(Ethos-U55 NPU) | 输出人脸框 + 5 点关键点(双眼/鼻尖/嘴角) |
| 对齐裁剪 | 按框与关键点裁剪 | 112×112 对齐人脸 + int8 量化 |
| 特征提取 | embedder(Ethos-U55 NPU) | 512 维特征 |
| 比对决策 | 与 /FACE/FACE.DB 全部账户比对 | 最大余弦相似度超阈值 → 门禁放行 |
识别为**连续运行**:开机直接进入识别页,每帧检测,检测到人脸才做特征提取与比对,识别结果持续刷新;识别成功不跳页、不停机,便于门禁场景连续放行。本工程以屏幕反馈与串口日志演示放行语义,实际门锁/继电器执行机构可经 GPIO 扩展接入。
## 二、模型训练
板端两个模型分工明确:检测模型负责"人在哪、脸朝哪"(框 + 5 点关键点),识别模型负责"这人是谁"(512 维特征)。两个模型都用 PyTorch / MMDetection 生态训练,训练在 PC 的 GPU 上完成,部署时转成 int8 量化模型烧进板端 Flash,见下文各节。
### 1. 数据集与预处理
| 任务 | 数据集 | 规模 | 预处理 |
| --- | --- | --- | --- |
| 识别 | MS1MV3(MS1M-ArcFace 清洗版) | 85,742 身份 / 5,822,653 图 | 5 点关键点对齐 → 相似变换裁剪 112×112 |
| 检测 | WIDERFace + 补充数据 | labelv2 标注 | bbox + 5 关键点 |
原始 MS1M 数据噪声较大,MS1MV3 按人脸检测置信度与特征距离做了多轮清洗;训练前先做人脸检测与 5 点关键点对齐,确保送入网络的都是对齐人脸。
### 2. 检测模型:SCRFD-500M
检测模型采用 InsightFace 开源的 **SCRFD**(Sample and Computation Redistribution for Face Detection)轻量级变体 **SCRFD-500M**,500M 指约 500 MFLOPs 的计算量级,专为端侧设计:
| 组件 | 配置 |
| --- | --- |
| backbone | MobileNetV1(stage_planes ) |
| neck | PAFPN(3 个输出层级) |
| head | SCRFDHead(GN 归一化、cls/reg 共享卷积、深度可分离卷积) |
| anchor | ratios 、scales 、base_sizes 、strides |
| 输入 | 640×640 |
设计上的两个特点:
1. **关键点与框同头回归**:head 同时输出 score / bbox / keypoints 三组预测,5 点关键点随框一起被监督,训练好后一次前向就能拿到对齐所需的全部信息;
2. **计算量重分配**:SCRFD 系列把骨干网络的计算量向高分辨率特征层倾斜(早期 stage 加宽、后期 stage 变浅),用更少的总算力拿到更高的精度。
训练配置(scrfd_500m.py,MMDetection 体系):
| 配置项 | 值 |
| --- | --- |
| 正负样本分配 | ATSSAssigner(topk=9) |
| 分类损失 | QualityFocalLoss(β=2.0,权重 1.0) |
| 框回归损失 | DIoULoss(权重 2.0) |
| 关键点回归损失 | SmoothL1Loss(β=1/9,权重 0.1) |
| 优化器 | SGD,lr=0.01,momentum=0.9,weight_decay=5e-4,lr_mult=8 |
| 学习率调度 | warmup 1500 步(linear,ratio 0.001),step 于 下降,共 640 epochs |
| 数据增强 | RandomSquareCrop(0.3~2.0 随机裁切比例)+ 缩放 640×640 + 随机翻转 + PhotoMetricDistortion(亮度 32、对比度/饱和度 0.5~1.5、色调 18) |
| 归一化 | (pixel - 127.5) / 128.0 |
关键点监督权重虽只有 0.1,但对后续对齐质量影响很大:识别模型要求 5 点精准贴合,关键点误差会直接放大为对齐裁剪的偏移。
### 3. 识别模型:MobileFaceNet + ArcFace 损失
识别模型是 **MobileFaceNet** 骨干 + **ArcFace 损失**,输出 **512 维**特征。ArcFace 在 Softmax 交叉熵基础上对特征向量做 L2 归一化,并在角度空间叠加余弦间隔(margin m=0.5,特征缩放 s=64),让模型学到区分度更大的特征分布:
| 配置项 | 值 |
| --- | --- |
| 损失函数 | ArcFace(余弦间隔 0.5,特征缩放 64) |
| 骨干网络 | MobileFaceNet(mbf,轻量、端侧友好) |
| 特征维度 | 512 |
| 训练数据 | MS1MV3(faces_emore 打包:85,742 身份 / 5,822,653 图,112×112) |
| 优化器 | SGD,lr=0.1,momentum=0.9,weight_decay=1e-4 |
| 训练规模 | batch 128,fp16 混合精度,40 epochs |
| 验证集 | LFW / CFP-FP / AGED-30 |
!(https://www.eefocus.com/forum/data/attachment/forum/202608/16/201736d77m1dtdbxvcdyvl.png)
### 4. 部署前微调与 INT8 量化
NPU 只吃 int8 模型,浮点模型需要先转 int8 再上板。这一步做了两个工程化处理:
**(1)PReLU → LeakyReLU 微调。** MobileFaceNet 原版激活是 PReLU(带可学习参数、斜率逐通道),在部分 NPU 上不受支持或量化效果差。做法:把激活层替换为固定斜率的 LeakyReLU,从原模型提取的兼容权重(mbf_leaky_init.pt)继续微调:
| 配置项 | 值 |
| --- | --- |
| 训练数据 | MS1MV3 重新打包(93,431 身份 / 5,179,509 图,RecordIO) |
| 优化器 | SGD,lr=0.01(原始 1/10),momentum=0.9,weight_decay=1e-4 |
| 训练规模 | batch 64,fp16,5 epochs(微调不需要长训) |
| Partial FC | sample_rate=0.1,每步只回传 10% 类别的分类头梯度,FC 计算量降为 1/10 |
**(2)静态 INT8 量化(PTQ)。** 用 50~300 张校准图做静态量化,两个模型分开校准:
| 模型 | 校准集 | 量化要点 |
| --- | --- | --- |
| 检测 | 含人脸的完整图片 | 部署时相机 640×480 RGB565 画面先 letterbox 补边到 640×640 再推理;校准图片同样模拟 RGB565 位深(R5G6B5)后 letterbox,贴近真实输入分布 |
| 识别 | 对齐后的 112×112 人脸图 | 直接静态量化 |
量化后两个模型精度损失均在可接受范围(门禁判定阈值在做完量化后重新标定)。板端实际部署选用轻量组合 det_500m + 微调版 MobileFaceNet;桌面评估以 buffalo_l 的 det_10g + w600k_r50(高精度参考模型)作为精度基线对照。
## 三、检测与识别流程
### 1. 采集与方向校正
OV5640 竖装,输出居中 3:4 裁剪的 480×640 RGB565,经 CEU DMA 直接写入内部 SRAM 的专用帧缓冲。因为摄像头是竖装的,采集帧需要做一次**逆时针 90° 旋转 + 水平镜像**校正为逻辑 640×480 空间;预览、检测、关键点与 112×112 特征裁剪共用同一方向,保证框、关键点与人脸贴合。传感器寄存器 0x3820/0x3821 只表示 OV5640 自身的镜像/翻转,竖装校正在采集侧完成,不依赖它们。
### 2. SCRFD 检测与关键点解码
SCRFD 输出 9 个张量:按 stride 8/16/32 各含 score、bbox、kps(关键点),与训练时的 anchor 设计一一对应。量化参数 zero_point 为 122/141/120。解码公式与 bbox 相同:
```c
point = center + (raw - zero_point) * scale * stride;
/* 关键点偏移 kps_offset = index * 10,坐标 clamp 到 640×480 */
```
只对 score ≥ 阈值(0.3)的 anchor 解码。每张脸得到 1 个框 + 5 个关键点(双眼、鼻尖、左右嘴角),识别页用绿色框 + 5 个十字点实时叠加在预览画面上。
!(https://www.eefocus.com/forum/data/attachment/forum/202608/16/201755zlnhjwrvqlvknkj7.jpg)
### 3. 对齐、特征与比对
检测到人脸后,按框与关键点裁剪出 112×112 对齐人脸并量化为 int8,送入识别模型(sub_1000_invoke),得到 512 字节特征。比对阶段与 SD 卡人脸库中全部动态账户做 Top-1/Top-2 余弦相似度比较:
| 判定条件 | 要求 |
| --- | --- |
| Top-1 相似度 | ≥ 0.62(普通账户阈值) |
| Top-1 与 Top-2 差距 | ≥ 0.05(领先间隔,避免近距误判) |
识别页右侧实时显示**最大余弦相似度百分比**与姓名(未达阈值显示"未达阈值");检测到多张人脸时给出多脸提示。每 100 ms 轮询一次比对结果,全流程板端完成。
## 四、人脸库与账户管理
人脸库以文件形式保存在 SD 卡:`/FACE/FACE.DB`。SD 挂载后自动创建 /FACE 目录,空卡自动原子生成默认空库。
| 字段 | 说明 |
| --- | --- |
| 文件头 | 魔数 + 版本 + CRC32 + 账户数 + 序号分配器 |
| 账户记录 | uint16 account_id + ASCII 名称(32 字节)+ 512 维 float 特征 |
| 容量 | 最多 16 个普通账户,单份库约 32.6 KiB |
账户管理走触屏界面(设置 → 账户管理),提供**新增 / 重命名 / 重录 / 删除**四项操作:
1. 新增/重录:采集 3 个单人推理帧,特征平均并归一化后先保留在 SDRAM 草稿区(不直接落盘);录入过程与其他账户查重,重录只忽略目标账户自身;确认后存储线程以事务文件(/FACE/FACE.TMP)**原子替换**正式库,替换成功才提交运行库;
2. 重命名/删除:同样走"暂存 → 原子替换"路径;
3. 降级保护:缺卡、目录创建失败或 FACE.DB 损坏时进入只读/非持久化降级——SDRAM 中已提交的库继续用于识别,未成功写卡的录入、重命名、重录、删除均不得激活,界面给出具体只读原因。
!(https://www.eefocus.com/forum/data/attachment/forum/202608/16/201807ox2jqbc4qjqrjxx5.jpg)
新增账户命名界面
!(https://www.eefocus.com/forum/data/attachment/forum/202608/16/201820k6itb5l854s8jbjb.jpg)
录入人脸界面
!(https://www.eefocus.com/forum/data/attachment/forum/202608/16/201827c44847q4rmjtzj80.jpg)
账户管理界面
## 五、系统架构与线程
工程共 5 条线程,职责单一:
| 线程 | 职责 |
| --- | --- |
| serial_cmd | UART9 命令控制台(诊断命令) |
| ui_thread | LVGL 5ms 主循环,识别页 / 设置页 |
| face_thread | 录入采集与确认 |
| storage_Thread | 开机先完成 SD 初始化与 FACE.DB 加载,之后处理保存/丢弃命令 |
| ai_thread | 摄像头/CEU 采集 50ms 节拍 + 静态 NPU worker(检测/特征),服务生命周期 |
线程间通过 app_bridge 命令/消息桥通信(FreeRTOS 静态队列 + 事件组)。注意两点设计约束:
1. **SD 初始化只归 storage 线程**:ai 线程不等待 SD,摄像头/推理与 SD 解耦;storage 线程在命令循环前完成 SD 挂载与人脸库加载,成功则进入 READY,失败则只读降级——避免人脸库"永远加载中";
2. **NPU 推理为静态单 worker**:只有 ai 线程的专用 worker 调用 Ethos-U/TFLM,其余线程经队列请求,杜绝 NPU 并发访问;AI 服务异常时按阶段降级(预览保持、推理重试)。
内存与缓存约定(板级验证,这是 RA8P1 端侧视觉工程最容易踩坑的部分):
| 数据 | 位置 | 约束 |
| --- | --- | --- |
| CEU 原始帧 s_ai_frame | 内部 SRAM 的 .ram_noinit_nocache 段,32 字节对齐 | CEU DMA 直接写入;放 SDRAM 会出现条纹、分块或错位,CPU 读取前先 invalidate 该段 cache |
| 预览双缓冲 | 内部 SRAM | CPU 写后必须 D-Cache clean,DAVE2D 直接读取 |
| 推理快照 | SDRAM 的 .sdram_noinit 段 | 仅 CPU 为静态 NPU worker 保存一帧;worker 完成前不可覆盖 |
| TFLM tensor arena | SDRAM 的 .sdram_noinit 段 | 仅供 NPU 大容量工作区使用 |
| 人脸正式库/候选/录入草稿 | SDRAM 的 .sdram_noinit 段 | 事务成功写卡后才从候选切换为运行库 |
| 人脸 FAT staging 缓冲 | SDRAM 的 .sdram_noinit_nocache 段,32 字节对齐 | SDHI DMAC 直接访问,避免 CPU 读到陈旧缓存行造成重启后 CRC 误判 |
| 模型权重 | OSPI Flash memory-mapped 槽位 | 推理前 CRC 校验,保持原访问方式 |
## 六、调试与验证
板端提供 UART9 命令控制台(2 Mbps),建议按下列顺序逐级验证(每步通过再进下一步,出问题能精确定位到采集、传输、推理哪个环节):
| 步骤 | 命令 | 验证内容 |
| --- | --- | --- |
| 1 | ai_preview_start | 实时预览(绕过 NPU 单独验证采集→LVGL 通路) |
| 2 | ai_test_pattern on | OV5640/CEU 图像通路(彩条测试图) |
| 3 | ai_preview_reference on | 仅验证内部 RGB565 → LVGL/LCD 路径 |
| 4 | ai_status | 查询状态:帧率、人脸数、NPU 诊断、匹配结果 |
| 5 | ai_start | 最后才验证 NPU 推理 |
推理侧的排查要点:
1. 模型部署到 OSPI 槽位后先做长度与 CRC32 校验(match=1 才算部署成功),校验失败会拒绝推理——先确认模型在位再怀疑算法;
2. NPU 工作区 arena 与输入/输出缓冲的 cache 一致性按第五节内存约定逐条核对;
3. 量化后的模型在板端重新标定判定阈值:用已注册人员与陌生人各测一组相似度分布,取两类分布的中间值。
PC 端评估脚本可以在上板前先验证模型与阈值(人脸匹配、相似度分布、模型基准),训练与评估闭环在第七节给出。
## 七、工程代码结构
板端固件:
| 路径 | 职责 |
| --- | --- |
| src/ai_thread_entry.c | AI 服务生命周期、CEU 预览 50ms 节拍、推理降级 |
| src/face_thread_entry.c | 录入采集与确认 |
| src/storage_Thread_entry.c | SD 初始化、FACE.DB 加载/原子保存 |
| src/serial_cmd_entry.c | UART9 打开与命令轮询 |
| src/ui_thread_entry.c | LVGL 主循环 |
| src/device/camera/ | OV5640 驱动与寄存器表、CEU 封装、GPIO 位模拟 SCCB |
| src/device/display/ | 显示抽象层(display_device / display_draw) |
| src/device/storage/ | SD 卡驱动、OSPI Flash 驱动、XMODEM 传输 |
| src/drivers/display/ | lcd_bus_spi / lcd_st7796 / lcd_touch_ft6336 |
| src/application/face_recognition/ai_inference.c | SCRFD/embedder 推理调度、输出张量读取与 cache 处理 |
| src/application/face_recognition/face_detection_postprocess.c | 检测解码(框 + 5 关键点) |
| src/application/face_recognition/face_embed_preprocess.c | 112×112 对齐裁剪与 int8 量化 |
| src/application/face_recognition/face_match.c | 余弦相似度 Top-1/Top-2 判定 |
| src/application/face_recognition/face_database.c | 人脸库事务、查重、只读降级 |
| src/application/face_recognition/ai_model_slot.c / ai_model_upload.c | OSPI 模型槽校验与上传 |
| src/application/face_recognition/models/model_0、model_1 | NPU 命令流(不含权重数据) |
| src/application/core/ | ai_preview(预览 + 框 + 关键点绘制)、ai_service、app_bridge / app_msg / app_state / app_events |
| src/application/ui/ | ui_manager(识别页 / 设置页)、ui_page_face、ui_page_settings、ui_components、ui_style、精简中文字库 |
| src/middleware/console/ | serial_cmd / cmd_config(UART9 命令注册) |
| src/middleware/lvgl_port/ | LVGL 显示与触摸适配层 |
| src/system/isr.c | 中断回调、printf 串口重定向 |
## 写在最后
本文介绍了基于 RA8P1 的端侧人脸门禁系统:公开数据集 + InsightFace 工具链训练出 SCRFD-500M 检测模型与 MobileFaceNet 识别模型,PReLU→LeakyReLU 微调 + 静态 INT8 量化后部署到板载 NPU,OV5640 + CEU 采集,SD 卡 FACE.DB 原子事务管理,连续识别 UI。RA8P1 单芯片完成"采集 → 推理 → 决策"全链路,无需上位机与网络,适合门禁、考勤、通行等端侧视觉场景。
感谢阅读,欢迎指正。
页:
[1]