课程设计:智能小区安保管理系统
0x00:设计要求
设计任务和要求:
安防系统主要由门禁管理系统、巡更系统等组成。
门禁管理系统:用卡片、按键、电子门锁等装置控制出入口门的开关。
巡更系统:在保安人员巡逻路线上设置发信器,以确认保安人员的巡视记录、时间等。
门禁系统功能如下:
- 进出通道的权限的管理:对通道进出权限的管理主要有进出通道的权限、进出通道的方式、进出通道的时段。
- 实时监控功能:系统管理人员可以通过微机实时查看每个门区人员的进出情况;也可以在紧急状态打开或关闭所有的门。
- 出入记录查询功能:系统可储存所有的进出记录、状态记录,可按不同的查询条件查询,配备相应考勤软件可实现考勤、门禁一卡通。
- 异常报警功能:在异常情况下可以实现微机报警或报警器报警,如:非法入侵、门超时未关等。
巡更系统功能如下:
- 保安值班人员巡更时.必须确认设定的巡视路线在规定时间区段内顺序到达每一巡更点,以巡更钥匙去触碰巡更点。
- 巡更资料也可以贮存在电脑内,供随时查询,并作考勤记录。
0x01:硬件设计 & 实物展示
设计框图 & 实物展示:
设计框图:
实物展示:
主节点PCB设计:
电源管理 & 对应外设:
3.3V | 5.0V |
---|---|
核心板 | OpenArt |
NRF24L01 | 门禁RFID |
OLED | ESP8266 |
蜂鸣器 | RG90舵机 |
具体原理图与PCB与之前的智能家居课设当中的设计基本一致,只是多引出了几个IO,读者可去智能家居智能家居这篇博客中进行下载(下载链接在文末百度网盘~)
0x02:软件设计
主节点:
门禁模式:
门禁卡识别其实非常简单,本次课设我使用的门禁卡识别模块是通过串口与单片机进行通讯的(波特率必须为9600)当时刚使用这个模块的时候,被店家给的教程给坑到了,其实注册卡根本不需要从串口助手发送指令注册,模块上有个按键,只要通过按键就可以进行注册卡、删除卡的操作。
识别卡号是否与已注册卡的卡号一致,如果一致,则开门(其实只需要使用strcmp(const char str1[], const char str2[]),的函数即可进行判断)
如下为整套系统切换模式 + 状态判断部分代码:
void ACCESS_HUB(void){
UI();
// OLED_P6x8Int(1, 2, INFRARD1_READ, 1);
// OLED_P6x8Str(3, 2, USART3_RX_BUF);
ux = camera_times();
if(Current_mode.Identify_mode == CAMERA){
if(ux > 0) {
printf("u%d ", ux);
Current_mode.Accmode = OPEN;
ux = 0;
}
// else if(camera_times() > 0 && camera_times() <= 5){printf("b%d\r\n", camera_times()-3); Beep_Bling();}
}
else if(Current_mode.Identify_mode == IC){
if(USART3_RX_STA & 0x8000){
if(!strcmp(USART3_RX_BUF, ZYQ)) {printf("ZYQ "); Current_mode.Accmode = OPEN;}
else if(!strcmp(USART3_RX_BUF, CXY)) {printf("CXY "); Current_mode.Accmode = OPEN;}
else if(!strcmp(USART3_RX_BUF, Security_1)) {printf("ZKA "); Current_mode.Accmode = OPEN;}
else if(!strcmp(USART3_RX_BUF, Security_2)) {printf("HCC "); Current_mode.Accmode = OPEN;}
}
memset(USART3_RX_BUF, 0, USART3_REC_LEN);
USART3_RX_STA = 0;
}
NRF_rec(rec_buff, rec_buff1, 5);
if(!memcmp(rec_buff, "b1", 2) || !memcmp(rec_buff1, "b1", 2)) {printf("b1 "); memset(rec_buff, 0, 6); memset(rec_buff1, 0, 6);}
else if(!memcmp(rec_buff, "b2", 2)|| !memcmp(rec_buff1, "b2", 2)) {printf("b2 "); memset(rec_buff, 0, 6); memset(rec_buff1, 0, 6);}
if(Current_mode.Accmode == OPEN) {
TIM_SetCompare2(TIM4, 350);
Beep_Bling_once();
delay_ms(5000);
Current_mode.Accmode = CLOSE;
}
else if(Current_mode.Accmode == CLOSE) TIM_SetCompare2(TIM4, 1200);
else if(Current_mode.Accmode == ALARM) Beep_Bling();
}
此部分相对较为简单,但是调试过程比较艰辛,配置及模块都出现过小问题,不过目前已经解决~
OpenArt交互 & 人脸识别:
OpenArt部分使用了星瞳官网给的比较简单的人脸辨别算法,通过计算当前帧图片的blobs与图像库中图片的blobs做差,相差较小的图像即为得出的结果,但是这样不过是否有人,都会有结果输出,不是我们识别人脸的初衷,所以我们需要在计算出差值后,在当前背景下估算出差值范围!!!这点很重要!!!
如下为实现代码:
- 采集图像部分:
import sensor, image, pyb
sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.GRAYSCALE
sensor.set_framesize(sensor.QQVGA) # or sensor.QQVGA (or others)
sensor.set_windowing((92,112))
sensor.skip_frames(10) # Let new settings take affect.
sensor.skip_frames(time = 2000)
num = 1 #设置被拍摄者序号,第一个人的图片保存到s1文件夹,第二个人的图片保存到s2文件夹,以此类推。每次更换拍摄者时,修改num值。
n = 20 #设置每个人拍摄图片数量。
#连续拍摄n张照片,每间隔3s拍摄一次。
while(n):
#红灯亮
#pyb.LED(RED_LED_PIN).on()
sensor.skip_frames(time = 1000) # Give the user time to get ready.等待3s,准备一下表情。
#保存截取到的图片到SD卡
print(n)
sensor.snapshot().save("singtown/s%s/%s.pgm" % (num, n) ) # or "example.bmp" (or others)
n -= 1
#pyb.LED(BLUE_LED_PIN).off()
print("Done! Reset the camera to see the saved image.")
- 识别交互部分:
import sensor, time, image, pyb
from machine import UART
import IPS114 as ips114
sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.GRAYSCALE
sensor.set_framesize(sensor.QQVGA) # or sensor.QQVGA (or others)
sensor.set_windowing((92,112))
sensor.skip_frames(10) # Let new settings take affect.
sensor.skip_frames(time = 2000) #等待5s
clock = time.clock()
uart = UART(2, baudrate = 115200)
ips114.ips_init()
#SUB = "s1"
NUM_SUBJECTS = 3 #图像库中不同人数,一共3人
NUM_SUBJECTS_IMGS = 10 #每人有20张样本图片
# 拍摄当前人脸。
area_ROI = (10, 5, 80, 90)
while(True):
#uart_flag = uart.any()
uart_flag = 1
if(uart_flag>0):
img = sensor.snapshot() # 获得图片
img.draw_rectangle(area_ROI,color = (0, 255, 0)) #绘制出ROI区域
#ips114.ips_display(img,img.width(),img.height()) # 显示图像
d0 = img.find_lbp(area_ROI)
#d0为当前人脸的lbp特征
img = None
pmin = 999999
num=0
def min(pmin, a, s):
global num
if a<pmin:
pmin=a
num=s
return pmin
for s in range(1, NUM_SUBJECTS+1):
dist = 0
for i in range(2, NUM_SUBJECTS_IMGS+1):
img = image.Image("singtown/s%d/%d.pgm"%(s, i))
d1 = img.find_lbp(area_ROI)
#d1为第s文件夹中的第i张图片的lbp特征
dist += image.match_descriptor(d0, d1)#计算d0 d1即样本图像与被检测人脸的特征差异度。
#print("Average dist for subject %d: %d"%(s, dist/NUM_SUBJECTS_IMGS))
pmin = min(pmin, dist/NUM_SUBJECTS_IMGS, s)#特征差异度越小,被检测人脸与此样本更相似更匹配。
#print(pmin)
if pmin < 5000:
print(num) # num为当前最匹配的人的编号。
if num == 1 and pmin < 4800:
uart.write("u1")
elif num == 2 and pmin < 4700:
uart.write("u2")
elif num == 3:
uart.write("u3")
QT上位机设计:
- UI
由于之前没有做过上位机开发,所以借这次课设的时间浅浅了解了一下上位机的设计(QT C++开发)并且麻烦实验室学长帮忙一起调试了一晚上~
学长的上位机教学博客大家可以参考学长这个系列的博客进行QT上位机的开发学习!
以下是我实现的一些槽函数:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_Server=new QTcpServer(this);
m_Socket=NULL;
//m_Socket=new QTcpSocket(this);
m_sLocalIP="192.168.43.3";
m_sRemoteIP="192.168.0.111";
m_sPort=2333;
isListening=false;
isConnect=false;
ui->lineEdit_listenIP->setText(m_sLocalIP);
ui->lineEdit_listenPort->setText(QString("%1").arg(m_sPort));
heartbeatTimer=new QTimer(this);
heartbeatTimer->setInterval(4000);
connect(heartbeatTimer,SIGNAL(timeout()),this,SLOT(heartbeatTimeout()));
heartbeatMsg=new char('t');
listen();
}
MainWindow::~MainWindow()
{
delete ui;
}
bool MainWindow::listen()
{
if(m_Server==NULL||isListening)
return false;
if(!m_Server->listen(QHostAddress(m_sLocalIP),m_sPort)){
ui->pushButton_listen->setText("监听");
log("开启监听失败:"+m_Server->errorString());
return false;
}
ui->pushButton_listen->setText("停止监听");
isListening=true;
connect(m_Server, SIGNAL(newConnection()), this, SLOT(handleNewConnection()));
log(QString("开始监听 %1 %2")
.arg(m_Server->serverAddress().toString())
.arg(m_Server->serverPort()));
return true;
}
void MainWindow::handleNewConnection()
{
if(heartbeatTimer->isActive())
heartbeatTimer->stop();
m_bufferReceive.clear();
qDebug()<<m_Socket;
if(m_Socket!=NULL){
log(QString("连接前state %1").arg(m_Socket->state()));
if(m_Socket->state()==QAbstractSocket::SocketState::ConnectedState){
m_Socket->abort();
}
m_Socket->deleteLater();
delete m_Socket;
m_Socket=NULL;
}
qDebug()<<m_Socket;
m_Socket = m_Server->nextPendingConnection();
connect(m_Socket, SIGNAL(readyRead()), this, SLOT(handleSocketReadyRead()));
connect(m_Socket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(handleSocketError(QAbstractSocket::SocketError)));
connect(m_Socket,SIGNAL(disconnected()),this,SLOT(handlSocketDisconnect()));
log(QString("新连接 %1 %2")
.arg(m_Socket->peerAddress().toString())
.arg(m_Socket->peerPort()));
log(QString("连接后state %1").arg(m_Socket->state()));
isConnect=true;
heartbeatTimer->start();
ui->textBrowser_heartbeat->clear();
}
void MainWindow::handleSocketError(QAbstractSocket::SocketError)
{
log(QString("Socket错误 %1").arg(m_Socket->errorString()));
log(QString("错误处state %1").arg(m_Socket->state()));
}
void MainWindow::handleSocketReadyRead()
{
QTime time = QTime::currentTime();
// ui->textBrowser_log->append(name + time.toString());
ui->textBrowser_recive->append(m_Socket->readAll() + time.toString());
}
void MainWindow::User_discreminate(void){
// UserNode *temp = head->next;
QString name;
QString str1 = m_Socket->readAll();
if(str1 == "u1") name = "ZYQ";
else if(str1 == "u2") name = "CXY";
else if(str1 == "u3") name = "ZKA";
else if(str1 == "u4") name = "HCC";
else name = str1;
QTime time = QTime::currentTime();
ui->textBrowser_log->append(name + time.toString());
}
void MainWindow::on_pushButton_listen_clicked()
{
m_sLocalIP=ui->lineEdit_listenIP->text();
m_sPort=ui->lineEdit_listenPort->text().toShort();
if(isListening){
if(m_Socket!=NULL){
if(m_Socket->state()==QAbstractSocket::SocketState::ConnectedState){
m_Socket->disconnectFromHost();
}
}
m_Server->close();
ui->pushButton_listen->setText("监听");
disconnect(m_Server, SIGNAL(newConnection()), this, SLOT(handleNewConnection()));
log("停止监听");
isListening=false;
}
else{
listen();
}
}
void MainWindow::log(QString text)
{
ui->textBrowser_log->append(text);
}
void MainWindow::log_u(UserNode *user){
ui->textBrowser_log->append(user->name+user->time.toString());
}
void MainWindow::handlSocketDisconnect()
{
log(QString("Socket disconnect %1").arg(m_Socket->state()));
disconnect(m_Socket, SIGNAL(readyRead()), this, SLOT(handleSocketReadyRead()));
disconnect(m_Socket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(handleSocketError(QAbstractSocket::SocketError)));
disconnect(m_Socket,SIGNAL(disconnected()),this,SLOT(handlSocketDisconnect()));
isConnect=false;
if(heartbeatTimer->isActive())
heartbeatTimer->stop();
}
void MainWindow::heartbeatTimeout()
{
if(m_Socket!=NULL&&m_Socket->state()==QAbstractSocket::SocketState::ConnectedState){
//char *data="t";
// m_Socket->write(heartbeatMsg);
// if(m_Socket->waitForBytesWritten()){
// ui->textBrowser_heartbeat->append(QTime::currentTime().toString("HH:mm:ss zzz")
// +":"+heartbeatMsg);
// }
// else
// ui->textBrowser_heartbeat->append(QTime::currentTime().toString("HH:mm:ss zzz")
// +":写心跳失败");
}
}
void MainWindow::on_pushButton_send_clicked()
{
QString sSend = ui->lineEdit_send->text();
if(isConnect){
m_Socket->write(sSend.toLatin1());
if(m_Socket->waitForBytesWritten())
log("写数据成功");
else
log("写数据失败");
}
else
log("未连接");
// log("信息即将导出:");
// temp = head->next;
// for(int i = 0; i < list_len; i++){
// log_u(temp);
// temp = temp->next;
// }
// log("所有信息已导出");
}
0x03:系统通讯
主 & 从节点通信:
主从节点之间使用了NRF24L01模块实现通信,两个从节点使用了两个不同的地址,在主节点接收过程中只需要读取不同的缓冲区内容即可对收到的内容进行判断!!!,较为简单,就不多赘述这部分了。
主节点 & PC上位机:
主节点与PC上位机之间是通过ESP8266进行通信的(其中ESP8266配置成了透传模式)PC作为服务器,通过监听套接字的IP地址端口,就可以建立连接、数据传输,代码部分可以看如上代码。
本篇博客写的较为匆忙,主要是备考考研时间不是很充裕,之后有机会会详细展开作每部分模块的教程,敬请期待,如代码中有什么问题,大家可以在评论区进行指正,如果有想要工程文件的小伙伴可以私聊我,看到之后会及时回复的!!