汽车正常运行状态,则一切正常,系统正常工作;若汽车处于停滞锁车状态,则本系统开始工作。此时就会监测在无大人的情况下,是否车内还有小孩,若有小孩,则当车内温度等升高到一定阈值的时候,就会进行报警提醒,避免小孩被遗忘或留在车内的现象。
实现目标以及方法:
①:本设计最主要的就是监测车内有无人员,利用视觉的技术对该情况监测。本系统利用摄像头采集车内图像,(后期可修改,传输方式)千兆网口传输图像到电脑进行图像处理。图像处理开发平台可以采用深度学习神经网络,人工智能的方法,用于获取人脸识别即可。最终,决定采取何种方式由实现效果来判定。
②:利用STM32单片机做另外一个主控系统,实现控制的功能。二氧化碳传感器,红外温度检测,(此处是利用传感器模块DS18B20)蜂鸣器报警模块。另外,与本设计第一主控MCU(STM32)进行通信,因此需要利用串口和单片机之间进行信息数据的交互。
项目总结:
主要功能是在驻车的时候用人脸检测技术判断车内有无人员,同时检测车内空气质量,若车内空气质量不适合用户待的话会自动报警以提醒人员下车。其中主要分为两部分工作,第一部分是是机器视觉人脸检测,我采用了当前国内嘉楠科技公司生产的K210模组,它采用RISC-V架构,达到了1TOPS的算力,功耗仅为300mW,能很好的满足机器视觉应用的需求,本次我主要应用这款芯片做人脸检测,采用yolo算法,将训练好的人脸检测模型部署到K210模块上,然后将检测结果通过串口输出检测结果给STM32单片机。第二部分是STM32单片机检测和控制部分,STM32单片机这边使用了TVOC气体传感器来检测车内空气质量,如甲醛浓度、CO2浓度、PM2.5浓度,若车内环境比较差,则单片机控制语音模块进行语音播报,以达到提醒用户的功能。所有信息用了OLED屏幕进行显示。
非常好用的深度学习嵌入式部署模组,虽然速度不算高,可分配资源也不多,但做简单应用还是可以的,且官方例程相对比较全,开源网站对于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],从0 到3 依顺时钟旋转。
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;
/*******************************************************************************
\* 文件名称:基于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();
}
}
}
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;
}
}
}
#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;
}