步驟1:硬件
這是我以前用來(lái)構(gòu)建的這個(gè)。我使用來(lái)自的部件以及我躺在房子周圍的東西100%構(gòu)建它。
PhidgetsBoards,Motors,Hardware
HUB0000-VINTHubPhidget
1108-磁傳感器
2xSTC1001-2.5AStepperPhidget
2x3324-42STH38NEMA-17雙極無(wú)齒輪步進(jìn)器
3x3002-Phidget電纜60厘米
3403-4端口集線器
3031-母尾5.5
3029-2線100‘絞線
3604-10mm白色LED
其他部件
24VDC2.0A電源
車庫(kù)廢木和金屬
拉鏈
底部切斷的塑料容器
第2步:設(shè)計(jì)機(jī)器人
我們需要設(shè)計(jì)一些可以從輸入料斗中取出一個(gè)珠子,將其放在網(wǎng)絡(luò)攝像頭下,然后將其移動(dòng)到適當(dāng)?shù)南渥又械臇|西。
珠子皮卡
我決定用2片圓形膠合板做第1部分,每個(gè)膠合板在同一個(gè)地方鉆一個(gè)洞。底部件固定,頂部件連接到步進(jìn)電機(jī),步進(jìn)電機(jī)可以在裝有珠子的料斗下方旋轉(zhuǎn)。當(dāng)孔在料斗下移動(dòng)時(shí),它會(huì)拾取一個(gè)珠子。然后我可以在網(wǎng)絡(luò)攝像頭下旋轉(zhuǎn)它,然后進(jìn)一步旋轉(zhuǎn),直到它與底部的孔匹配,此時(shí)它就會(huì)落下。
在這張圖片中,我正在測(cè)試系統(tǒng)能行得通。一切都是固定的,除了頂部的圓形膠合板,它與下面的步進(jìn)電機(jī)相連。網(wǎng)絡(luò)攝像頭尚未安裝。我現(xiàn)在只是使用Phidget控制面板轉(zhuǎn)向電機(jī)。
珠子存儲(chǔ)
下一部分是設(shè)計(jì)用于保持每種顏色的bin系統(tǒng)。我決定使用下面的第二個(gè)步進(jìn)電機(jī)來(lái)支撐和旋轉(zhuǎn)具有均勻間隔隔間的圓形容器。這可以用來(lái)旋轉(zhuǎn)珠子將從其中掉出的孔下方的正確隔間。
我使用紙板和膠帶來(lái)構(gòu)建它。這里最重要的是一致性-每個(gè)隔間應(yīng)該是相同的尺寸,整個(gè)東西應(yīng)該均勻加重,以便旋轉(zhuǎn)而不會(huì)跳過(guò)。
通過(guò)緊密貼合的蓋子完成珠子的移除一次只有一個(gè)隔間,所以可以倒出珠子。
網(wǎng)絡(luò)攝像頭安裝在料斗和下板孔位置之間的頂板上。這允許系統(tǒng)在掉落之前觀察珠子。LED用于照亮相機(jī)下方的珠子,并且環(huán)境光被阻擋,以提供一致的照明環(huán)境。這對(duì)于準(zhǔn)確的顏色檢測(cè)非常重要,因?yàn)榄h(huán)境光可以真正地消除感知顏色。
位置檢測(cè)
系統(tǒng)能夠檢測(cè)到珠子的旋轉(zhuǎn)非常重要分隔器。這用于在啟動(dòng)時(shí)設(shè)置初始位置,但也用于檢測(cè)步進(jìn)電機(jī)是否已經(jīng)不同步。在我的系統(tǒng)中,一個(gè)珠子有時(shí)會(huì)在拾取時(shí)卡住,系統(tǒng)需要能夠檢測(cè)并處理這種情況-通過(guò)備份和嘗試agian。
有很多方法可以處理這個(gè)(事情。我決定使用1108磁傳感器,在頂板的邊緣嵌入磁鐵。這允許我驗(yàn)證每次旋轉(zhuǎn)的位置。一個(gè)更好的解決方案可能是步進(jìn)電機(jī)上的編碼器,但我有一個(gè)1108左右,所以我使用它。
智能圖書(shū)分揀機(jī)器人設(shè)計(jì)完成機(jī)器人
此時(shí),一切都已經(jīng)完成出來(lái),并測(cè)試。現(xiàn)在是時(shí)候安裝好所有東西并轉(zhuǎn)向編寫(xiě)軟件。
2個(gè)步進(jìn)電機(jī)由STC1001步進(jìn)控制器驅(qū)動(dòng)。HUB000-USBVINT集線器用于運(yùn)行步進(jìn)控制器,以及讀取磁傳感器和驅(qū)動(dòng)LED。網(wǎng)絡(luò)攝像頭和HUB0000都連接到一個(gè)小型USB集線器。使用3031引線和一些電線以及24V電源為電機(jī)供電。
步驟3:編寫(xiě)代碼
C#和VisualStudio2015用于此項(xiàng)目。下載本頁(yè)頂部的源代碼并按照步驟進(jìn)行操作-主要部分概述如下
初始化
首先,我們必須創(chuàng)建,打開(kāi)和初始化Phidget對(duì)象。這是在表單加載事件和Phidget附加處理程序中完成的。
privatevoidForm1_Load(objectsender,EventArgse){
/*InitializeandopenPhidgets*/
=0;
+=Top_Attach;
+=Top_Detach;
+=Top_PositionChange;
;
=1;
+=Bottom_Attach;
+=Bottom_Detach;
+=Bottom_PositionChange;
;
=2;
=true;
+=MagSensor_Attach;
+=MagSensor_Detach;
+=MagSensor_SensorChange;
;
=5;
=true;
=0;
+=Led_Attach;
+=Led_Detach;
;
}
privatevoidLed_Attach(objectsender,e){
=true;
=true;
=true;
}
privatevoidMagSensor_Attach(objectsender,e){
=true;
=_1108;
=16;
}
privatevoidBottom_Attach(objectsender,e){
=true;
=bottomCurrentLimit;
=true;
=bottomVelocityLimit;
=bottomAccel;
=100;
}
privatevoidTop_Attach(objectsender,e){
=true;
=topCurrentLimit;
=true;
=-1;
=-topVelocityLimit;
=-topAccel;
=100;
}
我們還在初始化期間讀取任何已保存的顏色信息,因此可以繼續(xù)之前的運(yùn)行。
電機(jī)定位
電機(jī)處理代碼包括用于移動(dòng)電機(jī)的便利功能。我使用的電機(jī)每轉(zhuǎn)3,2001/16步,所以我為此創(chuàng)建了一個(gè)常量。
對(duì)于頂部電機(jī),我們希望能夠?qū)?個(gè)位置發(fā)送到電機(jī):網(wǎng)絡(luò)攝像頭,孔和定位磁鐵。有一個(gè)功能可以到達(dá)每個(gè)位置:
privatevoidnextMagnet(Booleanwait=false){
doubleposn=%stepsPerRev;
+=(stepsPerRev-posn);
if
while;
}
privatevoidnextCamera(Booleanwait=false){
doubleposn=%stepsPerRev;
if
+=;
else
+=+stepsPerRev);
if
while;
}
privatevoidnextHole(Booleanwait=false){
doubleposn=%stepsPerRev;
if
+=;
else
+=+stepsPerRev);
if
while;
}
在開(kāi)始運(yùn)行之前,使用磁傳感器對(duì)齊頂板。可以隨時(shí)調(diào)用alignMotor函數(shù)來(lái)對(duì)齊頂板。該功能首先快速將板轉(zhuǎn)動(dòng)至1轉(zhuǎn),直至其磁鐵數(shù)據(jù)高于閾值。然后它稍微后退一點(diǎn)并慢慢向前移動(dòng),捕捉傳感器數(shù)據(jù)。最后,它將位置設(shè)置為最大磁鐵數(shù)據(jù)位置,并將位置偏移重置為0.因此,最大磁鐵位置應(yīng)始終為
ThreadalignMotorThread;
BooleansawMagnet;
doublemagSensorMax=0;
privatevoidalignMotor{
//Findthemagnet
=;
sawMagnet=false;
+=magSensorStopMotor;
=-1000;
inttryCount=0;
tryagain:
+=stepsPerRev;
while
;
if{
if{
;
=false;
=false;
runtest=false;
return;
}
tryCount++;
(“Arewestuck?Tryingabackup.。.”);
-=600;
while;
gototryagain;
}
=-100;
magData=newList》;
+=magSensorCollectPositionData;
+=300;
while;
-=magSensorCollectPositionData;
=-topVelocityLimit;
KeyValuePairmax=magData[0];
foreach(KeyValuePairpairinmagData)
if
max=pair;
;
magSensorMax=;
=0;
while;
(“Alignsucceeded”);
}
List》magData;
privatevoidmagSensorCollectPositionData(objectsender,e){
全自動(dòng)分揀機(jī)器人多少錢(qián)一臺(tái)(newKeyValuePair;
}
privatevoidmagSensorStopMotor(objectsender,e){
if{
=-300;
-=magSensorStopMotor;
sawMagnet=true;
}
}
最后,通過(guò)將底部馬達(dá)發(fā)送到其中一個(gè)胎圈容器位置來(lái)控制底部馬達(dá)。對(duì)于這個(gè)項(xiàng)目,我們有19個(gè)職位。算法選擇最短路徑,順時(shí)針或逆時(shí)針轉(zhuǎn)動(dòng)。
privateintBottomPosition{
get{
intposn=%stepsPerRev;
if
posn+=stepsPerRev;
return(((posn*beadCompartments)/stepsPerRev));
}
}
privatevoidSetBottomPosition(intposn,boolwait=false){
posn=posn%beadCompartments;
doubletargetPosn=(posn*stepsPerRev)/beadCompartments;
doublecurrentPosn=%stepsPerRev;
doubleposnDiff=targetPosn-currentPosn;
//Keepitasfullsteps
posnDiff=*16;
if(posnDiff《=1600)
+=posnDiff;
else
-=(stepsPerRev-posnDiff);
if
while;
OpenCV用于從網(wǎng)絡(luò)攝像頭讀取圖像。在啟動(dòng)主排序線程之前啟動(dòng)相機(jī)線程。該線程不斷讀入圖像,使用Mean計(jì)算特定區(qū)域的平均顏色并更新全局顏色變量。該線程還使用HoughCircles來(lái)檢測(cè)珠子或頂板上的孔,以細(xì)化它正在尋找顏色檢測(cè)的區(qū)域。閾值和HoughCircles數(shù)字是通過(guò)反復(fù)試驗(yàn)確定的,并且在很大程度上取決于網(wǎng)絡(luò)攝像頭,光照和間距。
boolrunVideo=true;
boolvideoRunning=false;
VideoCapturecapture;
ThreadcvThread;
ColordetectedColor;
Booleandetecting=false;
intdetectCnt=0;
privatevoidcvThreadFunction{
videoRunning=false;
capture=newVideoCapture;
using(Windowwindow=newWindow{
Matimage=newMat;
Matimage2=newMat;
while{
;
if
break;
if
detectCnt++;
else
detectCnt=0;
if(detecting||circleDetectChecked||showDetectionImgChecked){
;
Matthres=;
無(wú)人快遞分揀機(jī)器人thres=;
if(showDetectionImgChecked)
image=thres;
if(detecting||circleDetectChecked){
CircleSegment[]bead=(,2,/*/4*/20,200,100,20,65);
if{
(bead[0].Center,3,newScalar;
(bead[0].Center,bead[0].Radius,newScalar;
if(bead[0].Radius》=55){
=bead[0].+(bead[0].Radius/2);
=bead[0].-(bead[0].Radius/2);
}else{
=bead[0].+;
=bead[0].-;
}
=15;
=15;
}else{
CircleSegment[]circles=(,2,/*/4*/5,200,100,60,180);
if{
Listxs=;
;
Listys=;
;
intmedianX=xs[/2];
intmedianY=ys[/2];
if
medianX=-15;
if
medianY=-15;
(medianX,medianY,100,newScalar;
if{
=medianX-7;
=medianY-7;
=15;
=15;
}
}
}
}
}
Rectr=newRect,
,
,
;
MatbeadSample=newMat;
ScalaravgColor=;
detectedColor=avgColor[2],avgColor[1],;
(r,newScalar;
;
;
videoRunning=true;
}
videoRunning=false;
}
}
privatevoidcameraStartBtn_Click(objectsender,EventArgse){
if{
cvThread=newThread(newThreadStart(cvThreadFunction));
runVideo=true;
;
=“stop”;
揚(yáng)中五軸分揀機(jī)器人while
;
}else{
runVideo=false;
;
=“start”;
}
}
顏色
現(xiàn)在,我們可以確定珠子的顏色,并根據(jù)該顏色決定將其放入哪個(gè)容器。
這個(gè)步驟依賴于顏色比較。我們希望能夠區(qū)分顏色以限制誤報(bào),但也允許足夠的閾值來(lái)限制假陰性。比較顏色實(shí)際上是非常復(fù)雜的,因?yàn)橛?jì)算機(jī)將顏色存儲(chǔ)為RGB的方式,以及人類感知顏色的方式并不是線性相關(guān)的。更糟糕的是,還必須考慮在下面觀察顏色的光的顏色。
有復(fù)雜的計(jì)算色差的算法。我們使用CIE2000,如果2種顏色與人類無(wú)法區(qū)分,則輸出接近1的數(shù)字。我們使用ColorMineC#庫(kù)來(lái)完成這些復(fù)雜的計(jì)算。已發(fā)現(xiàn)DeltaE值為5可在假陽(yáng)性和假陰性之間提供良好的折衷。
由于容器通常有更多顏色,因此最后一個(gè)位置保留為捕獲箱。我通常將這些設(shè)置為第二次通過(guò)機(jī)器。
List
colors=newList
;
List
colorPanels=newList
;
ListcolorsTxts=newList;
ListcolorCnts=newList;
constintnumColorSpots=18;
constintunknownColorIndex=18;
intfindColorPosition{
物流分揀機(jī)器人參數(shù)(“Findingcolor.。.”);
varcRGB=newRgb;
=;
=;
=;
intbestMatch=-1;
doublematchDelta=100;
for(inti=0;i《;i++){
varRGB=newRgb;
=colors[i].R;
=colors[i].G;
=colors[i].B;
doubledelta=(RGB,newCieDe2000Comparison;
//doubledelta=deltaE;
+“):”+;
if(delta《matchDelta){
matchDelta=delta;
bestMatch=i;
}
}
if{
(“Found?。≒osn:”+bestMatch+“Delta:”+matchDelta+“)”);
returnbestMatch;
}
if(《numColorSpots){
;
;
(newAction,newobject[]{-1});
writeOutColors;
return;
}else{
(“UnknownColor!”);
returnunknownColorIndex;
}
}
排序邏輯
排序功能將所有部分匯集在一起以實(shí)際排序珠子。該函數(shù)在專用線程中運(yùn)行;移動(dòng)頂板,檢測(cè)珠子顏色,將其放入垃圾箱,確保頂板保持對(duì)齊,計(jì)數(shù)珠子等。當(dāng)垃圾箱變滿時(shí),它也會(huì)停止運(yùn)行-否則我們最終會(huì)溢出珠子。
ThreadcolourTestThread;
Booleanruntest=false;
voidcolourTest
=true;
if
=true;
while{
nextMagnet;
;
try{
if(《(magSensorMax-4))
alignMotor;
}catch{alignMotor;}
nextCamera;
detecting=true;
while
;
(“DetectCount:”+detectCnt);
detecting=false;
Colorc=detectedColor;
(newAction
,newobject[]{c});
inti=findColorPosition;
SetBottomPosition;
nextHole;
colorCnts[i]++;
(newAction,newobject[]{i});
;
if(colorCnts[unknownColorIndex]》500){
=false;
=false;
runtest=false;
(newAction,null);
return;
}
}
}
privatevoidcolourTestBtn_Click(objectsender,EventArgse){
if(colourTestThread==null||?。﹞
colourTestThread=newThread(newThreadStart;
runtest=true;
;
=“STOP”;
=;
}else{
runtest=false;
=“GO”;
=;
}
}
此時(shí),我們有一個(gè)工作程序。一些代碼被遺漏在文章之外,所以看一下實(shí)際運(yùn)行它的源代碼。
中國(guó)快遞分揀機(jī)器人視頻北京并聯(lián)分揀機(jī)器人廠家教育用分揀機(jī)器人