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

        我们花费了四天时间打了一场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 * 2car.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_Taskglobal find_flagglobal 目标药房img.draw_rectangle(R, color=(225, 0, 0))#本来中值是80的,但返回值是框边缘,所以减去15就好  小于65是在左边,大于65是在右边#LoR = 0find_flag = 1Num = Finded_Numprint("目标病房号:", Num)目标药房 = Num#识别到目标药房,执行任务2Find_Task=2if 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 Rdef FindedNum(R, Finded_Num):#第一个参数是模板匹配的对象,第二个是它所代表的数字global Find_Taskglobal find_flagLoR = 0img.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 = 1Num = Finded_Numprint("识别到的数字是:", 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("正在识别病房号")#进行模板匹配,成功返回模版所在一个元组,否则返回Noner01 = 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.mzph.cn/news/754479.shtml

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

相关文章

3.18数据结构

一、数据结构----->用来组织存储数据 一组用来保存一种或多种特定关系的数据的集合&#xff08;组织和存储数据&#xff09; 程序 数据结构 算法 MVC&#xff1a;软件设计架构 M&#xff1a;数据的管理&#xff08;数据结构&#xff09; V&#xff1a;视图&#xff0c…

B003-springcloud alibaba 服务治理 nacos discovery ribbon feign

目录 服务治理服务治理介绍什么是服务治理相关方案 nacos实战入门搭建nacos环境安装nacos启动nacos访问nacos 将商品微服务注册进nacos将订单微服务注册进nacos订单服务通过nacos调用商品服务 实现服务调用的负载均衡什么是负载均衡代码实现负载均衡增加一个服务提供者自定义实…

离线数仓、实时数仓与数据湖

1 什么是数据仓库 数据仓库是一个为数据分析而设计的企业级数据管理系统。数据仓库可集中、整合多个信息源的大量数据&#xff0c;借助数据仓库的分析能力&#xff0c;企业可从数据中获得宝贵的信息进而改进决策。同时&#xff0c;随着时间的推移&#xff0c;数据仓库中积累的大…

《深入理解Maven:构建工具、常用命令与项目包技巧解析》

文章目录 何为构建工具 | ToolsMaven插件的常用命令 | Commondclean总结 | Summary compile总结 | Summary package总结 | Summary install总结 | Summary build和compile的区别 | Knowledge项目包技巧 | Method 在后端领域开发中&#xff0c;我们构建项目常用到了Maven和Gradl…

Science Robotics 封面论文:美国宇航局喷气推进实验室开发了自主蛇形机器人,用于冰雪世界探索

人们对探索冰冷的卫星&#xff08;如土卫二&#xff09;的兴趣越来越大&#xff0c;这可能具有天体生物学意义。然而&#xff0c;由于地表或冰口内的环境极端&#xff0c;获取样本具有挑战性。美国宇航局的喷气推进实验室正在开发一种名为Exobiology Extant Life Surveyor&…

【论文阅读笔记】Split frequency attention network for single image deraining

1.论文介绍 Split frequency attention network for single image deraining 用于单幅图像去噪的分频注意力网络 Paper Code 2023年 SIVP 2.摘要 雨纹对图像质量的影响极大&#xff0c;基于数据驱动的单图像去噪方法不断发展并取得了巨大的成功。然而&#xff0c;传统的卷积…

C/C++动态链接库的封装和调用

1 引言 静态链接库是在编译时被链接到程序中的库文件&#xff0c;在编译时&#xff0c;链接器将静态链接库的代码和数据复制到最终的可执行文件中。动态链接库是在程序运行时加载的库文件&#xff0c;在编译时&#xff0c;可执行文件只包含对动态链接库的引用&#xff0c;而不…

外贸网站文章批量生成器

随着全球贸易的不断发展&#xff0c;越来越多的企业开始关注外贸市场&#xff0c;而拥有高质量的内容是吸引潜在客户的关键之一。然而&#xff0c;为外贸网站生产大量优质的文章内容可能是一项耗时且繁琐的任务。因此&#xff0c;外贸网站文章批量生成软件成为了解决这一难题的…

语音识别:whisper部署服务器(远程访问,语音实时识别文字)

Whisper是OpenAI于2022年发布的一个开源深度学习模型&#xff0c;专门用于语音识别任务。它能够将音频转换成文字&#xff0c;支持多种语言的识别&#xff0c;包括但不限于英语、中文、西班牙语等。Whisper模型的特点是它在多种不同的音频条件下&#xff08;如不同的背景噪声水…

做跨境用哪种代理IP比较好?怎么选到干净的IP?

代理IP对于做跨境的小伙伴来说&#xff0c;都是必不可少的工具&#xff0c;目前出海的玩法已经是多种多样&#xff0c;开店、账号注册、短视频运营、直播带货、网站SEO等等都是跨境人需要涉及到的业务。而国外代理IP的获取渠道非常多&#xff0c;那么做跨境到底应该用哪种代理I…

基于Spring Boot的研究生志愿填报辅助系统

摘 要 二十一世纪我们的社会进入了信息时代&#xff0c;信息管理系统的建立&#xff0c;大大提高了人们信息化水平。传统的管理方式对时间、地点的限制太多&#xff0c;而在线管理系统刚好能满足这些需求&#xff0c;在线管理系统突破了传统管理方式的局限性。于是本文针对这一…

掌握请求控制权:Axios 如何取消请求的两种方法

在前端开发中&#xff0c;网络请求是非常常见的操作。而有时候&#xff0c;我们可能需要在发送请求后取消它&#xff0c;比如用户在请求还未完成时离开了当前页面或者执行了其他操作&#xff0c;本文将介绍如何在使用 Axios 发送请求时取消这些请求。 基本概念 在 Axios 中&am…

swagger使用手册

1.导入依赖 <!--引入swagger--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.7.0</version></dependency><dependency><groupId>io.springfox</…

AR/MR产品设计(二):如何用一双手完成与虚拟对象的自然交互

AR/MR产品设计&#xff08;二&#xff09;&#xff1a;如何用一双手完成与虚拟对象的自然交互 - 知乎 手是我们与现实世界交互最重要的方式&#xff0c;同样在虚实混合的世界中是最重要的交互方式 在AR/MR/VR的交互中&#xff0c;手势交互会作为XR的重要交互动作&#xff0c;因…

鸿蒙开发 一 (一)、框架了解

一、UI框架 开发范式名称 语言生态 UI更新方式 适用场景 适用人群 声明式开发范式 ArkTS语言 数据驱动更新 复杂度较大、团队合作度较高的程序 移动系统应用开发人员、系统应用开发人员 类Web开发范式 JS语言 数据驱动更新 界面较为简单的程序应用和卡片 Web前端…

视频技术1:使用ABLMediaServer推流rtsp

ABLMediaServer定位是高性能、高稳定、开箱即用、商用级别的流媒体服务器 下边展示了如何把1个mp3作为输入源&#xff0c;转换为rtsp流的过程。 作用&#xff1a;用rtsp模拟摄像头的视频流 1、启动ABLMediaServer ABLMediaServer-2024-03-13\WinX64\ABLMediaServer.exe 配…

聚类分析 | Matlab实现基于NNMF+DBO+K-Medoids的数据聚类可视化

聚类分析 | Matlab实现基于NNMFDBOK-Medoids的数据聚类可视化 目录 聚类分析 | Matlab实现基于NNMFDBOK-Medoids的数据聚类可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 NNMFDBOK-Medoids聚类&#xff0c;蜣螂优化算法DBO优化K-Medoids 非负矩阵分解&#xff08…

信息学奥赛一本通之MAC端VSCode C++环境配置

前提 安装 Visual Studio CodeVSCode 中安装 C/C扩展确保 Clang 已经安装&#xff08;在终端中输入命令&#xff1a;clang --version 来确认是否安装&#xff09;未安装&#xff0c;在命令行执行xcode-select --install 命令&#xff0c;会自行安装&#xff0c;安装文件有点大…

STM32实验DMA数据搬运小助手

本次实验做的是将一个数组的内容利用DMA数据搬运小助手搬运到另外一个数组中去。 最后的实验结果&#xff1a; 可以看到第四行的数据就都不是0了&#xff0c;成功搬运了过来。 DMA实现搬运的步骤其实不是很复杂&#xff0c;复杂的是结构体参数&#xff1a; 整个步骤为&#xf…

面试经典150题 -- 分治 (总结)

总的链接 : 面试经典 150 题 - 学习计划 - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台 108 . 将有序数组转换成二叉搜索树 : 链接 : . - 力扣&#xff08;LeetCode&#xff09; 思路 : 选择中间位置左边的数字作为根节点&#xff0c;然后递归调…