2021电赛F题制作分享

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本文小车能稳定实现2021电赛F题基础题目1、2,将在后文分享全流程制作,以下是电赛的基本任务要求和赛道。
任务要求文档

赛道图

题目要求简介:
基础任务就是一辆小车识别数字后前往不同房间,发挥部分就是两辆小车协同前往病房。
文章设计小车运行视频如下

小车运行视频


一、整体方案决策

1.题目分析

根据任务文档,我们分析作品所需以下多项基本功能:
1、数字识别功能
2、循迹功能
3、十字识别、T字识别
4、药物检测功能
5、红、黄、绿LED
6、远程通讯功能

2.设计方案

基础功能方案:
采用OPENMV实现数字识别与循迹功能,设计支架(3D打印或DIY)悬于车头正上方;
使用四驱小车配TB6612驱动作为执行机构;
安装于底盘两侧的灰度传感器进行十字或T字识别;
使用行程开关配合容器(3D打印或DIY)实现压力检测药物;
三色LED焊接在PCB上;
通过OLED显示各阶段状态,便于调试BUG;
扩展功能:MPU6050、串口3蓝牙
实物图

二、代码方案

1.OPENMV端

OPENMV端程序流程图
OPENMV程序流程图

##功能:通过串口输入切换模式1:数字识别、模式2:巡线模式
##数字识别:识别到数字后返回数字
##巡线模式:二值化巡线返回斜率和截距

THRESHOLD = (36, 61, 17, 82, 12, 67) # Grayscale threshold for dark things...
import sensor, image, time
from pyb import UART
from image import SEARCH_EX, SEARCH_DS
from pyb import LED
mode = 1
tta0 = 0
tta1 = 0
num = 0
min_degree = 0
max_degree = 179
enable_lens_corr = False # turn on for straighter lines...打开以获得更直的线条…
template1 = image.Image("/1.pgm")
template2 = image.Image("/2.pgm")
template3 = image.Image("/3.pgm")
template4 = image.Image("/4.pgm")



LED(1).on()
LED(2).on()
LED(3).on()

clock = time.clock()                # to process a frame sometimes.
uart = UART(3, 115200)#P4、5  TX、RX

while(True):
    if uart.any():
        mode = int(uart.read())

    if mode == 1:
        sensor.reset()
        sensor.set_contrast(1)
        sensor.set_gainceiling(16)
        sensor.set_framesize(sensor.QQVGA) #160x120
        sensor.set_pixformat(sensor.GRAYSCALE)

        while(mode == 1):
            if uart.any():
                mode = int(uart.read())
            clock.tick()
            img = sensor.snapshot()

            r1 = img.find_template(template1, 0.70, step=4, search=SEARCH_EX) #, roi=(10, 0, 60, 60))
            if r1:
                img.draw_rectangle(r1)
                print(r1)
                print("(X1:"+str(r1[0])+")"+'\r\n')
                uart.write("(X1:"+str(r1[0])+")"+'\r\n')
            r2 = img.find_template(template2, 0.70, step=4, search=SEARCH_EX) #, roi=(10, 0, 60, 60))
            if r2:
                img.draw_rectangle(r2)
                print(2)
                print("(X2:"+str(r2[0])+")"+'\r\n')
                uart.write("(X2:"+str(r2[0])+")"+'\r\n')
            r3 = img.find_template(template3, 0.70, step=4, search=SEARCH_EX) #, roi=(10, 0, 60, 60))
            if r3:
                img.draw_rectangle(r3)
                print(3)
                print("(X3:"+str(r3[0])+")"+'\r\n')
                uart.write("(X3:"+str(r3[0])+")"+'\r\n')
            r4 = img.find_template(template4, 0.70, step=4, search=SEARCH_EX) #, roi=(10, 0, 60, 60))
            if r4:
                img.draw_rectangle(r4)
                print(4)
                print("(X4:"+str(r4[0])+")"+'\r\n')
                uart.write("(X4:"+str(r4[0])+")"+'\r\n')

            print("FPS:"+str(clock.fps()))
################################################################################################
    if mode == 2:
        sensor.reset()
        sensor.set_pixformat(sensor.RGB565)
        sensor.set_framesize(sensor.QQQVGA) # 80x60 (4,800 pixels) - O(N^2) max = 2,3040,000.  #80x60  w,h
        sensor.skip_frames(time = 2000)     # WARNING: If you use QQVGA it may take seconds

        while(mode == 2):
            if uart.any():
                mode = int(uart.read())
            clock.tick()

#########################识别直线的截距、斜率##################
            #
            img = sensor.snapshot()
            img_1 = img.binary([THRESHOLD])#拍摄图像,将其转变为二进制储存在img变量
            line = img_1.get_regression([(100,100)], robust = True)

            if (line):
                rho_err = abs(line.rho())-img_1.width()/2 #截距偏移
                ##line.rho()线在最中心时,是±40   在最两边时为0,右边为负,左边为正
                ##所以rho_err只要偏离中心就都为负数
                if line.theta()>90:
                    theta_err = line.theta()-180    #角度偏移
                else:
                    theta_err = line.theta()
                img_1.draw_line(line.line(), color = 127)#画标志线
                if line.magnitude()>8:
                    output_str="[%d,%d]" % (rho_err,theta_err) #方式1
                    print("[rho_err,theta_err]")
                    print(output_str)
                    uart.write(output_str+'\r\n')

OPENMV进行红线循迹与数字识别功能,这两个功能通过stm32单片机发送模式切换指令控制
红线巡线状态下,OPENMV通过串口发送数据给stm32,数据格式为[XXX,XXX]对应[rho,theta]。
数字识别状态下,openmv通过sd卡内提前储存的数字模板与当前图像中的数字进行对比,识别率高则发送识别数据给stm32单片机,以数字1格式为例(X1:XXX),数字2则是(X2:XXX)其中XXX为数字在图像中的X坐标。
XXX由代码中r数组提取出来,r数组为模板识别成功后,得出的相应数据组成的数组,r[0]为其中的第0位数据,代表在摄像头视野图像中的此匹配模板矩形框的左上角X坐标。
由于代码实现部分是通过星瞳科技例程修改而来,非原创部分不做过多解释,需自行学习例程,以下为例程链接。
巡线小车例程
多模板匹配数字识别例程

2.STM32端

32程序整体流程图如下图所示
32程序流程图
整体程序分为初始化代码、发车处理代码、题目1、2操作代码。
硬件初始化就为常规处理,因此后文不做过多解释。

1.发车处理代码

以下为发车处理代码的程序流程图
发车处理流程图
发车处理函数如下

void SetOut_prepare(void)
{
	while(XJ_flag==0)
	{
		OLED_chTS(0);//识别数字
		usart1_TX(1);//发送切换循迹指令
		SB_flag=1;//识别模式标志位
		while(MB_num==0)MB_num=SZ_num;//等待识别出数字,将识别数字赋予MB_num变量
		SZ_num=0;//清零
		WZ_X[MB_num]=0;//清零
		OLED_chTS(1);//数字
		OLED_chTS(2);//等待放入药物
		SB_flag=0;//关闭数字识别
		usart1_TX(2);//发送循迹指令
		while(ZH_flag)
		{
			OLED_ShowString(0,16*3,"Trace after 2s",16);OLED_Refresh();
			if(YW_KEY==0)
			{
			delay_ms(1000);
			delay_ms(1000);
			if(YW_KEY==0)ZH_flag=0;	//等待装货检测		
			}	
		}
		XJ_flag=1;//打开循迹标志位
		OLED_chTS(3);//循迹中
	}
}

发车处理函数中涉及到以下多项功能函数与标志位
OLED_chTS(u8 num)//OLED中文提示,按num变量不同,显示设定好的语句
usart1_TX(u8 mode)//串口1发送函数,用于OPENMV切换模式,根据mode变量不同,发送不同指令给OPENMV
OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1)//OLED显示字符串函数,*chr参数为字符串
OLED_Refresh()//刷新OLED数据,使写入数据显示在OLED上
SB_flag代表识别模式开启,使定时器中断函数内的数字识别相关函数运行
SZ_num变量代表摄像头当前识别到的数字,根据定时器中断函数内的串口接收函数所得,后续由相关讲解
WZ_X[MB_num]后续在串口接收函数做相关讲解,此处仅做清零处理,避免使后续数据污染
ZH_flag为装货标志位,初始为1,使函数进行等待装货循环,当连续两秒检测到货物则清零跳出循环
XJ_flag循迹标志位,用于开启定时器中断函数内与循迹运行的相关函数

void OLED_chTS(u8 num)//中文提示
{
	u8 i;
	if(num==0)for(i=0;i<4;i++)OLED_ShowChinese(i*16,0,i,16);//识别数字
	else if(num==1)OLED_ShowNum(16*4,0,MB_num,1,16);//开头目标数字号
	else if(num==2)for(i=0;i<6;i++)OLED_ShowChinese(i*16,16,4+i,16);//等待放入药物
	else if(num==3){OLED_Clear();for(i=0;i<3;i++)OLED_ShowChinese(i*16,0,10+i,16);}//循迹中
	else if(num==4){OLED_Clear();for(i=0;i<3;i++)OLED_ShowChinese(i*16,0,13+i,16);}//转弯中
	else if(num==5){OLED_Clear();for(i=0;i<5;i++)OLED_ShowChinese(i*16,0,16+i,16);}//请拿开药物
	else if(num==6){OLED_Clear();for(i=0;i<3;i++)OLED_ShowChinese(i*16,0,21+i,16);}//调头中
	else if(num==7){OLED_Clear();for(i=0;i<5;i++)OLED_ShowChinese(i*16,0,24+i,16);}//抵达目的地
	else if(num==8)OLED_ShowNum(16*4,0,SZ_num,1,16);//路口目标数字号
	else if(num==9)OLED_ShowNum(16*4,16,WZ_X[SZ_num],3,16);//路口目标数字号的X坐标
	OLED_Refresh();	
}

根据相关注释可看出,对应num参数所代表的中文语句或数字
OLED_ShowChinese显示中文
OLED_ShowNum显示数字

void usart1_TX(u8 mode)//发送切换模式指令
{
	USART_PRINTF_FLAG=1;
	if(mode==1)printf("1");
	else if(mode==2)printf("2");
}

USART_PRINTF_FLAG确定从哪个串口printf函数发出数据,此处为串口1发出

void TIM3_IRQHandler(void)                            //TIM3中断
{
	if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
	{
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);   //清除TIMx的中断待处理位:TIM 中断源 
		if(CS_flag==0)//
		{
			if(count_flag==1)JS_num1++;
			else if(SBWZ_flag==0)JS_num2--;
			if(XJ_flag==1)
			{
				usart1_RX();//检测循迹数据
				output=rho_PID_calc(&rho_pid,rho,8)+theta_PID_calc(&theta_pid,theta,0);//处理循迹数据
				if(STOP_flag>20)STOP_flag=20;//连续十次都没有接受到
				if(STOP_flag!=20)motor_XZ(output);//电机循迹
				else motor_ZFZ(0,0);//停车
			}
			else if(SB_flag==1)usart1_RX();//检测识别数据
		}
		else {usart1_RX();usart3_RX();	}

	}
}

在定时器中断函数中有几种分支处理,分为测试处理,非测试处理包含(循迹处理,数字识别处理,计时处理,倒计时处理)
测试处理用于组装小车后检测小车硬件状态使用,仅运行usart1_RX();usart3_RX();进行串口接收数据测试
非测试处理则为正常运行情况:
1、通过count_flag、SBWZ_flag这两个标志位处理题目2中测速与控制识别位置的相关数据
2、通过XJ_flag、SB_flag决定摄像头工作模式下的数据处理,数字识别状态下只需要运行usart1_RX()即可,但循迹状态下则还要将接收数据进行PID处理,并通过STOP_flag判断小车是否循迹出界,当STOP_flag满足相关条件则强制停车,STOP_flag的大小由usart1_RX()得出。在未满足出界情况,小车将执行motor_XZ(output)函数,进行循迹前进。
其中的rho_PID_calc()、theta_PID_calc()PID代码出自以下链接,有需要同学请自行了解
从0编写一份PID控制代码

//处理从OPENMV接收到数据
//OPENMV发来的数据是[xxx,xxx]数据
//将括号内第一个数据处理到rho,第二个数据处理到thera
void usart1_RX(void)//接收函数
{
		u8 t=0;
		u8 flag=0;//位置标志位
		u8 z_flag=0;//中间标志位
		u8 w_flag=0;//']'结尾标志位
		u8 w_flag2=0;//')'结尾标志位
		if(USART1_RX_STA&0x8000)
		{
				rho=0;
				theta=0;
/************将接收的数据转化成stm32能使用的数据******************/					
				if(USART1_RX_BUF[0]==0x5B)  //判断是否是"["
				{
					while(USART1_RX_BUF[flag]!=0x2C)flag++;//判断是否是","
					z_flag=flag;
					while(USART1_RX_BUF[flag]!=0x5D)flag++;//判断是否是]
					w_flag=flag;
						for(t=z_flag-1;t>0;t--)
						{
						if(USART1_RX_BUF[t]!='-')
						rho+=(USART1_RX_BUF[t]-'0')*pow(10,z_flag-1 - t);
						else
							rho=-1*rho;
						}
						
						for(t=w_flag-1;t>z_flag;t--)
						{
						if(USART1_RX_BUF[t]!='-')
						theta+=(USART1_RX_BUF[t]-'0')*pow(10,w_flag-1 - t);//运算10的次方
						else
						theta=-1*theta;
						}
				}
				else if(USART1_RX_BUF[0]==0x28) //判断是否是"("
				{
					WZ_X[0]=0;
					while(USART1_RX_BUF[flag]!=0x29)flag++;//判断是否是")"
					w_flag2=flag;
					for(t=w_flag2-1;t>3;t--)
					{
					if(USART1_RX_BUF[t]!='-')
					WZ_X[0]+=(USART1_RX_BUF[t]-'0')*pow(10,w_flag2-1 - t);//运算10的次方
					else
					WZ_X[0]=-1*WZ_X[0];
					}
					SZ_num=USART1_RX_BUF[2]-'0';
					WZ_X[SZ_num]=WZ_X[0];
					if((SZ_num==MB_num)&(WZ_X[SZ_num]<80))ZX_flag=1;
					else if((SZ_num==MB_num)&(WZ_X[SZ_num]>80))ZX_flag=2;
					else if((SZ_num!=MB_num)&(WZ_X[SZ_num]<80))ZX_flag=2;
					else if((SZ_num!=MB_num)&(WZ_X[SZ_num]>80))ZX_flag=1;
				}
			USART1_RX_STA=0;
				z_flag=0;
				w_flag=0;
				flag=0;
			STOP_flag=0;//停止传输标志位
		}
		else  STOP_flag++;
}

此部分串口接收处理,需要结合串口接收例程理解,在此只解释原创部分
通过数据格式判断当前接收数据为循迹数据或数字识别数据
循迹数据:根据定义的数据格式,得出各节点在数据中的位置并赋予z_flag、w_flag,得出数据rho、theta的长度,再通过for循环将其中数据进行相加,得出数据rho、theta的大小、正负。
识别数据:根据定义的数据格式,得出末节点在数据中的位置并赋予w_flag,得出识别数字的X坐标,并赋予WZ_X[0],在通过SZ_num与MB_num对比,以及WZ_X[SZ_num]的大小得出ZX_flag的数值
(用于题目2决定左转或右转)

此处用意为:当前识别数字与目标数字是否吻合并判断识别X坐标的左右偏向,得出小车转向选择。一共有四种结果。识别数字与目标数字匹配,识别X坐标在左半边,小车左转,X坐标在右,小车右转;数字不匹配,则相对反之。
由于小车循迹过程中,会一直反馈数据,但当小车超界后无法反馈循迹数据,STOP_flag++,连续20次无接收,则通过中断函数判断小车超界并执行motor_ZFZ(0,0)函数进行停车。

2.题目1处理代码

题目1程序流程图
题目1程序流程图

  void TM1(void)
 {
	 //根据题目1病房号左转或右转
	 		delay_ms(1000);
			delay_ms(1000);
   while((GRAY3==0)||(GRAY4==0));//当这两个都检测到,意味着识别到十字路口才执行转弯
	 XJ_flag=0;//循迹标志位清零
	 motor_ZFZ(0,0);
	 delay_ms(1000);
	 OLED_chTS(4);//转弯中
	if(MB_num==1)ZW90_cmd(1);//转弯处理
	else ZW90_cmd(2);
	delay_ms(1000);
	XJ_flag=1;//循迹标志位置1		
	while(STOP_flag!=20);//只有持续识别不到直线才到终点
	XJ_flag=0;
	LEDR=1;//卸货指示灯
	OLED_chTS(5);//请拿走货物
	while(XH_flag)//等待卸货
	{
		if(YW_KEY==1)
		{
		delay_ms(1000);
		delay_ms(1000);
		if(YW_KEY==1)XH_flag=0;				
		}	
	}
	LEDR=0;
	OLED_chTS(6);//调头中
	if(MB_num==1)ZW180_cmd(2);
	else ZW180_cmd(1);
	delay_ms(1000);
	OLED_chTS(3);//循迹中
	XJ_flag=1;

	while((GRAY3==0)||(GRAY4==0));//当这两个都检测到,意味着识别到十字路口才执行转弯
	XJ_flag=0;
	motor_ZFZ(0,0);
	 delay_ms(1000);
	OLED_chTS(4);//转弯中
	if(MB_num==1)F_ZW90_cmd(2);
	else F_ZW90_cmd(1);
	motor_ZFZ(0,0);
	delay_ms(1000);
	XJ_flag=1;
	while(STOP_flag!=20);//只有持续识别不到直线才到终点
	XJ_flag=0;
	motor_ZFZ(0,0);
	LEDG=1;
	OLED_chTS(7);//抵达目的地
	while(1);

1、GRAY3、GRAY4为灰度传感器IO口,当传感器没有被触发时为0,感应到红线,即十字路口时为1,跳出while循环。
2、当感应到十字路口后小车将停车一秒后执行转弯操作,ZW90_cmd()为转弯函数,根据题目1的识别数字判断左右转。ZW90_cmd()包含motor_R()、motor_L(),内部参数决定转动时间≈转动角度,此处需要在调试阶段进行微调,因为小车电机动力直连电池,因此在不同电压下,电机转动同等时间,距离角度会不同**(根据实际调试情况,左右转时间可能不一致)**
3、当小车转弯后继续巡线,默认巡线出界时为终点,停车等待取货,取货步骤与前面放入类似,只是标志位改为XH_flag,然后执行掉头程序ZW180_cmd(),与ZW90_cmd()类似,只是时间更长、角度更大。剩下内容与开头类似,F_ZW90_cmd()为返程时的转弯函数,可一一对应理解。

//电机相关函数
void ZW90_cmd(u8 set)
{
		if(set==2)motor_R(5);
		else if(set==1)motor_L(5);
}
//小车左转程序time为左转时间
void motor_L(u8 time)
{
		u8 i;
		motor_ZFZ(1,2);
		motor_ZFZ(2,2);
		motor_ZFZ(3,1);
		motor_ZFZ(4,1);
		TIM_SetCompare1(TIM2,4000);
		TIM_SetCompare2(TIM2,4000);
		TIM_SetCompare3(TIM2,4000);
		TIM_SetCompare4(TIM2,4000);
		for(i=time;i>0;i--)delay_ms(100);
		motor_ZFZ(0,0);
}
//小车左转程序time为右转时间
void motor_R(u8 time)
{
		u8 i;
		motor_ZFZ(1,1);
		motor_ZFZ(2,1);
		motor_ZFZ(3,2);
		motor_ZFZ(4,2);
		TIM_SetCompare1(TIM2,4000);
		TIM_SetCompare2(TIM2,4000);
		TIM_SetCompare3(TIM2,4000);
		TIM_SetCompare4(TIM2,4000);
		for(i=time;i>0;i--)delay_ms(100);
		motor_ZFZ(0,0);
}
//测试电机正反转驱动
void motor_ZFZ(u8 id,u8 cmd)
{
	if(id==1)
	{
		if(cmd==0){A1=0;A2=0;}    
		else if(cmd==1){A1=1;A2=0;}   //
		else if(cmd==2){A1=0;A2=1;}   //
	}
	else if(id==2)
	{
		if(cmd==0){B1=0;B2=0;}    
		else if(cmd==1){B1=1;B2=0;}   //
		else if(cmd==2){B1=0;B2=1;}   //
	}
		else if(id==3)
	{
		if(cmd==0){C1=0;C2=0;}    
		else if(cmd==1){C1=1;C2=0;}   //
		else if(cmd==2){C1=0;C2=1;}   //
	}
		else if(id==4)
	{
		if(cmd==0){D1=0;D2=0;}    
		else if(cmd==1){D1=1;D2=0;}   //
		else if(cmd==2){D1=0;D2=1;}   //
	}
		else if(id==0)
	{
		if(cmd==0){A1=0;A2=0;B1=0;B2=0;C1=0;C2=0;D1=0;D2=0;}    
		else if(cmd==1){A1=1;A2=0;B1=1;B2=0;C1=1;C2=0;D1=1;D2=0;}   //轮子正转测试
		else if(cmd==2){A1=0;A2=1;B1=0;B2=1;C1=0;C2=1;D1=0;D2=1;}   //轮子反转测试
	}
}

3.题目2处理代码

题目2程序流程图

 void TM2(void)
 {
	 count_flag=1;//开始计算时间
	 //根据题目1病房号左转或右转
	 		delay_ms(1000);
			delay_ms(1000);
   while(GRAY3==0||GRAY4==0);//		初次检测到十字,用于计算小车速度
	 count_flag=0;//停止计时
	 V=10*S1/JS_num1;
	 JS_num2=10*S2/V;//							根据以上公式得出小车抵达识别位置所需时间
	 SBWZ_flag=0;//等待计时器把数据归零抵达位置
	 while(SBWZ_flag==0)if(JS_num2==0)SBWZ_flag=1;//等待抵达识别位置
	 XJ_flag=0;//停止循迹
	 motor_ZFZ(0,0);//停车
	 OLED_Clear();
	 OLED_chTS(0);//识别数字
	 //开始识别
	 ZX_flag=0;//清零一次
	 printf("1");
	 SB_flag=1;//识别标志位打开
	 
	 while(ZX_flag==0);//						识别到目标数值并判断方向后,ZX_flag不等于0,跳出循环卡点
	 SB_flag=0;
	 OLED_chTS(8);//数字号
	 OLED_chTS(9);//数字号
	 printf("2");
	 //															等待3秒,用于观察OLED现象
	 delay_ms(1000);
	 delay_ms(1000);
	 delay_ms(1000);
	 XJ_flag=1;//开始循迹
	 while((GRAY3==0)||(GRAY4==0));//当这两个都检测到,意味着识别到十字路口才执行转弯
	 XJ_flag=0;
	 motor_ZFZ(0,0);
	 delay_ms(1000);
	 OLED_chTS(4);//转弯中
	if(ZX_flag==1)ZW90_cmd(1);//ZX_flag=1 左转 ZX_flag=2 右转
	else ZW90_cmd(2);
	delay_ms(1000);
	XJ_flag=1;				//转弯后循迹
	while(STOP_flag!=20);//只有持续识别不到直线才到终点
	XJ_flag=0;
	LEDR=1;//卸货指示灯
	OLED_chTS(5);//请拿走货物
	while(XH_flag)//等待卸货
	{
		if(YW_KEY==1)
		{
		delay_ms(1000);
		delay_ms(1000);
		if(YW_KEY==1)XH_flag=0;				
		}	
	}
	LEDR=0;
	OLED_chTS(6);//调头中
	if(ZX_flag==1)ZW180_cmd(2);
	else ZW180_cmd(1);
	delay_ms(1000);
	OLED_chTS(3);//循迹中
	XJ_flag=1;

	while((GRAY3==0)||(GRAY4==0));//当这两个都检测到,意味着识别到十字路口才执行转弯
	XJ_flag=0;
	motor_ZFZ(0,0);
	 delay_ms(1000);
	OLED_chTS(4);//转弯中
	if(ZX_flag==1)F_ZW90_cmd(2);
	else F_ZW90_cmd(1);
	motor_ZFZ(0,0);
	delay_ms(1000);
	XJ_flag=1;			
	while(STOP_flag!=20);//只有持续识别不到直线才到终点
	XJ_flag=0;
	motor_ZFZ(0,0);
	LEDG=1;
	OLED_chTS(7);//抵达目的地
	while(1);
 }

题目2的操作与题目1类似,区别在于开头通过JS_num1计时计算出车速V,得出从第一十字到抵达识别位置所需时间,等待小车行走JS_num2到达识别位置,开启摄像头数字识别,从而得出ZX_flag的数值,判断左右转操作,可往前观察usart1_RX()函数内处理。

三、硬件方案

1.原理图与PCB

在这里插入图片描述
在这里插入图片描述
稳压模块使用了两款AMS1117模块,分别是3.3V、5V的,电源使用11.1V的航模电池,XT60接口。
其他模块对应PCB板子上对应插上即可

2.材料清单

以下是材料清单不完全图片,材料成本约为567.2,清单文件在文末会给出。
在这里插入图片描述


总结

1、按照资料制作的小车,只需调试ZW90_cmd、ZW180_cmd、F_ZW90_cmd内,对应的motor_R()、motor_L()中的参数即可实现效果
2、小车预留了MPU6050陀螺仪接口、HC-05蓝牙模块接口,可用于后续扩展功能。

链接:https://pan.baidu.com/s/1NFYKn5YgpUaa1BN3sJAEkQ
提取码:wp0l
–来自百度网盘超级会员V4的分享
欢迎在评论区讨论与咨询问题

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值