Arduino案例实操 -- 智能巡防小车(二)OpenMV黑线检测

2.1 OpenMV简介

2.2 OpenMV详细参数

这两小节都有相关博文做过介绍,这里直接甩链接就好,减少文章篇幅
链接 → OpenMV新手上路1 – OpenMV简介、参数描述

2.3 OpenMV IDE安装

2.4 OpenMV Cam连接

同样的,甩博文链接
OpenMV新手上路2 – 驱动、IDE安装及简单使用(window环境)

2.5 OpenMV项目搭建

2.5.1 OpenMV工程新建

打开OpenMV IDE,点击左上角的“文件→新建文件”进行新的OpenMV项目创建。
在这里插入图片描述
创建成功后IDE页面将打开一个“untitled_1.py”文件,并带有OpenMV项目代码的初始代码(类似新建Arduino例程时的初始代码是setup()函数和loop()函数),初始代码运行的功能为初始化OpenMV传感器,并循环将传感器获取到的图像显示到IDE上,同时打印帧数。
在这里插入图片描述

2.5.2 OpenMV颜色识别相关函数

在智能巡防小车的项目中,OpenMV的主要功能是做黑线识别(即颜色识别)和坐标值发送,关于OpenMV的颜色识别功能,有以下几个知识点需要掌握。

find_blobs函数

追踪小球是OpenMV用得最多的功能,实质上是通过find_blobs函数去找到图像中的指定色块,即在OpenMV图像视野中分辨出需要查找的颜色。
接下来看一下find_blobs函数的细节:

image.find_blobs(thresholds, roi=Auto, x_stride=2, y_stride=1, invert=False, area_threshold=10, pixels_threshold=10, merge=False, margin=0, threshold_cb=None, merge_cb=None)

这里的参数比较多。

  • thresholds是颜色的阈值,注意:这个参数是一个列表,可以包含多个颜色。如果你只需要一个颜色,那么在这个列表中只需要有一个颜色值,如果你想要多个颜色阈值,那这个列表就需要多个颜色阈值。注意:在返回的色块对象blob可以调用code方法,来判断是什么颜色的色块。
	red = (xxx,xxx,xxx,xxx,xxx,xxx)
	blue = (xxx,xxx,xxx,xxx,xxx,xxx)
	yellow = (xxx,xxx,xxx,xxx,xxx,xxx)
	
	img=sensor.snapshot()
	red_blobs = img.find_blobs([red])
	
	color_blobs = img.find_blobs([red,blue, yellow])
  • roi是“感兴趣区”,相当于在OpenMV图像视野中框选一个范围作为颜色检测的区域,roi的参数为4个,分别是起始点x,起始点y,结束点x,结束点y。
	left_roi = [0,0,160,240]
	blobs = img.find_blobs([red],roi=left_roi)
  • x_stride 就是查找的色块的x方向上最小宽度的像素,默认为2,可以作为检测时的干扰排除项使用,如果你只想查找宽度10个像素以上的色块,那么就设置这个参数为10:
	blobs = img.find_blobs([red],x_stride=10)
  • y_stride 就是查找的色块的y方向上最小宽度的像素,默认为1,如果你只想查找宽度5个像素以上的色块,那么就设置这个参数为5:
	blobs = img.find_blobs([red],y_stride=5)
  • invert 反转阈值,把阈值以外的颜色作为阈值进行查找。
  • area_threshold 面积阈值,如果色块被框起来的面积小于这个值,会被过滤掉。
  • pixels_threshold 像素个数阈值,如果色块像素数量小于这个值,会被过滤掉。
  • merge 合并,如果设置为True,那么合并所有重叠的blob为一个。
    注意:这会合并所有的blob,无论是什么颜色的。如果你想混淆多种颜色的blob,只需要分别调用不同颜色阈值的find_blobs。
	all_blobs = img.find_blobs([red,blue,yellow],merge=True)
	
	red_blobs = img.find_blobs([red],merge=True)
	blue_blobs = img.find_blobs([blue],merge=True)
	yellow_blobs = img.find_blobs([yellow],merge=True)
  • margin 边界,如果设置为1,那么两个blobs如果间距1一个像素点,也会被合并。
阈值

阈值即对某个特定颜色或某个色域规定的一组参数,一个颜色阈值的结构是这样的:

	red = (minL, maxL, minA, maxA, minB, maxB)

元组里面的数值分别是L A B 的最大值和最小值。
L A B参数可在图像直方图中直接看到数据。
在这里插入图片描述
在IDE里,还有更方便的阈值选择工具。

颜色阈值选择工具

OpenMV 的IDE里加入了阈值选择工具,极大的方便了对于颜色阈值的调试。
首先运行untitled_1.py让IDE里的帧缓冲区显示图案。
在这里插入图片描述然后打开“工具→机器视觉→阈值编辑器”。
在这里插入图片描述选择帧缓冲区可以获取IDE中的图像,选择图像文件可以另外选择一张图片进行阈值编辑。
在这里插入图片描述
选择帧缓冲区打开阈值编辑器,可看到如图中的二进制图像及原图像,拖动下方L A B最大值及最小值的滑轨改变参数可进行颜色阈值获取。
在这里插入图片描述想要获取源图像中蓝色小方块的阈值时,拖动L最小值与L最大值形成L值范围,确保在二进制图像中蓝色小方块所在的位置为白色,二进制图像中其他位置为黑色,A值与B值的调参方式也是如此,最后复制底部调参好的L A B数值。
在这里插入图片描述

blobs是一个列表

find_blobs对象返回的是多个blob的列表。(注意区分blobs和blob,这只是一个名字,用来区分多个色块,和一个色块)。
列表类似与C语言的数组,一个blobs列表里包含很多blob对象,blobs对象就是色块,每个blobs对象包含一个色块的信息。

	blobs = img.find_blobs([red])

blobs就是很多色块。
可以用for循环把所有的色块找一遍。

	for blob in blobs:
	    print(blob.cx())
blob色块对象

blob有多个方法:

  • blob.rect() 返回这个色块的外框——矩形元组(x, y, w, h),可以直接在image.draw_rectangle中使用。

  • blob.x() 返回色块的外框的x坐标(int),也可以通过blob[0]来获取。

  • blob.y() 返回色块的外框的y坐标(int),也可以通过blob[1]来获取。

  • blob.w() 返回色块的外框的宽度w(int),也可以通过blob[2]来获取。

  • blob.h() 返回色块的外框的高度h(int),也可以通过blob[3]来获取。

  • blob.pixels() 返回色块的像素数量(int),也可以通过blob[4]来获取。

  • blob.cx() 返回色块的外框的中心x坐标(int),也可以通过blob[5]来获取。

  • blob.cy() 返回色块的外框的中心y坐标(int),也可以通过blob[6]来获取。

  • blob.rotation() 返回色块的旋转角度(单位为弧度)(float)。如果色块类似一个铅笔,那么这个值为0 ~ 180°。如果色块是一个圆,那么这个值是无用的。如果色块完全没有对称性,那么你会得到0 ~ 360°,也可以通过blob[7]来获取。

  • blob.code() 返回一个16bit数字,每一个bit会对应每一个阈值。

  • blobs = img.find_blobs([red, blue, yellow], merge=True)

    如果这个色块是红色,那么它的code就是0001,如果是蓝色,那么它的code就是0010。注意:一个blob可能是合并的,如果是红色和蓝色的blob,那么这个blob就是0011。这个功能可以用于查找颜色代码。也可以通过blob[8]来获取。

  • blob.count() 如果merge=True,那么就会有多个blob被合并到一个blob,这个函数返回的就是这个的数量。如果merge=False,那么返回值总是1。也可以通过blob[9]来获取。

  • blob.area() 返回色块的外框的面积。应该等于(w * h)。

  • blob.density() 返回色块的密度。这等于色块的像素数除以外框的区域。如果密度较低,那么说明目标锁定的不是很好。
    比如,识别一个红色的圆,返回的blob.pixels()是目标圆的像素点数,blob.area()是圆的外接正方形的面积。

2.5.3 OpenMV黑线检测源码说明

下面以智能巡防小车的OpenMV源码做分段说明。
相关调用模块引入。

	import sensor, image, time   # 引入感光元件模块、图片处理模块、时间模块
	from pyb import UART          # 引入串口模块
	import json                     # 引入json字符串模块
	from pyb import LED           # 引入LED控制模块

设置项目中黑线检测的阈值,以及在图像中的感兴趣区(ROI)。

	user_threshold = (0, 32, -128, 127, -128, 127)  # 设置黑线阈值参数
	ROIS = (0,100,320,40)                                # 设置roi感兴趣区

设置串口及串口波特率。

#设置串口号及串口波特率
	uart = UART(3,115200)

初始化摄像头参数,包括感光元件复位,设置图像色彩,图像大小,以及在寻找色块(巡线)功能下需要关闭自动增益功能和白平衡功能。

#设置摄像头
	sensor.reset()                                    # 初始化感光元件
	sensor.set_pixformat(sensor.RGB565)           # 设置像素模式为彩色
	sensor.set_framesize(sensor.QVGA)             # 设置图像大小为QVGA:320*240
	sensor.skip_frames(time = 2000)                # 跳过前几帧的图片
	sensor.set_auto_gain(False)                     # 关闭自动增益 -- 巡线模式下需要关闭
	sensor.set_auto_whitebal(False)                # 关闭白平衡 -- 巡线模式下需要关闭

取得系统时间,并点亮板载RGB灯为白色,加强图像颜色识别。

# 获取系统时间
	clock = time.clock()
	
	# 打开板载LED灯(白色)
	LED(1).on()
	LED(2).on()
	LED(3).on()

新建全局变量。

	# 新建全局变量
	dis_sum = 0
	sum_count = 0
	value_get = False

进入while循环,开始计算运行时间,并用OpenMV摄像头进行拍照将图像传至IDE中,随后新建局部变量。

	while(True):
	    clock.tick()                  # 开始计算运行时间
	    img = sensor.snapshot()     # 获取一张图像
	
	    # 新建变量
	    distance = 0
	    distance_send = False

在获取的图像中进行roi区域划分和黑线颜色阈值检测,并把找到的黑线用矩形和矩形中心坐标表示出来,判断找到的黑线是否符合要求。

# 将find_blobs返回的blob对象赋值给b
    for b in img.find_blobs([user_threshold],roi=ROIS[0:4], merge=True):
        # 判断黑线色块大小,排除图像干扰
        if (b[4] >= 30) and (b[3] >= 30):
            img.draw_rectangle(b[0:4],color=(255,0,0))      # 画矩形
            img.draw_cross(b.cx(),b.cy(),color=(0,0,0))     # 画中心十字

            # 判断所检测到的黑线是否在图像范围内
            if b[5]< 0 or b[5] > 320:
                distance = 0
                distance_send = False
            else:
                distance = b[5]
                distance_send = True

将获取到的中心坐标每10次取一次平均值再取整,使用json脚本将角度值转换成字符串通过串口发送出去。

# 判断正确获取到黑线位置
    if distance_send == True:
        dis_sum += distance
        sum_count += 1

        # 取10次的坐标平均值
        if sum_count == 10:
            AngleValue = round(dis_sum/sum_count - 160)     # 计算角度值并取整数

            # LastAngleValue进行角度赋值
            if not value_get:
                LastAngleValue = AngleValue
                value_get = True

            # 对比角度是否发生变化
            if (LastAngleValue != AngleValue):
                ubuffer = json.dumps(AngleValue)            # 将新角度转换成字符串
                LastAngleValue = AngleValue                 # 将角度值进行赋值
                uart.write(ubuffer+'\n')                    # 串口发送角度值字符串
            dis_sum = 0
            sum_count = 0

为了用OpenMV做黑线检测,博主也是简单学了一门新语言(Micropython),并用简单的逻辑将摄像头捕捉到的黑线坐标变化通过串口进行发送,因为OpenMV为32位处理器,对每一帧图像的处理速度是比较快的,同时将坐标数据发送到8位处理器(UNO)上可能造成UNO的串口数据堵塞,处理不来,所以博主在OpenMV坐标发送前也是做了取平均值的操作,每10个坐标数据折算成1个平均值进行发送,减少UNO串口数据接收压力,坐标数据也相对更加稳定、准确(初学micropython对相关数学函数及逻辑使用方面不熟,Python大神勿喷

2.5.4 OpenMV固件上传

OpenMV IDE除了可以在线进行脚本运行仿真,也能将调试好的py脚本上传至OpenMV Cam成为OpenMV Cam的上电运行脚本。
调试好OpenMV脚本之后,断开脚本运行,在菜单栏“工具 → 将打开的脚本保存到OpenMV Cam(作为main.py)”,在OpenMV Cam中main.py为上电运行脚本,此操作相当于把OpenMV Cam内存中main.py文件进行替换。
在这里插入图片描述
在脚本正确上传过程中,OpenMV Cam板载的RGB灯会是红色状态,在上传完成后,RGB灯熄灭,如果不能正确上传脚本,请检查IDE是否正常连接了OpenMV Cam。

猜你喜欢

转载自blog.csdn.net/qq_36955622/article/details/113940339