1. 项目概述
本项目旨在设计并实现一个基于STM32的全栈人脸识别考勤系统。该系统结合了嵌入式开发、计算机视觉和数据库技术,实现了自动人脸检测、识别和考勤记录功能。
主要特点:
- 使用STM32F4系列微控制器作为主控制器
- 采用OpenCV进行人脸检测和识别
- Qt开发跨平台桌面应用程序,实现友好的用户界面
- SQLite嵌入式数据库存储员工信息和考勤记录
- 支持实时考勤、数据统计分析和报表生成
2. 系统设计
2.1 硬件设计
主要硬件模块及功能:
- STM32F407VGT6微控制器:系统的核心,负责协调各个模块工作
- OV7670摄像头模块:捕获实时图像,用于人脸检测和识别
- 3.5寸TFT LCD显示屏:显示系统界面和识别结果
- AS608指纹识别模块:作为辅助识别手段
- RC522 RFID读卡器:用于员工卡识别,提供备用签到方式
- ESP8266 WiFi模块:实现与服务器的无线通信,上传考勤数据
2.2 软件设计
3. 代码实现
3.1 人脸检测
以下是使用OpenCV实现人脸检测的代码示例:
#include <opencv2/opencv.hpp> #include <opencv2/objdetect.hpp> using namespace cv; class FaceDetector { private: CascadeClassifier face_cascade; public: FaceDetector(const std::string& cascade_file) { // 加载Haar级联分类器 if (!face_cascade.load(cascade_file)) { throw std::runtime_error("Error loading face cascade file"); } } std::vector<Rect> detectFaces(const Mat& frame) { Mat gray; std::vector<Rect> faces; // 转换为灰度图像 cvtColor(frame, gray, COLOR_BGR2GRAY); // 执行人脸检测 face_cascade.detectMultiScale(gray, faces, 1.1, 3, 0, Size(30, 30)); return faces; } void drawFaces(Mat& frame, const std::vector<Rect>& faces) { for (const auto& face : faces) { rectangle(frame, face, Scalar(255, 0, 0), 2); } } };
代码说明:
FaceDetector
类封装了人脸检测功能。- 构造函数加载Haar级联分类器文件。
detectFaces
方法接收一帧图像,返回检测到的人脸矩形区域。drawFaces
方法在原图上绘制检测到的人脸矩形框。
3.2 人脸识别
下面是使用LBPH算法实现人脸识别的代码示例:
#include <opencv2/face.hpp> #include <opencv2/opencv.hpp> using namespace cv; using namespace cv::face; class FaceRecognizer { private: Ptr<LBPHFaceRecognizer> model; public: FaceRecognizer() { model = LBPHFaceRecognizer::create(); } void train(const std::vector<Mat>& faces, const std::vector<int>& labels) { model->train(faces, labels); } void predict(const Mat& face, int& label, double& confidence) { model->predict(face, label, confidence); } void saveModel(const std::string& filename) { model->save(filename); } void loadModel(const std::string& filename) { model->read(filename); } };
代码说明:
FaceRecognizer
类封装了LBPH人脸识别器的功能。- 构造函数创建LBPH人脸识别器实例。
train
方法用于训练模型。predict
方法进行人脸识别,返回预测的标签和置信度。saveModel
和loadModel
方法用于保存和加载训练好的模型。
3.3 数据库操作
使用SQLite进行数据库操作的代码示例:
#include <sqlite3.h> #include <string> #include <stdexcept> #include <iostream> class Database { private: sqlite3* db; static int callback(void* data, int argc, char** argv, char** azColName) { // 处理查询结果的回调函数 for(int i = 0; i < argc; i++) { std::cout << azColName[i] << " = " << (argv[i] ? argv[i] : "NULL") << std::endl; } std::cout << std::endl; return 0; } public: Database(const std::string& dbName) { if (sqlite3_open(dbName.c_str(), &db) != SQLITE_OK) { throw std::runtime_error("Can't open database: " + std::string(sqlite3_errmsg(db))); } } ~Database() { sqlite3_close(db); } void executeQuery(const std::string& sql) { char* errMsg = nullptr; int rc = sqlite3_exec(db, sql.c_str(), callback, 0, &errMsg); if (rc != SQLITE_OK) { std::string error = "SQL error: " + std::string(errMsg); sqlite3_free(errMsg); throw std::runtime_error(error); } } void insertEmployee(const std::string& name, int id) { std::string sql = "INSERT INTO employees (name, id) VALUES ('" + name + "', " + std::to_string(id) + ");"; executeQuery(sql); } void recordAttendance(int employeeId, const std::string& timestamp) { std::string sql = "INSERT INTO attendance (employee_id, timestamp) VALUES (" + std::to_string(employeeId) + ", '" + timestamp + "');"; executeQuery(sql); } };
代码说明:
Database
类封装了SQLite数据库的基本操作。- 构造函数打开数据库连接,析构函数关闭连接。
executeQuery
方法执行SQL查询,使用回调函数处理结果。insertEmployee
方法插入新员工记录。recordAttendance
方法记录考勤信息。
3.4 Qt界面实现
以下是使用Qt实现主界面的代码示例:
#include <QMainWindow> #include <QPushButton> #include <QVBoxLayout> #include <QMessageBox> class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) { setWindowTitle("人脸识别考勤系统"); QWidget *centralWidget = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(centralWidget); QPushButton *btnAttendance = new QPushButton("考勤签到", this); QPushButton *btnManage = new QPushButton("员工管理", this); QPushButton *btnReport = new QPushButton("考勤报表", this); layout->addWidget(btnAttendance); layout->addWidget(btnManage); layout->addWidget(btnReport); setCentralWidget(centralWidget); connect(btnAttendance, &QPushButton::clicked, this, &MainWindow::onAttendanceClicked); connect(btnManage, &QPushButton::clicked, this, &MainWindow::onManageClicked); connect(btnReport, &QPushButton::clicked, this, &MainWindow::onReportClicked); } private slots: void onAttendanceClicked() { // 打开考勤签到界面 QMessageBox::information(this, "考勤签到", "正在打开摄像头进行人脸识别..."); // 这里可以调用人脸识别和考勤记录的相关函数 } void onManageClicked() { // 打开员工管理界面 QMessageBox::information(this, "员工管理", "正在打开员工管理界面..."); // 这里可以实现一个新的对话框或窗口来管理员工信息 } void onReportClicked() { // 生成考勤报表 QMessageBox::information(this, "考勤报表", "正在生成考勤报表..."); // 这里可以实现报表生成和显示的功能 } }; // 主函数 int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow mainWindow; mainWindow.show(); return app.exec(); }
代码说明:
MainWindow
类继承自QMainWindow
,实现了主界面的布局和功能。- 构造函数中创建了三个按钮:考勤签到、员工管理和考勤报表。
- 使用
QVBoxLayout
垂直布局来排列按钮。 - 通过
connect
函数将按钮的点击事件与相应的槽函数连接。 - 三个槽函数
onAttendanceClicked
、onManageClicked
和onReportClicked
分别处理不同按钮的点击事件。 - 主函数创建并显示主窗口,启动Qt事件循环。
3.5 STM32与Qt通信
以下是STM32与Qt程序通过串口通信的示例代码:
// STM32端代码(使用HAL库) #include "stm32f4xx_hal.h" UART_HandleTypeDef huart2; void UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; HAL_UART_Init(&huart2); } void SendData(uint8_t* data, uint16_t size) { HAL_UART_Transmit(&huart2, data, size, HAL_MAX_DELAY); } // Qt端代码 #include <QSerialPort> #include <QSerialPortInfo> class SerialCommunication : public QObject { Q_OBJECT public: SerialCommunication(QObject *parent = nullptr) : QObject(parent) { serial = new QSerialPort(this); connect(serial, &QSerialPort::readyRead, this, &SerialCommunication::handleReadyRead); } bool openPort(const QString &portName) { serial->setPortName(portName); serial->setBaudRate(QSerialPort::Baud115200); return serial->open(QIODevice::ReadWrite); } void closePort() { if (serial->isOpen()) { serial->close(); } } void sendData(const QByteArray &data) { if (serial->isOpen()) { serial->write(data); } } private slots: void handleReadyRead() { QByteArray data = serial->readAll(); emit dataReceived(data); } signals: void dataReceived(const QByteArray &data); private: QSerialPort *serial; }; // 在主窗口中使用SerialCommunication类 class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) { serialComm = new SerialCommunication(this); connect(serialComm, &SerialCommunication::dataReceived, this, &MainWindow::onDataReceived); // 初始化串口 if (serialComm->openPort("COM3")) { // 根据实际情况修改串口名 qDebug() << "Serial port opened successfully"; } else { qDebug() << "Failed to open serial port"; } } private slots: void onDataReceived(const QByteArray &data) { // 处理接收到的数据 qDebug() << "Received data:" << data; // 这里可以添加对接收数据的处理逻辑 } void sendCommandToSTM32(const QString &command) { serialComm->sendData(command.toUtf8()); } private: SerialCommunication *serialComm; };
代码说明:
SerialCommunication
类封装了Qt串口通信的功能。openPort
方法用于打开指定的串口。closePort
方法用于关闭串口。sendData
方法用于发送数据到STM32。handleReadyRead
槽函数处理接收到的数据,并通过信号dataReceived
发送出去。- 在
MainWindow
类中,我们创建了SerialCommunication
实例,并连接了数据接收的信号和槽。 onDataReceived
槽函数用于处理从STM32接收到的数据。sendCommandToSTM32
方法用于向STM32发送命令。
3.6 人脸识别与考勤逻辑集成
以下是将人脸识别与考勤逻辑集成到Qt应用程序中的示例代码:
#include <QCamera> #include <QCameraImageCapture> #include <QTimer> #include <QDateTime> #include <opencv2/opencv.hpp> class AttendanceSystem : public QObject { Q_OBJECT public: AttendanceSystem(QObject *parent = nullptr) : QObject(parent) { faceDetector = new FaceDetector("haarcascade_frontalface_default.xml"); faceRecognizer = new FaceRecognizer(); database = new Database("attendance.db"); camera = new QCamera(this); imageCapture = new QCameraImageCapture(camera); connect(imageCapture, &QCameraImageCapture::imageCaptured, this, &AttendanceSystem::processCapturedImage); // 每5秒捕获一次图像 QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &AttendanceSystem::captureImage); timer->start(5000); } public slots: void startAttendance() { camera->start(); } void stopAttendance() { camera->stop(); } private slots: void captureImage() { imageCapture->capture(); } void processCapturedImage(int id, const QImage &preview) { cv::Mat frame = QImageToMat(preview); std::vector<cv::Rect> faces = faceDetector->detectFaces(frame); for (const auto& face : faces) { cv::Mat faceROI = frame(face); int label; double confidence; faceRecognizer->predict(faceROI, label, confidence); if (confidence < 80.0) { // 假设置信度阈值为80 recordAttendance(label); emit attendanceRecorded(label); } } } void recordAttendance(int employeeId) { QDateTime currentTime = QDateTime::currentDateTime(); QString timestamp = currentTime.toString("yyyy-MM-dd hh:mm:ss"); database->recordAttendance(employeeId, timestamp.toStdString()); } private: FaceDetector *faceDetector; FaceRecognizer *faceRecognizer; Database *database; QCamera *camera; QCameraImageCapture *imageCapture; cv::Mat QImageToMat(const QImage &image) { cv::Mat mat; switch (image.format()) { case QImage::Format_RGB888: mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); cv::cvtColor(mat, mat, cv::COLOR_RGB2BGR); break; case QImage::Format_ARGB32_Premultiplied: mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); cv::cvtColor(mat, mat, cv::COLOR_RGBA2BGR); break; default: break; } return mat; } signals: void attendanceRecorded(int employeeId); };
代码说明:
AttendanceSystem
类集成了整个考勤系统的核心功能,包括摄像头控制、图像处理、人脸识别和考勤记录。构造函数中:
- 初始化
FaceDetector
、FaceRecognizer
和Database
对象。 - 设置 QCamera 和 QCameraImageCapture 用于图像捕获。
- 创建一个定时器,每5秒触发一次图像捕获。
- 初始化
startAttendance()
和stopAttendance()
方法用于启动和停止考勤过程。captureImage()
槽函数被定时器触发,用于捕获摄像头图像。processCapturedImage()
是核心处理函数:- 将 QImage 转换为 OpenCV 的 Mat 格式。
- 使用 FaceDetector 检测人脸。
- 对每个检测到的人脸进行识别。
- 如果识别置信度高于阈值,则记录考勤。
recordAttendance()
方法将考勤记录保存到数据库中,包括员工ID和时间戳。QImageToMat()
是一个辅助函数,用于将 Qt 的 QImage 转换为 OpenCV 的 Mat 格式。类中定义了
attendanceRecorded
信号,当成功记录考勤时发出,可用于更新UI或通知其他组件。整个系统通过定时捕获图像、检测人脸、识别身份、记录考勤的流程,实现了自动化的考勤功能。
该设计允许系统在后台持续运行,不需要人工干预即可完成考勤过程。
通过调整人脸识别的置信度阈值(此处设为80.0),可以平衡系统的准确性和灵敏度。
系统集成了数据库操作,确保考勤记录能够被永久保存和后续查询。
4. 项目总结
4.1 主要成果
- 成功实现了基于STM32和Qt的全栈人脸识别考勤系统。
- 集成了实时人脸检测和识别功能,提高了考勤效率。
- 开发了友好的用户界面,方便管理员操作和数据查询。
- 实现了考勤数据的自动化记录和统计分析功能。
4.2 技术亮点
- 采用OpenCV进行图像处理和人脸识别,提高了识别的准确性。
- 使用Qt框架开发跨平台桌面应用,提升了用户体验。
- 集成SQLite数据库,实现了高效的数据管理和查询。
- STM32与Qt的串口通信实现,使硬件控制更加灵活。