Vetrivel07/Automated-Logistics-and-Access-Security-Gate-with-Monitoring-and-Behavioral-Analysis

GitHub: Vetrivel07/Automated-Logistics-and-Access-Security-Gate-with-Monitoring-and-Behavioral-Analysis

一个基于 RFID 与隔离森林的本地智能门禁监控系统,解决访客进出记录与异常行为检测问题。

Stars: 0 | Forks: 0

# 自动物流与访问安全门监控与行为分析系统 **作者:** Vetrivel Maheswaran **课程:** ISTE730 — 物联网基础 | 2026年春季 **机构:** 罗彻斯特理工学院 --- ## 📋 目录 - [概述](#overview) - [系统架构](#system-architecture) - [功能特性](#features) - [硬件要求](#hardware-requirements) - [硬件引脚映射](#hardware-pin-mapping) - [软件要求](#software-requirements) - [项目结构](#project-structure) - [安装说明](#setup-instructions) - [运行系统](#running-the-system) - [仪表板](#dashboard) - [API 端点](#api-endpoints) - [异常检测](#anomaly-detection) - [配置](#configuration) - [串行消息格式](#serial-message-format) --- ## 概述 传统的门禁系统在入口处验证用户身份后就不再进行任何操作。本项目通过以下方式扩展基础门禁控制: - **基于 RFID 的边缘认证**(Arduino) - **实时事件日志记录**到本地 SQLite 数据库 - **实时 Web 仪表板**用于监控访问活动 - **每用户行为分析**(进入时间、停留时长、访问频率) - **混合异常检测**,结合基于规则的逻辑与隔离森林机器学习模型 整个系统在本地运行——**无需云端依赖,也不需要互联网连接**。 --- ## 系统架构 ``` RFID Card Scan ↓ Arduino UNO R3 (Edge Layer) ├── RC522 reads UID ├── Authorization check (hardcoded UIDs) ├── IN/OUT toggle per user ├── Servo + LED + Buzzer + LCD feedback └── JSON over USB Serial → COM3 ↓ Python Backend (localhost) ├── SerialReader → background daemon thread ├── AccessService → validation + pipeline ├── AnomalyService → rule-based + ML detection ├── Repository → SQLAlchemy → SQLite └── FastAPI → REST API + Dashboard ↓ http://localhost:8000 ├── Live dashboard (auto-refresh 5s) ├── User detail pages └── Anomaly alerts page ``` --- ## 功能特性 ### 硬件(边缘端) - 通过 RC522(SPI)进行 RFID 卡扫描 - 伺服电机控制闸门(0° 关闭 / 90° 打开) - 绿色 LED —— 授权通过 - 红色 LED —— 授权拒绝 - 蓝色 LED —— 空闲状态 - 蜂鸣器 —— 音频反馈(授权时短鸣,拒绝时警报) - Grove LCD RGB —— 带背光的状态显示 - 每张卡片的进出切换逻辑 ### 后端 - FastAPI REST API - SQLAlchemy ORM 配合 SQLite - 后台串行读取线程(独立于 API 层) - 仓库模式 —— 所有数据库查询隔离 - 服务层 —— 所有业务逻辑隔离 - Pydantic 模式验证 - 本地时间戳存储 ### 仪表板 - **实时扫描面板** —— 实时显示最近扫描(姓名、UID、状态、事件、时间戳、是否在场) - **允许与拒绝对比图** —— 环形图(全时段) - **今日统计** —— 总扫描数、允许、拒绝、异常、唯一用户(在午夜重置) - **实时时钟** —— 每秒更新 - **用户面板** —— 所有用户及其进出状态徽章,可点击 - **异常警报** —— 最新警报及其原因与时间戳 - **日志筛选器** —— 按日期范围和时间窗口筛选 - **用户详情页** —— 每个用户的分析图表、停留时长、完整历史 - **完整异常页面** —— 所有标记事件 ### 异常检测 - 离线时段访问检测(可配置时间窗口) - 快速扫描检测(滑动窗口算法) - 连续拒绝检测 - 隔离森林机器学习 —— 检测每个用户的异常访问时间模式 --- ## 硬件要求 ![硬件设计](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/882ee72344171022.png) | 组件 | 型号 | 说明 | |---|---|---| | 微控制器 | Arduino UNO R3 | 主控板 | | 基础扩展板 | Grove Base Shield | 用于 Grove 模块连接 | | RFID 读卡器 | RC522 | 13.56 MHz,SPI 通信 | | RFID 卡/钥匙 | MIFARE Classic | 必须为 13.56 MHz | | 伺服电机 | Grove 模拟伺服电机 | 闸门驱动 | | 绿色 LED | Grove LED | D4 | | 红色 LED | Grove LED | D5 | | 蓝色 LED | Grove LED | D6 | | 蜂鸣器 | Grove 蜂鸣器 | D8 | | LCD 显示屏 | Grove LCD RGB Backlight v4.0 | I2C | | 杜邦线 | 母对公 | 用于 RC522 连接 | | USB 线 | USB-A 转 USB-B | Arduino 连接至电脑 | --- ## 硬件引脚映射 ### RC522 RFID(杜邦线 — SPI) | RC522 引脚 | Arduino 引脚 | 说明 | |---|---|---| | SDA (SS) | D10 | SPI 片选 | | SCK | D13 | SPI 时钟 | | MOSI | D11 | SPI 数据输出 | | MISO | D12 | SPI 数据输入 | | RST | D7 | 复位 | | GND | GND | 地 | | 3.3V | 3.3V | **非 5V** | ### Grove 模块(基础扩展板) | 组件 | Grove 端口 | Arduino 引脚 | |---|---|---| | 伺服电机 | D3 | D3(PWM) | | 绿色 LED | D4 | D4 | | 红色 LED | D5 | D5 | | 蓝色 LED | D6 | D6 | | 蜂鸣器 | D8 | D8 | | LCD RGB | I2C | A4(SDA)、A5(SCL) | --- ## 软件要求 ### Arduino 库 通过 Arduino IDE → 库管理器安装: - `MFRC522`(由 GithubCommunity 提供) - `Grove LCD RGB Backlight`(由 Seeed Studio 提供) - `Servo`(内置,无需安装) ### Python 依赖 ``` fastapi==0.115.0 uvicorn==0.30.6 sqlalchemy==2.0.35 pyserial==3.5 pydantic==2.9.2 pydantic-settings==2.5.2 scikit-learn==1.5.2 numpy==2.1.1 pandas==2.2.3 jinja2==3.1.4 ``` ### 系统要求 - Python 3.10+ - Windows(已在 Windows 上测试,使用 COM3) - Arduino IDE 2.x --- ## 安装说明 ### 步骤 1 — Arduino 设置 1. 按照上方引脚映射表连接所有硬件 2. 打开 Arduino IDE 3. 通过 **工具 → 管理库** 安装所需库 4. 打开 `arduino/SecurityGate.ino` 5. 将程序上传至 Arduino UNO 6. 打开 **串口监视器**,波特率设为 **9600** 7. 扫描每张 RFID 卡 —— 复制打印出的 UID(格式:`XX XX XX XX`) 8. 关闭串口监视器 9. 打开 `SecurityGate.ino` 并替换其中的虚拟 UID: ``` const char AUTHORIZED_UIDS[][12] = { "XX XX XX XX", // replace with your real UID "XX XX XX XX", // replace with your real UID "XX XX XX XX" // replace with your real UID }; ``` 10. 再次上传 ### 步骤 2 — Python 环境设置 ``` # 创建并激活虚拟环境 uv init uv venv venv venv\Scripts\activate # Windows # source venv/bin/activate # Mac/Linux # 安装依赖 uv pip install -r requirements.txt ``` ### 步骤 3 — 配置用户名 编辑 `core/user_map.py` 将 UID 映射为显示名称: ``` USER_MAP = { "XX XX XX XX": "Alice", "XX XX XX XX": "Bob", "XX XX XX XX": "Charlie", } ``` ### 步骤 4 — 配置设置(可选) 编辑 `core/config.py` 修改以下配置: ``` SERIAL_PORT: str = "COM3" # change to your Arduino port SERIAL_BAUD_RATE: int = 9600 # must match Arduino Serial.begin() ANOMALY_HOUR_START: int = 10 # start of normal operating hours ANOMALY_HOUR_END: int = 15 # end of normal operating hours ANOMALY_RAPID_SCAN_LIMIT: int = 5 # max scans before rapid scan alert ANOMALY_RAPID_SCAN_WINDOW: int = 60 # seconds for rapid scan window ``` --- ## 运行系统 ### 重要规则 - ❌ 启动 Python 前必须**关闭**串口监视器 - ✅ Arduino 必须**通过 USB 连接** - ✅ 虚拟环境必须**已激活** ### 启动系统 ``` # 确保你在项目目录中 cd python # 激活 venv(如果尚未激活) venv\Scripts\activate # 运行后端 uvicorn main:app --port 8000 ``` ### 打开仪表板 ``` http://localhost:8000 ``` ### 预期启动终端输出 ``` [Main] Starting Security Gate System v1.0.0 [Main] Database initialized. [SerialReader] Thread started on COM3 [Main] Serial reader started on COM3 [SerialReader] Connected to COM3 [SerialReader] Non-scan message received: {'event': 'system_ready', ...} ``` ### 预期卡片扫描终端输出 ``` [SerialReader] Received → uid=C3 22 E0 56 status=allowed event=IN [AccessService] Saved → id=1 uid=C3 22 E0 56 status=allowed event=IN [AccessService] ✓ Pipeline complete → anomaly=False ``` ### 全新启动(重置数据库) ``` # 先停止 Python(Ctrl+C) # 删除数据库 del access_logs.db # Windows # rm access_logs.db # Mac/Linux # 重启 uvicorn main:app --port 8000 ``` --- ## 仪表板 | URL | 描述 | |---|---| | `http://localhost:8000` | 主仪表板 | | `http://localhost:8000/dashboard/user.html?uid=XX XX XX XX` | 用户详情页 | | `http://localhost:8000/dashboard/anomalies.html` | 所有异常警报 | | `http://localhost:8000/docs` | 自动生成的 API 文档 | --- ## API 端点 | 方法 | 端点 描述 | |---|---|---| | GET | `/api/latest` | 最新扫描事件 | | GET | `/api/logs` | 最近 50 条日志 | | GET | `/api/logs/filter` | 按日期和时间范围筛选 | | GET | `/api/logs/{id}` | 按 ID 查询单条日志 | | GET | `/api/logs/user/{uid}` | 查询某 UID 的所有日志 | | GET | `/api/anomalies` | 所有标记为异常的日志 | | GET | `/api/stats` | 全时段统计 | | GET | `/api/stats/today` | 今日统计(在午夜重置) | | GET | `/api/users` | 所有唯一用户及其统计 | | GET | `/api/behaviour/{uid}` | 某用户的分析行为 | ### 筛选端点参数 ``` GET /api/logs/filter?date_from=2026-04-18&date_to=2026-04-18&time_from=10:00&time_to=15:00 ``` | 参数 | 必填 | 格式 | 描述 | |---|---|---|---| | `date_from` | 是 | YYYY-MM-DD | 开始日期 | | `date_to` | 是 | YYYY-MM-DD | 结束日期 | | `time_from` | 否 | HH:MM | 当天开始时间 | | `time_to` | 否 | HH:MM | 当天结束时间 | --- ## 异常检测 ### 第一层 — 基于规则 | 规则 | 触发条件 | 配置键 | |---|---|---| | 离线时段 | 在 10:00 之前或 15:00 之后扫描 | `ANOMALY_HOUR_START`、`ANOMALY_HOUR_END` | | 快速扫描 | 同一卡片在 60 秒内扫描 5 次以上 | `ANOMALY_RAPID_SCAN_LIMIT`、`ANOMALY_RAPID_SCAN_WINDOW` | | 拒绝连续 | 同一 UID 连续拒绝 3 次以上 | 硬编码(3) | ### 第二层 — 隔离森林机器学习 - **算法:** `sklearn.ensemble.IsolationForest` - **特征:** 小时(0–23) - **训练:** 每次扫描后使用完整用户历史重新训练 - **激活条件:** 每个用户至少 10 条历史记录 - **污染率:** 0.1(预期约 10% 的训练数据为异常) - **输出:** -1 表示异常,1 表示正常 ### 检测流程 ``` Every scan → Rule 1: Off-hours check (fast, always runs) Rule 2: Rapid scan check (DB query, sliding window) Rule 3: Denied streak check (last 5 records) ML: Isolation Forest (only if 10+ history records) → First match wins → is_anomaly=True, reason saved to DB ``` --- ## 配置 所有配置均在 `core/config.py` 中: ``` # 串行 SERIAL_PORT: str = "COM3" SERIAL_BAUD_RATE: int = 9600 SERIAL_TIMEOUT: int = 2 SERIAL_RECONNECT_DELAY: int = 5 # 数据库 DATABASE_URL: str = "sqlite:///./access_logs.db" # 异常检测 ANOMALY_HOUR_START: int = 10 # scans before this hour = suspicious ANOMALY_HOUR_END: int = 15 # scans after this hour = suspicious ANOMALY_RAPID_SCAN_LIMIT: int = 5 ANOMALY_RAPID_SCAN_WINDOW: int = 60 # seconds # 仪表板 DASHBOARD_RECENT_LOGS_LIMIT: int = 50 ``` --- ## 串行消息格式 Arduino 发送以起始/结束标记包裹的 JSON: ``` <{"uid":"C3 22 E0 56","status":"allowed","event":"IN"}> <{"uid":"C3 22 E0 56","status":"allowed","event":"OUT"}> <{"uid":"AE F3 10 06","status":"denied","event":"NONE"}> ``` | 字段 | 取值 | 说明 | |---|---|---| | `uid` | `XX XX XX XX` | RFID 卡的 UID(十六进制) | | `status` | `allowed` / `denied` | 授权结果 | | `event` | `IN` / `OUT` / `NONE` | 进出切换(拒绝时为 NONE) | `<` 和 `>` 标记使 Python 串行读取器能够可靠地解析消息,即使传输过程中出现字节丢失。 --- ## 数据库架构 **表:** `access_logs` | 列名 | 类型 | 说明 | |---|---|---| | `id` | 整数 | 自增主键 | | `uid` | 字符串(20) | RFID 卡的 UID | | `status` | 字符串(10) | `allowed` 或 `denied` | | `event` | 字符串(4) | `IN`、`OUT` 或 `NONE` | | `timestamp` | 日期时间 | 本地扫描时间 | | `is_anomaly` | 布尔值 | 是否标记为异常 | | `anomaly_reason` | 字符串(200) | 异常原因描述 | --- ## 故障排查 | 问题 | 原因 | 解决方法 | |---|---|---| | `PermissionError: COM3` | 串口监视器处于打开状态 | 关闭 Arduino IDE 串口监视器 | | `ModuleNotFoundError: serial` | 名为 `serial` 的文件夹与 pyserial 冲突 | 将文件夹重命名为 `serial_comm` | | RC522 固件:`0x00` | 接线问题或电压错误 | 检查 3.3V 电源,验证 SPI 引脚连接 | | 今日统计显示为 0 | 时区不匹配 | 确保使用 `datetime.now()`(而非 `utcnow()`) | | 卡片无法读取 | 天线信号弱 | 调用 `rfid.PCD_SetAntennaGain(rfid.RxGain_max)` | | 仪表板未更新 | 筛选器处于激活状态 | 点击筛选器上的重置按钮 | --- ## 技术栈 | 层级 | 技术 | |---|---| | 嵌入式 | Arduino C/C++、MFRC522、伺服电机、rgb_lcd | | 通信 | USB 串行、JSON、基于标记的消息封装 | | 后端 | Python 3.10+、FastAPI、Uvicorn | | 数据库 | SQLite、SQLAlchemy ORM | | 验证 | Pydantic v2 | | 串行通信 | PySerial | | 机器学习 | scikit-learn(IsolationForest)、NumPy | | 前端 | HTML5、CSS3、JavaScript、Chart.js | | 字体 | IBM Plex Sans、IBM Plex Mono |
标签:Apex, Arduino, AV绕过, FastAPI, ISTE730, Python, RC522, RFID, SQLAlchemy, SQLite, Streamlit, TCP/UDP协议, UNO R3, USB Serial, 串口通信, 云计算, 仪表盘, 停留时长, 入场时间, 孤立森林, 安全闸机, 异常检测, 无云依赖, 无后门, 智能门禁, 本地部署, 机器学习, 混合检测, 物联网, 罗彻斯特理工学院, 自动化物流, 规则引擎, 访问控制, 访问频率, 边缘计算, 逆向工具, 门禁系统