本系统设计以Kendryte K210为人脸图像识别的核心芯片,以STM32为系统逻辑控制芯片,同时加入语音模块、触摸屏和RFID模块。系统共四个功能模式,分别是人脸识别、口罩检测、特征学习、门禁通卡。人脸识别采用基于YOLO的人脸检测算法和特征提取算法,同时将模型部署至K210运行。系统可对人脸特征进行学习,实现了对人脸信息的实时录入,方便了门禁系统的管理。硬件方面系统还加入了RFID模块、语音播报模块、TFT触摸屏等。RFID模块主要实现了对用户ID卡识别,并将ID卡信息通过串口传送到STM32进行用户信息匹配。语音模块和触摸屏组成了系统人机交互部分,系统界面简洁操作简单,同时具备语音播报功能,智能化程度高。
STM32f103zet6、Maixduino、RFID模块、JZ-TRIG语音播报、触摸屏、LCD、摄像头、角度舵机、锂电池、稳压管(两个,一个给系统供电,一个单独给舵机供电)
软件工具:MDK、Maixpy IDE、串口助手(调试用)
程序组成:人脸检测程序、人脸五点特征点提取、口罩检测程序、特征学习程序、RFID模块识别程序
人脸识别、特征学习
代码参考Maixpy论坛一些前辈的经验,请大家多多支持Maixpy。
def face_recognize():
global flag_enter
global task_mask,task_ld,task_fe
global sum_3
global sum_4
global now_mode
img = sensor.snapshot()
code = kpu.run_yolo2(task_fd, img)
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_128 = face_cut.resize(128, 128)
a = face_cut_128.pix_to_ai()
# a = img.draw_image(face_cut_128, (0,0))
# Landmark for face 5 points
fmap = kpu.forward(task_ld, face_cut_128)
plist = fmap[:]
le = (i.x() + int(plist[0] * i.w() - 10), i.y() + int(plist[1] * i.h()))
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]
T = image.get_affine_transform(src_point, dst_point)
a = image.warp_affine_ai(img, img_face, T)
a = img_face.ai_to_pix()
# a = img.draw_image(img_face, (128,0))
del (face_cut_128)
# calculate face feature vector
fmap = kpu.forward(task_fe, img_face)
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 > 85:
a = img.draw_string(i.x(), i.y(), ("%s :%2.1f" % (
names[index], max_score)), color=(0, 255, 0), scale=2)
sum_3+=1
if sum_3 == 10:
uart_A.write(b'i')
print(sum_3)
sum_3=0
sum_4=0
now_mode = 0
else:
a = img.draw_string(i.x(), i.y(), ("X :%2.1f" % (
max_score)), color=(255, 0, 0), scale=2)#串口数据预留
sum_4+=1
if sum_4 == 10:
uart_A.write(b'j')
print(sum_4)
sum_3=0
sum_4=0
now_mode = 0
#特征值学习
if flag_enter==1 :
print('feature study')
#features_data = uart_A.read()
#if features_data:
#stu_name = features_data.decode('utf-8')
#print("stu_name=",stu_name)
with open("/sd/features.txt", "a") as f:
f.write(str(feature)) #信息写入SD卡
record_ftrs.append(feature) #人脸特征追加到record_ftrs列表
#names.append(stu_name) #追加到姓名列表
f.write("\n")
f.close()
flag_enter=0
uart_A.write(b'i')
now_mode = 0
sum_3=0
sum_4=0
#写入sd卡
break
口罩检测
def mask_recognize():
#print('Hello')
global sum_1
global sum_2
global now_mode
code = kpu.run_yolo2(task_mask, img)
if code:
totalRes = len(code)
for item in code:
confidence = float(item.value())
itemROL = item.rect()
classID = int(item.classid())
if confidence < 0.52:
a = img.draw_rectangle(itemROL, color=color_B, tickness=5)
continue
#有口罩
if classID == 1 and confidence > 0.76:
a = img.draw_rectangle(itemROL, color_G, tickness=5)
if totalRes == 1:
drawConfidenceText(img, (0, 0), 1, confidence)
sum_1+=1
if sum_1 == 10:
uart_A.write(b'i')
sum_1=0
sum_2=0
now_mode = 0
blank()
break
#无口罩
else:
a = img.draw_rectangle(itemROL, color=color_R, tickness=5)
if totalRes == 1:
drawConfidenceText(img, (0, 0), 0, confidence)
sum_2+=1
if sum_2 == 10:
uart_A.write(b'j')
sum_1=0
sum_2=0
now_mode = 0
blank()
break
RFID程序
unsigned char ReadTagId(unsigned char *idout)
{
unsigned char status;
unsigned char i;
unsigned char pkt[12];
status = STATUS_ERR;
if(Uart1RxDataConut > 0) //判断串口是否接收到数据
{
Delay(150000); //等待串口接收完成,根据视情况调整延时
if(Rx1Flag == 1)//判断串口是否接收到一帧完整数据
{
Rx1Flag = 0;
for(i=0;i<12;i++) //自动读卡号数据包固定为12字节
{
pkt[i] = Uart1RxBuf[i]; //将串口接收数组内的数据复制到pkt数组中
}
if(RxCheckSum(pkt,12) == STATUS_OK) //判断校验和是否正确
{
if(pkt[4] == STATUS_OK) //判断是否正确的读到卡
{
//04 0C 02 20 00 04 00 45 96 B7 8A 3F
if((pkt[0] == 0x04)&&(pkt[1] == 0x0C)&&(pkt[2] == 0x02)&&(pkt[3] == 0x20))//对数据包进行判断
{
for(i=0;i<4;i++)//获取4字节卡号
{
idout[i] = pkt[i+7];//从数组的第7个字节开始为卡号,共4字节
}
if((idout[0] == 0xFD)&&(idout[1] == 0x68)&&(idout[2] == 0x01)&&(idout[3] == 0xAF)&&(Function4_Flag == 1))//对数据包进行判断
{
GPIO_ResetBits(GPIOB,GPIO_Pin_0);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
jpgDisplay("0:/three.jpg");
Delay(4000000);
TIM_SetCompare2(TIM2, 2500);
Delay(20000000);
TIM_SetCompare2(TIM2, 500);
jpgDisplay("0:/one.jpg");
Function4_Flag = 0;
}
else if((idout[0] == 0x5D)&&(idout[1] == 0xD2)&&(idout[2] == 0x56)&&(idout[3] == 0xAF)&&(Function4_Flag == 1))//对数据包进行判断
{
GPIO_ResetBits(GPIOB,GPIO_Pin_0);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
jpgDisplay("0:/three.jpg");
Delay(4000000);
TIM_SetCompare2(TIM2, 2500);
Delay(20000000);
TIM_SetCompare2(TIM2, 500);
jpgDisplay("0:/one.jpg");
Function4_Flag = 0;
}
else if((idout[0] == 0x4D)&&(idout[1] == 0xA0)&&(idout[2] == 0xFC)&&(idout[3] == 0xAE)&&(Function4_Flag == 1))//对数据包进行判断
{
GPIO_ResetBits(GPIOB,GPIO_Pin_0);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
jpgDisplay("0:/three.jpg");
Delay(4000000);
TIM_SetCompare2(TIM2, 2500);
Delay(20000000);
TIM_SetCompare2(TIM2, 500);
jpgDisplay("0:/one.jpg");
Function4_Flag = 0;
}
else if((idout[0] == 0x68)&&(idout[1] == 0x54)&&(idout[2] == 0xED)&&(idout[3] == 0x3D)&&(Function4_Flag == 1))//对数据包进行判断
{
GPIO_ResetBits(GPIOB,GPIO_Pin_0);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
jpgDisplay("0:/three.jpg");
Delay(4000000);
TIM_SetCompare2(TIM2, 2500);
Delay(20000000);
TIM_SetCompare2(TIM2, 500);
jpgDisplay("0:/one.jpg");
Function4_Flag = 0;
}
else if((idout[0] == 0x87)&&(idout[1] == 0x84)&&(idout[2] == 0xED)&&(idout[3] == 0x3D)&&(Function4_Flag == 1))//对数据包进行判断
{
GPIO_ResetBits(GPIOB,GPIO_Pin_0);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
jpgDisplay("0:/three.jpg");
Delay(4000000);
TIM_SetCompare2(TIM2, 2500);
Delay(20000000);
TIM_SetCompare2(TIM2, 500);
jpgDisplay("0:/one.jpg");
Function4_Flag = 0;
}
else if((idout[0] == 0xB6)&&(idout[1] == 0xDE)&&(idout[2] == 0xDC)&&(idout[3] == 0x6B)&&(Function4_Flag == 1))//对数据包进行判断
{
GPIO_ResetBits(GPIOB,GPIO_Pin_0);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
jpgDisplay("0:/three.jpg");
Delay(4000000);
TIM_SetCompare2(TIM2, 2500);
Delay(20000000);
TIM_SetCompare2(TIM2, 500);
jpgDisplay("0:/one.jpg");
Function4_Flag = 0;
}
else
{
if(Function4_Flag == 1)
{
GPIO_SetBits(GPIOB,GPIO_Pin_0);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
jpgDisplay("0:/four.jpg");
TIM_SetCompare2(TIM2, 500);
Delay(20000000);
jpgDisplay("0:/one.jpg");
Function4_Flag = 0;
}
}
status = STATUS_OK; //成功返回0
}
}
}
}
for(i=0;i<Uart1RxDataConut;i++)//清空串口接收数组
{
Uart1RxBuf[i] = 0x00;
}
Uart1RxDataConut = 0x00;
}
return status; //失败返回1
}
1、人脸识别和口罩检测都是图像识别类的,在检测环境不良或者模型精度无法得到保证时,应该加入一个检测次数判断,比如设定识别成功10次才算成功,或者识别正确或者错误两者谁先达到这个阈值再判定最终结果。这个在我的人脸和口罩程序中均有体现,欢迎讨论。
2、STM32和K210的通信我没有采用通信协议,因为发送的字符串为简单的几个字母,且通信线长度很短,一般不用担心通信出错问题。
3、如果想要集成度高一点,大家可以采用串口屏,控制也可以用K210做,不过我用K210不够熟练,所以没采用它来干这活。
4、舵机最好跟控制系统分开供电,这样工作时不会影响系统的电压。(稳压管一定要调准确,不要接反。)
我的这个课题属于视觉类,自己在一些底层识别算法的基础相较薄弱,大家如果跟我一样的话,建议先复刻一下别人的工程,再学习相关的识别算法,本人学习的是YOLO 相关的检测方法,内容大多跟卷积神经网络相关,数学要求较高,大家如果有什么学习的好办法欢迎分享鸭。这个工程大家如果有需要的可以联系我邮箱[email protected]