一、赛题

1. 任务

设计制作一个运动目标控制与自动追踪系统。系统包括模拟目标运动的红色光斑位置控制系统和指示自动追踪的绿色光斑位置控制系统。系统结构示意及摆放位置见图 1(a)。图中两个激光笔固定在各自独立的二维电控云台上。
在这里插入图片描述

红色激光笔发射的光斑用来模拟运动目标,光斑落在正前方距离 1m 处的白色屏幕上,光斑直径≤1cm。红色光斑位置控制系统控制光斑能在屏幕范围内任意移动。

绿色激光笔发射的光斑由绿色光斑位置系统控制,用于自动追踪屏幕上的红色光斑,指示目标的自动追踪效果,光斑直径≤1cm。绿色激光笔放置线段如图 1(b)所示,该线段与屏幕平行,位于红色激光笔两侧,距红色激光笔距离大于 0.4m、小于 1m。绿色激光笔在两个放置线段上任意放置。

屏幕为白色,有效面积大于 0.6╳0.6m2。用铅笔在屏幕中心画出一个边长0.5m 的正方形,标识屏幕的边线;所画的正方形的中心为原点,用铅笔画出原点
位置,所用铅笔痕迹宽≤1mm。

2. 要求

  • 基本要求
    (1)设置运动目标位置复位功能。执行此功能,红色光斑能从屏幕任意位置回到原点。光斑中心距原点误差≤2cm。
    (2)启动运动目标控制系统。红色光斑能在 30 秒内沿屏幕四周边线顺时针移动一周,移动时光斑中心距边线距离≤2cm。
    (3)用约 1.8cm 宽的黑色电工胶带沿 A4 纸四边贴一个长方形,构成 A4 靶纸。将此 A4 靶纸贴在屏幕自定的位置。启动运动目标控制系统,红色光斑能在30 秒内沿胶带顺时针移动一周。超时不得分,光斑完全脱离胶带一次扣 2 分,连续脱离胶带移动 5cm 以上记为 0 分。
    (4)将上述 A4 靶纸以任意旋转角度贴在屏幕任意位置。启动运动目标控制
    系统,要求同(3)。
  • 发挥部分
    (1)运动目标位置复位,一键启动自动追踪系统,控制绿色光斑能在 2 秒内追踪红色光斑,追踪成功发出连续声光提示。此时两个光斑中心距离应≤3cm。
    (2)运动目标重复基本要求(3)~(4)的动作。绿色激光笔发射端可以放置在其放置线段的任意位置,同时启动运动目标及自动追踪系统,绿色光斑能自动追踪红色光斑。启动系统 2 秒后,应追踪成功,发出连续声光提示。此后,追踪过程中两个光斑中心距离大于 3cm 时,定义为追踪失败,一次扣 2 分。连续
    追踪失败 3 秒以上记为 0 分。运动目标控制系统和自动追踪系统均需设置暂停键。同时按下暂停键,红色
    和绿色光斑应立即制动,以便测量两个光斑中心距离。
    (3)其他。

3. 说明

(1)红色、绿色光斑位置控制系统必须相互独立,之间不得有任何方式通信;光斑直径小于 1cm;屏幕上无任何电子元件;控制系统不能采用台式计算机或笔记本电脑。不符合要求不进行测试。
(2)基本要求(3)、(4)未得分不进行发挥部分(2)的测试。

二、任务清单

1. 硬件部分

  • 主控板: MSP432P401R
  • 电池: 18650锂电池
  • 识别: OpenMV H7 Plus
  • 声光: 蜂鸣器、RGB灯
  • 电压转换: TB6612
  • 视觉云台: 3D打印、某宝自购
  • 仪器: 3D打印机
  • 转向: 数字舵机(7.4v驱动)
  • 稳压:LM2596S DC-DC
  • 激光笔:红、绿
  • 70*70cm屏幕及A4纸
  • 其他: 1m直尺、铜柱、螺丝螺母、开关、面包板等。

2. 软件部分

编译器: Keil、OpenMV IDE
建模软件: SketchUp Pro 2022
切片软件: Cura
编程方式: 库函数
编程语言: C、Python

三、赛题分析

阅读题后可知,屏幕的高度和位置都是自定的,且在摆放完成后不再改变 以及需要自带屏幕,而激光发射点是自定的,也就是你可以在距屏幕外的1m处任意改变摆放位置,但在摆放完成后不再改变,此题不限板子,stm32、树莓派、TI任选即可。

在基础题中,我第一、二、三问是直接写死的,即通过第一题将激光指向中心点进行位置校准,第二、三题即可通过第一题的校准进行功能实现,在第二、三题中,我先将红色激光点指向屏幕左下角,然后在完成相应任务,即控制舵机转动指定角度、激光笔

1. 基础题

第一题

由题知,该题为目标点置位,也就是将红激光点复位至屏幕中心处,屏幕、激光位置都是已知的,我的建议是测出中心点相对于激光的角度位置,直接通过按下置位按钮,俩舵机转向相应角度,即激光指向屏幕中心,第一题就此完成。

示例如下:

void basic_one(void)
{
	Steer_Up_angle(109);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
	Steer_Down_angle(76);
}

第二题

第二题同理,直接初始化将激光指向一个角,然后控制舵机转向指定角度和距离。

示例如下:

void basic_two(void)
{
	Steer_Up_angle(106);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
	Steer_Down_angle(90);
	while(1)
	{
		int i;
		for(i=96;i<123;i++)
		{
			if(i==105) Steer_Down_angle(91);
			Steer_Up_angle(i);
			delay_ms(80);
		}
		for(i=88;i>=63;i--)
		{
			if(i==87) Steer_Up_angle(123);
			Steer_Down_angle(i);
			delay_ms(80);
		}
		for(i=122;i>=95;i--)
		{
			if(i==115) Steer_Down_angle(62);
			Steer_Up_angle(i);
			delay_ms(80);
		}
		for(i=63;i<=90;i++)
		{
			Steer_Down_angle(i);
			delay_ms(80);
		}
	}
}

第三题

由于第三题同理A4纸黑色矩型是由自己摆放的,所有该题可按第二题做相同处理,直接初始化将激光指向一个角,然后控制舵机转向指定角度和距离。

示例如下:

void basic_three(void)
{
	Steer_Up_angle(106); 
	Steer_Down_angle(92);
	while(1)
	{
		int i;
		for(i=113;i<=123;i++)
		{
			Steer_Up_angle(i);
			delay_ms(200);
		}
		for(i=88;i>=76;i--)
		{
			Steer_Down_angle(i);
			delay_ms(200);
		}
		for(i=122;i>=112;i--)
		{
			Steer_Up_angle(i);
			delay_ms(200);
		}
		for(i=76;i<=92;i++)
		{
			Steer_Down_angle(i);
			delay_ms(200);
		}
	}
}

第四题

该题同第三题的区别为,由自己摆放改为专家指定位置摆放,所以位置不定,具体做法如下:
①找出黑色矩型四个角坐标,确定矩形位置。
②计算每条边的斜率,实际只需测出俩条边即可(俩俩平行)。
③初始化走向指定矩形一个角,然后按照指定位置移动。

  • 1. 获取四个角坐标
img = sensor.snapshot().lens_corr(strength = 1.8, zoom = 1.0)
 for r in img.find_rects(threshold = 10000,roi=ROI4):
     # 判断矩形边长是否符合要求
     if r.w() > 20 and r.h() > 20:
         # 在屏幕上框出矩形
         #img.draw_rectangle(r.rect(), color = (255, 0, 0), scale = 4)
         # 获取矩形角点位置
         corner = r.corners()
         # 在屏幕上圈出矩形角点
         img.draw_circle(corner[0][0], corner[0][1], 5, color = (0, 0, 255), thickness = 2, fill = False)
         img.draw_circle(corner[1][0], corner[1][1], 5, color = (0, 0, 255), thickness = 2, fill = False)
         img.draw_circle(corner[2][0], corner[2][1], 5, color = (0, 0, 255), thickness = 2, fill = False)
         img.draw_circle(corner[3][0], corner[3][1], 5, color = (0, 0, 255), thickness = 2, fill = False)

     # 打印四个角点坐标, 角点1的数组是corner[0], 坐标就是(corner[0][0],corner[0][1])
     # 角点检测输出的角点排序每次不一定一致,矩形左上的角点有可能是corner0,1,2,3其中一个
     corner1_str = f"corner1 = ({corner[0][0]},{corner[0][1]})"
     corner2_str = f"corner2 = ({corner[1][0]},{corner[1][1]})"
     corner3_str = f"corner3 = ({corner[2][0]},{corner[2][1]})"
     corner4_str = f"corner4 = ({corner[3][0]},{corner[3][1]})"
     PaiXun(corner[0][0],corner[1][0],corner[2][0],corner[3][0],corner[0][1],corner[1][1],corner[2][1],corner[3][1])

以上获取了四个角坐标,那怎么确定哪个角对应哪个角呢?别急,听我一一道来。
在这里插入图片描述

如上图,无论你怎么摆放,我都拟定最小的Y点坐标为起始点,即①角为起始角,即一开始我先令激光移动到该点后再沿矩形移动。

在这里插入图片描述

可发现,无论你怎么摆放,我拟定的移动路径都是:最小的Y点坐标 —> 最大的X点坐标 —> 最大的Y点坐标 —> 最小的X点坐标。所以接下来你只需驱动出这些点位坐标即可,对获取的四个点坐标进行处理,如下:

def PaiXun(x11,x22,x33,x44,y11,y22,y33,y44):
    global x1, x2, x3, x4, y1, y2, y3, y4


    if y11<y22 and y11<y33 and y11<y44:
        x1=x11
        y1=y11
        if x22>x33 and x22>x44:
            x2=x22
            y2=y22
            if x33>x44:
                x3=x33
                y3=y33
                x4=x44
                y4=y44
            else:
                x3=x44
                y3=y44
                x4=x33
                y4=y33
        elif x33>x22 and x33>x44:
            x2=x33
            y2=y33
            if x22>x44:
                x3=x22
                y3=y22
                x4=x44
                y4=y44
            else:
                x3=x44
                y3=y44
                x4=x22
                y4=y22
        elif x44>x22 and x44>x33:
            x2=x44
            y2=y44
            if x22>x33:
                x3=x22
                y3=y22
                x4=x33
                y4=y33
            else:
                x3=x33
                y3=y33
                x4=x22
                y4=y22

    elif y22<y11 and y22<y33 and y22<y44:
        x1=x22
        y1=y22
        if x11>x33 and x11>x44:
            x2=x11
            y2=y11
            if x33>x44:
                x3=x33
                y3=y33
                x4=x44
                y4=y44
            else:
                x3=x44
                y3=y44
                x4=x33
                y4=y33
        elif x33>x11 and x33>x44:
            x2=x33
            y2=y33
            if x11>x44:
                x3=x11
                y3=y11
                x4=x44
                y4=y44
            else:
                x3=x44
                y3=y44
                x4=x11
                y4=y11
        elif x44>x11 and x44>x33:
            x2=x44
            y2=y44
            if x11>x33:
                x3=x11
                y3=y11
                x4=x33
                y4=y33
            else:
                x3=x33
                y3=y33
                x4=x11
                y4=y11

    elif y33<y11 and y33<y22 and y33<y44:
        x1=x33
        y1=y33
        if x11>x22 and x11>x44:
            x2=x11
            y2=y11
            if x22>x44:
                x3=x22
                y3=y22
                x4=x44
                y4=y44
            else:
                x3=x44
                y3=y44
                x4=x22
                y4=y22
        elif x22>x11 and x22>x44:
            x2=x22
            y2=y22
            if x11>x44:
                x3=x11
                y3=y11
                x4=x44
                y4=y44
            else:
                x3=x44
                y3=y44
                x4=x11
                y4=y11
        elif x44>x11 and x44>x22:
            x2=x44
            y2=y44
            if x11>x22:
                x3=x11
                y3=y11
                x4=x22
                y4=y22
            else:
                x3=x22
                y3=y22
                x4=x11
                y4=y11

    else:
        x1=x44
        y1=y44
        if x11>x22 and x11>x33:
            x2=x11
            y2=y11
            if x22>x33:
                x3=x22
                y3=y22
                x4=x33
                y4=y33
            else:
                x3=x33
                y3=y33
                x4=x22
                y4=y22
        elif x22>x11 and x22>x33:
            x2=x22
            y2=y22
            if x11>x33:
                x3=x11
                y3=y11
                x4=x33
                y4=y33
            else:
                x3=x33
                y3=y33
                x4=x11
                y4=y11
        elif x33>x11 and x33>x22:
            x2=x33
            y2=y33
            if x11>x22:
                x3=x11
                y3=y11
                x4=x22
                y4=y22
            else:
                x3=x22
                y3=y22
                x4=x11
                y4=y11

根据处理后的结果进行操作,即(x1,y1)为起始点,从(x1,y1)依次移动到(x2,y2),(x3,y3),(x4,y4),在得到以上结果后具体怎么移动舵机呢?
这里我将舵机转向的精度控制到最小,也就是依次只对占空比进行±,而不是直接控制转向的角度,根据之前算出的四个点坐标进行计算,求出指定边斜率传给舵机进行移动,例如矩形为45°摆放,则俩舵机同时移动相同的角度,占空比+1,示例如下:

void basic_four(void)
{
	if(openmv_rx2=='1')
	{		
		steer_x--;
		MAP_Timer_A_setCompareValue(TIMER_A3_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_2, steer_x);
	}
	else if(openmv_rx2=='2')
	{
		steer_x++;
		MAP_Timer_A_setCompareValue(TIMER_A3_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_2, steer_x);
	}
	else if(openmv_rx2=='3')
	{
		steer_y--;
		MAP_Timer_A_setCompareValue(TIMER_A3_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_3, steer_y);
	}
	else if(openmv_rx2=='4')
	{
		steer_y++;	
		MAP_Timer_A_setCompareValue(TIMER_A3_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_3, steer_y);
	}	
	delay_ms(8);	
}

这里需要注意的是把控好延时的处理,因为精准的延迟可加大舵机的精确度。

2. 拓展题

第一题

该题为激光追踪,即绿色激光笔追向红色激光笔,此期间 红色激光笔开启置位功能,且用时不超过2s。绿色激光笔位置不定。
简单来说,只需将绿色激光笔在不同位置指向红色激光笔的置位点即可。
我这边红色激光是由单片机控制的舵机转向,绿色激光则是直接用openmv控制舵机转向。

  • 怎么在视觉上准确分为出红绿激光点呢?
    你会发现俩激光点的阈值是一样的,所以你可以通过发现色块的方式去区分红绿激光点,激光点小于等于1cm即可,经过本人测试,在相同条件下,绿色激光笔发射出来的激光点是远比红色亮且识别出的色块大些的,如果不放心你还可以调节激光点的大小去加大准确度。这样红绿激光点就能准确区分。

示例如下:

    img = sensor.snapshot() # Take a picture and return the image.

    blobs = img.find_blobs([red_threshold])
    if blobs:
        max_ID=[-1,-1]
        max_ID=find_max(blobs)   #找最大两色块
        pan_error = blobs[max_ID[1]].cx()-img.width()/2
        tilt_error = blobs[max_ID[1]].cy()-img.height()/2



        if flag==0:
            print(flag)
            if abs(blobs[max_ID[0]].cx()-blobs[max_ID[1]].cx())<30:
                flag=1
            else:
                pan_servo.angle(pan_servo.angle()+out)

        if flag==1:
            print(flag)
            if abs(blobs[max_ID[0]].cy()-blobs[max_ID[1]].cy())<=25:
                while(1):
                    uart.write("f")
                    flag=0
            else:
                tilt_servo.angle(tilt_servo.angle()+2)

第二题

由于该题本人并未实现,实现后再来与大家分享。

四、技术交流

疑难解答或技术交流联系下方wx即可👇👇👇

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐