一、前言

本人有幸参加2023年全国大学生电子设计竞赛,我们团队选择了E题-运动目标控制与自动追踪系统,我在团队中负责视觉部分,因为我们手头有两个OpenMV,故选择了使用OpenMV。

二、问题分析与设计

针对题目要求,我们团队设计出相应的背景板(如下图)

前两问我们团队是半写死的,这里我不做过多追叙。

基础部分我只说明第三问的(第四问和第三问同样道理)

基础部分第三问:红色激光点沿着A4长方形边运动

这里的解决思想就是让Openmv检测到长方形的四边,并将长方形四角坐标反馈给单片机处理。

很幸运的告诉大家,OpenMV官方已经有相关的驱动程序了

import sensor, image, time

sensor.reset()
sensor.set_pixformat(sensor.RGB565) # 灰度更快(160x120 max on OpenMV-M7)
sensor.set_framesize(sensor.QQVGA)
sensor.skip_frames(time = 2000)
clock = time.clock()

while(True):
    clock.tick()
    img = sensor.snapshot()

    # 下面的`threshold`应设置为足够高的值,以滤除在图像中检测到的具有
    # 低边缘幅度的噪声矩形。最适用与背景形成鲜明对比的矩形。

    for r in img.find_rects(threshold = 10000):
        img.draw_rectangle(r.rect(), color = (255, 0, 0))
        for p in r.corners(): img.draw_circle(p[0], p[1], 5, color = (0, 255, 0))
        print(r)

    print("FPS %f" % clock.fps())

上面的程序会将OpenMV检测到的矩形的四角坐标打印出来,效果如下图

这里要注意,这里返回的坐标是矩形四角在OpenMV中的坐标,坐标返回顺序是左下、右下、右上、左上。如果要实际使用的话,需要处理。

当然,如果可以做到识别的话,接下来就是数据处理,让其运动起来。这里提一嘴,我们团队用的是线性回归方程,将目标角坐标转化为PWM值,实现瞬移效果。

以下是OpenMV与STM32通信代码,可以实现坐标数据的传输与数据过滤,我完整放出了,求三连~

import sensor, image, time , math, pyb, ustruct
from pyb import LED, UART
thresholds = [(1, 20, -20, 19, -13, 17)]
sensor.reset()
sensor.set_pixformat(sensor.RGB565) # 灰度更快(160x120 max on OpenMV-M7)
sensor.set_framesize(sensor.QQVGA)
sensor.skip_frames(time = 2000)
clock = time.clock()
sensor.set_vflip (True)
sensor.set_hmirror (True)
led = pyb.LED(3).on()
uart = UART(3,115200)   #定义串口3变量
uart.init(115200, bits=8, parity=None, stop=1)
flag = 1
check = 0
x_range = 0
y_range = 0
x_last_range = 0
y_last_range = 0
high = 0
high_range = 0
wight = 0
wight_range = 0
inix = 0
iniy = 0
wight_xiangsu = 0
high_xiangsu = 0
li_list = []
x_list = []
y_list = []

while(True):
    clock.tick()
    img = sensor.snapshot()
    for r in img.find_rects(threshold = 33000):
        img.draw_rectangle(r.rect(), color = (255, 0, 0))
        led = pyb.LED(3).off()
        led = pyb.LED(2).on()
        for li in r.corners():
            img.draw_circle(li[0], li[1], 5, color = (0, 255, 0))
            #print(li,xielu)
            #print(check)
            if check >= 10 and flag < 4: # 上电10次求出平均值和标志点坐标
                flag += 1               # 实验发现10次求平均值后得到的原始数据会乱序(总体向前3个单位)
                li_list.append(li)      # 加注flag < 4既等待3个单位以顺序正确
                led = pyb.LED(2).off()
                led = pyb.LED(1).on()
                inix = (x_range/10)
                iniy = (y_range/10)
                wight_xiangsu = (wight_range / 10)
                high_xiangsu = (high_range / 10)


            else:
                li_list.append(li)
                if len(li_list) >= 4: # 当li_list里数据超过4个但不超过5个时(正常情况)绘制辅助边框
                    if len(li_list) < 5: # 实验发现当等待3个单位后,数据长度异常为5位 以此过滤
                        print(li_list,check,flag)
                        x_list = [x[0] for x in li_list]# 读取元组
                        y_list = [y[1] for y in li_list]
                        if flag == 4:  #  平均值得出后执行绘制辅助区域程序
                            wight = (wight_xiangsu-inix)/21
                            high = (high_xiangsu-iniy)/30
                            wucha = wight-high
                            print("误差为 :",wucha)
                        else:
                            if abs(x_list[3]-x_last_range) < 10 and check >= 1 and abs(y_list[3]-y_last_range) < 10: # 10次检测中过滤误差大于10的数据
                                x_range += x_list[3]
                                y_range += y_list[3]
                                wight_range = wight_range + (x_list[1] + x_list[2])/2
                                high_range = high_range + (y_list[0] + y_list[1])/2
                                check+=1
                                x_last_range = x_range / check
                                y_last_range = y_range / check
                                #print('next')
                            elif  check == 0:
                                x_range += x_list[3]
                                y_range += y_list[3]
                                wight_range = wight_range + (x_list[1] + x_list[2])/2
                                high_range = high_range + (y_list[0] + y_list[1])/2
                                check+=1
                                x_last_range = x_range / check
                                y_last_range = y_range / check
                                #print('first')
                            else:
                                x_last_range = x_range / check
                                y_last_range = y_range / check
                                #print('error')
                        #data = bytearray([0xb3,0xb3,inix,iniy,x_list[0]-inix,x_list[1]-inix,x_list[2]-inix,x_list[3]-inix,y_list[0]-iniy,y_list[1]-iniy,y_list[2]-iniy,y_list[3]-iniy,0x5b])# 重新构建的坐标系发送32处理/?在openmv内部处理
                        data = bytearray([0xb3,0xb3,x_list[0],x_list[1],x_list[2],x_list[3],y_list[0],y_list[1],y_list[2],y_list[3],0x5b])
                        uart.write(data)
                        x_list.clear()
                        y_list.clear()
                        li_list.clear()
                    else:
                        x_list.clear()
                        y_list.clear()
                        li_list.clear()

视频效果如下

2023年电赛E题OpenMV沿矩形划线

发挥部分追踪问题

这里相信大家都会遇到一个问题,那就是——黑色胶带吸红光

针对这个问题,两种解决办法

第一种:采用更亮的激光笔,这样可以有效解决问题,但是发热严重

第二种:调整OpenMV图像设置

sensor.skip_frames(time=200) # 跳过多少帧
sensor.set_auto_exposure(False, 1000)#调节曝光度
sensor.set_auto_gain(False) # 关闭增益(色块识别时必须要关)
sensor.set_vflip (True)
sensor.set_hmirror (True)

只要在追踪方云台端的OpenMV程序中加入以上代码可解决问题,即使红色光较弱也可以完美识别到。

三、总结

以上可以完美解决视觉部分问题,只是很可惜,因为各种原因,最终只拿到了省一,无缘国赛,不过还是想在此与大家分享此次有意义的经历。如果大家有不懂得地方可以在评论区留言,或者私信我,一起探讨!如有不对的地方还望各位大佬斧正!求三连~谢谢大家!

转自:
https://blog.csdn.net/m0_66418395/article/details/133280948