2020年电赛G题:非接触物体尺寸形态测量训练指南

非接触物体尺寸形态测量

一、题目要求

具体内容详见非接触物体尺寸形态测量(G 题)——行走的皮卡丘

设计并制作一个非接触式物体形状和尺寸自动测量装置,装置的布置图如图 1所示,测量装置放置在图中所示的测量装置区内,被测目标放置在图中被测目标放置区内,装置能测量被测目标的形状、尺寸、测量头中心点与被测目标之间的距离等参数,并用激光束指示出被测目标的中心位置。背景板竖立放置在目标后5cm 处,图 2 为目标和背景板放置示意图。

要求
(1) 选择某规则形状的平面目标,放在被测目标放置区的中心线位置上,按测量键后开始测量,完成测量后,在装置上显示出该目标物体边长(如果目标选择的是圆形目标,显示出直径)、几何形状和目标与测量头的距离,整个测量和指示过程要求总用时不超过 2 分钟。 (25 分)
(2) 更换目标板,在摆放区内中心线上放置目标和背景板,显示距离、形状、尺寸(边长),要求测量用时不超过 2 分钟。 (25 分)
(3) 自动寻找目标测量:测量头处于中心线方向(0º), 目标摆放在目标放置区内任选位置;按测试键后,装置自动寻找目标,测量并显示距离、形状、尺寸、用激光笔指示几何中心,用时不超过 3 分钟,越短越好。 (30 分)
(4) 立体目标测量:随机抽取篮球、排球、足球中一个,重复(3)测量,判断球类品种、测量与球表面最近距离。用时不超过 2 分钟。 (15 分)
(5) 其他。 (5 分)
(6) 设计报告 。 (20 分)

二、器件选型

2.1 摄像头及处理器模块

由于是第一次接触这种结合视觉处理的测控类题目,没有摄像头,只能找隔壁控制组借的OpenMV来做题,也算是初步的学习摄像头的使用。

OpenMV摄像头有自己的官方手册以及相关使用说明:星曈科技,里面对各个功能以及相关API都有一定的说明。
中文入门教程:OpenMV嵌入式图像处理中文入门教程

2.2 舵机

舵机云台并没有去别处购买,而是使用之前学长参加工赛留下的机械臂边角料搭建,事实上做这道题的第一天大多数时间其实也消耗在了机械机构的构思和组装调试上。一台舵机提供水平方向运动,一台提供竖直方向运动。

舵机选用了LD-3015MG和LD-220MG的组合,两者都是数字舵机,工作电压6-7.4V,PWM高电平脉宽范围500~2500us控制的270°舵机,能满足完成该题的需要,数据手册如下。

LD-220MG LD-3015MG

2.3 电源

电源分为两部分,一部分是摄像头以及处理器模块、蓝牙模块、测距模块、激光头等5V供电的元件,通过充电宝供电;另一部分专门为舵机供电,由学生电源经过LM2596S降压模块转为7.4V,学生电源可以替换为12V航模电池。两部分电源通过0欧姆电阻单点接地。
接线板用洞洞板焊接的。

关于各模块组装的注意事项

摄像头模块一定要倒着安装,正着安装在舵机旋转过程中很容易发生模块脱落甚至usb口断裂脱离,不便于调试。
OpenMV核心板上只引出了一对3V3和GND,可以使用包装内的包含的拓展版多引出几个GND便于连接其他电路和模块。
基本搭建完毕后图片,此时tof测距模块还在赶来的路上……


顺便放点细节图

细节1 细节2 细节3
正面固定,激光头下置,这个位置刚好不会被变焦镜头挡住激光 背面,整个结构全靠卡住,一滴胶也没用。顺便可以架蓝牙模块 这个连接件真心好用

三、各功能模块实现思路

请添加图片描述

3.1 颜色形状的识别

任务中需要识别的颜色形状包括以下九种:

红色 绿色 蓝色
矩形 红色矩形 绿色矩形 蓝色矩形
圆形 红色圆形 绿色圆形 蓝色圆形
三角 红色三角 绿色三角 蓝色三角

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方法,来判断是什么颜色的色块。
roi “感兴趣区”
x_stride 查找的色块的x方向上最小宽度的像素,默认为2、
y_stride 就是查找的色块的y方向上最小宽度的像素,默认为1、
invert 反转阈值,把阈值以外的颜色作为阈值进行查找
area_threshold 面积阈值,如果色块被框起来的面积小于这个值,会被过滤掉
pixels_threshold 像素个数阈值,如果色块像素数量小于这个值,会被过滤掉
merge 合并,如果设置为True,那么合并所有重叠的blob为一个。

通过设置area_threshold或pixels_threshold可以有效的去除小目标和由于颜色阈值设置不当或者环境光造成的干扰,有效识别目标色块。
函数返回值为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),也可以通过blob[7]来获取。
blob.code() 返回一个16bit数字,每一个bit会对应每一个阈值。如果这个色块是红色,那么它的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.code()就可以实现,唯一需要注意的就是范围最好稍微大一点防止环境光发生变化是无法正常识别。
对于形状的识别,传统的思路大多数都是使用直线识别,然后根据多条直线角度计算识别,或者就是使用打包好的函数。在使用过后发现识别的准确率不高,缺少有效的识别函数。

识别方式简化
题目并没有强制要求测量的尺寸形状必须是可以旋转任意角度的,那么假定所有被测物体都为正向放置,此时对于识别到的色块外框内的全部像素来说,待测形状的色块在框体内的占空比是一定的,理论值如下图所示。

占空比计算方法如下:

pic_duty = max_blob.pixels()/max_blob.w()/max_blob.h()
#max_blob.pixels()	-》色块像素点数统计
#max_blob.w()		-》色块外框宽度
#max_blob.h()		-》色块外框高度

实际测试过程中,并不完全符合理论值,所以对范围要进行扩大,经过简化后最后选定的范围:矩形(0.9-1.0)、圆形(0.72-0.85)、三角(0.4-0.65)
使用变焦镜头
默认镜头视野范围很大,待测物与间隔2-3m,占据画面内像素区域较小,不好判断。安装手动变焦镜头后,通过改变镜头的焦距可以有效放大感兴趣区域,画面内有效信息更多。
在实际使用中,将视野范围调整至以目标中心为中心,上下左右各1m的范围较佳。
相机参数的设置
由于摄像头是倒置的,为了便于观察和操作最好对图像进行翻转

sensor.set_hmirror(True) #水平方向翻转
sensor.set_vflip(True) #垂直方向翻转

为了保证稳定性,自动增益/白平衡/曝光全部关闭或设成定值

sensor.set_auto_gain() 
#自动增益开启(True)或者关闭(False)。在使用颜色追踪时,需要关闭自动增益。
sensor.set_auto_whitebal() 
#自动白平衡开启(True)或者关闭(False)。在使用颜色追踪时,需要关闭自动白平衡。
sensor.set_auto_exposure(enable[\, exposure_us])
#enable 打开(True)或关闭(False)自动曝光。默认打开。
#如果 enable 为False, 则可以用 exposure_us 设置一个固定的曝光时间(以微秒为单位)。

此外最好跳过一些帧,等待感光元件变稳定。

sensor.skip_frames(n=10) #跳过n张照片,在更改设置后,跳过一些帧,

3.2 舵机云台控制

目标追踪
首先是关于目标追踪,在3.1中我们提到,blob对象有多种方法,其中包括blob.cx() 和blob.cy(),在图中以这两个方法的返回值画十字,可以发现他们的位置其实就是色块的中心位置,也就是图形的几何中心。题目要求用激光笔指向图形中心,安装时使激光点对准画面中心,追踪目标时令画面中心不断逼近色块中心直至重合或接近。
PID
OpenMV库包含PID.py,调用函数可以实现pid控制的参数设定和计算。

from pid import PID

pan_pid = PID(p=0.05, i=0.02, imax=20)
til_pid = PID(p=0.042, i=0.02, imax=36)

pan_error = max_blob.cx()-img.width()/2 
til_error = max_blob.cy()-img.height()/2
pan_output= pan_pid.get_pid(pan_error,1)/2
til_output= til_pid.get_pid(til_error,1)

if((abs(pan_error*pan_error)>9.5) and (abs(pan_error)>4.5 or abs(til_error)>4.5)):
	degreeMotor1 = degreeMotor1 - pan_output
	degreeMotor2 = degreeMotor2 - til_output

3.3 尺寸计算和测距

测距是比较容易实现的需求,安装ToF测距拓展板,iic通信,通过distance_read() 函数读取距离即可,测量结果较为准确,达到mm级别精度,可以加滤波算法让数值输出更稳定。安装之后如果发现测量结果与实际情况相差较大,建议排查是不是被测物体与拓展板中心的传感元件不在同一直线上。拓展板使用官方例程如下:

from machine import I2C
from vl53l1x import VL53L1X
import time

i2c = I2C(2)
distance = VL53L1X(i2c)

while True:
    print("range: mm ", distance.read())
    time.sleep_ms(50) #一定要加

在我的思路中,尺寸计算和测距其实是同一件事,当我们实现了精确的测距,在判断颜色形状正确的情况下,自然可以实现精确的尺寸测量。理论依据如下图所示,当物体远离镜头时,随着距离的增加,目标在摄像头感光元件上的成像也就越小,占据的像素点越少,显然θ1>θ2。

考虑到正放时,矩形、圆形、三角的边长或直径与框体宽度基本一致,因此以色块框体宽度作为尺寸测量的依据,推导公式如下:
(1) 设镜头到目标距离为d,到感光元件距离a;
设目标尺寸w,成像大小(像素宽度)为t ;
(2) 由t/a=w/d,可知 t = f(d,w) = a*d/w ; 但参数a未知,故重新测试数据拟合;(后来想到没有a也可以先计算d/w再拟合出线性函数,但是因为另一种方式也做出了所以没再尝试)
(3) 测量多种形状尺寸组合的实际尺寸w、距离d和对应的像素宽度t ;
(4) 在MATLAB中使用工具箱进行拟合,将d设为1次、w设为2次时,标准差较小f(d,w) = p00 + p10*d + p01*w + p11*d*w + p02*w*w
(这样做出来数据每个人应该有较大差异,以下参数仅供参考)

p00 =       204.3 # (76.54, 324.1)
p10 =    -0.06741 # (-0.1111, -0.02368)
p01 =      -3.338 # (-5.366, -1.31)
p11 =    0.002346 # (0.001795, 0.002898)
p02 =    0.008529 # (0.002729, 0.01433)

3.4 球类的识别(模板匹配)

由于能找到的球类只有篮球,所以暂时只做了单模板匹配,分类后使用多目标匹配即可(没有遇到显著问题)。模板匹配单独写了一个模式,通过按键切换,设置为灰度模式,图像大小QQVGA,一方面像素少好计算,不然计算速度慢帧率会很低,另一方面模板匹配要求模板尺寸小于等于32px*32px
先不打开云台,对篮球进行拍摄,框住篮球所在区域点击右键保存成.bmp文件,如下图

然后通过转换工具转换成.pgm灰度图,我使用的是一个在线转换工具:convertio,转换完成后存入SD卡,把卡插在OpenMV的tf卡槽里面,移植模板匹配程序,设定模板路径,设定相似度阈值。

template = image.Image("/bkb.pgm")

# find_template(template, threshold, [roi, step, search])
# ROI: The region of interest tuple (x, y, w, h).
# Step: The loop step used (y+=step, x+=step) use a bigger step to make it faster.
# Search is either image.SEARCH_EX for exhaustive search or image.SEARCH_DS for diamond search
# Note1: ROI has to be smaller than the image and bigger than the template.
# Note2: In diamond search, step and ROI are both ignored.
r = img.find_template(template, 0.70, step=4, search=SEARCH_EX) #, roi=(10, 0, 60, 60))
#find_template(template, threshold, [roi, step, search]),threshold中
#的0.7是相似度阈值,roi是进行匹配的区域(左上顶点为(10,0),长80宽60的矩形),
#注意roi的大小要比模板图片大,比frambuffer小。
#把匹配到的图像标记出来
if r:
	img.draw_rectangle(r)
	img.draw_cross(int(r[0]+r[2]/2), int(r[1]+r[3]/2)) # cx, cy

实际使用中的效果效果还可,测量范围内基本可以成功识别,但是测距遇到一些问题,因为测距模块和镜头和激光笔都不在同一直线上,导致激光笔指向篮球正中时测距模块发出的粒子并没有打在篮球表面而是打到背景,解决方法是牺牲激光指示的准确度强行让云台下降一定角度使模块准确测量球体表面与摄像头之间距离。
degreeMotor2 = degreeMotor2 - til_output - (80.0/distan)*20

这张就是激光笔打准了,测距模块没对上的情况,篮球位置有点低了
这张就是激光笔打准了,测距模块没对上的情况,篮球位置有点低了。

四、实际测试

当时只截了这两张图,不同形状激光笔打的很准,尺寸测量也基本准确。
测试截图

最后把main.py和pid.py存进SD卡脱机运行,距离测量误差范围±20mm,尺寸测量误差范围±5mm,完全满足题目要求。
以上全部内容,单人耗时三天完成。

物联沃分享整理
物联沃-IOTWORD物联网 » 2020年电赛G题:非接触物体尺寸形态测量训练指南

发表评论