目录
一、预想功能
基于RISC-V MCU CH32V307开发板和以K210作为核心单元的MaixBit开发板所开发的装置,该装置运行时旋转,他可以通过判断是否检测到人脸,如若检测到人脸便判断是否佩戴了安全帽,通过串口发送数据到CH32V307,CH32V307通过PWM输出驱动电机停止旋转并串口发送数据包到syn6288语音模块播报检测结果。
二、系统框架
三、硬件介绍
MaixBit开发板:其以K210作为核心单元,由其进行人脸识别,在识别到人脸的基础上判断是否识别到了安全帽,并通过比较人脸与安全帽的xy坐标判断其位置是否为佩戴状态,通过结果发送相应数据给到 RISC-V MCU CH32V307开发板 。
RISC-V MCU CH32V307开发板:接收MaixBit开发板所发送过来的数据,运行时输出PWM控制电机旋转,接收到数据后输出PWM控制电机停止旋转并通过数据不同发送数据包控制syn6288语音模块播报不同识别结果。
Sipeed 2.4寸显示屏:外接MaixBit开发板,可以实时显示摄像头识别检测的结果。
l298n:电机驱动模块,接收RISC-V MCU CH32V307开发板的pwm信号来控制电机旋转的速度。
LM2596S可调降压模块:将电池电压降为5V供给CH32V307与K210使用。
SYN6288语音模块:接收由开发板发送过来的数据包,进行不同内容的语音播报。
四、软件介绍
1、K210程序
import sensor, image, lcd, time
import KPU as kpu
import gc, sys
from machine import UART
from Maix import GPIO
from fpioa_manager import fm
import utime,ustruct
input_size = (224, 224)
labels = ['1', '2']
anchors = [2.09, 1.66, 0.63, 0.56, 0.47, 0.41, 1.44, 1.12, 0.97, 0.91]
fm.register(GPIO.GPIOHS9,fm.fpioa.UART2_TX) #串口A发送
fm.register(GPIO.GPIOHS10,fm.fpioa.UART2_RX)
fm.register(GPIO.GPIOHS7,fm.fpioa.UART1_TX)
fm.register(GPIO.GPIOHS8,fm.fpioa.UART1_RX)
uart_A = UART(UART.UART2, 115200, 8, None, 1, timeout=1000, read_buf_len=4096)#配置串口A
uart_B = UART(UART.UART1, 115200, 8, 0, 1, timeout=1000, read_buf_len=4096)
data1 = 0x02
data2 = 0x03
n = 0
def lcd_show_except(e):
import uio
err_str = uio.StringIO()
sys.print_exception(e, err_str)
err_str = err_str.getvalue()
img = image.Image(size=input_size)
img.draw_string(0, 10, err_str, scale=1, color=(0xff,0x00,0x00))
lcd.display(img)
def main(anchors, labels = None, model_addr="/sd/m.kmodel", sensor_window=input_size, lcd_rotation=0, sensor_hmirror=False, sensor_vflip=True):
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_windowing(sensor_window)
sensor.set_hmirror(sensor_hmirror)
sensor.set_vflip(sensor_vflip)
sensor.run(1)
global n
lcd.init(type=1)
lcd.rotation(lcd_rotation)
lcd.clear(lcd.WHITE)
if not labels:
with open('labels.txt','r') as f:
exec(f.read())
if not labels:
print("no labels.txt")
img = image.Image(size=(320, 240))
img.draw_string(90, 110, "no labels.txt", color=(255, 0, 0), scale=2)
lcd.display(img)
return 1
try:
img = image.Image("startup.jpg")
lcd.display(img)
except Exception:
img = image.Image(size=(320, 240))
img.draw_string(90, 110, "loading model...", color=(255, 255, 255), scale=2)
lcd.display(img)
try:
task = None
task = kpu.load(model_addr)
kpu.init_yolo2(task, 0.8, 0.6, 5, anchors) # threshold:[0,1], nms_value: [0, 1]
while(True):
img = sensor.snapshot()
t = time.ticks_ms()
objects = kpu.run_yolo2(task, img)
t = time.ticks_ms() - t
uart_A.write(chr(data1))
#uart_B.write(chr(data2))
if objects: #识别到目标
for obj in objects:
pos = obj.rect()
a = pos[1] #定义目标坐标
if obj.classid()==1:
if objects:
for obj in objects:
pos = obj.rect()
b = pos[1]
if obj.classid() == 1:
n =n+1 #计数识别到人脸的次数
if n ==8:
uart_A.write(chr(obj.classid())) #发送目标代号
n=0
else:
if a-b<35: #比较坐标位置
n=0
uart_A.write(chr(data2))
img.draw_rectangle(pos)
img.draw_string(pos[0], pos[1], "%s : %.2f" %(labels[obj.classid()], obj.value()), scale=2, color=(255, 0, 0))
img.draw_string(0, 200, "t:%dms" %(t), scale=2, color=(255, 0, 0))
lcd.display(img)
except Exception as e:
raise e
finally:
if not task is None:
kpu.deinit(task)
if __name__ == "__main__":
try:
# main(anchors = anchors, labels=labels, model_addr=0x300000, lcd_rotation=0)
main(anchors = anchors, labels=labels, model_addr="/sd/model-24873.kmodel")
except Exception as e:
sys.print_exception(e)
lcd_show_except(e)
finally:
gc.collect()
2、CH32V307程序
串口接收配置
void USART2_Printf_Init(uint32_t baudrate)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //使能串口2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟
USART_DeInit(USART2);
/* USART2 TX-->A.2 RX-->A.3 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //设置PA2为复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置PA3为浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = baudrate; //设置串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; //1个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //发送和接收模式
USART_Init(USART2, &USART_InitStructure); //初始化串口
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级为1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //中断优先级初始化
USART_Cmd(USART2, ENABLE); //使能串口
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //开启中断
}
中断配置
u16 a;
void USART2_IRQHandler(void)
{
// static u16 a;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //中断产生
{
USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志
a = USART_ReceiveData(USART2); //接收数据
}
}
语音模块函数
void SYN6288(USART_TypeDef * pUSARTx,char * str)
{
char * p = str;
int len = 0,check=0xFD,k;
while( *p++ != 0 )
{
len++;
}
len+=3;
USART3_SendByte(USART3,0xFD); //发送帧头
USART3_SendByte(USART3,len >>8);
USART3_SendByte(USART3,len);
check = check ^ (len>>8) ^ (len);
USART3_SendByte(USART3,0x01);
USART3_SendByte(USART3,0x01);
check = check ^ 0x01 ^ 0x01;
for( k = 0; k < len-3; k++ )
{
USART3_SendByte(USART3,*str);
check ^= ( *str );
str++;
}
USART3_SendByte(USART3,check); //发送数据包
Delay_Ms(150*len);
}
PWM初始化配置
void xuanzhuan(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;//定义三个结构体
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
//开启相关时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//A1 GPIO初始化
TIM_TimeBaseStructure.TIM_Period = 199;
TIM_TimeBaseStructure.TIM_Prescaler =4199;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 定时器初始化
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
// TIM_OC3Init(TIM2, &TIM_OCInitStructure);
// TIM_OC4Init(TIM2, &TIM_OCInitStructure);
//定时器通道初始化
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
// TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
// TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);
//预装值初始化
TIM_Cmd(TIM2, ENABLE);
//打开定时器
}
接收数据进行播报
if(a == 01)
{
TIM_SetCompare1(TIM2,199);
TIM_SetCompare2(TIM2,199);
SYN6288(USART3,"未佩戴安全帽");
}
if(a == 03)
{
TIM_SetCompare1(TIM2,199);
TIM_SetCompare2(TIM2,199);
SYN6288_Speech(USART3,"已佩戴安全帽");
}
五、实际效果
识别时MaixBit开发板外接2.4寸显示屏观察识别结果
未识别到安全帽
识别到安全帽
完整视频演示
一种检测有无佩戴安全帽的装置