系统总体分为硬件设计和软件设计,硬件设计就是基于树莓派2代B型和Arduino的硬件基础平台搭建,能够适应小型、灵活的现实需求,并为软件设计提供可观的计算能力承载和算法改进空间。软件设计就是在嵌入式Arduino上构建控制系统,用来控制物料车基本的移动,机器臂的抓取,摄像头的位置等操作,而在树莓派上构建软件开发环境,在OpenCV框架下,通过C++语言实现二维码的检测和颜色的识别。
硬件包括Arduino Mega2560、树莓派 2 代 B 型、摄像头模块(720P)、对接串口线、SD存储卡,HDMI toVGA转接器、VGA 显示器、USB 键盘鼠标。
本系统使用的摄像头是 360D70 智能摄像头内核,该摄像头支持 720P 高清分辨率,镜头视角对角视角120度,可以保证监控区域的无死角监控;其次,该摄像头支持红外线以及USB 免驱接口输入,便于本项目系统中各个模块的集成。
图1硬件系统关系图
本系统中要解决的主要两个问题是二维码的识别和物料颜色的识别。
二维码图像识别的流程是:采集图像、图像预处理、条形码识别,得到图像中的条形码信息。需要解决好二个问题,即图像处理和条形码识别问题。
(1)图像处理问题
图像处理部分是条码识别的前期工作,需要使用强大的图象处理工具来进行,包括图像的读入、条形码区域的裁剪、滤波、二值化处理等,得到高质量图像。条码识别就是在这个图像的基础上实现,所以图像处理的质量直接关系到条码能否正确识别。
本系统中树莓派采用官方的 Linux 操作系统, 适合于本系统中基于 OpenCV 框架下的算法移植。OpenCV 是一个基于 BSD 许可(开源)发行的跨平台计算机视觉库,可以运行在 Linux、Windows 和 Mac OS 操作系统上。 它轻量级而且高效———由一系列 C 函数和少量 C++类构成,同时提供了 Python、Ruby、MATLAB 等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法,本文拟采用OpenCV 及其 C++接口完成算法移植和软件系统搭建。
(2)二维码识别问题
条形码识别问题,就是采用什么方法把图像上的条形码转化成相应的文本信息。如果采用根据条形码编码原理来识别条码,因为条形码种类多,每种条形码编码方式不同,开发人员不仅要熟悉各种条形码的编码规则,而且要针对每种条形码编写相应的解码程序,具有实现起来难度大、编程量大等缺点。而采用条形码识别工具包来进行条形码识别,一可以大大简化编程工作量;二不需要熟悉各种条形码的编码规则,识别工具包都能自行判断;三来识别率高、速度快。
目前条形码识别工具包有很多,但开源的条形码开发工具包不多,主要有两个,一是Zbar工具包,另一是ZXing工具包,两者都具有解码多种格式的一维和二维条形码功能,并且这两种工具包支持多平台和多语种版本供开发者使用。ZBar工具是基于C语言编写,解码效率高于ZXing,所以在Linux平台下,结合OpenCV进行条形码识别,首选Zbar工具包。
Zbar条形码工具包的环境设置,只涉及到一个库文件libzbar-0.lib和一个头文件Zbar.h,把库文件复制到OpenCV的库文件目录下,把头文件Zbar.h加到当前编译的工程中就可以了,编译时就可以就能找到这些文件进行编译。
识别的第二步是获取要识别的图像,获取图像的方法是通过电脑摄像头或者其他图像采集设备,实时获取要识别的条形码图像, 方法是利用OpenCV的库函数,先创建设备和图像两个变量。然后利用库函数获取摄像头中的图像,就可以从摄像头中获取图像。
图像预处理的目标是提高别的速度和识别的正确率。简单的图像处理方法是对整幅图像进行去噪声和二值化处理;复杂的处理方法是从整个图像中裁剪取条形码区域,去噪声和二值化,必要时还要进行旋转、放大和缩小等处理,最终获得高质量的图像。
图像预处理好了,就可以利用Zbar进行条形码的识别,需要如下步骤:
要从图像中识别条形码,必须先创建Zbar图像阅读器,并且进行相应设置。Zbar阅读器设置好了以后,接着读入图像,获取图像信息,为识别条形码做准备。
上述识别准备工作做好以后,接下就是从图像中读取条形码数据,把识别的数据从UTF8格式转成ASCII格式,这样识别出来的条形码数据变量就可以对该数据根据需要进行相应的处理。
图2二维码检测效果
//二维码检测,返回值为布尔值
bool readQRCode(const Mat inputImage)
{
ImageScanner scanner;// 所转化成的灰度图像,定义一个扫描仪
scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1);
Mat imageGray;
cvtColor(inputImage,imageGray,CV_RGB2GRAY);
int width = imageGray.cols;
int height = imageGray.rows;
// 在Zbar中进行扫描时候,需要将OpenCV中的Mat类型转换为(uchar *)类型,raw中存放的是图像的地址;对应的图像需要转成Zbar中对应的图像zbar::Image
uchar *raw = (uchar *)imageGray.data;
Image imageZbar(width, height, "Y800", raw, width * height);
scanner.scan(imageZbar); //扫描条码
Image::SymbolIterator symbol = imageZbar.symbol_begin();
if(imageZbar.symbol_begin()==imageZbar.symbol_end())
{
//cout<<"查询条码失败,请检查图片!"<get_type_name()<get_data()<get_data();
}
//imshow("Source Image",inputImage);
imageZbar.set_data(NULL,0);
return true;
}
}
数字图像处理中常用的采用模型是RGB(红,绿,蓝)模型和HSV(色调,饱和度,亮度),RGB广泛应用于彩色监视器和彩色视频摄像头,我们平时的图片一般都是RGB模型。而HSV模型更符合人描述和解释颜色的方式,HSV的彩色描述对人来说是自然且非常直观的。
HSV模型中颜色的参数分别是:色调(H:hue),饱和度(S:saturation),亮度(V:value)。由A. R. Smith在1978年创建的一种颜色空间, 也称六角锥体模型(Hexcone Model)。
(1)色调(H:hue):用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,品红为300°;
(2)饱和度(S:saturation):取值范围为0.0~1.0,值越大,颜色越饱和。
(3)亮度(V:value):取值范围为0(黑色)~255(白色)
RGB转成HSV,设 (r, g, b) 分别是一个颜色的红、绿和蓝坐标,它们的值是在 0 到 1 之间的实数。设 max 等价于 r, g 和 b 中的最大者。设 min 等于这些值中的最小者。要找到在 HSV 空间中的 (h, s, v) 值,这里的 h ∈ [0, 360)是角度的色相角,而 s, v ∈ [0,1] 是饱和度和亮度,方法如下:
max=max(R,G,B)
min=min(R,G,B)
if R = max, H = (G-B)/(max-min)
if G = max, H = 2 + (B-R)/(max-min)
if B = max, H = 4 + (R-G)/(max-min)
H = H * 60
if H < 0, H = H + 360
V=max(R,G,B)
S=(max-min)/max
OpenCV下有个函数可以直接将RGB模型转换为HSV模型,OpenCV中H∈ [0, 180), S ∈ [0, 255], V ∈ [0, 255]。我们知道H分量基本能表示一个物体的颜色,但是S和V的取值也要在一定范围内,因为S代表的是H所表示的那个颜色和白色的混合程度,也就说S越小,颜色越发白,也就是越浅;V代表的是H所表示的那个颜色和黑色的混合程度,也就说V越小,颜色越发黑。经过实验,识别蓝色的取值是 H在100到140,S和V都在90到255之间。一些基本的颜色H的取值可以如下设置:
Green 38-75,Blue 75-130,Red 160-179
OpenCV 的文档中是这样解释的:原本输出的 HSV 的取值范围分别是 0-360, 0-1, 0-1;但是为了匹配目标数据类型 OpenCV 将每个通道的取值范围都做了修改,于是就变成了 0-180, 0-255, 0-255,并且同时解释道:为了适应 8bit 0-255 的取值范围,将 hue 通道 0-360 的取值范围做了减半处理,这就是为什么 OpenCV 中 H通道的取值范围是0-180。
图3 HSV色域范围
判断颜色主要通过inRange函数,通俗的来讲,这个函数就是判断src中每一个像素是否在[lowerb,upperb]之间,注意集合的开闭。如果结果为是,那么在dst相应像素位置填上255,反之则是0。一般我们把dst当作一个mask来用,如上例所示。
官方的解释为:Checks if array elements lie between the elements of two other arrays. 即检查数组元素是否在另外两个数组元素值之间。这里的数组通常也就是矩阵Mat或向量。要特别注意的是:该函数输出的dst是一幅二值化之后的图像。
针对单通道图像
dst(I) = lowerb(I)0 ≤ src(I)0 < upperb(I)0
即,如果一幅灰度图像的某个像素的灰度值在指定的高、低阈值范围之内,则在dst图像中令该像素值为255,否则令其为0,这样就生成了一幅二值化的输出图像。
针对三通道图像
dst(I) = lowerb(I)0 ≤ src(I)0 < upperb(I)0 ∧ lowerb(I)1 ≤ src(I)1 < upperb(I)1 ∧lowerb(I)2 ≤ src(I)2 < upperb(I)2
//颜色检测,返回值为颜色序号
int ColorDetect(int color, const Mat &inputImage)
{
int point_x;
int iLowH, iHighH;
int iLowS = 43, iHighS = 255, iLowV = 46, iHighV = 255;
switch(color)
{
case RED:
iLowH = 0;
iHighH = 10; //10
break;
case GREEN:
iLowH = 33; //35
iHighH = 80; //77
break;
case BLUE:
iLowH = 100; //100
iHighH = 134;
break;
}
//Mat img = imread("color.jpg",1);
Mat img, imgHSV;
inputImage.copyTo(img);
cvtColor(img, imgHSV, COLOR_BGR2HSV);//转为HSV
//imwrite("hsv.jpg",imgHSV);
Mat imgThresholded;
inRange(imgHSV, Scalar(iLowH, iLowS, iLowV), Scalar(iHighH, iHighS, iHighV), imgThresholded); //Threshold the image
//开操作 (去除一些噪点) 如果二值化后图片干扰部分依然很多,增大下面的size
Mat element = getStructuringElement(MORPH_RECT, Size(40, 40));
morphologyEx(imgThresholded, imgThresholded, MORPH_OPEN, element);
//闭操作 (连接一些连通域)
morphologyEx(imgThresholded, imgThresholded, MORPH_CLOSE, element);
//namedWindow("Thresholded Image",CV_WINDOW_NORMAL);
//imshow("Thresholded Image", imgThresholded);
vector >contours;
vectorhierarchy;
findContours(imgThresholded, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);//查找轮廓
int count = 0;
Point pt[512];//存储连通区域个数
Moments moment;//矩
vectorCenter;//创建一个向量保存重心坐标
for (int i=0;i WIDTH_MIN) && (point_x < WIDTH_MAX) ? color : 0;
point_x = 0;
return result;
}
图5 颜色检测效果
基于以上功能的实现,我们采用树莓派与 Arduino 相结合的工作方式进行路径的简单规划、二维码识别以及物料颜色的识别。通过对各个阶段的数据分析来进行物料
的抓取、放置等操作。流程概括如下:
本方案采用 Arduino 与树莓派结合的控制方案,当车停在二维码区域后,通过控制机械臂使摄像头对二维码进行拍摄,然后串口发送命令至树莓派对二维码进行识别,并将识别后的数据通过串口返回并在 OLED 屏上显示。
因为物料需要按照二维码识别的顺序进行抓取,假如我们扫描得到的二维码顺序为213,而此时物料摆放的位置我们并不知道,因此需要使用摄像头对每个物料的颜色进行识别,由于每种颜色分别有对应的序号,红色为 1,绿色为 2,蓝色为 3,因此通过颜色识别后我们可以得到从左至右三个物料的序号,例如为 132,即红蓝绿,那么我们通过一些处理之后,便可以得到我们需要的抓取顺序,我们设从左至右三个位置分别对应编号 012,则此时我们将能够得到抓取顺序为 201,也就是我们此时应先抓取右侧绿色物料,然后抓取左侧红色物料,最后抓取中间蓝色物料,程序实现可参考例程进行分析。方案中我们抓取第一个物料放置到车左侧,抓取第二个物料放置到车右侧,最后一个物料我们用机械爪抓取放置到车中间。
由以上步骤我们便已经知道了二维码顺序,以及各颜色物料在车上的放置位置,
然后我们进入到物料放置区域,因为物料放置时从左至右对应颜色为蓝绿红,即序号 321,我们设放置区域从左至右对应编号分别为 012,则经过处理之后可以得到放置顺序。
Arduino loop()函数
void loop()
{
#ifdef MOVE_DBG
moveTo(0, 1.0, false, MOVE_FORWARD); //前进
delay(1000);
QRDetect(); //此时得到二维码顺序
delay(1000);
moveTo(-0.7, 0, false, MOVE_LEFT); //右平移
Colour() ; //颜色检测
// 根据抓取顺序进行物料抓取 1:left ;2:right
Putt();
delay(1000);
moveTo(0, -0.5, false, MOVE_BACK); //后退
#endif
// if(!digitalRead(debug_sensor))
// SendCmd(QR);
while(1);
}
基本实现了搭载机械臂的物料车基于麦克纳姆轮底盘上的灵活移动,以及二维码识别、颜色识别、抓取、放置等动作;对机械臂的控制、二维码识别、颜色识别、以及树莓派开机自启动程序有了一定的了解。同时对研究中出现的一些问题还有待改进:
源码包含 Arduino和树莓派两部分,已经上传Github和CSDN资源,有需要参考的可以自取。
https://github.com/kilotwo/ArduinoProject
有朋友可能需要源码中使用的 MultiLCD -> OLED支持库和 ServoTimer2 -> 舵机支持库现已经上传至CSDN资源,有需要的朋友可以自取。https://download.csdn.net/download/kilotwo/11099574
【opencv】目标识别——HSV颜色识别
OpenCV学习笔记——HSV颜色空间超极详解&inRange函数用法及实战
颜色空间RGB与HSV(HSL)的转换
在此表示感谢。
关于整个过程有任何问题欢迎指正与交流♪(^∀^●)ノ