具有自动泊车功能的电动车赛题解析TI杯2022年省级大学生电子设计竞赛联赛

具有自动泊车功能的电动车赛题解析TI杯2022年省级大学生电子设计竞赛联赛_第1张图片

项目要求:        (1)根据题目要求 在30s内将小车倒入库2,并能够自动出库,整个过程中小车不压边界线。(20分)

                          (2)30s内将小车侧方进入库5中并出库,整个过程小车不压边界线。(20分)

单项倒车入库/出库②:移除图 2 中库 1、库 3 内停放的车辆,逐条按照第(1)项要求,电动车完成“邻库无车”时的倒车入库/出库。(20 分)
                        (4)单项侧方入库/出库②:移除图 3 中库 4、库 6 内停放的车辆,逐条按照第(2)项要求,电动车完成“邻库无车”时的侧方入库/出库。(20 分)
                        (5)连续倒车与侧方入库/出库①:如果参赛者确认其作品能连续完成第(1)、(2)项功能要求,可直接测试本项,成功完成的将在第(1)、(2)项得分基础上加 5 分。此刻泊车场地(图 1)中的库 1、3、4、6 内均居中停有车辆。电动车自图 1 中“发车区 1”内一键启动泊车,按第(1)、2)项要求,连续完成自动倒车及侧方入库/出库。(5 分)
                        (6)连续倒车与侧方入库/出库②:如果参赛者确认能够连续完成第(3)、
(4)项功能要求,则直接测试第(6)项,成功完成的可在第(3)、(4)项得分基础上加 5 分。此刻同时移除泊车场地(图 1)中的库 1、3、4、6 内所有停放车辆,电动车自图 1 中“发车区 1”内一键启动泊车,按照第(3)、(4)项要求,连续完成自动倒车与侧方入库/出库。(5 分)
                        (7)其他 (10 分)

观看了整个项目要求之后第一反应就排除了红外的方案,因为红外的精度达不到题目的要求。立马就决定用OPPENMV进行图像处理,在前进的过程中通过find_blobs函数得到所画框的左顶点y轴的值,通过串口给到stm32f407通过pid计算给出相应差值达到巡线的目的。(这里没有在openmv中计算是因为我所用的这款openmv的版本有点低,容易卡死。)走直线的项目要求达到了,下一个就是在需要停车进行泊车的地方进行泊车,这个我是通过识别跳变的方式来进行计数的,在相应需要停下的位置停车。最后就是泊车的整个过程了,这个方法有很多种,具体如何去做得看各自的喜好以及对模块掌握的要求,我泊车是开环写的,比较简单。

下面是openmv的具体实现方法

import sensor, image, time,math,pyb
from pyb import UART,LED
import json
import ustruct
import pyb
#THRESHOLD = (91, 3)    #二分化系数
ROI=(15,45,65,9)
BINARY_VISIBLE = True
sensor.reset()
sensor.set_vflip(True)          #垂直翻转
sensor.set_hmirror(True)        #水平翻转
sensor.set_pixformat(sensor.RGB565)         #彩色
#sensor.set_pixformat(sensor.GRAYSCALE)     #黑白
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 2000)
sensor.set_auto_gain(False) # must be turned off for color tracking
sensor.set_auto_whitebal(False) # must be turned off for color tracking
                                #red_threshold_01=((7, 67, -39, 66, -23, 84))
black_threshold=(1, 12, -66, 48, -12, 48)
clock = time.clock()
ROIQIANM=(4,172,61,25)
ROIZHONGJIAN=(139,104,34,49)

ROIXUNXIAN=(3,165,75,52)

ROIJISHU=(187,180,95,24)

ROIXUN=(190,218,40,23)

ROICHEKU=(300,140,18,44)
uart = UART(3,115200)
uart.init(115200,bits=8,parity=None,stop=1)
b=0
c=0
d=0
daoche=0
H=0
I=0
J=0
while(True):
    clock.tick()
    img = sensor.snapshot()
    #寻找色块
    img.draw_rectangle(ROIZHONGJIAN)
    img.draw_rectangle(ROIQIANM)
    #img.draw_rectangle(ROIJISHU)
    blobs0 = img.find_blobs([black_threshold],roi=ROIQIANM)
    if blobs0:
       largest_blob0 = max(blobs0,key=lambda c:c.pixels())
       img.draw_rectangle(largest_blob0.rect())
    blobs1 = img.find_blobs([black_threshold],roi=ROIQIANM)
    if blobs1:
       largest_blob1 = max(blobs1,key=lambda c:c.pixels())
       img.draw_rectangle(largest_blob1.rect())
    if c*b==1 and blobs0 and blobs1:
       d=d+1
       c=0
       b=0
    blobs2 = img.find_blobs([black_threshold],roi=ROIZHONGJIAN,pixels_threshold = 10)
    if blobs2:
       b=1
       largest_blob2 = max(blobs2,key=lambda c:c.pixels())
       img.draw_rectangle(largest_blob2.rect())
    else:
        c=1
    blobs3 = img.find_blobs([black_threshold],roi=ROIXUN)
    if blobs3:
       largest_blob3 = max(blobs3,key=lambda c:c.pixels())
       img.draw_rectangle(largest_blob3.rect())
       daoche=largest_blob3.y()

    blobs4 = img.find_blobs([black_threshold],roi=ROICHEKU)
    if blobs4:
       largest_blob4 = max(blobs4,key=lambda c:c.pixels())
       img.draw_rectangle(largest_blob4.rect())
       I=1
    else:
        J=1
    if J*I==1:
        H=H+1
        I=0
        J=0
    if blobs0:
        output_str=bytearray([0x2c,0x12,largest_blob0.cy(),d,daoche,H,0x5B])
        uart.write(output_str)
        print(largest_blob0.cy(),d,daoche,H)

这里串口的收发数据直接进行封包,给两个包头,一个包尾一共8位数据。STM32F407方面就直接解包,将数据进行读取、使用。

下面是32的串口对数据的收,解包录用:

#include "sys.h"
#include "usart.h"	
#include "led.h"
#include "pwm.h"
static u8 Cx=0,Cy=0,Cw=0,Ch=0;
extern int dier;
extern int dier1;
extern int zuihou;
int xunbudao=0;
// 	 
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h"					//ucos 使用	  
#endif
  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 
}; 

FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{ 	
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
	USART1->DR = (u8) ch;      
	return ch;
}
#endif
 
#if EN_USART1_RX   //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误   	
u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART_RX_STA=0;       //接收状态标记	

//初始化IO 串口1 
//bound:波特率
void uart_init(u32 bound){
   //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟
 
	//串口1对应引脚复用映射
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1
	
	//USART1端口配置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
	GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10

   //USART1 初始化设置
	USART_InitStructure.USART_BaudRate = bound;//波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
  USART_Init(USART1, &USART_InitStructure); //初始化串口1
	
  USART_Cmd(USART1, ENABLE);  //使能串口1 
	
	//USART_ClearFlag(USART1, USART_FLAG_TC);
	
#if EN_USART1_RX	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断

	//Usart1 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器、

#endif
	
}



int Vertical_Kp1=50;
int Vertical_Kd1=10;
int y,x,w,h;
int back1;
int biaozhi=1;
extern uint8_t Fore,Left,Right;
extern int mubiaoshudu;
extern int ruku;
extern int buxuanxian;
int left,right,on,down;
int leftpwm=0,rightpwm=0;
   int Bluetooth_data;
void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	int lastcx;

u8 com_data; 
		u8 i;
		static u8 RxCounter1=0;
		static u16 RxBuffer1[10]={0};
		static u8 RxState = 0;	
		static u8 RxFlag1 = 0;

		if( USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)  	   //接收中断  
		{
				USART_ClearITPendingBit(USART1,USART_IT_RXNE);   //清除中断标志
				com_data = USART_ReceiveData(USART1);			
				if(RxState==0&&com_data==0x2C)  //0x2c帧头
				{
					RxState=1;
					RxBuffer1[RxCounter1++]=com_data;
				}
		
				else if(RxState==1&&com_data==0x12)  //0x12帧头
				{
					RxState=2;
					RxBuffer1[RxCounter1++]=com_data;
				}
		
				else if(RxState==2)
				{
					RxBuffer1[RxCounter1++]=com_data;

					if(RxCounter1>=10||com_data == 0x5B)       //RxBuffer1接受满了,接收数据结束
					{
						RxState=3;
						RxFlag1=1;
						Cx=RxBuffer1[RxCounter1-5];
						Cy=RxBuffer1[RxCounter1-4];
						Cw=RxBuffer1[RxCounter1-3];
						Ch=RxBuffer1[RxCounter1-2];
						
					}
				}
		
				else if(RxState==3)		//检测是否接受到结束标志
				{
						if(RxBuffer1[RxCounter1-1] == 0x5B)
						{
									USART_ITConfig(USART1,USART_IT_RXNE,DISABLE);//关闭DTSABLE中断
									if(RxFlag1)
									{
									//LED1=!LED1;
										leftpwm=150;
										rightpwm=150;
										y=Cy;
										x=Cx;
										w=Cw;
										h=Ch;
									if(back1!=1||dier==0&&dier1==1||zuihou==0)
									{
								 TIM_SetCompare1(TIM2,1550+(48*(Cx-185)));//175										
									}
									if(ruku==1&&buxuanxian==0)
									{
									 //TIM_SetCompare1(TIM2,1550+(60*(223-Cw)));//78偏大
										
									}
										
								//停车标志
									if(Cy==7&&biaozhi==1)
										{
											//mubiaoshudu=0;
										Set_Pwm(0,0);
										back1=1;								
										}
									
									}
									RxFlag1 = 0;
									RxCounter1 = 0;
									RxState = 0;
									USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
						}
						else   //接收错误
						{			
									RxState = 0;
									RxCounter1=0;
									for(i=0;i<10;i++)
									{
											RxBuffer1[i]=0x00;      //将存放数据数组清零
									}
						}
				} 
	
				else   //接收异常
				{
						RxState = 0;
						RxCounter1=0;
						for(i=0;i<10;i++)
						{
								RxBuffer1[i]=0x00;      //将存放数据数组清零
						}
				}

		}
		else{xunbudao=1;}

	
}


#endif	

void usart6_init(u32 bound){
   //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE); //使能GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6,ENABLE);//使能USART1时钟
 
	//串口1对应引脚复用映射
	GPIO_PinAFConfig(GPIOG,GPIO_PinSource9,GPIO_AF_USART6); //GPIOA9复用为USART1
	GPIO_PinAFConfig(GPIOG,GPIO_PinSource14,GPIO_AF_USART6); //GPIOA10复用为USART1
	
	//USART1端口配置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_14; //GPIOA9与GPIOA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
	GPIO_Init(GPIOG,&GPIO_InitStructure); //初始化PA9,PA10

   //USART1 初始化设置
	USART_InitStructure.USART_BaudRate = bound;//波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
  USART_Init(USART6, &USART_InitStructure); //初始化串口1
	
  USART_Cmd(USART6, ENABLE);  //使能串口1 
	
	//USART_ClearFlag(USART1, USART_FLAG_TC);
	
#if EN_USART1_RX	
	USART_ITConfig(USART6, USART_IT_RXNE, ENABLE);//开启相关中断

	//Usart1 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART6_IRQn;//串口1中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器、

#endif
	
}

void USART6_IRQHandler(void)                	//串口1中断服务程序
{


  if(USART_GetITStatus(USART6,USART_IT_RXNE)!=0)  // 接收中断标志位拉高
  {
//    Bluetooth_data=USART_ReceiveData(USART6); 
//    LED1=!LED1	;    // 保存接收到的指令
//		if(Bluetooth_data==0x00)Fore=0,Back=0,Left=0,Right=0; // 刹车
//		
//    else if(Bluetooth_data==0x01)Fore=1,Back=0,Left=0,Right=0;
//		// 前进
//    else if(Bluetooth_data==0x05)Fore=0,Back=1,Left=0,Right=0; // 后退
//    else if(Bluetooth_data==0x03)Fore=0,Back=0,Left=1,Right=0; // 左转
//    else if(Bluetooth_data==0x07)Fore=0,Back=0,Left=0,Right=1; // 右转
//    else                    Fore=0,Back=0,Left=0,Right=0;
//		
		
  }
}



int Vertical1(int a,int b) 
{
  int PWM_out;
  
  PWM_out = Vertical_Kp1*(b-a);
	if(PWM_out<10&&PWM_out>-10)
	{PWM_out=0;}
  if(PWM_out>50&&PWM_out<-50)
	{PWM_out=0;}
	return PWM_out*50+1500;
} 

然后主函数对串口发过来的数据直接处理利用,就可以达到后续的一系列的项目要求了,纵观整个项目,难以程度比较简单,对于没有学过openmv 的同学而言就有一定的难度了,需要学习然后收发数据,以及利用什么函数用来巡线。

在这个项目的期间也遇到不少的麻烦,回想起来,在第一个倒车入库后出库,开始第二侧方停车的时候应该如何计数,当时确实难道我了,但是通过一次次尝试,最终利用一共比较不稳妥的方式给解决了,出库的时候将舵机的角度给小一点,让小车转一个大湾,当小车再次扫到边界线的时候就可以开始进行侧方的计数了(我倒车入库的标志位所画的框和侧方停车的框是一个框,我这里是将这个数值付给一个变量,让这个标志位继续计数,达到7的时候停车开始侧方)

将这几个问题解决了之后,后续就剩下了简单的 pid的调整,泊车舵机的角度的调整,侧方的角度的调整。将这几个简单的数值调整好了之后整个项目就完成了

你可能感兴趣的:(算法,c语言,python)