21年电赛-送药小车—基于OpenMV的寻迹+检测路口+数字识别(多模版匹配)(附代码)

news2024/9/21 22:47:51

        我们花费了四天时间打了一场21年的电赛改编题——智能送药小车。虽然结果不尽人意,但这是我学习32以来第一次正式的打比赛,对我来说要学习的东西,所增长的经验真的特别多(虽然基本上都是学长在出力~)下来我就把关于这次比赛的感悟和大家分享一下。

一、OpenMV寻迹

        我们使用OpenMV进行寻迹。思路就是先对图像进行二值化处理,只让OpenMV识别出红线,然后通过线性回归方法检测出直线。通过计算出直线的平移偏移量和旋转偏移量,然后带入pid算法中来控制小车进行巡线。

#pid巡线前进
def lookfor_line():
    # 使用PID算法计算控制信号
    rho_output = rho_pid.get_pid(直线平移偏移量, 1)
    theta_output = theta_pid.get_pid(直线旋转偏移量, 1)
    output = rho_output + theta_output
    #print("kkkkkkkkkkkkkk", end="")
    #print(output)
    # 根据PID输出调整小车的速度
    output = output * 2
    car.run(80+ output, 80 - output)


# 计算直线的平移偏移量(直线中点到图像中心的距离)
直线平移偏移量 = abs(line.rho()) - img.width() / 2
# 计算直线的旋转偏移量(直线与垂直方向的夹角)
if line.theta() > 90:
    直线旋转偏移量 = line.theta() - 180
else:
    直线旋转偏移量 = line.theta()
# 如果直线的强度(长度)足够大,则认为是有效直线,需要控制小车
if stats.mean() > 3:
    lookfor_line()

二、OpenMV检测路口

        识别数字的摄像头无非就发送三种数据,要么目标药房在左,要么目标药房在右,要么没识别到直接前进。下面的摄像头只负责检测到路口然后执行上面摄像头发下来的操作即可,不必区分十字路口还是T字路口。

        我们检测路口的思路也很简单,只要判断红色区域(二值化之后是白色区域)的面积突然增大即可。我们的摄像头在一般巡线的时候红色区域的面积在15左右,当有路口出现的时候,面积会到23左右。

stats = img.get_statistics()
#遇到了路口
if stats.mean()>18:
    #执行相关逻辑代码

三、OpenMV数字识别

        我们使用的是OpenMV的多模板匹配。效果还行。


模版的采集:

·        就是先将车搭好,然后让车开到指定地点,就最好是后面的T字路口处,让T字路口的那四张照片全部落在OpenMV的正中央,将这些数字的几种形态全部拍摄成模版。可以将数字分为两大类。1~2,3~8。因为1~2是固定路线,所以只要拍摄正脸照用来发车前识别就行。3~8可以分为左边,最左边,右边,最右边和正面五中情况。所以我们一共拍摄了32张模板,然后将模板转化为pgm格式,像素大小为35*35因为不能超过openmv的像素大小。35*35的像素我们感觉识别速度和准确率都还行,官方给的建议是不超过60*80。

        我们是用电脑截图,然后改像素转格式。截图时用的代码:

import sensor,time,image

#重置传感器
sensor.reset()

sensor.set_contrast(1)#设置对比度
sensor.set_gainceiling(16)#设置增益上限
sensor.set_framesize(sensor.QQVGA)#设置图像分辨率为 QQVGA
sensor.set_pixformat(sensor.GRAYSCALE)#设置图像格式为灰度
#sensor.set_windowing(0, 40, 160, 40)#设置窗口大小,后面ROI设置也会以这个为新的基准
# 拍摄图像并保存
while (1):
    img = sensor.snapshot()
    time.sleep_ms(10)

多模板匹配:       

        多模版匹配其实就是用多个模版调用模板匹配函数 find_template 来进行模板匹配。如果进行多模板匹配时检测不到模板,可以适当降低感兴趣区域面积 roi ,使用EX识别,减少 threshold 的值等。如果错误率太高可以增加 threshold 的值,增加模板等

import time, image,sensor,math,pyb
#引入两种匹配算法
from image import SEARCH_EX, SEARCH_DS
from pyb import Pin, Timer,LED
uart = pyb.UART(3, 115200, timeout_char = 1000)     #定义串口3变量
#重置传感器
sensor.reset()

sensor.set_contrast(1)#设置对比度
sensor.set_gainceiling(16)#设置增益上限
sensor.set_framesize(sensor.QQVGA)#设置图像分辨率为 QQVGA
sensor.set_pixformat(sensor.GRAYSCALE)#设置图像格式为灰度
#sensor.set_windowing(0, 0, 160, 160)#设置窗口大小,后面ROI设置也会以这个为新的基准

#=====================================加载模板图片
template01 = image.Image("/1.pgm")
template02 = image.Image("/2.pgm")
template03 = image.Image("/3.pgm")
template04 = image.Image("/4fly.pgm")
template05 = image.Image("/5.pgm")
template06 = image.Image("/6.pgm")
template07 = image.Image("/7.pgm")
template08 = image.Image("/8fly.pgm")


template3L = image.Image("/3L.pgm")
template3LL = image.Image("/3LL.pgm")
template3R = image.Image("/3R.pgm")
template3RR = image.Image("/3RR.pgm")

template4L = image.Image("/4L.pgm")
template4LL = image.Image("/4LL.pgm")
template4R = image.Image("/4R.pgm")
template4RR = image.Image("/4RR.pgm")

template5L = image.Image("/5L.pgm")
template5LL = image.Image("/5LL.pgm")
template5R = image.Image("/5R.pgm")
template5RR = image.Image("/5RR.pgm")

template6L = image.Image("/6L.pgm")
template6LL = image.Image("/6LL.pgm")
template6R = image.Image("/6R.pgm")
template6RR = image.Image("/6RR.pgm")

template7L = image.Image("/7L.pgm")
template7LL = image.Image("/7LL.pgm")
template7R = image.Image("/7R.pgm")
template7RR = image.Image("/7RR.pgm")

template8L = image.Image("/8L.pgm")
template8LL = image.Image("/8LL.pgm")
template8R = image.Image("/8R.pgm")
template8RR = image.Image("/8RR.pgm")

# 1.轮询1~8,直至识别到。  2.根据f103给的值,单纯识别那个数
Find_Task = 1#第几次匹配
目标药房 = 0#需要到达的药房
find_flag = 0 #只声明,未使用


#=======================================第一次模版匹配
#####  FindTask == 1 时使用
#开始位置匹配
def FirstFindTemplate(template):
    kuang = (50,38,70,53)
    R = img.find_template(template, 0.6, step=1,roi=kuang, search=SEARCH_EX)   #只检测中间的
    img.draw_rectangle(kuang, color=(255,255,255))
    return R
#匹配具体值
def FirstFindedNum(R, Finded_Num): #第一个参数是模板匹配的对象,第二个是它所代表的数字
   global Find_Task
   global find_flag
   global 目标药房
   img.draw_rectangle(R, color=(225, 0, 0))

   #本来中值是80的,但返回值是框边缘,所以减去15就好  小于65是在左边,大于65是在右边
   #LoR = 0
   find_flag = 1
   Num = Finded_Num
   print("目标病房号:", Num)
   目标药房 = Num
   #识别到目标药房,执行任务2
   Find_Task=2

   if Num == 1:
       uart.write("1")

   uart.write("start")
   print("start")




#=======================================第二次模版匹配
#####  FindTask == 2 时使用
#模板匹配
def FindTemplate(template):
    kuang = (0, 30, 160, 50)
    R = img.find_template(template, 0.8, step=1, roi=kuang, search=SEARCH_EX)
    img.draw_rectangle(kuang, color=(255, 255, 255))
    return R

def FindedNum(R, Finded_Num):#第一个参数是模板匹配的对象,第二个是它所代表的数字
    global Find_Task
    global find_flag
    LoR = 0
    img.draw_rectangle(R, color=(225, 0, 0))

    #本来中值是80的,但返回值是框边缘,所以减去15就好  小于65是在左边,大于65是在右边
    if R[0] >= 65:
        LoR = 2    #2是右
    elif 0< R[0] <65:
        LoR = 1    #1是左
    find_flag = 1
    Num = Finded_Num
    print("识别到的数字是:", Num, "此数字所在方位:", LoR) #打印模板名字
    if LoR == 1:
        uart.write("a")
        print("a")
    elif LoR == 2:
        uart.write("b")
        print("b")


#=======================================主循环
clock = time.clock()
# Run template matching
time.sleep(1)
while (True):
    clock.tick()
    img = sensor.snapshot()


    #==============================开始位置识别数字
    if Find_Task == 1:

        print("正在识别病房号")


       #进行模板匹配,成功返回模版所在一个元组,否则返回None
        r01 = FirstFindTemplate(template01)
        r02 = FirstFindTemplate(template02)
        r03 = FirstFindTemplate(template03)
        r04 = FirstFindTemplate(template04)
        r05 = FirstFindTemplate(template05)
        r06 = FirstFindTemplate(template06)
        r07 = FirstFindTemplate(template07)
        r08 = FirstFindTemplate(template08)


        #判断哪个模板匹配成功
        if r01:
             FirstFindedNum(r01,1)
        elif r02:
             FirstFindedNum(r02,2)
        elif r03:
             FirstFindedNum(r03,3)
        elif r04:
             FirstFindedNum(r04,4)
        elif r05:
             FirstFindedNum(r05,5)
        elif r06:
             FirstFindedNum(r06,6)
        elif r07:
             FirstFindedNum(r07,7)
        elif r08:
             FirstFindedNum(r08,8)
        #else:
             #print("没检测到目标病房!!!")


    #=============================路中识别数字
    elif Find_Task == 2:

        print("正在识别地标号")
        #break##退出用来调试

        #判断需要数字3~8中断哪一个

        if 目标药房 == 3:
            #进行模板匹配  //这里每个数字至少给3个模板, 但给五六个其实也行
            r3 = FindTemplate(template03)
            r3L = FindTemplate(template3L)
            r3LL = FindTemplate(template3LL)
            r3R = FindTemplate(template3R)
            r3RR = FindTemplate(template3RR)

            #判断哪个模板匹配成功
            if r3L:
                FindedNum(r3L, 3)
            elif r3LL:
                FindedNum(r3LL, 3)
            elif r3R:
                FindedNum(r3R, 3)
            elif r3RR:
                FindedNum(r3RR, 3)
            elif r3:
                FindedNum(r3, 3)

        elif 目标药房 == 4:
            #进行模板匹配  //这里每个数字至少给3个模板, 但给五六个其实也行
            #r4 = FindTemplate(template04)
            r4L = FindTemplate(template4L)
            r4LL = FindTemplate(template4LL)
            r4R = FindTemplate(template4R)
            r4RR = FindTemplate(template4RR)

            #判断哪个模板匹配成功,并将成功匹配的相应数据发送给主控

            if r4L:
                FindedNum(r4L, 4)
            elif r4LL:
                FindedNum(r4LL, 4)
            elif r4R:
                FindedNum(r4R, 4)
            elif r4RR:
                FindedNum(r4RR, 4)
            #elif r4:
                #FindedNum(r4, 4)

        elif 目标药房 == 5:
            #进行模板匹配  //这里每个数字至少给3个模板, 但给五六个其实也行
            r5 = FindTemplate(template05)
            r5L = FindTemplate(template5L)
            r5LL = FindTemplate(template5LL)
            r5R = FindTemplate(template5R)
            r5RR = FindTemplate(template5RR)

            #判断哪个模板匹配成功,并将成功匹配的相应数据发送给主控

            if r5L:
                FindedNum(r5L, 5)
            elif r5LL:
                FindedNum(r5LL, 5)
            elif r5R:
                FindedNum(r5R, 5)
            elif r5RR:
                FindedNum(r5RR, 5)
            elif r5:
                FindedNum(r5, 5)

        elif 目标药房 == 6:
            #进行模板匹配  //这里每个数字至少给3个模板, 但给五六个其实也行
            r6 = FindTemplate(template06)
            r6L = FindTemplate(template6L)
            r6LL = FindTemplate(template6LL)
            r6R = FindTemplate(template6R)
            r6RR = FindTemplate(template6RR)

            #判断哪个模板匹配成功,并将成功匹配的相应数据发送给主控

            if r6L:
                FindedNum(r6L, 6)
            elif r6LL:
                FindedNum(r6LL, 6)
            elif r6R:
                FindedNum(r6R, 6)
            elif r6RR:
                FindedNum(r6RR, 6)
            elif r6:
                FindedNum(r6, 6)


        elif 目标药房 == 7:
            #进行模板匹配  //这里每个数字至少给3个模板, 但给五六个其实也行
            r7 = FindTemplate(template07)
            r7L = FindTemplate(template7L)
            r7LL = FindTemplate(template7LL)
            r7R = FindTemplate(template7R)
            r7RR = FindTemplate(template7RR)

            #判断哪个模板匹配成功,并将成功匹配的相应数据发送给主控
            if r7L:
                FindedNum(r7L, 7)
            elif r7LL:
                FindedNum(r7LL, 7)
            elif r7R:
                FindedNum(r7R, 7)
            elif r7RR:
                FindedNum(r7RR, 7)
            elif r7:
                FindedNum(r7, 7)

        elif 目标药房 == 8:
            #进行模板匹配  //这里每个数字至少给3个模板, 但给五六个其实也行
            #r8 = FindTemplate(template08)
            r8L = FindTemplate(template8L)
            r8LL = FindTemplate(template8LL)
            r8R = FindTemplate(template8R)
            r8RR = FindTemplate(template8RR)

            #判断哪个模板匹配成功,并将成功匹配的相应数据发送给主控

            if r8L:
                FindedNum(r8L, 8)
            elif r8LL:
                FindedNum(r8LL, 8)
            elif r8R:
                FindedNum(r8R, 8)
            elif r8RR:
                FindedNum(r8RR, 8)
            #elif r8:
                #FindedNum(r8, 8)


        #else: time.sleep_ms(100)
    else: time.sleep_ms(100)

    print(clock.fps(),Find_Task, 目标药房)

         我会将模板和完整代码放到我的资源中,如果大家需要的话可以自行下载。

四、有点小疑问         

        最后我们发现我们的小车连着电脑跑的时候,数字识别的准确率很高,而且小车跑的也很稳。但如果脱机跑的时候,就有很大情况会识别不到数字,而且小车也经常会在转弯时遇到问题。我们使用的是充电宝,想着应该和供电关系不大吧~~~,求解答!!!

        最后的最后,请驻足欣赏一下我们的可怜的直流电机战车!!!

                      

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

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

相关文章

上位机图像处理和嵌入式模块部署(qmacvisual畸变矫正)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 大部分同学在开始做计算机图像的时候&#xff0c;是没有意识到畸变矫正这个问题的。当然不仅仅是畸变矫正&#xff0c;很多同学还会忽略光源的问题…

qt使用Windows经典风格,以使QTreeView或QTreeWidge有节点线或加号

没有使用Windows经典风格的QTreeView或QTreeWidget显示如下&#xff1a; 使用Windows经典风格的QTreeView或QTreeWidget显示如下&#xff1a; 树展开时&#xff1a; 树未展开时&#xff1a; 可以看到&#xff1a; 未使用Windows经典风格时&#xff0c;QTreeView或QTreeWidget…

Memcached-分布式内存对象缓存系统

目录 一、NoSQL 介绍 二、Memcached 1、Memcached 介绍 1.1 Memcached 概念 1.2 Memcached 特性 1.3 Memcached 和 Redis 区别 1.4 Memcached 工作机制 1.4.1 内存分配机制 1.4.2 懒惰期 Lazy Expiration 1.4.3 LRU&#xff08;最近最少使用算法&#xff09; 1.4.4…

孙溟㠭于北京大学北大书店现场创作

孙溟㠭于北京大学北大书店现场创作篆刻作品 孙溟㠭北大书店现场创作 孙溟㠭于北京大学北大书店展览期间现场创作 孙溟㠭北京大学篆刻展现场创作 图文/氿波

【C语言】守护进程(daemon)的输出到一个文本文件

一、常用的守护进程函数 void daemonize () {//deamonizepid_t pid fork();if( pid > 0 ){ //parent exitexit(0);}//child continuesetsid();chdir("/");close(0);open("/dev/null", O_RDWR);//no env debugif(!getenv("debug")){cl…

Flask中的Blueprints:模块化和组织大型Web应用【第142篇—Web应用】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Flask中的Blueprints&#xff1a;模块化和组织大型Web应用 在构建大型Web应用时&#xff0…

面试经典150题(114-118)

leetcode 150道题 计划花两个月时候刷完之未完成后转&#xff0c;今天完成了5道(114-118)150 gap 了一周&#xff0c;以后就不记录时间了。。 114.(70. 爬楼梯) 题目描述&#xff1a; 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不…

【Qt图形界面引擎(一)】:第一个Qt程序

跨平台图形界面引擎&#xff0c;接口简单&#xff0c;易上手&#xff0c;一定程度简化内存。 Qt发展史 1991年由Qt Company开发的跨平台C图形用户界面应用程序开发框架2008年&#xff0c;Qt Company科技被诺基亚公司收购&#xff0c;Qt也因此成为诺基亚旗下的编程语言工具2012…

【神经网络 基本知识整理】(激活函数) (梯度+梯度下降+梯度消失+梯度爆炸)

神经网络 基本知识整理 激活函数sigmoidtanhsoftmaxRelu 梯度梯度的物理含义梯度下降梯度消失and梯度爆炸 激活函数 我们知道神经网络中前一层与后面一层的连接可以用y wx b表示&#xff0c;这其实就是一个线性表达&#xff0c;即便模型有无数的隐藏层&#xff0c;简化后依旧…

十、MySQL主从架构配置

一、资源配置 主库&#xff1a;192.168.134.132 从库&#xff1a;192.168.134.133 从库&#xff1a;192.168.134.134 二、主从同步基本原理&#xff1a; master用户写入数据&#xff0c;会生成event记录到binary log中&#xff0c;slave会从master读取binlog来进行数据同步…

如何将大华dav视频转mp4?一键无损清晰转换~

Digital Audio Video&#xff08;DAV&#xff09;文件格式源于数字监控领域&#xff0c;旨在解决视频监控数据的存储和回放问题。随着数字监控技术的发展&#xff0c;DAV格式逐渐成为监控设备记录视频的标准格式&#xff0c;广泛应用于安防系统、摄像头监控等场景。 MP4文件格式…

Transformer的前世今生 day03(Word2Vec

前情回顾 由上一节&#xff0c;我们可以得到&#xff1a; 任何一个独热编码的词都可以通过Q矩阵得到一个词向量&#xff0c;而词向量有两个优点&#xff1a; 可以改变输入的维度&#xff08;原来是很大的独热编码&#xff0c;但是我们经过一个Q矩阵后&#xff0c;维度就可以控…

【ARM】DSTREAM上面的各个指示灯代表什么意思?

【更多软件使用问题请点击亿道电子官方网站查询】 1、 文档目标 对于DStream仿真器上面的指示灯亮灭代表的意义进行分析。 2、 问题场景 主要对于DStream仿真器的使用过程中&#xff0c;不同的情况下面仿真器的指示灯会进行相应的亮灭。了解一下不同指示灯的亮灭所提示的信息…

大模型算命,对比下kimi和ChatGPT,AI离道破天机就差一个你信不信

我不懂算命&#xff0c;但很好奇。 先随便问问kimi&#xff0c;它先通过网络找到了参考&#xff0c;直接就给出了结论&#xff0c;看着有点儿草率&#xff0c;像极了街头算命先生。 给点儿专业书籍让他学习学习&#xff0c;它就开始认真了&#xff0c;看上去有些专业。 试试…

51-32 CVPR’24 | 3DSFLabelling,通过伪自动标注增强 3D 场景流估计

24 年 2 月&#xff0c;鉴智机器人、剑桥大学和上海交通大学联合发布CVPR24工作&#xff0c;3DSFLabelling: Boosting 3D Scene Flow Estimation by Pseudo Auto-labelling。 提出 3D 场景自动标注新框架&#xff0c;将 3D 点云打包成具有不同运动属性的 Boxes&#xff0c;通过…

File文件对象

在计算机系统中&#xff0c;文件是非常重要的存储方式。Files&#xff08;java.nio.file.Files&#xff09;提供了多种方法来处理文件系统中的文件。比直接使用File文件要方便。 Files工具类:读取指定文件中的所有文本 package study1;import java.io.IOException; import ja…

(一)Neo4j下载安装以及初次使用

&#xff08;一&#xff09;下载 官网地址&#xff1a;Neo4j Graph Database & AnamConnect data as its stored with Neo4j. Perform powerful, complex queries at scale and speed with our graph data platform.https://neo4j.com/ &#xff08;二&#xff09;安装并配…

C++Qt学习——QFile、QPainter、QChart

目录 1、QFile&#xff08;文本读写&#xff09;——概念 1.1、拖入三个控件&#xff0c;对pushButton进行水平布局&#xff0c;之后整体做垂直布局 1.2、按住控件&#xff0c;转到槽&#xff0c;写函数 1.3、打开文件控件 A、首先引入以下两个头文件 B、设置点击打开文件控…

搭建 es 集群

一、VMware准备机器 首先准备三台机器 这里我直接使用 VMware 构建三个虚拟机 都是基于 CentOS7 然后创建新用户 部署 es 需要单独创建一个用户&#xff0c;我这里在构建虚拟机的时候直接创建好了 然后将安装包上传 可以使用 rz 命令上传&#xff0c;也可以使用工具上传 工…

Apache Doris 如何基于自增列满足高效字典编码等典型场景需求

自增列&#xff08;auto_increment&#xff09;是数据库中常见的一项功能&#xff0c;它提供一种方便高效的方式为行分配唯一标识符&#xff0c;极大简化数据管理的复杂性。当新行插入到表中时&#xff0c;数据库系统会自动选取自增序列中的下一个可用值&#xff0c;并将其分配…