ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-11-10 16:30:01  阅读:361  来源: 互联网

标签:roi blobs 电赛 终点线 flag LineFlag 2021 识别 OpenMV


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

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

目录

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

1.题目介绍

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

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

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

在这里插入图片描述

数字字模

2.图像部分分析

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

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

2.1识别道路

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

后期转向OpenMV的方案。

2.2识别数字

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

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

二、识别道路部分

1.巡线-红色实线

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

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

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

请添加图片描述

2.终点线-黑色虚线

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

如下图所示,识别到六个以上矩形块即可视为终点线。
请添加图片描述

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)
	# 发送帧尾

标签:roi,blobs,电赛,终点线,flag,LineFlag,2021,识别,OpenMV
来源: https://blog.csdn.net/DCCSDNDC/article/details/121249961

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有