STM32F103RCT6+GY90614测温模块+Openmv进行人脸识别
最终效果:
(1).两种测温模式:
a.准确测定28-48°C水温,与测温枪误差在0.5°C内。测试距离能到10CM。
b.准确测量人体头部与手腕处温度,与测温枪误差在0.5°C内。
(2).温度超过阈值,蜂鸣器响;否则不响。阈值在28-38°C内可调。
(3).准确识别三个队友。
(4).是否佩戴口罩,没有佩戴口罩蜂鸣器报警。
(5).现场学习与判断,在保证判别精度的情况下,能学习3个人。
采用正点原子mini板作为主控板
1.GY90614与STM32之间:I2C协议
2.Openmv与STM32之间:串口通信
3.OLED与STM32之间:I2C协议
无接触温度测量模块:
MLX90614与超声波测距,进行温度补偿。
原理:距离越远,测得的温度就越低,下面我们设某一时刻的距离为distance,温度为temperature,物体实际表面温度为t0,OLED屏显示温度为:t ;利用MATLAB拟合工具箱:t0作为因变量,distance与temperature作为自变量,进行拟合,比较拟合优度R-Square与均方误差MSE,确定最终的函数关系。
身份识别装置:
识别队友:采用Openmv提供的识别代码,寻找lbp
特征。
口罩判别:采用色块识别,识别普通口罩(蓝色医用口罩)。
现场学习与身份识别:现场拍10张照片,存入内存卡。
总体照片
硬件连接:
STM32与Openmv:
PA9 <——> USART3_Rx(P5)
PA10 <——>USART3_Tx(P4)
STM32与OLED:
PB6<——>SCL
PB7<——>SDA
STM32与MLX90614:
PB8<——>SCL
PB9<——>SDA
Mini板3个按键&外加5个按键:
mini:
WK_UP<——>测量体温或者物温
KEY0<——>温度阈值减1°C
KEY1<——>温度阈值加1°C
PC6<——>现场学习后,识别录入的人脸
PC7<——>现场学习,录入人脸
PC8<——>识别三个队友
PC9<——>是否戴口罩
软件部分:
KEIL5与OpenMVIDE
Face_recognition.c
#include "stm32f10x.h"
#include "usart.h"
#include "OLED_I2C.h"
#include "key.h"
#include "beef.h"
#include "stdio.h"
#include "delay.h"
void Face_recognition(void)
{
u8 key;
u8 t3,i;
u8 number=0;
unsigned char temp[20],x;
KEY_Init();
BEEF_Init();
delay_init();
usart_init(115200);
while(1)
{
display_2shenfenshibie();
OLED_ShowStr(0,2,"Stata:",2);
if(PCin(6)==0)
{ //delay_ms(300);
printf("3");
OLED_ShowStr(48+8,2,"compare",2);
break;
}
if(PCin(7)==0) /*按键0录取人脸*/
{
delay_ms(300);
printf("1"); /*通过串口发送1给Openmv*/
OLED_ShowStr(48+8,2,"study",2);
}
if(PCin(8)==0) /*按键1进行人脸对比*/
{
printf("2"); /*通过串口发送2给Openmv*/
OLED_ShowStr(48+8,2,"identify",2);
break;
}
if(PCin(9)==0)
{
delay_ms(300);
printf("4");
OLED_ShowStr(48+8,2,"isMask",2);
break;
}
}
while(1)
{
if(USART_RX_STA&0x8000)
{
number=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
for(t3=0;t3<20;t3++)
{
temp[t3]='\0';
}
for(t3=0;t3<number;t3++)
{
temp[t3]=USART_RX_BUF[t3];
while((USART1->SR&0X40)==0);//等待发送结束
}
/*判断是否没戴口罩
是:不响
否:蜂鸣器响1S
*/
if(temp[0]=='N')
{
BEEF=1;
delay_ms(1000);
BEEF=0;
}
OLED_CLS();
OLED_ShowStr(0,6,temp,1);
USART_RX_STA=0;
}
if(number>0)
{
break;
}
}
}
main.c
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "adc.h"
#include "mlx90614.h"
#include "stdio.h"
#include "beef.h"
#include "OLED_I2C.h"
#include "stm32f10x.h"
#include "key.h"
#include "exti.h"
#include "SRF05.h"
#include "string.h"
unsigned char Atemp=37,Mod=0,MOD=0; //Mod温度模式选择 MOD工作模式选择
int main(void)
{
unsigned char t[20],t1[20];
unsigned char len[20];
float temp,length;
delay_init();
SMBus_Init();//初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
usart_init(115200); //串口初始化为115200
LED_Init(); //LED端口初始化
BEEF_Init();
I2C_Configuration();
OLED_Init();
OLED_CLS();
OLED_ON();
EXTIX_Init(); //外部中断初始化
Hcsr05Init();
while(1)
{
if(MOD==0)
{
PCout(4)=1;
if(Mod==0){
OLED_ShowStr(0,2,"Mode: Object",2);
sprintf(t1,"%d",Atemp);
OLED_ShowStr(0,0,"Alarm Temp:",2);
OLED_ShowStr(96,0,t1,2);
OLED_ShowStr(96+16,0,"0",1);// 0
OLED_ShowStr(96+16+8,0,"C",2);// C
temp=SMBus_ReadTemp(); //读取温度
//length= Hcsr05GetLength();//读取距离
if(temp>Atemp)
BEEF=1;
else
BEEF=0;
sprintf(t,"%.1f",temp);
sprintf(len,"%4.1f",length);
OLED_ShowStr(0,4,"Temp:",2);
OLED_ShowStr(0+48,4,t,2);//温度值
OLED_ShowStr(0+48+48,4,"0",1);// 0
OLED_ShowStr(0+48+48+8,4,"C",2);// C
//OLED_ShowStr(0,6,"len:",2);
//OLED_ShowStr(0+40,6,len,2);
//OLED_ShowStr(0+40+56,6,"cm",2);
delay_ms(1000);
}
else{
OLED_ShowStr(0,2,"Mode: Body",2);
sprintf(t,"%d",Atemp);
OLED_ShowStr(0,0,"Alarm Temp:",2);
OLED_ShowStr(96,0,t,2);
OLED_ShowStr(96+16,0,"0",1);// 0
OLED_ShowStr(96+16+8,0,"C",2);// C
temp=SMBus_ReadTemp()+2.7; //读取温度
if(temp>Atemp)
BEEF=1;
else
BEEF=0;
sprintf(t,"%.1f",temp);
OLED_ShowStr(0,4,"Temp:",2);
OLED_ShowStr(48,4,t,2);//值
OLED_ShowStr(48+48,4,"0",1);// 0
OLED_ShowStr(48+48+8,4,"C",2);// C
delay_ms(1000);
}
}
else
{
PCout(4)=0;
Face_recognition();
}
delay_ms(500);
}
}
OpenMVIDE:
"""
作者: XJB LXP CLX
时间: 2020年11月20日22:00
代码功能:与STM32使用,进行stm32通过串口发送消息给openmv,openmv收到信息后进行人脸识别操作,并将结果传送回STM32单片机进行应答!
"""
import sensor, time, image, pyb,lcd
from pyb import UART
##定义全局变量
global flag_count
flag_count=0
RED_LED_PIN = 1
BLUE_LED_PIN = 3
uart = UART(3,115200,timeout_char=1000)
data=bytes([0x0d,0x0a])
#=====================================人脸识别:识别现场学习的人======================#
def face_recognition1():
sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.GRAYSCALE
sensor.set_framesize(sensor.B128X128) # or sensor.QQVGA (or others)
sensor.skip_frames(10) # Let new settings take affect.
sensor.skip_frames(time = 5000) #等待5s
NUM_SUBJECTS = flag_count #图像库中不同人数,一共4人
NUM_SUBJECTS_IMGS = 10 #每人有3张样本图片
img = sensor.snapshot()
d0 = img.find_lbp((0, 0, img.width(), img.height()))
#d0为当前人脸的lbp特征
img = None
pmin = 99999
num=0
for s in range(1, NUM_SUBJECTS+1):
dist = 0
print("1111")
for i in range(2, NUM_SUBJECTS_IMGS+1):
img = image.Image("singtown/temp%d/%d.pgm"%(s, i))
d1 = img.find_lbp((0, 0, img.width(), img.height()))
#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)#特征差异度越小,被检测人脸与此样本更相似更匹配。
if (dist/NUM_SUBJECTS_IMGS)<pmin:
pmin=(dist/NUM_SUBJECTS_IMGS)
#uart.write("%d"%pmin)
num=s
print(pmin)
print(num) # num为当前最匹配的人的编号。
if num==1:
uart.write("person1")
uart.write(data)
elif num==2:
uart.write("person2")
uart.write(data)
elif num==3:
uart.write("person3")
uart.write(data)
sensor.set_pixformat(sensor.RGB565) # or sensor.GRAYSCALE
sensor.set_framesize(sensor.QQVGA2) # Special 128x160 framesize for LCD Shield.
#============================人脸识别:识别三个队友=============================================#
def face_recognition():
sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.GRAYSCALE
sensor.set_framesize(sensor.B128X128) # or sensor.QQVGA (or others)
#sensor.set_windowing((92,112))
sensor.skip_frames(10) # Let new settings take affect.
sensor.skip_frames(time = 5000) #等待5s
NUM_SUBJECTS = 3 #图像库中不同人数,一共3人
NUM_SUBJECTS_IMGS = 10 #每人有10张样本图片
# 拍摄当前人脸。
img = sensor.snapshot()
d0 = img.find_lbp((0, 0, img.width(), img.height()))
#d0为当前人脸的lbp特征
img = None
pmin = 999999
num=0
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((0, 0, img.width(), img.height()))
#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)#特征差异度越小,被检测人脸与此样本更相似更匹配。
if (dist/NUM_SUBJECTS_IMGS)<pmin:
pmin=(dist/NUM_SUBJECTS_IMGS)
num=s
print(num) # num为当前最匹配的人的编号。
#uart.write(str(num))
if num==1:
uart.write("You are XJB!")
uart.write(data)
elif num==2:
uart.write("You are CLX!")
uart.write(data)
elif num==3:
uart.write("You are LXP!")
uart.write(data)
sensor.set_pixformat(sensor.RGB565) # or sensor.GRAYSCALE
sensor.set_framesize(sensor.QQVGA2) # Special 128x160 framesize for LCD Shield.
#================================现场学习=====================================#
def take_photos():
sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.GRAYSCALE
sensor.set_framesize(sensor.B128X128) # or sensor.QQVGA (or others)
sensor.skip_frames(10) # Let new settings take affect.
sensor.skip_frames(time = 2000)
global flag_count
flag_count= flag_count+1 #设置被拍摄者序号,第一个人的图片保存到s1文件夹,第二个人的图片保存到s2文件夹,以此类推。每次更换拍摄者时,修改num值。
n = 10 #设置每个人拍摄图片数量。
#连续拍摄n张照片,每间隔3s拍摄一次。
while(n):
#红灯亮
pyb.LED(RED_LED_PIN).on()
sensor.skip_frames(time = 2000) # Give the user time to get ready.等待3s,准备一下表情。
#红灯灭,蓝灯亮
pyb.LED(RED_LED_PIN).off()
pyb.LED(BLUE_LED_PIN).on()
#保存截取到的图片到SD卡
print(n)
sensor.snapshot().save("singtown/temp%s/%s.pgm" % (flag_count, n) ) # or "example.bmp" (or others)
n -= 1
pyb.LED(BLUE_LED_PIN).off()
print("Done! Reset the camera to see the saved image.")
print("\n第%s个人拍照"%(flag_count+1))
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA2)
#================================口罩判别标志位============================#
def face_mask_xianyan():
import sensor, image, time
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 200)
sensor.set_auto_gain(False) # must be turned off for color tracking
sensor.set_auto_whitebal(False) # must be turned off for color tracking
clock = time.clock()
threshold = [32, 64, -20, -10, -17, 0] # 口罩特征识别参数
print("Thresholds learned...")
print("Tracking colors...")
sensor.skip_frames(time = 1000)
flag=40 ##均值滤波:40张图片中有20张就判定为有口罩,否则判定为没有口罩
count=0
flag_mask=False
for i in range(flag):
img = sensor.snapshot()
listt=img.find_blobs([threshold], pixels_threshold=100, area_threshold=100, merge=True, margin=10)
print(listt)
if listt!=[]:
count=count+1
for blob in listt:
img.draw_rectangle(blob.rect())
img.draw_cross(blob.cx(), blob.cy())
lcd.display(img) # Take a picture and display the image.
sensor.skip_frames(time = 10)
if count >=20:
flag_mask=True
break
else:
lcd.display(img)
print(count)
if flag_mask:
wenzi="YES!MASK!"
uart.write("YES!MASK!")
uart.write(data)
else:
wenzi="No MASK!!!"
uart.write("No MASK!!!")
uart.write(data)
sensor.skip_frames(time = 3000)
return "reset",wenzi
#==================================main===============================#
if __name__=='__main__':
temp="reset"
wenzi=" "
while(1):
if temp=="reset":
sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.RGB565) # or sensor.GRAYSCALE
sensor.set_framesize(sensor.QQVGA2) # Special 128x160 framesize for LCD Shield.
lcd.init() # Initialize the lcd screen.
temp="OK"
if uart.any():
num1 =uart.readline().decode()
print(num1)
if(num1=='1'):
take_photos() #现场学习功能
elif(num1=='2'):
face_recognition() #人脸识别功能:识别三个队友
elif(num1=='3'):
face_recognition1() #人脸识别功能:识别现场学习的人员
elif(num1=='4'):
[temp,wenzi]=face_mask_xianyan() #口罩检测功能
##以下是画边框线
if temp=="OK":
img=sensor.snapshot()
img.draw_string(0, 0, wenzi, color = (0, 255, 0), scale = 2, mono_space = False,
char_rotation = 0, char_hmirror = False, char_vflip = False,
string_rotation = 0, string_hmirror = False, string_vflip = False)
img.draw_rectangle(15, 20, 100, 100, color = (255, 0, 0), thickness = 2, fill = False)
lcd.display(img) # Take a picture and display the image.
以下是部分测试效果:
不足之处:
由于篇幅所限,录入人脸部分的代码并未给出,有需要自行下载。
由于本人以及队友水平所限,在做此题之前从来没有接触过OpenMV,电赛四天三夜从零开始肝OpenMV,已经尽了最大努力。在此感谢队友的配合与努力,明年继续肝。
a.识别队友功能与背景影响太大,在录入照片的地方识别效果最佳,在其他环境识别效果不好,甚至出错。
b.利用距离补偿温度这一过程拟合效果不太理想,超声波测距传感器本身有所限制。
c.口罩部分我们只考虑了蓝色口罩,其实,其他颜色的口罩也能加进去。
改进方向:
识别队友部分可以先把人脸捕获,再找lbp特征,而不是直接找整张图片的lbp特征,这样可以减小环境因素的影响。