B42 - 基于STM32单片机与K210人脸识别模块的儿童滞留小车报警系统

任务

汽车正常运行状态,则一切正常,系统正常工作;若汽车处于停滞锁车状态,则本系统开始工作。此时就会监测在无大人的情况下,是否车内还有小孩,若有小孩,则当车内温度等升高到一定阈值的时候,就会进行报警提醒,避免小孩被遗忘或留在车内的现象。
实现目标以及方法:
①:本设计最主要的就是监测车内有无人员,利用视觉的技术对该情况监测。本系统利用摄像头采集车内图像,(后期可修改,传输方式)千兆网口传输图像到电脑进行图像处理。图像处理开发平台可以采用深度学习神经网络,人工智能的方法,用于获取人脸识别即可。最终,决定采取何种方式由实现效果来判定。
②:利用STM32单片机做另外一个主控系统,实现控制的功能。二氧化碳传感器,红外温度检测,(此处是利用传感器模块DS18B20)蜂鸣器报警模块。另外,与本设计第一主控MCU(STM32)进行通信,因此需要利用串口和单片机之间进行信息数据的交互。

项目总结:
主要功能是在驻车的时候用人脸检测技术判断车内有无人员,同时检测车内空气质量,若车内空气质量不适合用户待的话会自动报警以提醒人员下车。其中主要分为两部分工作,第一部分是是机器视觉人脸检测,我采用了当前国内嘉楠科技公司生产的K210模组,它采用RISC-V架构,达到了1TOPS的算力,功耗仅为300mW,能很好的满足机器视觉应用的需求,本次我主要应用这款芯片做人脸检测,采用yolo算法,将训练好的人脸检测模型部署到K210模块上,然后将检测结果通过串口输出检测结果给STM32单片机。第二部分是STM32单片机检测和控制部分,STM32单片机这边使用了TVOC气体传感器来检测车内空气质量,如甲醛浓度、CO2浓度、PM2.5浓度,若车内环境比较差,则单片机控制语音模块进行语音播报,以达到提醒用户的功能。所有信息用了OLED屏幕进行显示。

实物

B42 - 基于STM32单片机与K210人脸识别模块的儿童滞留小车报警系统_第1张图片
B42 - 基于STM32单片机与K210人脸识别模块的儿童滞留小车报警系统_第2张图片

K210模块

非常好用的深度学习嵌入式部署模组,虽然速度不算高,可分配资源也不多,但做简单应用还是可以的,且官方例程相对比较全,开源网站对于micropython相关的资料也多,故值得学习。

我对官方给的参考例程改动不大,就是结合了串口,将人脸识别到的结果进行输出。串口输出格式为自定义,为了尽量简单,没有做校验,毕竟只是实验,没有工业应用。

#实验名称:人脸检测
#开发板官方:01Studio
#更改设计:燃烧电子
'''
运行代码,将摄像头正对自己,可以看到将自己的脸检测出来。
系统默认摄
像头是前置。注要将开发板竖起来!!!,即 LCD横屏显示。
'''
import sensor,lcd,time
import KPU as kpu
from machine import UART,Timer
from fpioa_manager import fm

#映射串口引脚
fm.register(6, fm.fpioa.UART1_RX, force=True)
fm.register(7, fm.fpioa.UART1_TX, force=True)

#初始化串口
uart = UART(UART.UART1, 115200, read_buf_len=4096)
#uart.write('Hello 01Studio!')

#设置摄像头
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
#sensor.set_vflip(0)    #设置摄像头 0前置/1后置

lcd.init() #LCD初始化
lcd.rotation(2) #屏幕方向设定,【dir】取值范围[0-3],从03 依顺时钟旋转。

clock = time.clock()

#task = kpu.load(0x300000) #需要将模型(face.kfpkg)烧写到flash的 0x300000 位置
task = kpu.load("/sd/facedetect.kmodel") #模型SD卡上

#模型描参数
anchor = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658, 5.155437, 6.92275, 6.718375, 9.01025)

#初始化yolo2网络
a = kpu.init_yolo2(task, 0.5, 0.3, 5, anchor)

#计数变量
Counter=0
#定时器回调函数
def fun(tim):
    global Counter
    Counter = Counter + 1
#定时器0初始化,周期1秒
tim = Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PERIODIC, period=1000, callback=fun)

num=0
while(True):
    clock.tick()
    img = sensor.snapshot()
    code = kpu.run_yolo2(task, img) #运行yolo2网络

    #识别到人脸就画矩形表示
    if code:
        for i in code:
            print(i)
            b = img.draw_rectangle(i.rect())
            num=num+1;
            #print(type(i))
            #print(i.objnum())
            #print(type(i.objnum()))
            #print(type(str(i.objnum())))
    #LCD显示
    lcd.display(img)

    print(clock.fps())   #打印FPS

    if Counter:  #1S发送一次,不能太快了
        Counter=0
        uart.write('$N'+str(num)+'@') #数据回传
    num=0;

B42 - 基于STM32单片机与K210人脸识别模块的儿童滞留小车报警系统_第3张图片
B42 - 基于STM32单片机与K210人脸识别模块的儿童滞留小车报警系统_第4张图片
B42 - 基于STM32单片机与K210人脸识别模块的儿童滞留小车报警系统_第5张图片

原理图

B42 - 基于STM32单片机与K210人脸识别模块的儿童滞留小车报警系统_第6张图片

资源使用

B42 - 基于STM32单片机与K210人脸识别模块的儿童滞留小车报警系统_第7张图片

STM32核心板部分

主程序

/*******************************************************************************

\* 文件名称:基于STM32单片机与K210人脸识别模块的儿童滞留小车报警系统

\* 实验目的:1.

\* 2.

\* 程序说明:完整程序Q:277 227 2579;@: itworkstation@ hotmail.com

\* 日期版本:本项目分享关键细节,熟悉使用单片机的可做参考代码。完整讲解+源代码工程可联系获取,可定制。

*******************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include "main.h"  
#include "ds18b20.h"  
#include "usart1.h" 
#include "usart2.h" 
#include "JQ_8400.h" 
#include "key.h"
#include "key01.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/**
  * @说明     主函数
  * @参数     None 
  * @返回值   None
  */
STRUCT_NVICPriority NVICPriority_Structure;
int main(void)
{
	short DS18B20_temp;   //温度值	16位有符号   

	LCD_Show.CO2H =600;
	LCD_Show.TH = 280;
	
	LED_Init();
	LED_Control(OFF);
	OLED_Init();
	LCD_Display();
	
	NVICPriority_Structure.Usart1 = 1;
	NVICPriority_Structure.Usart2 = 2;
	USART1_Init();
	USART2_Init();  
	
	JQ8400_Init();     
	JQ8400_6x00SendCmd(SET_SOUND,0x1A);		   //音量,十六进制输入  
	JQ8400_6x00SendCmd(SELECTE_MODE,MODE_DQTZ);		   //设定循环模式,单曲停止
	JQ8400_6x00SendCmd(SELECTE_PLAY,2);	 //播放曲目2
	
	Delay_ms(1000);	
	LCD_Menu = LCD_SHOWMESSAGE;LCD_refresh = TRUE;LCD_Display();	
	while(DS18B20_Init())	//DS18B20初始化	检测是否存在 
	{			
		Delay_ms(20);
	}	
	do
	{
		DS18B20_temp=DS18B20_Get_Temp();  //DS18B20程序的部分
	}while(DS18B20_temp == 850);
	
	
	Key01_Init();
	Key_Init();
	while(1){		
		Proc_DS18B20();
		Proc_PyCv(); 
		Proc_TVOC();
		Key01_Scan();
		Proc_Key();
		LCD_Display();			
	}
}



空气质量传感器

#ifndef __TVOC_h
#define __TVOC_h

#include "stm32f10x.h"
#include "Def_config.h"

#define TVOC_Buffer_Length 100
#define TVOC_Length 5

typedef struct
{
	char TVOC_Rec_Buffer[TVOC_Buffer_Length];
	ENUM_JUDGE isGetData;		//是否获取到数据
	ENUM_JUDGE isParseData;	//是否解析完成
	u16 TVOC;		//字符串形式存储   TVOC浓度
	u16 CO2;		//字符串形式存储   CO2浓度
	u16 PA;		//字符串形式存储     甲醛浓度  甲醛的英文字母缩写是PA
	ENUM_JUDGE isUsefull;		//信息是否有效
} _TVOCData;

extern _TVOCData TVOC_Data;

void TVOC_RecHandle(u8 Res);
void parseTVOCBuffer(void);
void TVOC_Clear_Data(void);


#endif 




#include "TVOC.h" 
#include 
#include 

_TVOCData TVOC_Data;
void TVOC_Clear_Data(void)
{
	TVOC_Data.isGetData = FALSE;
	TVOC_Data.isParseData = FALSE;
	TVOC_Data.isUsefull = FALSE;
	memset(TVOC_Data.TVOC_Rec_Buffer, 0, TVOC_Buffer_Length);      //清空
	TVOC_Data.TVOC = 0;
	TVOC_Data.CO2 = 0;
	TVOC_Data.PA = 0;
}
char  TVOC_RX_BUF[TVOC_Buffer_Length]; //接收缓冲,最大TVOC_Buffer_Length个字节.末字节为换行符 
u8 point2Tvoc = 0;
void TVOC_RecHandle(u8 Res)
{
	if(Res == 0x5f)
	{
		point2Tvoc = 0;	
	}
	TVOC_RX_BUF[point2Tvoc++] = Res;
	if(point2Tvoc >= 8)									   
	{
		memset(TVOC_Data.TVOC_Rec_Buffer, 0, TVOC_Buffer_Length);      //清空
		memcpy(TVOC_Data.TVOC_Rec_Buffer, TVOC_RX_BUF, point2Tvoc); 	//保存数据
		TVOC_Data.isGetData = TRUE; 

		memset(TVOC_RX_BUF, 0, TVOC_Buffer_Length);      //清空
	}		
	if(point2Tvoc >= TVOC_Buffer_Length)
	{
		point2Tvoc = TVOC_Buffer_Length;
	}	
}
//int HexToDec(u16 hexNum)
//{

//}
void parseTVOCBuffer(void)
{
	u16 TVOC=0,CO2=0,PA=0;
	if (TVOC_Data.isGetData)
	{
		TVOC_Data.isGetData = FALSE;
		if(TVOC_Data.TVOC_Rec_Buffer[0] == 0x5f)
		{
			TVOC_Data.isParseData = TRUE;
			TVOC_Data.isUsefull = TRUE;
			
			TVOC = (u16)TVOC_Data.TVOC_Rec_Buffer[1];
			TVOC = (TVOC<<8) | TVOC_Data.TVOC_Rec_Buffer[2];

			PA = (u16)TVOC_Data.TVOC_Rec_Buffer[3];
			PA = (PA<<8) | TVOC_Data.TVOC_Rec_Buffer[4];
			
			CO2 = (u16)TVOC_Data.TVOC_Rec_Buffer[5];
			CO2 = (CO2<<8) | TVOC_Data.TVOC_Rec_Buffer[6];
			
			TVOC_Data.TVOC = TVOC;
			TVOC_Data.PA = PA;
			TVOC_Data.CO2 = CO2;
		}
		else
		{
			TVOC_Clear_Data();
		}
	}
}









python CV

k210和STM32通信驱动

#ifndef __PyCv_H
#define __PyCv_H

#include "stm32f10x.h"
#include "Def_config.h"

#define PyCv_Buffer_Length 50
#define PyCv_Length 5

typedef struct
{
	char PyCv_Rec_Buffer[PyCv_Buffer_Length];
	ENUM_JUDGE isGetData;		//是否获取到数据
	ENUM_JUDGE isParseData;	//是否解析完成
	char PersonNum[PyCv_Length];		//字符串形式存储
	ENUM_JUDGE isUsefull;		//信息是否有效
} _PyCvData;

extern _PyCvData PyCv_Data;

void PyCv_RecHandle(u8 Res);
void PyCv_Clear_Data(void);
void parsePyCvBuffer(void);

#endif 


#include "PyCv.h"
#include 
#include 

_PyCvData PyCv_Data;
char  PyCv_RX_BUF[PyCv_Buffer_Length]; //接收缓冲,最大PyCv_Buffer_Length个字节.末字节为换行符 
u8 point2 = 0;
void PyCv_RecHandle(u8 Res)
{
	if(Res == '$')
	{
		point2 = 0;	
	}
	PyCv_RX_BUF[point2++] = Res;
	if(Res == '@' || point2 >3)									   
	{
		memset(PyCv_Data.PyCv_Rec_Buffer, 0, PyCv_Buffer_Length);      //清空
		memcpy(PyCv_Data.PyCv_Rec_Buffer, PyCv_RX_BUF, point2); 	//保存数据
		PyCv_Data.isGetData = TRUE; 
		point2 = 0;
		memset(PyCv_RX_BUF, 0, PyCv_Buffer_Length);      //清空
	}		
	if(point2 >= PyCv_Buffer_Length)
	{
		point2 = PyCv_Buffer_Length;
	}	
}
u8 PyCv_Find(char *a)                   // 串口命令识别函数
{ 
    if(strstr(PyCv_Data.PyCv_Rec_Buffer,a)!=NULL)
	    return 1;
	else
		return 0;
}
void PyCv_Clear_Data(void)
{
	PyCv_Data.isGetData = FALSE;
	PyCv_Data.isParseData = FALSE;
	PyCv_Data.isUsefull = FALSE;
	memset(PyCv_Data.PersonNum, 0, PyCv_Length);      //清空
	memset(PyCv_Data.PyCv_Rec_Buffer, 0, PyCv_Buffer_Length);      //清空
}
void parsePyCvBuffer(void)
{
	char *subString;
	char *subStringNext;
	if (PyCv_Data.isGetData)
	{
		PyCv_Data.isGetData = FALSE;
		if(PyCv_Find("$N"))
		{
			PyCv_Data.isParseData = TRUE;
			PyCv_Data.isUsefull = TRUE;
			
			subString = strstr(PyCv_Data.PyCv_Rec_Buffer, "N"); 
			subString++;
			subStringNext = strstr(PyCv_Data.PyCv_Rec_Buffer, "@");
			memcpy(PyCv_Data.PersonNum, subString, subStringNext - subString);	
		}
		else
		{
			PyCv_Data.isParseData = FALSE;
			PyCv_Data.isUsefull = FALSE;
		}
	}
}





DS18B20温度

#ifndef __DS18B20_H
#define __DS18B20_H 

#include "main.h"
//	 
//DS18b20数字温度传感器驱动代码	 

//GPIOX_CLH 端口配置高寄存器 
//31 30  29 28   27 26  25  24  23 22  21  20  19 18  17  16 
//CNF7   MODE7   CNF6   MODE6   CNF5   MODE5   CNF4   MODE4 
//15 14  13 12   11 10  9  8    7  6   5   4   3  2   1   0
//CNF3   MODE3   CNF2   MODE2   CNF1   MODE1   CNF0   MODE0

//GPIOX_CRH 端口配置高寄存器 
//31 30  29 28   27 26  25  24  23 22  21  20  19 18  17  16 
//CNF15  MODE15  CNF14  MODE14  CNF13  MODE13  CNF12  MODE12 
//15 14  13 12   11 10  9  8    7  6   5   4   3  2   1   0
//CNF11  MODE11  CNF10  MODE10  CNF9   MODE9   CNF8   MODE8

//eg1:
//IO方向设置
//#define DS18B20_IO_IN()  {GPIOA->CRH&=0XFFFFFFF0;GPIOA->CRH|=8;}
//#define DS18B20_IO_OUT() {GPIOA->CRH&=0XFFFFFFF0;GPIOA->CRH|=3;}
//IO操作函数										   
//#define	DS18B20_DQ_OUT PAout(8) //数据端口	PA8 
//#define	DS18B20_DQ_IN  PAin(8)  //数据端口	PA8 


//说明:如上面PA8 口就是使用CNF8 和MODE8    下面的0XFFFFFFF0  0位出现在CRH的0-3bit    8和3不变 不需要移动
//说明:如下面PB15口就是使用CNF15 和MODE15  下面的0X0FFFFFFF  0位出现在CRH的28-31bit  8和3不变 需要左移28位  就是移到bit28-31位 
//


//IO方向设置
#define DS18B20_IO_IN()  {GPIOB->CRH&=0X0FFFFFFF;GPIOG->CRH|=8<<28;}
#define DS18B20_IO_OUT() {GPIOB->CRH&=0X0FFFFFFF;GPIOG->CRH|=3<<28;}
IO操作函数											   
#define	DS18B20_DQ_OUT PBout(15) //数据端口	 
#define	DS18B20_DQ_IN  PBin(15)  //数据端口	


   	
u8 DS18B20_Init(void);//初始化DS18B20
short DS18B20_Get_Temp(void);//获取温度
void DS18B20_Start(void);//开始温度转换
void DS18B20_Write_Byte(u8 dat);//写入一个字节
u8 DS18B20_Read_Byte(void);//读出一个字节
u8 DS18B20_Read_Bit(void);//读出一个位
u8 DS18B20_Check(void);//检测是否存在DS18B20
void DS18B20_Rst(void);//复位DS18B20    
#endif

#include "ds18b20.h"  


void DQ_mode(u8 mode)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能PORTG口时钟 
	if(mode == 1)
	{
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;				//PORTG.11 推挽输出
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		  
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOB, &GPIO_InitStructure);
		GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_RESET);
	}
	else
	{	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;				//PORTG.11 推挽输出
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 		  
		GPIO_Init(GPIOB, &GPIO_InitStructure);
	}	
}	

//复位DS18B20
void DS18B20_Rst(void)	   
{                 
	DQ_mode(1); 	//SET PG11 OUTPUT
    DS18B20_DQ_OUT=0; 	//拉低DQ
    Delay_us(750);    	//拉低750us
    DS18B20_DQ_OUT=1; 	//DQ=1 
	Delay_us(15);     	//15US
}
//等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
u8 DS18B20_Check(void) 	   
{   
	u8 retry=0;
	DQ_mode(0);	//SET PG11 INPUT	 
    while (DS18B20_DQ_IN&&retry<200)
	{
		retry++;
		Delay_us(1);
	};	 
	if(retry>=200)return 1;
	else retry=0;
    while (!DS18B20_DQ_IN&&retry<240)
	{
		retry++;
		Delay_us(1);
	};
	if(retry>=240)return 1;	    
	return 0;
}
//从DS18B20读取一个位
//返回值:1/0
u8 DS18B20_Read_Bit(void) 	 
{
    u8 data;
	DQ_mode(1);	//SET PG11 OUTPUT
    DS18B20_DQ_OUT=0; 
	Delay_us(2);
    DS18B20_DQ_OUT=1; 
	DQ_mode(0);	//SET PG11 INPUT
	Delay_us(12);
	if(DS18B20_DQ_IN)data=1;
    else data=0;	 
    Delay_us(50);           
    return data;
}
//从DS18B20读取一个字节
//返回值:读到的数据
u8 DS18B20_Read_Byte(void)     
{        
    u8 i,j,dat;
    dat=0;
	for (i=1;i<=8;i++) 
	{
        j=DS18B20_Read_Bit();
        dat=(j<<7)|(dat>>1);
    }						    
    return dat;
}
//写一个字节到DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(u8 dat)     
 {             
    u8 j;
    u8 testb;
	  DQ_mode(1);	//SET PG11 OUTPUT;
    for (j=1;j<=8;j++) 
	{
        testb=dat&0x01;
        dat=dat>>1;
        if (testb) 
        {
            DS18B20_DQ_OUT=0;	// Write 1
            Delay_us(2);                            
            DS18B20_DQ_OUT=1;
            Delay_us(60);             
        }
        else 
        {
            DS18B20_DQ_OUT=0;	// Write 0
            Delay_us(60);             
            DS18B20_DQ_OUT=1;
            Delay_us(2);                          
        }
    }
}
//开始温度转换
void DS18B20_Start(void) 
{   						               
    DS18B20_Rst();	   
	  DS18B20_Check();	 
    DS18B20_Write_Byte(0xcc);	// skip rom
    DS18B20_Write_Byte(0x44);	// convert
} 

//初始化DS18B20的IO口 DQ 同时检测DS的存在
//返回1:不存在
//返回0:存在    	 
u8 DS18B20_Init(void)
{
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能PORTG口时钟 
	
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;				//PORTG.11 推挽输出
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		  
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);

 	GPIO_SetBits(GPIOB,GPIO_Pin_15);    //输出1

	DS18B20_Rst();

	return DS18B20_Check();
}  
//从ds18b20得到温度值
//精度:0.1C
//返回值:温度值 (-550~1250) 
short DS18B20_Get_Temp(void)
{
    u8 temp;
    u8 TL,TH;
	  short tem;
    DS18B20_Start ();  			// ds1820 start convert
    DS18B20_Rst();
    DS18B20_Check();	 
    DS18B20_Write_Byte(0xcc);	// skip rom
    DS18B20_Write_Byte(0xbe);	// convert	    
	  TL=DS18B20_Read_Byte(); 	// LSB   
    TH=DS18B20_Read_Byte(); 	// MSB 
	
	    	  
    if(TH>7)
    {
        TH=~TH;
        TL=~TL; 
        temp=0;					//温度为负  
    }else temp=1;				//温度为正	  	  
    tem=TH; 					//获得高八位
    tem<<=8;    
    tem+=TL;					//获得底八位
    tem=(float)tem*0.625;		//转换  
	//printf(" %d ",tem);
	if(temp)return tem; 		//返回温度值
	else return -tem;    
}

你可能感兴趣的:(单片机嵌入式,stm32,51单片机,计算机视觉,视觉检测,人脸检测)