电赛OpenMV巡线巡迹

一段小废话:我是22年参加的电赛,当时只有省赛,巡迹我们也是用的openmv,当时也没用过,就是在官网现学,也不难,但是里面不会教你具体怎么巡迹,只是一些基础使用的语法和算法,我也在网上查了很多,CSND上要么查出来一堆废话,或者要么源码要收钱,或者代码可读性很差,几乎用处不大。最后还是在b站上看到一位老哥做送药小车的比赛,从他的视频得到一些思路。然后自己根据具体的任务,把算法结合起来,完成任务。对于想要快速上手的人来说,思路是最重要的,给一堆函数有什么作用,不会组合使用就完全没有作用,有了思路之后再去查某个任务要用什么函数才是最正确的。

我们当时的任务是要完成邻库有车和邻库无车的倒车入库。规定是要倒入第二个库。

因此我的思路就是从起点出发,找到第三个直角的位置停下。后面侧方停车的原理同样。

下面直接上代码讲解:每一步的实现效果图当时没有保存,因为我本来不喜欢写东西,就没记录,但是看到这部分内容确实不太行,所以就想着写一下,让大家能够快速上手思路

 1:读取图片并预处理(建议使用灰度图,RGB的干扰比较多)

sensor.set_pixformat(sensor.GRAYSCALE)#转为灰度图
sensor.set_framesize(sensor.QVGA)#分辨率
sensor.skip_frames(time = 2000)#初始给2秒的停顿让相机稳定
sensor.set_auto_gain(False)#灰度图巡迹的时候建议关闭自动增益调节功能,可能会影响要二值图的完整性。
sensor.set_auto_whitebal(False)#同样也不建议开启,白平衡

第二行用来设置分辨率的,分辨率太高的话,运行速度会很慢,而且会出来卡顿的情况,不建议用,因为使用QQQVGA、QQVGA就差不多了,不需要特别清晰的图像。这个分辨率改了的话,图像画布大小就变了,后面设置roi区域就需要重新绘制。

2:通信。设置好和主板一样的波特率之后,通过openmv的tx给板子的rx发送信息,使用openmv的rx接收板子tx发送来的信息。

uart = UART(3,115200)#通信的波特率设置,与主板需要设置成一样才能进行通信

这个部分我们当时没用,因为板子当时的通信有一个脚坏了,信息只有一方能接收到,当时因为时间比较紧,我们也就没去继续排查。如果想使用rx和tx通信的可以去别的博主那边参考一下。我是使用的下面这个方法。

#开启管脚
ain1 =  Pin('P0', Pin.OUT_PP)
ain2 =  Pin('P1', Pin.OUT_PP)
ain3 =  Pin('P2', Pin.OUT_PP)
ain4 =  Pin('P3', Pin.OUT_PP)

使用openmv上的管脚输出高低电平,来给主板发送指令。用二进制的组合方式,可以发送16种,对于只需要发送离散信息的来说肯定够用了。比如,只发:左转、右转等,如果是想要发送具体的偏转角之类的,就需要使用异步uart进行通信。

我们当时是发送停车,左转,右转,左急转等

3:巡迹

roi1 = [(1, 34, 42, 207),#0

(283,36,37,207),#1

(145,0,37,35),#2

(108,0,26,34),#3

(196,1,27,34),#4

(147,72,38,23),#5

(68,64,46,31),#6

(216,63,32,32),#7

(82,201,162,39),#8

(72,126,37,57),#9

(208,130,39,51),#10

(0,0,320,240),#11

(0,79,320,162)]#12

我使用很多的roi框,来区分当前看到图像属于什么情况。

直接用鼠标在摄像头显示页面上框选区域,下面就会出现roi的4个坐标,复制过来即可。

图像二值化    img = sensor.snapshot().binary([THRESHOLD])

 这个图可能会有点乱,因为都是黑白线。

解释一下:不想看的直接可以跳到后面,有清晰的讲解

多个 roi 框的组合应用,生成直角,边线,平行,距离过近,距 离过远,距离正好,无效等 例如:

当框 0,1,2,5 全为 True 同时 3 和 4 为 False 时,识别为直 角,统计直角数量,当记到 3 时,给 MSP432 发送倒车指令,同时将 直角数量置零。当完成倒车入库后,出库之后进入侧方停车的过程中, 会识别倒车入库赛道剩下的直角,造成干扰,两种解决方案:①令小 车在驶出倒车入库赛道后,MSP432 给 openmv 发送将直角数置零。② 下一次统计直角数量变为 5 时,开始侧方停车。

当框 2,3,4 全为 True 时,识别为箭头干扰或者车辆干扰,不 识别成直角

当框 6 和 7 为 True 时,识别为距离过远,需要右转

当框 9 和 10 为 True 时,识别为距离正好 当框 8 为 True 时,识别为距离过近,需要左转

在框 12 内进行霍夫变换 get_regression([(255,255)],阈值为 255,全白,返回 r=xcosθ+ysinθ, 计算 rho_err = abs(line.rho())-img.width()/2,

if line.theta()>90:

        theta_err = line.theta()-180

else:

        theta_err = line.theta()

因此根据 theat_err 的正负值来进行判断左右转向。 当 theat_err 过于倾斜时,会触发左急转弯,防止触线。

我没保留其他的图,只有这个图,下面我用其他的颜色来区分一下:

当上面3个绿框都存在白色的情况下,就代表是直角,当然我们摄像头的位置,不会是这样的,角度会架的比较低。像下面这样,我用画笔画的一个示意图 。因此如果想要识别直角就可以用这三个框。要完成别的识别的话就再加框。

代码示例如下,其他框的判断同理:第一个参数【(255,95)】这个是二值图的阈值,这个是事先试出来的,试出来哪个阈值范围是白色。如果赛道识别的颜色,也一样同理。可以使用阈值选择器来选择阈值。

if img.find_blobs([(255, 95)],roi=roi1[0],area_threshold=100):
    left_flag=1
if img.find_blobs([(255, 95)],roi=roi1[1],area_threshold=100):
    right_flag=1

选择阈值选择器,进去之后自然就知道怎么用

直角示意图

 到了侧方停车的时候,因为有一个横着的箭头,也能满足这个情况。所以这个情况会有bug

因此我才把上面那个框,换成了3个,只有中间框有白色,并且下面那个红框也有白色,旁边两个框没有的情况,才是直角 。当统计到3个直角的时候,发送信息。其实是一直都在发送信息,只不过当在第3个直角时,发送停车信息。

然后为了保持和边线在一定的距离。我又增加了两组的框。

 如果当白线在框1和框2的位置的话,说明距离比较远了,需要向靠近边线的地方靠近。如果在框3和框4的位置,说明距离比较近了,需要远离。如果在框5内了,说明距离已经特别近了。需要急转逃离,不然正常角度的转弯可能还是会压线,当然这种情况是比较少见的,只要说预防,万一出现,还有补救的可能,不至于直接g了,因为在场上比赛的时候,电机是最大的不确定因素,很有可能给一样的pwm波,但是转的速度会慢很多。注:图中的框的位置需要根据直接摄像头在车上的位置进行调节。自己到时候连上电脑,在场地试一下就知道。

接下来就是偏转角的问题。

line = img.get_regression([(255,255)],roi=roi1[12])

 在框12内做像素值的线性计算,使用get_regression函数即可,第一个参数表示像素范围,我们只需要计算255即白色区域,第二个参数指定roi区域。

得到的y=wx+b的形式。

得到line里面包括line.rho()和line.theta(),可以理解成w和b,但是b是0~180的,所以我当时判断如果b大于90的话,减去180,变成负的,或者直接判断是小于90还是大于90,就可以判断是往左偏,还是往右偏。

之后的侧方也是一样的道理

然后是邻库有车和无车的区别

两种解决方案:(1):把摄像头的角度架第一点,就看不到车了,那样干扰也会变少,甚至箭头的干扰也没有了,邻库有车和无车就用同样的算法。

(2)把有车的情况下,因为看到的是车头,把它当成和侧方停车那个横着的箭头一样的情况排除就行了。同样还是检测第三个直角。

下面是完整代码:不知道为什么openmv的注释会自己删了。因为最近忙的很,就不写注释啦,代码也比较简单,既然都知道我的思路了,没必要硬看代码,不同题目代码也不一样。注:当时是拿了省一,所以这个代码思路还是有用的。大伙加油吧,好好备赛,电赛没多难。还有目标识别部分没写,等下回什么时候比较空的时候再写。

import sensor, image, time,ustruct
from pyb import Pin, Timer
from pyb import UART,LED
sensor.reset()
#sensor.set_pixformat(sensor.RGB565)
sensor.set_pixformat(sensor.GRAYSCALE)#转为灰度图
sensor.set_framesize(sensor.QVGA)#分辨率
sensor.skip_frames(time = 2000)#初始给2秒的停顿让相机稳定
sensor.set_auto_gain(False)#灰度图巡迹的时候建议关闭自动增益调节功能,可能会影响要二值图的完整性。
sensor.set_auto_whitebal(False)#同样也不建议开启,白平衡
uart = UART(3,115200)#通信的波特率设置,与主板需要设置成一样才能进行通信
LED(1).on()
LED(2).on()
LED(3).on()
#开启管脚
ain1 =  Pin('P0', Pin.OUT_PP)
ain2 =  Pin('P1', Pin.OUT_PP)
ain3 =  Pin('P2', Pin.OUT_PP)
ain4 =  Pin('P3', Pin.OUT_PP)
ain1.high()
clock = time.clock()
roi1 =	 [(1, 34, 42, 207),
            (283,36,37,207),
            (145,0,37,35),
            (108,0,26,34),
            (196,1,27,34),
            (147,72,38,23),
            (68,64,46,31),
            (216,63,32,32),
            (82,201,162,39),
            (72,126,37,57),
            (208,130,39,51),
            (0,0,320,240),
            (0,79,320,162),
            (69,1,171,48),#小车
            (2,1,148,47)]##小车和边线

THRESHOLD = (0, 95)
r_num=0
car=0
car_1=0
flag_r=0
flag_c=0
#发送信息
def outuart(w,b,flag1,flag2):
    ain2.low()
    ain3.low()
    ain4.low()
    global uart;
    f_w=0
    f_b=0
    if flag2==0:
        if flag1==1:
            f_w=1
        else:
            f_w=0
        if flag1==1:
            f_b=1
        else:
            f_b=0
    if flag1==2 and flag2==1:
        w,b,f_w,f_b=(0,0,0,0)
    if flag2==2:
        print('准备倒车')
        ain4.high()
        time.sleep_ms(200)
    else:
        if  b>0 and f_w==0 and f_b==0 and flag1!=3:
            print("不平行左转")
            ain3.high()
        if  b<0 and f_w==0 and f_b==0:
            print('不平行右转')
            ain3.high()
            ain4.high()
        if  b>0 and f_w==1 and f_b==1:
            print('直角且不平行左转')
            ain3.high()
        if  b<0 and f_w==1 and f_b==1:
            print('直角且不平行右转')
            ain3.high()
            ain4.high()
        if w==0 and b==0 and f_w==0 and f_b==0:
            print('平行不调整')
        if  w>33 and b>0 and f_w==0 and f_b==0 and flag1==3:
            print("距离过近-左转急")
            ain2.high()
            ain3.high()
while(True):
    clock.tick()
    img = sensor.snapshot().binary([THRESHOLD])
    #像素值线性计算,计算偏转角,y=wx+b
    line = img.get_regression([(255,255)],roi=roi1[12])

    if (line):#如果存在白色区域
        #w
        rho_err = abs(line.rho())-img.width()/2
        if line.theta()>90:
            #b
            theta_err = line.theta()-180
        else:
            theta_err = line.theta()
        img.draw_line(line.line(), color = 127)
        if line.magnitude():
            outdata=[rho_err,theta_err,0]
            print(outdata)
            #标志位
            left_flag,right_flag,up_flag,down_flag,left_up_flag,right_up_flag=0,0,0,0,0,0
            up_down_flag,up_down_left_flag,up_down_right_flag,bian_left,bian_right=0,0,0,0,0
            car_flag=0
            #绘制前面的rio框,方便查看
            for rec in roi1:
                img.draw_rectangle(rec, color=(255,0,0))
            #根据框内是否有白色区域来判断当前图像是什么情况
            if img.find_blobs([(255, 95)],roi=roi1[0],area_threshold=100):
                left_flag=1
            if img.find_blobs([(255, 95)],roi=roi1[1],area_threshold=100):
                right_flag=1
            if img.find_blobs([(255, 95)],roi=roi1[2],area_threshold=100):
                print('up')
                up_flag=1
            if img.find_blobs([(255, 95)],roi=roi1[3],area_threshold=100):
                print('left_up')
                left_up_flag=1
            if img.find_blobs([(255, 95)],roi=roi1[4],area_threshold=100):
                print('right_up')
                right_up_flag=1
            if img.find_blobs([(255, 95)],roi=roi1[5],area_threshold=100):
                print('up_down')
                up_down_flag=1
            if img.find_blobs([(255, 95)],roi=roi1[6],area_threshold=100):
                print('up_down_left')
                up_down_left_flag=1
            if img.find_blobs([(255, 95)],roi=roi1[7],area_threshold=100):
                print('up_down_right')
                up_down_right_flag=1
            if img.find_blobs([(255, 95)],roi=roi1[8],area_threshold=100):
                down_flag=1
            if (left_flag==1 and right_flag==1 and up_flag==1 and left_up_flag==0 and right_up_flag==0 and up_down_flag==1) or (left_flag==1 and up_flag==1 and left_up_flag==0 and right_up_flag==0 and up_down_flag==1) or (right_flag==1 and up_flag==1 and left_up_flag==0 and right_up_flag==0 and up_down_flag==1) :
                print('直角')
                if flag_r==0 and flag_c==1:
                    flag_r=1
                    flag_c=0
                    r_num+=1
                outuart(outdata[0],outdata[1],1,0)
            if up_flag==1 and left_up_flag==1 and right_up_flag==1 and up_down_flag==0:
                print('箭头')
            if img.find_blobs([(255, 95)],roi=roi1[13],area_threshold=3500):
                car_flag=1
            if left_flag==1 and right_flag==1 and up_down_flag==0 and down_flag==0:
                print('边线')
                if car_flag==0:
                    flag_c=1
                    flag_r=0
                if img.find_blobs([(255, 95)],roi=roi1[12]) and (-88>=outdata[1]>=-90 or 88<=outdata[1]<=90):
                    print('平行')
                    if img.find_blobs([(255, 95)],roi=roi1[9],area_threshold=100) :
                        bian_left=1
                    if img.find_blobs([(255, 95)],roi=roi1[10],area_threshold=100) :
                        bian_right=1
                    if bian_right==1 and bian_left==1:
                        outuart(0,0,2,1)
                        print('距离正好')
                    if up_down_left_flag==1 and up_down_right_flag==1:
                        print('距离过远')
                        outuart(outdata[0],outdata[1],2,0)
                        outuart(0,-10,2,0)
                else:
                    outuart(outdata[0],outdata[1],2,0)
            if down_flag==1:
                print('距离过近')
                outuart(outdata[0],10,3,0)

            if img.find_blobs([(255, 95)],roi=roi1[13],area_threshold=3500):
                if (flag_r==0 and flag_c==1) or (flag_r==1 and flag_c==0):
                    car+=1
                    flag_c=0
                    flag_r=0

            #if car==1 and img.find_blobs([(255, 95)],roi=roi1[14],area_threshold=3000) :
                #car_1=1
            if car==2:
                outuart(0,0,2,2)
                r_num=0
                car=0
                car_1=0
    else:
        print('不符合')
    #print(r_num)
    print(car)
    #print(car_1)
    if r_num==3:
        outuart(0,0,2,2)
        r_num=0

  • 32
    点赞
  • 236
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值