人脸识别考勤系统项目总文档
1. 这是什么项目
本项目是一个多端协同的人脸识别考勤系统。它不是只有一个程序,而是由四个主要部分组成:
| 部分 | 所在目录 | 主要职责 | 可以把它理解成 |
|---|---|---|---|
| STM32 固件端 | stm32/ |
接收 K210 识别结果、判断是否允许签到、加密上传记录、控制 OLED/蜂鸣器/指示灯/补光灯 | 考勤机主控板 |
| K210 识别端 | k210/ |
摄像头采集、人脸检测、人脸特征比对、添加/删除本地人脸、上传人脸照片、同步人脸档案 | 摄像头和 AI 识别模块 |
| C++ 服务端 | server/ |
提供 HTTP API、保存考勤记录、时间段、学生班级档案、人脸档案、照片和同步状态 | 中心服务器 |
| Qt 管理端 | QT/ |
Windows/Android 图形管理界面,用于查看记录、统计、配置时间段、管理班级、人脸照片和照片对应学生信息 | 管理后台 |
整个系统的目标是:
- K210 看到人脸并识别出一个人。
- K210 通过串口把识别结果发给 STM32。
- STM32 判断当前是否在允许签到的时间段内,并检查是否重复签到。
- 如果允许签到,STM32 将签到数据用 AES-128-CBC 加密后通过 ESP8266 发给服务端。
- 服务端保存数据,并提供给 Qt 管理端查看、统计、导出和管理。
- Qt 管理端修改的配置会同步给服务端,再由 STM32 或 K210 拉取。
2. 新手先理解几个概念
2.1 人脸 ID 和学号不是同一个东西
K210 本地添加人脸时,会生成一个原始人脸编号,例如 ID:1、ID:2。这个编号只表示 K210 本地第几个录入的人脸。
真实考勤需要的是学号,例如 20240001。所以项目里通过人脸档案维护“人脸 ID 到学号”的绑定:
{
"face_profiles": [
{
"face_id": "1",
"student_id": "20240001"
},
{
"face_id": "2",
"student_id": "20240002"
}
]
}
含义是:K210 本地人脸 1 对应真实学号 20240001。
2.2 签到时间段默认是空的
系统默认不创建“上午签到”“中午签退”“下午签到”“晚上签退”等固定时间段。新部署时:
{
"time_slots": []
}
如果没有配置时间段,STM32 会认为当前不在任何考勤时段内,识别到人脸也不会允许签到。需要在 Qt 管理端“配置中心”里新增时间段并保存同步。
2.3 服务端是数据中心
当前服务端以 SQLite 数据库为主,数据库文件是:
attendance.db
服务端还会使用以下本地资源:
| 文件或目录 | 作用 |
|---|---|
attendance.db |
主数据库,保存记录、配置、学生班级档案、人脸档案、同步状态 |
faces/ |
人脸照片目录,保存 K210 上传的 .jpg |
backups/ |
数据库备份目录 |
服务端数据以 SQLite 为准,考勤记录、时间段配置、学生班级档案、人脸档案和同步状态都保存在 attendance.db 中。
2.4 同步版本号是什么
服务端维护一个全局同步版本号 sync_version。当考勤记录、时间段、学生档案、班级信息、人脸档案、人脸照片、删除任务等数据发生变化时,服务端会把版本号加一。
设备同步时会带上自己上次同步到的版本:
GET /api/v1/sync/state?client=stm32&last_version=3
如果服务端当前版本还是 3,会返回 changed:false。如果服务端当前版本是 4 或更高,会返回 changed:true 并带上该设备需要的数据。
这样做的好处是:设备不用不停下载所有数据,只要版本没变就不用重新处理。
3. 总体架构
flowchart LR
Camera["摄像头"] --> K210["K210 识别端"]
K210 -->|"UART1: ID, NAME, CONF"| STM32["STM32 主控"]
STM32 -->|"USART1 AT 指令"| ESP1["ESP8266"]
ESP1 -->|"HTTP + AES 签到"| Server["C++ 服务端 8082"]
K210 -->|"UART2 AT 指令"| ESP2["K210 侧 ESP8266"]
ESP2 -->|"HTTP 上传照片/同步人脸档案"| Server
Qt["Qt 管理端 Windows/Android"] -->|"HTTP API"| Server
Server --> SQLite["attendance.db"]
Server --> Faces["faces/*.jpg"]
4. 数据流说明
4.1 正常签到流程
- K210 摄像头采集画面。
- K210 使用三个 KPU 模型执行人脸检测、关键点定位和特征提取。
- K210 将当前人脸特征与 SD 卡中的
recordftr.txt做比对。 - 如果分数大于阈值,K210 认为识别成功。
- K210 通过 UART1 发送给 STM32:
ID:20240001,NAME:20240001,CONF:0.95
- STM32 解析串口数据。
- STM32 检查置信度是否超过
0.75。 - STM32 检查当前时间是否落在已启用的时间段内。
- STM32 检查本地缓存、今日记录缓存和服务端查重结果,避免同一个学生在同一时段重复签到。
- STM32 生成 JSON:
{
"student_id": "20240001",
"name": "20240001",
"confidence": 0.95,
"status": "success",
"class_id": "1"
}
- STM32 使用 AES-128-CBC 加密并做 Base64 编码。
- STM32 通过 ESP8266 发送:
POST /api/v1/attendance/checkins
X-Api-Token: ...
X-Encrypted: AES128-CBC
- 服务端解密、校验字段、写入 SQLite。
- Qt 管理端刷新后可以看到新记录。
4.2 新增人脸流程
- 在 K210 正常识别界面短按按键,进入添加人脸模式。
- 摄像头检测到陌生人脸后,再短按确认。
- K210 将人脸特征保存到 SD 卡
recordftr.txt。 - K210 将对应照片保存到
/sd/face_photos/<face_id>.jpg。 - K210 将照片 ID 放入上传队列。
- 网络任务空闲时,K210 通过 ESP8266 上传照片到服务端:
POST /api/v1/faces/photos/<face_id>
- Qt 管理端进入“人脸管理”,刷新后可以看到照片。
- 管理员在该 face_id 的编辑弹窗中填写真实学号、姓名和班级,并保存人脸档案。
- 服务端保存人脸档案并触发同步。
- K210 拉取同步状态后更新本地
face_mapping.txt,识别发送时把face_id转成真实学号,recordftr.txt中的原始ID:<face_id>保持不变。
4.3 删除人脸流程
- Qt 管理端删除某个学生或人脸。
- 服务端删除照片、解除人脸绑定,按需要删除考勤记录。
- 服务端写入待删除任务
pending_delete,并提升同步版本。 - K210 定期检查同步请求或拉取统一同步状态。
- K210 在本地删除对应特征和照片。
- K210 向服务端发送 ACK:
POST /api/v1/sync/ack
- 服务端移除待删除任务。
5. 目录结构
code/
├── docs/ # 项目文档
├── server/
│ ├── server_encrypted.cpp # C++ HTTP 服务端,当前推荐版本
├── QT/
│ ├── CMakeLists.txt # Qt 客户端构建配置
│ ├── clear_qt_cache.ps1 # 清理 Qt Creator/CMake 项目缓存
│ ├── main.cpp # Qt 程序入口
│ ├── qml/Main.qml # Qt 主界面
│ ├── src/ # Qt C++ 业务逻辑、网络客户端、模型
│ ├── android/ # Android 打包配置和图标
│ └── assets/ # 桌面端图标
├── k210/
│ └── main.py # K210 主程序
└── stm32/
├── User/
│ └── main.c # STM32 主循环
├── Hardware/ # STM32 外设驱动与业务模块
├── System/ # STM32 延时、调试等基础模块
├── Library/ # STM32 标准外设库
├── Start/ # 启动文件和芯片头文件
├── Project.uvprojx # Keil 工程文件
├── Project.uvoptx # Keil 选项文件
└── 连接图.md # 硬件接线表
6. 默认配置总览
| 配置项 | 当前值 | 所在位置 |
|---|---|---|
| 服务端端口 | 8082 |
server/server_encrypted.cpp |
| 服务端、STM32、K210 默认 API Token | atk_nKfVWLhMjL0RGSfRll3R_sdJS6IrIjuV |
server/server_encrypted.cpp、stm32/User/main.c、k210/main.py |
| 服务端默认 CORS 来源 | http://localhost |
server/server_encrypted.cpp |
| STM32 WiFi SSID | yzfh |
stm32/User/main.c |
| STM32 WiFi 密码 | yzfhroot |
stm32/User/main.c |
| STM32 服务端 IP | 27.106.113.205 |
stm32/User/main.c |
| STM32 班级编号 | 1 |
stm32/User/main.c |
| K210 WiFi SSID | yzfh |
k210/main.py |
| K210 WiFi 密码 | yzfhroot |
k210/main.py |
| K210 服务端 IP | 27.106.113.205 |
k210/main.py |
| Qt 默认服务端地址 | http://127.0.0.1:8082 |
QT/src/ApiClient.cpp、QT/src/AppController.cpp |
| Qt API Token 来源 | 本地 QSettings 或环境变量 ATTENDANCE_API_TOKEN |
QT/src/AppController.cpp、QT/src/ApiClient.cpp |
| 默认签到时间段 | 空数组 | Qt、服务端、STM32 |
| AES 算法 | AES-128-CBC + PKCS#7 + Base64 | stm32/Hardware/Crypto.c、server/server_encrypted.cpp、QT/src/CryptoUtils.cpp |
注意:这些配置里包含真实网络信息和默认 Token。如果要部署到公网,建议改成自己的值;服务端可用环境变量覆盖 Token,Qt 可在配置中心保存 Token 或读取环境变量,STM32 与 K210 需要改代码后重新烧录或上传脚本。
7. 硬件连接概览
完整连接表在 stm32/连接图.md。常用连接如下:
| 模块 | STM32 引脚 | 功能 |
|---|---|---|
| ESP8266 | PA9 / PA10 | USART1,STM32 通过 AT 指令访问 WiFi 和 HTTP |
| K210 | PB10 / PB11 | USART3,K210 把识别结果发给 STM32 |
| OLED | PB8 / PB9 | I2C 显示 |
| 调试串口 | PA2 / PA3 | USART2 调试输出 |
| 成功 LED | PB4 | 高电平点亮 |
| 蜂鸣器 | PB5 | 低电平触发 |
| 光敏传感器 | PB0 | ADC 输入 |
| 补光灯 | PB1 | 高电平点亮 |
| DS1302 CLK/DAT/RST | PA4 / PA5 / PA6 | 软件模拟时序 |
K210 侧:
| 模块 | K210 引脚 | 功能 |
|---|---|---|
| STM32 UART | 24 / 25 | UART1,发送识别结果给 STM32 |
| ESP8266 UART | 34 / 35 | UART2,上传照片和同步配置 |
| 按键 | 16 | 短按添加,长按删除,超长按删除全部 |
| RGB LED | 12 / 13 / 14 | 状态提示 |
8. 加密与鉴权
8.1 API Token
除 OPTIONS 预检请求外,服务端要求业务接口请求头中带 Token:
X-Api-Token: atk_nKfVWLhMjL0RGSfRll3R_sdJS6IrIjuV
服务端可用环境变量覆盖:
$env:ATTENDANCE_API_TOKEN="你的新Token"
Qt 端也会读取同名环境变量;STM32 和 K210 目前是写在代码里的常量,需要改代码后重新烧录或上传脚本。
8.2 AES 参数
三端必须保持完全一致:
| 参数 | 值 |
|---|---|
| 算法 | AES-128-CBC |
| 填充 | PKCS#7 |
| 编码 | Base64 |
| Key | 2b 7e 15 16 28 ae d2 a6 ab f7 15 88 09 cf 4f 3c |
| IV | 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |
业务 JSON 接口统一使用 AES-128-CBC + PKCS#7 + Base64 传输。POST JSON 请求体需要携带 X-Encrypted: AES128-CBC,GET/DELETE/POST 的 JSON 响应由服务端加密后返回 X-Encrypted: AES128-CBC。K210 人脸照片 JPEG 因体积和分块传输限制保留原始二进制上传/下载,照片列表、同步状态、删除确认等 JSON 仍走加密链路。
9. 推荐启动顺序
- 启动服务端,用 Qt 或带 Token 与 AES 解密能力的客户端确认
/api/v1/system/health返回健康状态。 - 启动 Qt 管理端,在配置中心填写服务端地址和 API Token,点击“保存”后再点“测试连接”。
- 在 Qt 管理端新增签到时间段并保存。
- 烧录并启动 STM32,确认 OLED 显示系统就绪。
- 启动 K210,确认摄像头画面、模型加载和 WiFi 连接正常。
- 在 K210 添加人脸,等待照片上传到服务端。
- 在 Qt 管理端为照片对应的人脸 ID 点击“编辑”,填写真实学号、姓名和班级并保存人脸档案。
- K210 同步人脸档案后,再让同一人进行识别签到。
- 在 Qt 管理端查看考勤记录和统计结果。
10. 一键理解各端关系
| 你想做的事 | 应该操作哪里 | 背后发生什么 |
|---|---|---|
| 改服务端地址 | Qt 配置中心、STM32/K210 代码常量 | 各端 HTTP 请求指向新服务器 |
| 设置允许签到的时间 | Qt 配置中心 | Qt 保存到服务端,STM32 定期下载 |
| 给人脸维护学生信息 | Qt 人脸管理 | 服务端保存学生档案和人脸档案,K210 同步后更新本地 face_mapping.txt,识别发送时转换成学号 |
| 添加人脸 | K210 按键短按 | K210 保存特征和照片,照片上传服务端 |
| 删除人脸 | Qt 人脸管理或 K210 删除模式 | Qt 删除会生成服务端同步任务;K210 本地删除只影响本地 |
| 查看记录 | Qt 考勤记录页 | Qt 从服务端读取最近记录 |
| 导出报表 | Qt 记录页或统计页 | Qt 把当前加载/筛选结果写成 CSV |
11. 新手常见问题
11.1 为什么识别成功但没有签到成功
常见原因:
- 没有配置签到时间段。
- 当前时间不在任何启用的时间段内。
- STM32 时间不正确,DS1302 失效且网络对时失败。
- 同一学生在当前时间段已经签过。
- K210 发给 STM32 的 ID 不是想要的真实学号。
- 置信度低于
0.75。 - STM32 无法连接服务端,记录暂存在本地等待同步。
11.2 为什么 Qt 看不到 K210 的照片
可能原因:
- K210 侧 ESP8266 没连上 WiFi。
- K210 服务端 IP 或 Token 不正确。
- K210 本地照片保存在
/sd/face_photos,但还没轮到网络队列上传。 - 服务端
faces/目录没有写入权限。 - Qt 配置的服务端地址不是 K210 上传照片的那个服务端。
11.3 为什么删除后 K210 还认识这个人
Qt 删除会先在服务端生成待删除任务,K210 需要同步后才会删除本地 SD 卡中的特征。可以等待 K210 定期同步,也可以通过服务端同步请求接口触发。
11.4 为什么改了 Token 后有的端还能用,有的端不能用
服务端和 Qt 可以读取环境变量 ATTENDANCE_API_TOKEN。STM32 与 K210 的 Token 写在代码里,必须同步修改并重新烧录或上传脚本。
12. 文档索引
| 文档 | 内容 |
|---|---|
docs/00_项目总文档.md |
总体介绍、架构、运行顺序、概念说明 |
docs/01_STM32固件端文档.md |
STM32 固件、硬件模块、签到判断、加密上传 |
docs/02_K210识别端文档.md |
K210 人脸识别、按键操作、照片上传、同步 |
docs/03_服务端文档.md |
服务端 API、SQLite、同步版本、部署安全 |
docs/04_Qt管理端文档.md |
Qt 界面功能、数据模型、本地缓存、PC/Android 适配 |