2021电赛F题送药小车视觉部分的一种思路(双OpenMV法)

这篇具有很好参考价值的文章主要介绍了2021电赛F题送药小车视觉部分的一种思路(双OpenMV法)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

使用两块OpenMV解答送药小车视觉部分

前言:
最近参加了2021年电赛的F题,因为诸多原因未能完赛,现将图像识别部分的记录一下,交流学习。

一、2021电赛F题题目回顾与分析

1.题目介绍

因为只介绍视觉部分,我们就节选相关的部分吧。

设计并制作智能送药小车,模拟完成在医院药房与病房间药品的送取作业。院区结构示意如图 1 所示。院区走廊两侧的墙体由黑实线表示。走廊地面上画有居中的红实线,并放置标识病房号的黑色数字可移动纸张。药房和近端病房号(1、 2 号)如图 1 所示位置固定不变,中部病房和远端病房号(3-8 号)测试时随机设定。

工作过程:参赛者手动将小车摆放在药房处(车头投影在门口区域内,面向病房),手持数字标号纸张由小车识别病房号,将约 200g 药品一次性装载到送药小车上;小车检测到药品装载完成后自动开始运送;小车根据走廊上的标识信息自动识别、寻径将药品送到指定病房(车头投影在门口区域内),点亮红色指示灯,等待卸载药品;病房处人工卸载药品后,小车自动熄灭红色指示灯,开始返回;小车自动返回到药房(车头投影在门口区域内,面向药房)后,点亮绿色指示灯。

送药小车视觉模块,2021电赛,单片机,自动驾驶,stm32

送药小车视觉模块,2021电赛,单片机,自动驾驶,stm32

2.图像部分分析

由题意可知,图像部分大致可以分为

  • 识别道路
    • 路中央红线巡线
    • 路口识别
    • 终点线黑色虚线识别
  • 识别数字
    • 开始位置识别数字
    • 路口识别两个数字
    • 路口识别四个数字

2.1识别道路

识别道路有很多方案,我们组前期错误的选择了红外循线的方案。这种方案精度低,而且会受环境影响。

后期转向OpenMV的方案。

2.2识别数字

识别数字有很多方案,比如OpenMV、K210、树莓派、Jetson nano甚至x86架构的单板计算机都可以用,但是因为前期准备的原因我们只实现了OpenMV的方案。

这里还是要说一下,OpenMV算力有限,实在是难堪重任,并不是本题的最优解法。

二、识别道路部分

1.巡线-红色实线

这里我们采用的是匿名飞控给无人机写的一套OpenMV代码,略作修改。

核心思想是在图像的上、中、下、左、右各划出一个细长条的区域,在各自区域内检测是否有指定大小的红色色块,再根据五个部分红色色块的有无即可判定是直线还是路口、是何种路口以及直线的倾角和偏移量。

如下图所示,左边只有上、中、下有小方框,是直线;右边上、中、下、左、右都有小方框,是路口。

送药小车视觉模块,2021电赛,单片机,自动驾驶,stm32

2.终点线-黑色虚线

终点线是黑色虚线,可以视为两厘米见方的黑色小矩形,可以使用OpenMV内置的矩形检测函数检测指定大小范围的矩形,当矩形数量足够多时即视为终点线。

如下图所示,识别到六个以上矩形块即可视为终点线。
送药小车视觉模块,2021电赛,单片机,自动驾驶,stm32

3.代码实现

import sensor, image, time, math, struct
from pyb import UART
import json
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
# 要检测颜色,所以使用彩色模式

sensor.set_framesize(sensor.QQVGA)
# 使用QQVGA降低画质提升运行速度

sensor.skip_frames(time=3000)
sensor.set_auto_whitebal(False)
# 颜色检测一定要关闭自动白平衡

clock = time.clock()
uart = UART(1, 115200)
uart.init(115200, bits=8, parity=None, stop=1)
# 上面是串口通信的部分

Red_threshold =[(13, 40, -2, 57, 11, 47),(29, 50, 13, 79, 15, 67),(33, 50, 16, 73, 2, 61)]
# 红色的LAB阈值,在赛场上需要重新进行标定,可使用OpenMV IDE自带的阈值编辑器进行标定

ROIS = {
	'down':   (0, 105, 160, 15),
	'middle': (0, 52,  160, 15),
	'up':	 (0,  0,  160, 15),
	'left':   (0,  0,  15, 120),
	'right':  (145,0,  15, 120),
	'All':	(0,  0,  160,120),
}
# 划分了上中下左右五个部分

class LineFlag(object):
	flag = 0
	cross_y = 0
	delta_x = 0
class EndFlag(object):
	endline_type = 0
	endline_y = 0
LineFlag=LineFlag()
EndFlag=EndFlag()

# 红色实线部分函数
def find_blobs_in_rois(img):
	global ROIS
	roi_blobs_result = {}
	for roi_direct in ROIS.keys():
		roi_blobs_result[roi_direct] = {
			'cx': -1,
			'cy': -1,
			'blob_flag': False
		}
	for roi_direct, roi in ROIS.items():
		blobs=img.find_blobs(Red_threshold, roi=roi, merge=True, pixels_area=10)
		if len(blobs) == 0:
			continue
		largest_blob = max(blobs, key=lambda b: b.pixels())
		x,y,width,height = largest_blob[:4]
		if not(width >=3 and width <= 45 and height >= 3 and height <= 45):
			continue
		roi_blobs_result[roi_direct]['cx'] = largest_blob.cx()
		roi_blobs_result[roi_direct]['cy'] = largest_blob.cy()
		roi_blobs_result[roi_direct]['blob_flag'] = True
	
	
	if (roi_blobs_result['down']['blob_flag']):
		if (roi_blobs_result['left']['blob_flag']and roi_blobs_result['right']['blob_flag']):
			LineFlag.flag = 2 #十字路口或丁字路口
		elif (roi_blobs_result['left']['blob_flag']):
			LineFlag.flag = 3 # 左转路口
		elif (roi_blobs_result['right']['blob_flag']):
			LineFlag.flag = 4 # 右转路口
		elif (roi_blobs_result['middle']['blob_flag']):
			LineFlag.flag = 1 #直线
		else:
			LineFlag.flag = 0 # 未检测到
	else:
		if(roi_blobs_result['middle']['blob_flag']and roi_blobs_result['up']['blob_flag']):
			if (roi_blobs_result['left']['blob_flag']and roi_blobs_result['right']['blob_flag']):
				LineFlag.flag = 5 # 即将跨过十字路口
			elif (roi_blobs_result['left']['blob_flag']):
				LineFlag.flag = 6 # 即将跨过左拐丁字路口
			elif (roi_blobs_result['right']['blob_flag']):
				LineFlag.flag = 7 # 即将跨过右拐丁字路口
			else:
				LineFlag.flag = 8 # 直线(无down块)
		else:
			LineFlag.flag = 0
			
	# 下面这部分是特例的判断,防止出现
	# “本来是直线道路,但是太靠近左侧或者右侧,被识别成了左转或者右转”
	# 这样的特殊情况
	if (LineFlag.flag == 3 and roi_blobs_result['left']['cy']<10):
		LineFlag.flag = 1
	if (LineFlag.flag == 4 and roi_blobs_result['right']['cy']<10):
		LineFlag.flag = 1
	if (LineFlag.flag == 3 and roi_blobs_result['down']['cx']<30):
		LineFlag.flag = 1
	if (LineFlag.flag == 4 and roi_blobs_result['down']['cy']>130):
		LineFlag.flag = 1
	
	# 计算两个输出值,路口交叉点的纵坐标和直线时红线的偏移量
	LineFlag.cross_y = 0
	LineFlag.delta_x = 0
	
	if (LineFlag.flag == 1 or LineFlag.flag == 2 or LineFlag.flag == 3 or LineFlag.flag == 4) :
		LineFlag.delta_x = roi_blobs_result['down']['cx']
	elif (LineFlag.flag == 5 or LineFlag.flag == 6 or LineFlag.flag == 7 or LineFlag.flag == 8):
		LineFlag.delta_x = roi_blobs_result['middle']['cx']
	else:
		LineFlag.delta_x = 0
	
	if (LineFlag.flag == 2 or LineFlag.flag == 5):
		LineFlag.cross_y = (roi_blobs_result['left']['cy']+roi_blobs_result['right']['cy'])//2
	elif (LineFlag.flag == 3 or LineFlag.flag == 6):
		LineFlag.cross_y = roi_blobs_result['left']['cy']
	elif (LineFlag.flag == 4 or LineFlag.flag == 7):
		LineFlag.cross_y = roi_blobs_result['right']['cy']
	else:
		LineFlag.cross_y = 0
	
# 终点线黑色虚线部分的函数
def find_endline(img):
	endbox_num = 0
	for r in img.find_rects(threshold = 10000):
		endbox_size = r.magnitude()
		endbox_w = r.w()
		endbox_h = r.h()
		k=1

		# 筛选黑色矩形大小
		if (endbox_size<24000*k*k and endbox_h<25*k and endbox_w<25*k) :
			endbox_num = endbox_num + 1;
	
	# 判断是否是终点线
	EndFlag.endline_type = 0
	if (endbox_num>2 and endbox_num<6):
		EndFlag.endline_type = 1 # 检测到第一条终点线
	elif(endbox_num >=6 ):
		EndFlag.endline_type = 2 # 检测到两条终点线
	else:
		EndFlag.endline_type = 0 # 未检测到终点线

while(True):
	clock.tick()
	global img
	img = sensor.snapshot()
	# 拍照

	img = img.replace(vflip=1,hmirror=1,transpose=0)
	# 因为是倒装的做上下颠倒

	find_blobs_in_rois(img)
	# 巡线函数

	find_endline(img)
	# 找终点线函数

	FH = bytearray([0xc3,0xc3])
	uart.write(FH)
	# 发送帧头

	data = bytearray([LineFlag.flag, LineFlag.delta_x, LineFlag.cross_y, EndFlag.endline_type])
	uart.write(data)
	# 发送内容

	ED = bytearray([0xc4,0xc4])
	uart.write(ED)
	# 发送帧尾

4.接口定义

Line.flag

数值 含义
00 未检测到直线
01 直线
02 十字路口或丁字路口
03 左转路口(顶部10像素以下)
04 右转路口(顶部10像素以下)
05 即将跨过十字路口(无down块)
06 即将跨过左拐丁字路口(无down块)
07 即将跨过右拐丁字路口(无down块)
08 直线(无down块)

LineFlag.delta_x

数值 含义
0~160 赛道红色中心线底部的X轴水平位置,左小右大;无down块时,返回中部的middle块X轴水平位置

LineFlag.cross_y

数值 含义
0~120 赛道红色中心线十字路口或丁字路口交叉点的Y轴竖直位置,上小下大

EndFlag.endline_type

数值 含义
00 未检测到终点线
01 检测到第一根终点线
02 检测到第二根终点线

三、识别数字部分

1.总体思路

1.1 识别方法

由题目可知,我们要同时识别两个或四个数字,这里有很多办法,我们的办法是让相机尽可能加高、使用广角镜头以便同时能看到四个数字。

如下图所示,小车的高度刚好卡在了25cm的限高,以便同时看到四个数字。

送药小车视觉模块,2021电赛,单片机,自动驾驶,stm32

我们再图像内划分出了五个ROI区域,依次检测,即可检测到处于五种不同位置的数字了。

送药小车视觉模块,2021电赛,单片机,自动驾驶,stm32

1.2 模型训练

OpenMV可以跑TensorFlow Lite模型,具体如何训练可以参考下面这篇博客。

https://blog.csdn.net/qq_36300069/article/details/118071444

训练模型的网站如下

https://studio.edgeimpulse.com/

这个网站可以把模型打包好导入OpenMV中,数据集是自己拍的照片,一共八十张训练集、二十张测试集。参数上我选择的是160*160像素、灰度图、训练100步,模型选的0.35的V2模型。

数据集如下图所示,左边是训练集,右边是测试集。

送药小车视觉模块,2021电赛,单片机,自动驾驶,stm32
训练结束后取得了不错的效果,识别准确率都在70%以上。

1.3 图像处理

仅仅是神经网络模型识别准确率还不高,我们使用了一些方法对图像进行一些处理来提高识别的成功率。

  • 镜头畸变校正
  • 缩小图像(避免画面损失)
  • 翻转图像(因为倒装)
  • 透视校正
  • 反相、红色填充黑色后再反相(去除红色影响)

如下图所示,左侧是未处理的图像,右图是处理后的图像。
送药小车视觉模块,2021电赛,单片机,自动驾驶,stm32

总体流程如下

送药小车视觉模块,2021电赛,单片机,自动驾驶,stm32

2.代码实现

import sensor, image, time, os, tf
from pyb import UART
import json

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.VGA)
sensor.skip_frames(time=2000)

net = "trainedv13.tflite"

# 透视校正用的四个点
TARGET_POINTS = [(143,210),
                 (495,214),
                 (640,480),
                 (0,480)]
                 
# 识别数字的五个区域
ROI0 = (210,170,170,170)
ROI1 = (20,0,170,170)
ROI2 = (150,0,170,170)
ROI3 = (285,0,170,170)
ROI4 = (430,0,170,170)

# 反相后红色阈值
xred_threshold = (51, 84, -31, -3, -26, -2)

# 各区域识别数字准确度门槛
keyline_0 = 0.7
keyline_1 = 0.6
keyline_2 = 0.65
keyline_3 = 0.65
keyline_4 = 0.6

ans_num = 0

clock = time.clock()

uart = UART(3, 115200)
uart.init(115200, bits=8, parity=None, stop=1)

while(True):
    clock.tick()
	
	# 拍照并进行一堆预处理
    img = sensor.snapshot().lens_corr(strength = 1.7, zoom = 0.55)
    img = img.replace(vflip=1,hmirror=1,transpose=0)
    img = img.rotation_corr(corners = TARGET_POINTS)
    img = img.negate()
    img = img.binary([xred_threshold], invert=False, zero=True)
    img = img.negate()

	# 识别中心数字
    for obj in tf.classify(net, img, roi=ROI0, min_scale=1.0, scale_mul=0.8, x_overlap=0.5, y_overlap=0.5):
        out = obj.output()
        max_idx = out.index(max(out))

        if max(out)>keyline_0:
            ans_0 = max_idx + 1
        else:
            ans_0 = 0
	
		# 识别左起第一个数字
    for obj in tf.classify(net, img, roi=ROI1, min_scale=1.0, scale_mul=0.8, x_overlap=0.5, y_overlap=0.5):
        out = obj.output()
        max_idx = out.index(max(out))

        if max(out)>keyline_1:
            ans_1 = max_idx + 1
        else:
            ans_1 = 0

	# 识别左起第二个数字
    for obj in tf.classify(net, img, roi=ROI2, min_scale=1.0, scale_mul=0.8, x_overlap=0.5, y_overlap=0.5):
        out = obj.output()
        max_idx = out.index(max(out))

        if max(out)>keyline_2:
            ans_2 = max_idx + 1
        else:
            ans_2 = 0
	
	# 识别左起第三个数字
    for obj in tf.classify(net, img, roi=ROI3, min_scale=1.0, scale_mul=0.8, x_overlap=0.5, y_overlap=0.5):
        out = obj.output()
        max_idx = out.index(max(out))

        if max(out)>keyline_3:
            ans_3 = max_idx + 1
        else:
            ans_3 = 0

	# 识别左起第四个数字
    for obj in tf.classify(net, img, roi=ROI4, min_scale=1.0, scale_mul=0.8, x_overlap=0.5, y_overlap=0.5):
        out = obj.output()
        max_idx = out.index(max(out))

        if max(out)>keyline_4:
            ans_4 = max_idx + 1
        else:
            ans_4 = 0

# 串口通信模块
    FH = bytearray([0xc3,0xc3])
    uart.write(FH)

    data = bytearray([ans_1, ans_2, ans_3, ans_4, ans_0])
    uart.write(data)

    ED = bytearray([0xc4,0xc4])
    uart.write(ED)

3.识别效果

实际识别效果尚可,但是帧率极低,联机状态只有大约0.4fps。文章来源地址https://www.toymoban.com/news/detail-611525.html

四、总结反思

  1. 只识别两个区域即可。四个数字的路口如果检测不到就随便去一个,走错了再掉头就好。可以提升帧率。
  2. 结合上一条,如果有两块OpenMV各自识别一个数字,帧率提升更明显。
  3. 结合上一条,将OpenMV换成K210,识别效果和帧率会更好。实际完赛的组大多是使用这个方法的。
  4. 如果使用树莓派或者jetson nano自然更好,也有部分组别用了这个方法。
  5. 电赛控制题日趋智能化,树莓派和神经网络模型将会逐渐成为常态。

到了这里,关于2021电赛F题送药小车视觉部分的一种思路(双OpenMV法)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 电赛智能送药小车_OpenMV巡线&识别十字路口完整代码

      整体思路 :通过划分ROI区域分区进行识别,中央 ROI 区域为巡线,左右两侧的 ROI_L 和 ROI_R 为十字路口识别 . 主程序如下 : # 本文代码中所导入的pid.py就是OpenMV官网上例程的代码 需要完整源码请私信我。

    2024年02月12日
    浏览(59)
  • 分享21年电赛F题-智能送药小车-做题记录以及经验分享

    自己是今年准备电赛的同学一名,电赛结束了,想把自己之前刷过的题目,通过这篇文章,来分享一波做这道题的思路和调试方法 自己在做之前的电赛题目时,也苦苦没有思路,不知道该怎么去下手,面对题目的要求和限制,应该如何去分析和实现 由于我们主要是准备小车

    2024年02月13日
    浏览(41)
  • 2021电赛F题数字识别和巡线部分

    文章之前12月发了一次,但是我后来申请的免毕设后,用到了一些文字,所以删了这篇文章,但是还是查重了,于是我把一些程序讲解先删了,等毕设结束后再编辑加上。 这次电赛我没有准备多少东西,只提前准备了图像识别和坐标计算一类的。在20年的省赛帮过雪地调过k

    2024年02月07日
    浏览(57)
  • 2023电赛E题视觉部分

    该部分主要要完成正方形区域的识别,并返回对应的坐标,但是由于距离1m,过远。因此需要引入图像增强,下面代码完成基本流程测试,仅供参考: import sensor import image import time   # 初始化摄像头 sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) sensor.skip_fr

    2024年02月14日
    浏览(42)
  • 全国电子设计竞赛-国二--智能送药小车

    不过这一次的比赛是真的很幸运,因为参加过比赛都知道,这一次没有了线下比赛,线上评审,不过拿到试题的那一天也是蛮紧张的,我们队伍做的是F题智能送药小车,现在的控制类的题目是真的越来越难了,相对于之前的控制类的题目。 还是讲一下比赛期间发生的一些事

    2024年02月14日
    浏览(74)
  • 智能送药小车(一)——K210巡线

    这部分基本就是例程上的内容,添加了一点注释。关于相关的运用到的函数解释,可以参考K210颜色识别.

    2024年02月12日
    浏览(38)
  • 智能送药小车(二)——K210物体检测,训练模型与部署

    线上训练模型参考这篇: Maixhub模型训练平台整体流程 下面仅介绍利用Mx-yolov3训练模型并部署在k210上的整体流程。 利用K210采集数据集 使用说明:插上合适的SD卡(最大32GB),采集不同类别图像时要在代码相应位置进行修改(有注释提示),程序运行后按下KEY会进行图像采集

    2024年02月14日
    浏览(44)
  • 2022电赛小车开源代码讲解开源

    2022电赛小车我认为有主要是几个主要的问题,我将分这几个部分来讲解 目录 一、循迹 二、蓝牙通信,双车数据传输 三、起始路口的识别 四、分叉路口的识别 五、源码  2022电赛,双车稳定行驶_哔哩哔哩_bilibili 循迹我们组用的是五路灰度,灰度跟红外对管的作用是差不多的

    2024年02月09日
    浏览(43)
  • 2022电赛小车跟随行驶系统(C题)复盘

    此次的电赛感觉结束的挺突然的,在找到方向准备细化地进行调试的时候却是没有足够时间来完成代码上的完善。想着此次遗憾,写一篇博客来记录一下自己的经历吧,总结总结此次的电赛时光。   电赛题目  首先便是题目的选择,由于我们之前校电赛时简单接触过小车的题

    2024年02月16日
    浏览(51)
  • MSP430F5529循迹小车 2022电赛 C题

    编辑时间2022/8/21 选用材料:主控MSP430F5529、直流减速电机(带光电编码器)、TB6612电机驱动、超声波测距、灰度传感器、无线透传、OLED屏显示。 使用灰度传感器巡线,超声波检测前后车距作为位置环反馈,通过位置环调节前后车距离。 :MSP430   循迹    无线串口透

    2024年02月16日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包