Files
bishe-docs/README.md
T
2026-10-19 08:05:19 +00:00

15 KiB
Raw Blame History

人脸识别考勤系统项目总文档

1. 这是什么项目

本项目是一个多端协同的人脸识别考勤系统。它不是只有一个程序,而是由四个主要部分组成:

部分 所在目录 主要职责 可以把它理解成
STM32 固件端 stm32/ 接收 K210 识别结果、判断是否允许签到、加密上传记录、控制 OLED/蜂鸣器/指示灯/补光灯 考勤机主控板
K210 识别端 k210/ 摄像头采集、人脸检测、人脸特征比对、添加/删除本地人脸、上传人脸照片、同步人脸档案 摄像头和 AI 识别模块
C++ 服务端 server/ 提供 HTTP API、保存考勤记录、时间段、学生班级档案、人脸档案、照片和同步状态 中心服务器
Qt 管理端 QT/ Windows/Android 图形管理界面,用于查看记录、统计、配置时间段、管理班级、人脸照片和照片对应学生信息 管理后台

整个系统的目标是:

  1. K210 看到人脸并识别出一个人。
  2. K210 通过串口把识别结果发给 STM32。
  3. STM32 判断当前是否在允许签到的时间段内,并检查是否重复签到。
  4. 如果允许签到,STM32 将签到数据用 AES-128-CBC 加密后通过 ESP8266 发给服务端。
  5. 服务端保存数据,并提供给 Qt 管理端查看、统计、导出和管理。
  6. 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 正常签到流程

  1. K210 摄像头采集画面。
  2. K210 使用三个 KPU 模型执行人脸检测、关键点定位和特征提取。
  3. K210 将当前人脸特征与 SD 卡中的 recordftr.txt 做比对。
  4. 如果分数大于阈值,K210 认为识别成功。
  5. K210 通过 UART1 发送给 STM32:
ID:20240001,NAME:20240001,CONF:0.95
  1. STM32 解析串口数据。
  2. STM32 检查置信度是否超过 0.75。
  3. STM32 检查当前时间是否落在已启用的时间段内。
  4. STM32 检查本地缓存、今日记录缓存和服务端查重结果,避免同一个学生在同一时段重复签到。
  5. STM32 生成 JSON:
{
  "student_id": "20240001",
  "name": "20240001",
  "confidence": 0.95,
  "status": "success",
  "class_id": "1"
}
  1. STM32 使用 AES-128-CBC 加密并做 Base64 编码。
  2. STM32 通过 ESP8266 发送:
POST /api/v1/attendance/checkins
X-Api-Token: ...
X-Encrypted: AES128-CBC
  1. 服务端解密、校验字段、写入 SQLite。
  2. Qt 管理端刷新后可以看到新记录。

4.2 新增人脸流程

  1. 在 K210 正常识别界面短按按键,进入添加人脸模式。
  2. 摄像头检测到陌生人脸后,再短按确认。
  3. K210 将人脸特征保存到 SD 卡 recordftr.txt。
  4. K210 将对应照片保存到 /sd/face_photos/<face_id>.jpg。
  5. K210 将照片 ID 放入上传队列。
  6. 网络任务空闲时,K210 通过 ESP8266 上传照片到服务端:
POST /api/v1/faces/photos/<face_id>
  1. Qt 管理端进入“人脸管理”,刷新后可以看到照片。
  2. 管理员在该 face_id 的编辑弹窗中填写真实学号、姓名和班级,并保存人脸档案。
  3. 服务端保存人脸档案并触发同步。
  4. K210 拉取同步状态后更新本地 face_mapping.txt,识别发送时把 face_id 转成真实学号,recordftr.txt 中的原始 ID:<face_id> 保持不变。

4.3 删除人脸流程

  1. Qt 管理端删除某个学生或人脸。
  2. 服务端删除照片、解除人脸绑定,按需要删除考勤记录。
  3. 服务端写入待删除任务 pending_delete,并提升同步版本。
  4. K210 定期检查同步请求或拉取统一同步状态。
  5. K210 在本地删除对应特征和照片。
  6. K210 向服务端发送 ACK:
POST /api/v1/sync/ack
  1. 服务端移除待删除任务。

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. 推荐启动顺序

  1. 启动服务端,用 Qt 或带 Token 与 AES 解密能力的客户端确认 /api/v1/system/health 返回健康状态。
  2. 启动 Qt 管理端,在配置中心填写服务端地址和 API Token,点击“保存”后再点“测试连接”。
  3. 在 Qt 管理端新增签到时间段并保存。
  4. 烧录并启动 STM32,确认 OLED 显示系统就绪。
  5. 启动 K210,确认摄像头画面、模型加载和 WiFi 连接正常。
  6. 在 K210 添加人脸,等待照片上传到服务端。
  7. 在 Qt 管理端为照片对应的人脸 ID 点击“编辑”,填写真实学号、姓名和班级并保存人脸档案。
  8. K210 同步人脸档案后,再让同一人进行识别签到。
  9. 在 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 为什么识别成功但没有签到成功

常见原因:

  1. 没有配置签到时间段。
  2. 当前时间不在任何启用的时间段内。
  3. STM32 时间不正确,DS1302 失效且网络对时失败。
  4. 同一学生在当前时间段已经签过。
  5. K210 发给 STM32 的 ID 不是想要的真实学号。
  6. 置信度低于 0.75。
  7. STM32 无法连接服务端,记录暂存在本地等待同步。

11.2 为什么 Qt 看不到 K210 的照片

可能原因:

  1. K210 侧 ESP8266 没连上 WiFi。
  2. K210 服务端 IP 或 Token 不正确。
  3. K210 本地照片保存在 /sd/face_photos,但还没轮到网络队列上传。
  4. 服务端 faces/ 目录没有写入权限。
  5. 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 适配