2023年电赛---运动目标控制与自动追踪系统(E题)—— 视觉部分

news2025/6/10 11:53:43

文章目录

  • 一、前言
  • 二、视觉部分
    • 2.1:k210识别激光点
    • 2.2:k210识别方框和4个角点
  • 三、总结

一、前言

🌷此次电赛我负责的部分主要是视觉,所以我着重和详细讲解一下视觉部分,不止限于此次电赛,而是从这次电赛视觉部分更好得学习和了解视觉,以及如何去运用视觉,以便在下次比赛中能更好的理解和使用!

这次电赛我使用的视觉开发板是K210,这里先给出官方教程文档:MaixPy 文档

这是openmv的官方文档,其实它和k210都是使用python进行编写的,代码部分其实大差不差(串口方面书写会稍有差异),openmv的文档会更加详细,这里也给出:MicroPython 和 OpenMV Cam 中文文档

注意,下面的代码是根据功能分模块写的,实际结合运用时要稍作修改和整合。由于是视觉新手,可能会存在少许问题或者值得优化的地方,希望各位能在评论区或者私信指出,我会很高兴接收和与大家交流!

二、视觉部分

2.1:k210识别激光点

🌺识别激光的大体思路不难:使用find_blobs(函数即可,这里的难点是如何使识别到的激光灵敏且准确!
对图像进行了哪些处理可以看看以下代码,给出了详细解释!但是还是要根据具体情况来调,在我当时的场景下识别是非常准确和灵敏的。(绿色圈中的就是激光正中心,左边红色的是光影)
在这里插入图片描述

# Untitled - By: Yaoyao - 周四 8月 3 2023

import sensor, image, time, lcd

from machine import UART #串口库函数
from Maix import GPIO
from fpioa_manager import fm # GPIO重定向函数(引脚映射


fm.register(GPIO.GPIOHS10, fm.fpioa.UART1_TX)
fm.register(GPIO.GPIOHS11, fm.fpioa.UART1_RX)

uart = UART(UART.UART1, 115200, 8, None, 1, timeout=1000, read_buf_len=4096)


lcd.init(freq=15000000)
sensor.reset()                      # Reset and initialize the sensor. It will
                                    # run automatically, call sensor.run(0) to stop
sensor.set_pixformat(sensor.RGB565) # Set pixel format to RGB565 (or GRAYSCALE)
sensor.set_framesize(sensor.QVGA)   # Set frame size to QVGA (320x240)
sensor.set_windowing((176,176))
sensor.skip_frames(time = 2000)     # Wait for settings take effect.
#sensor.set_auto_whitebal(False) # turn this off..
sensor.set_auto_exposure(True, 80)#在这里调节曝光度,调节完可以比较清晰地看清激光点
#sensor.set_contrast(1) #设置对比度范围为[-2,+2]
sensor.set_auto_gain(False)#关闭自增益
sensor.set_brightness(-1) #摄像头亮度,范围为[-2,+2]
#水平方向翻转
sensor.set_hmirror(True)
# 垂直方向翻转
sensor.set_vflip(True)
clock = time.clock()                # Create a clock object to track the FPS.


clock = time.clock()
green_threshold=[(90, 100, -85, 29, 3, 90)]
red_threshold = [(60, 255, -20, 20, -20, 20)]
#red_threshold = [(44, 93, 7, 45, -8, 14)]


#识别激光点代码
def color_blob(threshold):

    blobs = img.find_blobs(threshold,x_stride=1, y_stride=1, area_threshold=0, pixels_threshold=0,merge=False,margin=1)

    if len(blobs)>=1 :#有色块
        # Draw a rect around the blob.
        b = blobs[0]
        #img.draw_rectangle(b[0:4]) # rect
        cx = b[5]
        cy = b[6]
        for i in range(len(blobs)-1):
            #img.draw_rectangle(b[0:4],color = (0,255,0)) # rect
            cx = blobs[i][5]+cx
            cy = blobs[i][6]+cy
        cx=int(cx/len(blobs))
        cy=int(cy/len(blobs))
        img.draw_cross(cx, cy,color = (255,0,0)) # cx, cy

        return int(cx), int(cy)
    return -1, -1 #表示没有找到


while(True):
    clock.tick()
    img = sensor.snapshot()
    img.draw_cross((90,90),color = (0,0,255),size = 10, thickness=1)
    cx, cy = color_blob(red_threshold)
    dot_str = str(cx) + ',' + str(cy)+'+'
    uart.write( "@" + dot_str + "!")  # 使用换行符作为消息的结束,方便 Arduino 一次读取一个坐标
    #time.sleep_ms(20)
    #print("@" + dot_str + "!")
    #print("cx:",cx,"cy",cy)
    img.draw_circle(cx, cy, 5, color=127, thickness=2)
    lcd.display(img, x_scale=1, y_scale=1)
  • sensor.set_auto_exposure(True, 80\ sensor.set_brightness(-1):通过调整曝光度\亮度,可以控制图像的明暗程度,从而创造出不同的视觉效果
  • 关闭白平衡和自增益 sensor.set_auto_whitebal(False)和sensor.set_auto_gain(False)。但是如果调节出来没结果。可以尝试打开看看(所以还是说要看具体环境,根据具体环境进行调节!)
  • 提高分辨率,较高的分辨率,能够提供更好的效果。但是注意!分辨率太高会占用过多的图像处理内存,可能会导致爆内存!所以还是要根据实际效果进行调整!
  • 将图像画质设置成GRAYSCALE,好处在于:如果只有两个颜色,更容易进行分辨,而且每个像素所占空间也减少了,可以适当的增加分辨率。但是也有缺点,如果是多颜色识别就会出现问题。(这里因为我使用RGB565图像效果就已经很好了,就没有必要设置了)

2.2:k210识别方框和4个角点

🌻场景 : 在这次比赛中,我们的摄像头是始终固定不动的,其中k210一项重要的功能就是识别黑色方框矩形的四个顶点,并且把坐标发送给主控板。
此处先上代码

import sensor, image, lcd, time

from machine import UART #串口库函数
from Maix import GPIO
from fpioa_manager import fm # GPIO重定向函数(引脚映射


fm.register(GPIO.GPIOHS10, fm.fpioa.UART1_TX)
fm.register(GPIO.GPIOHS11, fm.fpioa.UART1_RX)

uart = UART(UART.UART1, 9600, 8, None, 1, timeout=1000, read_buf_len=4096)

lcd.init(freq=15000000)
sensor.reset()                      # Reset and initialize the sensor. It will
                                    # run automatically, call sensor.run(0) to stop
sensor.set_pixformat(sensor.RGB565) # Set pixel format to RGB565 (or GRAYSCALE)
sensor.set_framesize(sensor.QQVGA)   # Set frame size to 160x120 分辨率的相机传感器。

sensor.set_windowing((120,120))
#水平方向翻转
sensor.set_hmirror(True)
# 垂直方向翻转
sensor.set_vflip(True)
sensor.skip_frames(time = 10000)     # Wait for settings take effect.
clock = time.clock()                # Create a clock object to track the FPS.

flag = 0

x1 = 0
y1 = 0
x2 = 0
y2 = 0
x3 = 0
y3 = 0
x4 = 0
y4 = 0

while(True):
    clock.tick()                    # 更新FPS时钟
    img = sensor.snapshot()         # 拍摄图片并返回图像
    img.draw_cross((60,60),color = (255,0,0),size = 5, thickness=1)
    # -----矩形框部分-----
    print("寻找矩形")
    # 在图像中寻找矩形
    for r in  img.find_rects():

        # 判断矩形边长是否符合要求
        if r.w() >20  and r.h() > 20 and r.w() < 100 and r.w() < 100:

            flag += 1
            # 在屏幕上框出矩形
            print("找到了矩形",flag)
            #img.draw_rectangle(r.rect(), color = (255, 0, 0), thickness = 2, fill = False)

            # 获取矩形角点位置
            corner = r.corners()
            ## 在屏幕上圆出矩形角点;四个角点坐标, 角点1的数组是corner[0], 坐标就是(corner[0][0],corner[0][1])
            #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, 255, 0), thickness = 2, fill = False)

            print("左下角:",corner[0][0], corner[0][1],",右下角 ",corner[1][0], corner[1][1],",右上角:",corner[2][0], corner[2][1],",左上角:",corner[3][0], corner[3][1])
            x1 += corner[0][0]
            y1 += corner[0][1]
            x2 += corner[1][0]
            y2 += corner[1][1]
            x3 += corner[2][0]
            y3 += corner[2][1]
            x4 += corner[3][0]
            y4 += corner[3][1]

    # 在屏幕上显示图像,此部分会降低帧率
    lcd.display(img)
    if(flag == 3):
        x1 = x1/3
        y1 = y1/3
        x2 = x2/3
        y2 = y2/3
        x3 = x3/3
        y3 = y3/3
        x4 = x4/4
        y4 = y4/4
        # 将四个角点的坐标数据打包成一个字符串b
        corner_str = str(x1) + ',' + str(y1) + ',' + str(x2) + ',' + str(y2) + ',' + str(x3) + ',' + str(y3) + ',' + str(x4) + ',' + str(y4)
        uart.write( "@" + corner_str + "!")  # 使用换行符作为消息的结束,方便 Arduino 一次读取一个坐标
        print("@" + corner_str + "!")
        # 停止1秒钟
        time.sleep(1)
        break

    # 打印帧率
    #print(clock.fps())

如何寻找矩形并且得到四个坐标角点这个不难,这里的问题是:因为在实际中我们除了运行找矩形的代码,还要在K210上运行找激光(可能还有其它图像处理的代码),因为k210的算力不是很高,可能直接导致这个错误MemoryError: Out of fast Frame Buffer Stack Memory! Please reduce the resolution of the image you are running this algorithm on to bypass this issue!# Hello World Example,是因为openmv/k210处理图像部分的内存不够了

可以通过以下措施来解决看看:

  • 减少图像的分辨率:降低图像分辨率可以减少所需的内存量。您可以使用 sensor.set_framesize() 函数来设置图像帧大小。

  • 减少图像处理的区域:使用 sensor.set_windowing() 函数设置感兴趣的图像区域,只处理所需的部分图像。

  • 减少使用Frame Buffer:Frame Buffer Stack Memory是OpenMV处理图像时使用的内存,因此您可以尝试减少使用Frame Buffer。例如,您可以使用 img.copy() 函数来创建图像的副本,而不是直接在原始图像上进行处理。

  • 优化代码:您可以尝试优化代码以减少内存使用量。例如,您可以使用 img.draw_rectangle() 函数的 merge 参数来减少绘制矩形时所需的内存。

从这个场景和功能下,其实四个坐标顶点只需要获取一次即可,不需要持续运行这个代码,于是在上面代码中加入了一个flag标志变量,当在最开始运行代码时,识别三次矩形,将坐标平均值求出(包装准确性),然后通过串口发送给主控。这样一来只有最开始很少一段时间在进行“寻找矩形”这部分代码,后面就可以进行其他操作,这样可以减少Frame Buffer的使用。(个人认为上面提到的使用 img.copy() 函数也是可行的,大家可以自行尝试!)

三、总结

这次电赛是我第一次参加这种大型比赛,四天三夜的时间学习了很多,有泪水、有压力也有焦虑,但是重要的是坚持自己的内心不后悔和坚定的走下。

同时这次比赛让我学习到理论和实践之间的距离其实是很遥远的,以及经验和实践的重要性。一些看起来很合理的理论,但是实现却和理想差很多,你要考虑到硬件的性能、这个函数的参数有哪些作用,如何去调节、环境的影响…

所以接下来的学习和比赛中,要更深入的学习,去更好的理解,才能更好的运用,并且勤加实践,才能更好的将理论和实践结合!!

最后,借用史铁生先生的一句话结束这篇文章吧:“命定的局限尽可永在,不屈的挑战却不可须臾或缺 ”

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/863040.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

canfestival_主站发送同步对象触发主站PDO发送

1.入口处 2.开启定时器 3.调用定时器函数 4.切换到初始化状态&#xff0c;自动切换到预操作状态&#xff0c;最后进入操作状态 看到在预操作状态下&#xff0c;进行了通信状态的切换&#xff0c;调用相应的函数&#xff0c;如下&#xff1a; 5.调用开启SYNC的函数 查找对象字典…

让机器人懂得人类“常识”,3D语义地图能做到吗?

机器人需要一张保姆级地图。 随着机器人的智能化技术不断迭代&#xff0c;对于复杂的行为决策、人机交互等任务仅感知环境的空间几何信息已无法满足要求&#xff0c;它需要让机器人能够像人一样&#xff0c;懂得环境中的物体类别及其位置&#xff0c;即环境的语义信息。以扫地机…

字节跳动基于火山引擎DataLeap的一站式数据治理架构实践

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 在7月22日举行的 ArchSummit 全球架构师峰会&#xff08;深圳站&#xff09;上&#xff0c;来自火山引擎DataLeap的技术专家为大家带来了字节跳动基于火山引擎DataL…

day16:static、final、常量(static final)、

一、static 特点&#xff1a;属于类 、存储在方法去、只有一份或者只执行一次、随类运行执行 可以修饰静态变量 、静态方法 、静态代码块 静态变量能否继承&#xff1f; 静态变量属于类&#xff0c;是共享的资源&#xff0c;不认为是被继承的 静态变量不可以定义在静态方法中…

数据库数据恢复-Oracle数据库数据恢复案例

数据库数据恢复环境&#xff1a; Oracle数据库ASM磁盘组有4块成员盘。 数据库故障&分析&#xff1a; Oracle数据库ASM磁盘组掉线 &#xff0c;ASM实例无法挂载&#xff0c;用户联系我们要求恢复oracle数据库。 数据库数据恢复工程师拿到磁盘后&#xff0c;先将所有磁盘以只…

批量翻译多个文件夹,让你的文件管理更智能高效!

大家好&#xff01;对于经常需要管理大量文件夹的你来说&#xff0c;每次手动逐个改名实在是一项繁琐且易出错的工作。现在&#xff0c;我们为你带来一款强大的文件夹批量改名工具&#xff0c;让你能够轻松实现多个文件夹的批量翻译&#xff0c;让你的文件管理更智能高效 第一…

学会烟感监控有多重要?办公楼真的有必要吗?

随着城市化和建筑规模的不断扩大&#xff0c;火灾风险也日益凸显。为了保障员工和财产的安全&#xff0c;以及及时应对潜在的火灾威胁&#xff0c;办公楼需要采取积极的预防措施。 因此&#xff0c;在这一背景下&#xff0c;安装有效的烟感监控系统变得至关重要。 客户案例 ​…

AI抢饭碗!多部由Midjourney+Runway,制作的电影火了!丨IDCF

ChatGPT等生成式AI正在重塑各个行业的工作模式&#xff0c;尤其是影视领域。最近&#xff0c;多部由MidjourneyRunway生成式AI制作的电影预告片在社交平台上火了。 一部名叫的《芭本海默》的电影从对白、场景、人物、切镜完全由生成式AI制作完成并受到了用户的好评。该片结合了…

PHP 之房贷计算器、组合贷

一、等额本金 // &#xff08;等额本金&#xff09; //$loanAmount>贷款金额 //$loanPeriod>贷款年限 //$interestRate>贷款利息 function calculateEqualPrincipalPayment($loanAmount, $loanPeriod, $interestRate) {$monthlyPrincipal $loanAmount / ($loanPerio…

C#引用Web Service 类型方法,添加搜索本地服务器Web Service 接口调用方法

首先保证现在网络能调用web service接口&#xff0c;右键项目添加服务引用 ![![在这里插入图片描述](https://img-blog.csdnimg.cn/555ba4fa5e2a418f8f85539a9406bcd6.png) 点击高级 添加web服务 输入搜索的服务器接口&#xff0c;选中你要添加调用的方法即可 添加完成调用方…

性能优化-react阻止子组件重渲染

因为父组件的状态变更会触发子组件的渲染&#xff0c;可以用shouldComponentUpdate或memo来阻止。下面就来介绍这两种方法。 类组件-shouldComponentUpdate 注&#xff1a;变化需要在render中打印&#xff0c;在component中检测不到 核心代码: 子组件中用shouldComponentUpda…

[保研/考研机试] KY87 鸡兔同笼 北京大学复试上机题 C++实现

描述 一个笼子里面关了鸡和兔子&#xff08;鸡有2只脚&#xff0c;兔子有4只脚&#xff0c;没有例外&#xff09;。已经知道了笼子里面脚的总数a&#xff0c;问笼子里面至少有多少只动物&#xff0c;至多有多少只动物。 输入描述&#xff1a; 每组测试数据占1行&#xff0c;…

互联网企业帮助中心,普遍都会有的痛点与挑战!

互联网企业不乏在线创意软件工具的“学习、帮助和支持”内容&#xff0c;但他们知道&#xff0c;他们增长最快的细分市场——新手用户——很难找到看似简单的问题的答案&#xff0c;例如“我如何裁剪这张照片”&#xff1f;等等常见问题&#xff0c;一时难觅。自然而然&#xf…

【MySQL】InnoDB存储引擎详解

InnoDB引擎是MySQL5.5版本之后默认的存储引擎 逻辑存储结构 首先是表空间Tablespace&#xff08;ibd文件&#xff09;&#xff1a;一个mysql实力可以对应多个表空间&#xff0c;用于存储及记录&#xff0c;索引等数据 这些存储记录&#xff0c;索引等数据中是用段(Segment)来…

使用KETTLE工具在Oracle和Dm8之间迁移数据

oracle 代码测试数据 CREATE TABLE PRODUCT_CATEGORY ( PRODUCT_CATEGORYID NUMBER(11,0) NOT NULL , NAME VARCHAR2(255) NOT NULL ENABLE, PRIMARY KEY (PRODUCT_CATEGORYID) )INSERT ALL into PRODUCT_CATEGORY(PRODUCT_CATEGORYID, NAME)VALUES(2,国学) into PRODUCT_CATEG…

可以重复烧写的语音ic有哪些特征和优势

目录 一、简介可擦写的语音芯片&#xff0c;其实就是MCUflash的架构&#xff0c;无其他说法&#xff0c;就这一种说法。这个就是它最大的特征尤其是SOP8的封装类型的芯片&#xff0c;是区别于OTP类型的另一个品类&#xff0c;基本上OTP的语音芯片适用的场景。他都可以满足和替代…

【冒泡排序及其优化】

冒泡排序及其优化 冒泡排序核心思想 冒泡排序的核⼼思想就是&#xff1a;两两相邻的元素进⾏⽐较 1题目举例 给出一个倒序数组&#xff1a;arr[10]{9,8,7,6,5,4,3,2,1,0} 请排序按小到大输出 1.1题目分析 这是一个完全倒序的数组&#xff0c;所以确定冒泡排序的趟数&#xff0…

机器学习笔记之优化算法(十三)关于二次上界引理

机器学习笔记之优化算法——关于二次上界引理 引言回顾&#xff1a;利普希兹连续梯度下降法介绍 二次上界引理&#xff1a;介绍与作用二次上界与最优步长之间的关系二次上界引理证明过程 引言 本节将介绍二次上界的具体作用以及它的证明过程。 回顾&#xff1a; 利普希兹连续…

svg圆形进度条

css3实现的圆形进度条较复杂&#xff0c;代码量较多&#xff0c;本文稍微讲解下如何使用svg实现圆形进度条。 svg实现一个圆用<circle>元素&#xff1a; <svg viewBox"0 0 100 100" xmlns"http://www.w3.org/2000/svg"><circle cx"50…

腾讯云服务器CPU标准型S5/S6/SA3/SR1/SA2处理器大全

腾讯云服务器CVM标准型CPU处理器大全&#xff0c;包括标准型S6、SA3、SR1、S5、S5se、SA2、S4、SN3ne、S3、SA1、S2ne实例CPU处理器型号大全&#xff0c;标准型S6云服务器CPU采用Intel Ice Lake(2.7GHz/3.3GHz)&#xff0c;标准型S5采用Intel Xeon Cascade Lake 8255C/Intel Xe…