基于K210与STM32的人脸识别门禁 实现掉地仓储,人体雷达检测

本系统设计以Kendryte K210为人脸图像识别的核心芯片,以STM32为系统逻辑控制芯片,OLED显示控制信息,蓝牙通讯实现命令控制。人体雷达检测模块,当有人来时自动开启人脸识别。同时也可以使用按键控制人脸识别开启与关闭。

人脸识别
一、MaixPy IDE开发——人脸开发基于K210与STM32的人脸识别门禁 实现掉地仓储,人体雷达检测_第1张图片

1、配置步骤如下
从 maixhub (https://www.maixhub.com/index/index/detail/id/235.html)按照说明下载模型, 获得模型smodel, 就是 加密版本的kmodel,下载前需要注册一下账号基于K210与STM32的人脸识别门禁 实现掉地仓储,人体雷达检测_第2张图片基于K210与STM32的人脸识别门禁 实现掉地仓储,人体雷达检测_第3张图片

2.、程序理解

首先先是人脸模型的实现
总共用了三个模型, 分别是:

人脸检测模型, 这和前面的人脸检测使用的是同一个模型, 即找到人脸
人脸关键点检测模型,从前面找到的人脸中找到人脸的眼睛 鼻子和嘴巴的位置
人脸特征提取模型, 从一张人脸图片中得出一个特征值
步骤如下:
检测到人脸
裁出人脸,找到人脸的眼睛鼻子嘴巴, 这里裁成了128x128的图
把人脸图中的脸旋转到标准位置
用特征提取模型提取出人脸的特征值
有了前面的基础, 这里的程序就能看懂了,也就不再进行详细的阐述了,只不过是从之前的使用一个模型,变成了按照顺序分别使用三个模型,再加上一点简单的图像裁减和旋转处理,都是调用API,仔细看一遍代码就知道具体的细节是如何实现的了
三个模型文件可以选择放在外部的SD卡里面的(也可以把三个模型文件就放在flash中),具体操作步骤是,用SD卡的读卡器插到电脑

  if code: # 如果检测到人脸
                for i in code: # 迭代坐标框
                    # Cut face and resize to 128x128
                    a = img.draw_rectangle(i.rect()) # 在屏幕显示人脸方框
                    face_cut=img.cut(i.x(),i.y(),i.w(),i.h()) # 裁剪人脸部分图片到 face_cut
                    face_cut_128=face_cut.resize(128,128) # 将裁出的人脸图片 缩放到128 * 128像素
                    a=face_cut_128.pix_to_ai() # 将猜出图片转换为kpu接受的格式
                    #a = img.draw_image(face_cut_128, (0,0))
                    # Landmark for face 5 points
                    fmap = kpu.forward(task_ld, face_cut_128) # 运行人脸5点关键点检测模型
                    plist=fmap[:] # 获取关键点预测结果
                    le=(i.x()+int(plist[0]*i.w() - 10), i.y()+int(plist[1]*i.h())) # 计算左眼位置, 这里在w方向-10 用来补偿模型转换带来的精度损失
                    re=(i.x()+int(plist[2]*i.w()), i.y()+int(plist[3]*i.h())) # 计算右眼位置
                    nose=(i.x()+int(plist[4]*i.w()), i.y()+int(plist[5]*i.h())) #计算鼻子位置
                    lm=(i.x()+int(plist[6]*i.w()), i.y()+int(plist[7]*i.h())) #计算左嘴角位置
                    rm=(i.x()+int(plist[8]*i.w()), i.y()+int(plist[9]*i.h())) #右嘴角位置
                    a = img.draw_circle(le[0], le[1], 4)
                    a = img.draw_circle(re[0], re[1], 4)
                    a = img.draw_circle(nose[0], nose[1], 4)
                    a = img.draw_circle(lm[0], lm[1], 4)
                    a = img.draw_circle(rm[0], rm[1], 4) # 在相应位置处画小圆圈
                    # align face to standard position
                    src_point = [le, re, nose, lm, rm] # 图片中 5 坐标的位置
                    T=image.get_affine_transform(src_point, dst_point) # 根据获得的5点坐标与标准正脸坐标获取仿射变换矩阵
                    a=image.warp_affine_ai(img, img_face, T) #对原始图片人脸图片进行仿射变换,变换为正脸图像
                    a=img_face.ai_to_pix() # 将正脸图像转为kpu格式
                    #a = img.draw_image(img_face, (128,0))
                    del(face_cut_128) # 释放裁剪人脸部分图片
                    # calculate face feature vector
                    fmap = kpu.forward(task_fe, img_face) # 计算正脸图片的196维特征值
                    feature=kpu.face_encode(fmap[:]) #获取计算结果
                    reg_flag = False
                    scores = [] # 存储特征比对分数
                    for j in range(len(record_ftrs)): #迭代已存特征值
                        score = kpu.face_compare(record_ftrs[j], feature) #计算当前人脸特征值与已存特征值的分数
                        scores.append(score) #添加分数总表
                    max_score = 0
                    index = 0
                    for k in range(len(scores)): #迭代所有比对分数,找到最大分数和索引值
                        if max_score < scores[k]:
                            max_score = scores[k]
                            index = k
                    if max_score > ACCURACY: # 如果最大分数大于85, 可以被认定为同一个人
                        a = img.draw_string(i.x()-30, i.y(), ("%s :%2.1f" % (names[index], max_score)), color=(0, 255, 0), scale=2)

人体检测模块可以利用IO口来进行设置,由于大部分人体雷达是IO口输出高低电平来实现是否存在人。所以在代码 if code:前面加个

        if human.value():来读取是否有人存在

人脸仓储模块代码如下  可以实现蓝牙发送指令仓储人脸数据也可以使用按键仓储人脸数据

fm.register(11,fm.fpioa.UART1_TX)#串口引脚映射
fm.register(10,fm.fpioa.UART1_RX)#这两个引脚是可以任意修改的

com = UART(UART.UART1, 9600, 8,1,0,timeout=500, read_buf_len=4096)#构建串口对象

#定时器中断回调函数
tem = ''
b = []
check = 0  #存储
save = 0   #临时存储
switch = 0 #切换
key=0
def on_timer(timer):  #回调函数
    global check
    global save
    data = []
    data = com.read()
    if data!=None:
        print(data)
        check = 1#代表存储人脸特征
        if data == b'A':
            save = 0  #不存到SD卡中
        elif data == b'C':
            save = 1  #存到SD卡中
        print(data)
#定时器中断初始化
tim = Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_ONE_SHOT, period=500,unit=Timer.UNIT_MS,callback=on_timer, arg=on_timer,start=False) #定时器初始化
def set_key_state_left(*_):    #按键仓储模块
    global key_left
    key_left = True
    utime.sleep_ms(50)

key_gpio.irq(set_key_state_left, GPIO.IRQ_RISING, GPIO.WAKEUP_NOT_SUPPORT)#按键中断函数 当key_gpio被按下时执行set_key_state_left函数

feature_file_exists = 0
for v in os.ilistdir('/sd'):#检查sd卡中的密钥目录或文件.sd卡应格式化为fat32
    if v[0] == 'features.txt' and v[1] == 0x8000:#0x8000 is file
        feature_file_exists = 1

record_ftr=[] #空列表 用于存储当前196维特征
record_ftrs=[] #空列表 用于存储按键记录下人脸特征, 可以将特征以txt等文件形式保存到sd卡后,读取到此列表,即可实现人脸断电存储。
names = ['MR.1', 'MR.2', 'MR.3', 'Mr.4', 'Mr.5', 'Mr.6', 'Mr.7', 'Mr.8', 'Mr.9' , 'Mr.10'] # 人名标签,与上面列表特征值一一对应。
reco = ''
record = []
def save_feature(feat):#仓储数据到SD卡中
    with open('/sd/features.txt','a') as f:
        record =ubinascii.b2a_base64(feat)
        f.write(record)
st = ''
if(feature_file_exists):
    print("start")
    with open('/sd/features.txt','rb') as f:
        s = f.readlines()
        print(len(s))
        for line in s:
            record_ftrs.append(bytearray(ubinascii.a2b_base64(line)))

 这里注意:如果把模型文件放到SD卡中,记得放进去的模型文件名和脚本文件里面的名字取成一样的。
如何实现断电存储?
主要是将人脸计算出来的特征以txt等文件形式保存到sd卡后,读取到数组中,每一个计算出来的特征值与数组中的名字一一对应,即可实现断电存储人脸信息。使用SD卡这种方法需要在SD卡中建立features.txt文件。
两者的串口通信?
STM32和K210之间我采取串口通信的方式,STM32那边我配置的是串口2,波特率设置成115200,K210部分设置TX和RX引脚,在调用串口初始化函数即可,使用杜邦线连接对应的引脚,这里注意串口通信要反接,TX脚连接的是RX脚,RX脚连接的是TX脚。
如果是实现k210和stm32的通信需要注意以下几点。1、串口必须进行反接,如果多次连接没有反应,可以更换连接线试试。2、程序应当仔细检查,硬件的故障概率较小,通常bug是在程序处。可以先将两个板卡连接到电脑处,分别用电脑串口助手进行测试,通过测试后将两者进行连接。3、两边发送数据的格式是否一致,比如是否均为10进制数据,或者均为ascii值。

蓝牙通讯软件可以使用WxBit 图形化编程WxBit 图形化编程来实现

通过简单的模块化设计实现简单的蓝牙通讯功能基于K210与STM32的人脸识别门禁 实现掉地仓储,人体雷达检测_第4张图片

也可以使用蓝牙通讯软件来实现通讯基于K210与STM32的人脸识别门禁 实现掉地仓储,人体雷达检测_第5张图片

蓝牙通讯部分代码如下

void USART2_Init(u32 bound)
{
   //GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
 
	
	/*  配置GPIO的模式和IO口 */
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;//TX			   //串口输出
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;	    //复用推挽输出
	GPIO_Init(GPIOA,&GPIO_InitStructure);  /* 初始化串口输入IO */
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;//RX			 //串口输入
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;		  //模拟输入
	GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化GPIO */
	

   //USART2 初始化设置
	USART_InitStructure.USART_BaudRate = bound;//波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
	USART_Init(USART2, &USART_InitStructure); //初始化串口2
	
	USART_Cmd(USART2, ENABLE);  //使能串口2 
	
	USART_ClearFlag(USART2, USART_FLAG_TC);
		
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启相关中断

	//Usart2 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;//串口2中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;		//子优先级2
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器、	
}

/*******************************************************************************
* 函 数 名         : USART2_IRQHandler
* 函数功能		   : USART2中断函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/ 
void USART2_IRQHandler(void)                	//串口2中断服务程序
{
	u8 r;
	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //接收中断
	{
		r =USART_ReceiveData(USART2);//(USART2->DR);	//读取接收到的数据
//				USART_SendData(USART1,r);
		while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
		if(r=='1')//接收到1,点亮LED
			{
				open(10);	//当接受到1时执行以下函数
				printf("开锁成功\r\n");
			}	
			else if(r=='A'){
					USART_SendData(USART1,'A');
			}
			else if(r=='C'){
					USART_SendData(USART1,'C');
			}

			else{play_music(1);}
			USART2_RX_STA=0;	
		
	
	} 
	
} 	 

你可能感兴趣的:(stm32,嵌入式硬件,单片机)