步骤1:硬件
这是我以前用来构建的这个。我使用来自的部件以及我躺在房子周围的东西100%构建它。
PhidgetsBoards,Motors,Hardware
HUB0000-VINTHubPhidget
1108-磁传感器
2xSTC1001-2.5AStepperPhidget
2x3324-42STH38NEMA-17双极无齿轮步进器
3x3002-Phidget电缆60厘米
3403-4端口集线器
3031-母尾5.5
3029-2线100‘绞线
3604-10mm白色LED
其他部件
24VDC2.0A电源
车库废木和金属
拉链
底部切断的塑料容器
第2步:设计机器人
我们需要设计一些可以从输入料斗中取出一个珠子,将其放在网络摄像头下,然后将其移动到适当的箱子中的东西。
珠子皮卡
智能图书分拣机器人设计我决定用2片圆形胶合板做第1部分,每个胶合板在同一个地方钻一个洞。底部件固定,顶部件连接到步进电机,步进电机可以在装有珠子的料斗下方旋转。当孔在料斗下移动时,它会拾取一个珠子。然后我可以在网络摄像头下旋转它,然后进一步旋转,直到它与底部的孔匹配,此时它就会落下。
物流分拣机器人设计说明书在这张图片中,我正在测试系统能行得通。一切都是固定的,除了顶部的圆形胶合板,它与下面的步进电机相连。网络摄像头尚未安装。我现在只是使用Phidget控制面板转向电机。
珠子存储
下一部分是设计用于保持每种颜色的bin系统。我决定使用下面的第二个步进电机来支撑和旋转具有均匀间隔隔间的圆形容器。这可以用来旋转珠子将从其中掉出的孔下方的正确隔间。
我使用纸板和胶带来构建它。这里最重要的是一致性-每个隔间应该是相同的尺寸,整个东西应该均匀加重,以便旋转而不会跳过。
通过紧密贴合的盖子完成珠子的移除一次只有一个隔间,所以可以倒出珠子。
网络摄像头安装在料斗和下板孔位置之间的顶板上。这允许系统在掉落之前观察珠子。LED用于照亮相机下方的珠子,并且环境光被阻挡,以提供一致的照明环境。这对于准确的颜色检测非常重要,因为环境光可以真正地消除感知颜色。
位置检测
系统能够检测到珠子的旋转非常重要分隔器。这用于在启动时设置初始位置,但也用于检测步进电机是否已经不同步。在我的系统中,一个珠子有时会在拾取时卡住,系统需要能够检测并处理这种情况-通过备份和尝试agian。
有很多方法可以处理这个(事情。我决定使用1108磁传感器,在顶板的边缘嵌入磁铁。这允许我验证每次旋转的位置。一个更好的解决方案可能是步进电机上的编码器,但我有一个1108左右,所以我使用它。
完成机器人
此时,一切都已经完成出来,并测试。现在是时候安装好所有东西并转向编写软件。
2个步进电机由STC1001步进控制器驱动。HUB000-USBVINT集线器用于运行步进控制器,以及读取磁传感器和驱动LED。网络摄像头和HUB0000都连接到一个小型USB集线器。使用3031引线和一些电线以及24V电源为电机供电。
步骤3:编写代码
C#和VisualStudio2015用于此项目。下载本页顶部的源代码并按照步骤进行操作-主要部分概述如下
初始化
首先,我们必须创建,打开和初始化Phidget对象。这是在表单加载事件和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;
}
我们还在初始化期间读取任何已保存的颜色信息,因此可以继续之前的运行。
电机定位
电机处理代码包括用于移动电机的便利功能。我使用的电机每转3,2001/16步,所以我为此创建了一个常量。
对于顶部电机,我们希望能够将3个位置发送到电机:网络摄像头,孔和定位磁铁。有一个功能可以到达每个位置:
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;
}
在开始运行之前,使用磁传感器对齐顶板。可以随时调用alignMotor函数来对齐顶板。该功能首先快速将板转动至1转,直至其磁铁数据高于阈值。然后它稍微后退一点并慢慢向前移动,捕捉传感器数据。最后,它将位置设置为最大磁铁数据位置,并将位置偏移重置为0.因此,最大磁铁位置应始终为
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){
(newKeyValuePair;
}
privatevoidmagSensorStopMotor(objectsender,e){
if{
=-300;
-=magSensorStopMotor;
sawMagnet=true;
}
}
最后,通过将底部马达发送到其中一个胎圈容器位置来控制底部马达。对于这个项目,我们有19个职位。算法选择最短路径,顺时针或逆时针转动。
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用于从网络摄像头读取图像。在启动主排序线程之前启动相机线程。该线程不断读入图像,使用Mean计算特定区域的平均颜色并更新全局颜色变量。该线程还使用HoughCircles来检测珠子或顶板上的孔,以细化它正在寻找颜色检测的区域。阈值和HoughCircles数字是通过反复试验确定的,并且在很大程度上取决于网络摄像头,光照和间距。
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=;
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”;
while
;
}else{
runVideo=false;
;
=“start”;
}
}
颜色
现在,我们可以确定珠子的颜色,并根据该颜色决定将其放入哪个容器。
这个步骤依赖于颜色比较。我们希望能够区分颜色以限制误报,但也允许足够的阈值来限制假阴性。比较颜色实际上是非常复杂的,因为计算机将颜色存储为RGB的方式,以及人类感知颜色的方式并不是线性相关的。更糟糕的是,还必须考虑在下面观察颜色的光的颜色。
有复杂的计算色差的算法。我们使用CIE2000,如果2种颜色与人类无法区分,则输出接近1的数字。我们使用ColorMineC#库来完成这些复杂的计算。已发现DeltaE值为5可在假阳性和假阴性之间提供良好的折衷。
由于容器通常有更多颜色,因此最后一个位置保留为捕获箱。我通常将这些设置为第二次通过机器。
List
colors=newList
;
List
colorPanels=newList
;
ListcolorsTxts=newList;
ListcolorCnts=newList;
constintnumColorSpots=18;
constintunknownColorIndex=18;
intfindColorPosition{
(“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!(Posn:”+bestMatch+“Delta:”+matchDelta+“)”);
returnbestMatch;
}
if(《numColorSpots){
;
;
(newAction,newobject[]{-1});
writeOutColors;
return;
}else{
(“UnknownColor!”);
returnunknownColorIndex;
}
}
排序逻辑
排序功能将所有部分汇集在一起以实际排序珠子。该函数在专用线程中运行;移动顶板,检测珠子颜色,将其放入垃圾箱,确保顶板保持对齐,计数珠子等。当垃圾箱变满时,它也会停止运行-否则我们最终会溢出珠子。
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”;
=;
}
}
此时,我们有一个工作程序。一些代码被遗漏在文章之外,所以看一下实际运行它的源代码。
分拣机器人直销物流分拣机器人设计果蔬分拣机器人结构