1. 系統總體設計和分析
系統總體分爲硬件設計和軟件設計,硬件設計就是基於樹莓派2代B型和Arduino的硬件基礎平臺搭建,能夠適應小型、靈活的現實需求,併爲軟件設計提供可觀的計算能力承載和算法改進空間。軟件設計就是在嵌入式Arduino上構建控制系統,用來控制物料車基本的移動,機器臂的抓取,攝像頭的位置等操作,而在樹莓派上構建軟件開發環境,在OpenCV框架下,通過C++語言實現二維碼的檢測和顏色的識別。
2. 系統硬件系統設計
硬件包括Arduino Mega2560、樹莓派 2 代 B 型、攝像頭模塊(720P)、對接串口線、SD存儲卡,HDMI toVGA轉接器、VGA 顯示器、USB 鍵盤鼠標。
本系統使用的攝像頭是 360D70 智能攝像頭內核,該攝像頭支持 720P 高清分辨率,鏡頭視角對角視角120度,可以保證監控區域的無死角監控;其次,該攝像頭支持紅外線以及USB 免驅接口輸入,便於本項目系統中各個模塊的集成。
圖1硬件系統關係圖
3. 系統軟件系統設計
本系統中要解決的主要兩個問題是二維碼的識別和物料顏色的識別。
3.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圖像閱讀器;
- 獲取圖像信息;
- 從圖像中識別條形碼數據;
- 把識別的數據從UTF8格式轉成ASCII格式。
要從圖像中識別條形碼,必須先創建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<<"查詢條碼失敗,請檢查圖片!"<<endl;
return false;
}
else
{
for(;symbol != imageZbar.symbol_end();++symbol)
{
cout<<"Type:"<<endl<<symbol->get_type_name()<<endl<<endl;
cout<<"Code:"<<endl<<symbol->get_data()<<endl<<endl;
zbar_data = symbol->get_data();
}
//imshow("Source Image",inputImage);
imageZbar.set_data(NULL,0);
return true;
}
}
3.2 物料顏色的識別
數字圖像處理中常用的採用模型是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<vector <Point> >contours;
vector<Vec4i>hierarchy;
findContours(imgThresholded, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);//查找輪廓
int count = 0;
Point pt[512];//存儲連通區域個數
Moments moment;//矩
vector<Point>Center;//創建一個向量保存重心座標
for (int i=0;i<contours.size();i++)//讀取每一個輪廓求取重心
{
Mat temp(contours.at(i));
Scalar color(255, 255, 255);
moment = moments(temp, false);
if (moment.m00 != 0)//除數不能爲0
{
pt[i].x = cvRound(moment.m10 / moment.m00);//計算重心橫座標
pt[i].y = cvRound(moment.m01 / moment.m00);//計算重心縱座標
point_x = pt[i].x;
}
Point p = Point(pt[i].x, pt[i].y);//重心座標
circle(img, p, 1, color, 5, 8);//原圖畫出重心座標
count++;//重心點數或者是連通區域數
Center.push_back(p);//將重心座標保存到Center向量中
}
//cout << "重心點個數:" << Center.size() << endl;
//cout << "輪廓數量:" << contours.size() << endl;
//顯示染色檢測結果,並保存圖片
//imshow("result", inputImage);
string name = "end"+int2string(color)+".jpg";
imwrite(name, img);
int result = (point_x > WIDTH_MIN) && (point_x < WIDTH_MAX) ? color : 0;
point_x = 0;
return result;
}
圖4 顏色識別過程
圖5 顏色檢測效果
4 系統執行思路與流程
基於以上功能的實現,我們採用樹莓派與 Arduino 相結合的工作方式進行路徑的簡單規劃、二維碼識別以及物料顏色的識別。通過對各個階段的數據分析來進行物料
的抓取、放置等操作。流程概括如下:
- 通過Arduino中的程序來進行路線的規劃;
- 掃描二維碼獲取要抓取物料的順序;
- 對各個物料進行顏色識別,從而按照二維碼所獲得的順序進行物料抓取;
- 根據二維碼及物料放置順序抓取物料並放置對應區域。
圖6 程序執行流程
4.1 二維碼掃描
本方案採用 Arduino 與樹莓派結合的控制方案,當車停在二維碼區域後,通過控制機械臂使攝像頭對二維碼進行拍攝,然後串口發送命令至樹莓派對二維碼進行識別,並將識別後的數據通過串口返回並在 OLED 屏上顯示。
4.2 物料抓取
因爲物料需要按照二維碼識別的順序進行抓取,假如我們掃描得到的二維碼順序爲213,而此時物料擺放的位置我們並不知道,因此需要使用攝像頭對每個物料的顏色進行識別,由於每種顏色分別有對應的序號,紅色爲 1,綠色爲 2,藍色爲 3,因此通過顏色識別後我們可以得到從左至右三個物料的序號,例如爲 132,即紅藍綠,那麼我們通過一些處理之後,便可以得到我們需要的抓取順序,我們設從左至右三個位置分別對應編號 012,則此時我們將能夠得到抓取順序爲 201,也就是我們此時應先抓取右側綠色物料,然後抓取左側紅色物料,最後抓取中間藍色物料,程序實現可參考例程進行分析。方案中我們抓取第一個物料放置到車左側,抓取第二個物料放置到車右側,最後一個物料我們用機械爪抓取放置到車中間。
4.3 物料放置
由以上步驟我們便已經知道了二維碼順序,以及各顏色物料在車上的放置位置,
然後我們進入到物料放置區域,因爲物料放置時從左至右對應顏色爲藍綠紅,即序號 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);
}
5. 總結與展望
基本實現了搭載機械臂的物料車基於麥克納姆輪底盤上的靈活移動,以及二維碼識別、顏色識別、抓取、放置等動作;對機械臂的控制、二維碼識別、顏色識別、以及樹莓派開機自啓動程序有了一定的瞭解。同時對研究中出現的一些問題還有待改進:
- 麥克納姆輪移動平臺因具有平面上3 個自由度的移動能力而逐漸受到了更多的關注。然而麥克納姆輪移動平臺的輪子並不能夠直接轉向,它是依靠 4個輪子各自不同轉速的相互配合來實現全向移動,因此每一個輪子的運動都對整體的運動方向和速度大小有着很大的貢獻,若一個輪子出現與地面脫離接觸以及打滑等情況,則整個移動平臺的移動方向則會發生很大的偏離,因此可以從控制算法或者增加懸掛來解決麥克納姆輪打滑等問題,實現麥克納姆輪移動平臺的精確運動。
- 樹莓派供電不足時,雖然會工作但是有時會出現異常,故可靠的供電方式對樹莓派很重要,除了MicroUSB接口供電,還可以使用GPIO接口進行供電或者USB接口逆向供電。
- 相電流的大小跟步進電機的扭力有直接關係,我們通過加大4988板子的電流配置來控制扭力。驅動板是通過一個小的電位器來實現對輸出電流的配置。點位與電流的關係滿足移動的公式關係,故只需要改變電路中的電阻即可調節電流。
- 電機停止轉動時的滋滋聲是正常的,因爲步進電機的特點是走特定的角度而不是一直轉,所以步進電機都有一個參數,步距角。由於這個角非常小,並且不一定在電機物理所處位置,所以步進電機停止時也需要通電,從而保證步進電機不會自動跳到物理步距角上,因爲這個特性是的步進電機在靜止時會有電流聲,這屬於正常現象。
源碼
源碼包含 Arduino和樹莓派兩部分,已經上傳Github和CSDN資源,有需要參考的可以自取。
https://github.com/kilotwo/ArduinoProject
部分參考
【opencv】目標識別——HSV顏色識別
OpenCV學習筆記——HSV顏色空間超極詳解&inRange函數用法及實戰
顏色空間RGB與HSV(HSL)的轉換
在此表示感謝。
關於整個過程有任何問題歡迎指正與交流♪(^∀^●)ノ