Obsah:
Video: Robotické třídění korálků: 3 kroky (s obrázky)
2024 Autor: John Day | [email protected]. Naposledy změněno: 2024-01-30 08:22
V tomto projektu budeme stavět robota, který bude Perlerovy korálky třídit podle barev.
Vždy jsem chtěl postavit robota pro třídění barev, takže když se moje dcera začala zajímat o výrobu perliček Perler, viděl jsem to jako ideální příležitost.
Perlerovy korálky se používají k vytváření fúzovaných uměleckých projektů tak, že se mnoho korálků položí na věšák a poté se roztaví pomocí žehličky. Obvykle nakupujete tyto korálky v obřích baleních 22 000 smíšených barev a trávíte spoustu času hledáním požadované barvy, takže jsem si myslel, že jejich tříděním zvýšíte efektivitu umění.
Pracuji pro Phidgets Inc., takže jsem pro tento projekt používal převážně Phidgets - ale to bylo možné provést pomocí jakéhokoli vhodného hardwaru.
Krok 1: Hardware
Zde je to, co jsem použil k vytvoření tohoto. Postavil jsem to na 100% pomocí dílů z phidgets.com a věcí, které jsem měl povalující se po domě.
Phidgets Desky, Motory, Hardware
- HUB0000 - VINT Hub Phidget
- 1108 - Magnetický senzor
- 2x STC1001 - 2,5A krokový phidget
- 2x 3324 - 42STH38 NEMA -17 bipolární bezpřevodový stepper
- 3x 3002 - kabel Phidget 60 cm
- 3403 - 4portový rozbočovač USB2.0
- 3031 - ženský pigtail 5,5 x 2,1 mm
- 3029 - 2vodičový 100 'kroucený kabel
- 3604 - 10 mm bílá LED (sáček po 10)
- 3402 - USB webová kamera
Ostatní díly
- Napájecí zdroj 24 V DC 2,0 A
- Vyhoďte dřevo a kov z garáže
- Kravaty na zip
- Plastová nádoba s odříznutým dnem
Krok 2: Navrhněte robota
Potřebujeme navrhnout něco, co může ze vstupního zásobníku odebrat jeden korálek, umístit jej pod webovou kameru a poté přesunout do příslušného koše.
Vyzvednutí korálků
Rozhodl jsem se udělat 1. část se 2 kusy kulaté překližky, každý s otvorem vyvrtaným na stejném místě. Spodní část je pevná a horní část je připevněna k krokovému motoru, který ji může otáčet pod násypkou naplněnou korálky. Když díra putuje pod násypku, sebere jeden korálek. Poté jej mohu otočit pod webovou kamerou a poté dále otáčet, dokud se neshoduje s otvorem ve spodním dílu, v kterém místě propadne.
Na tomto obrázku testuji, že systém může fungovat. Všechno je pevné, kromě horního kulatého kusu překližky, který je připevněn k krokovému motoru mimo pohled zespodu. Webová kamera ještě nebyla připojena. V tuto chvíli používám ovládací panel Phidget k přepnutí na motor.
Skladování korálků
Další částí je návrh systému přihrádek pro uložení každé barvy. Rozhodl jsem se použít druhý krokový motor níže k podepření a otočení kulatého kontejneru s rovnoměrně rozmístěnými oddíly. To lze použít k otočení správné přihrádky pod otvorem, ze kterého kulička vypadne.
Postavil jsem to pomocí lepenky a lepicí pásky. Nejdůležitější je zde konzistence - každé oddělení by mělo mít stejnou velikost a celé by mělo být rovnoměrně vyváženo, aby se točilo bez přeskakování.
Odstranění korálků se provádí pomocí těsně přiléhajícího víka, které odhaluje jednu komoru najednou, takže je možné kuličky vylévat.
Fotoaparát
Webová kamera je namontována přes horní desku mezi násypku a umístění otvoru spodní desky. To umožňuje systému podívat se na korálek, než jej upustí. K osvětlení kuliček pod kamerou se používá LED dioda a okolní světlo je blokováno, aby bylo zajištěno konzistentní osvětlení. To je velmi důležité pro přesnou detekci barev, protože okolní osvětlení může skutečně odhodit vnímanou barvu.
Detekce polohy
Je důležité, aby systém dokázal detekovat otáčení separátoru kuliček. To se používá k nastavení počáteční polohy při spuštění, ale také ke zjištění, zda se krokový motor nedostal ze synchronizace. V mém systému se při vyzvednutí někdy zasekne korálek a systém potřeboval, aby dokázal tuto situaci detekovat a zvládnout - tak, že trochu zazálohuji a zkusím to znovu.
Existuje mnoho způsobů, jak to zvládnout. Rozhodl jsem se použít magnetický senzor 1108 s magnetem vloženým do okraje horní desky. To mi umožňuje ověřit polohu při každém otočení. Lepším řešením by pravděpodobně byl kodér na krokovém motoru, ale měl jsem ležet 1108, takže jsem to použil.
Dokončete robota
V tuto chvíli bylo vše zpracováno a testováno. Je na čase vše pěkně připojit a přejít na software pro psaní.
2 krokové motory jsou poháněny krokovými regulátory STC1001. Rozbočovač HUB000 - USB VINT se používá ke spouštění krokových ovladačů, čtení magnetického senzoru a ovládání LED. Webová kamera a HUB0000 jsou připojeny k malému rozbočovači USB. K napájení motorů je použit 3031 pigtail a nějaký drát.
Krok 3: Napište kód
Pro tento projekt se používají C# a Visual Studio 2015. Stáhněte si zdroj v horní části této stránky a postupujte podle něj - hlavní části jsou uvedeny níže
Inicializace
Nejprve musíme vytvořit, otevřít a inicializovat objekty Phidget. To se provádí v případě načtení formuláře a obslužných rutin připojení Phidget.
private void Form1_Load (odesílatel objektů, EventArgs e) {
/ * Inicializujte a otevřete Phidgets */
top. HubPort = 0; top. Attach += Top_Attach; top. Detach += Top_Detach; top. PositionChange += Top_PositionChange; top. Open ();
bottom. HubPort = 1;
bottom. Attach += Bottom_Attach; bottom. Detach += Bottom_Detach; bottom. PositionChange += Bottom_PositionChange; dole. Open ();
magSensor. HubPort = 2;
magSensor. IsHubPortDevice = true; magSensor. Attach += MagSensor_Attach; magSensor. Detach += MagSensor_Detach; magSensor. SensorChange += MagSensor_SensorChange; magSensor. Open ();
led. HubPort = 5;
led. IsHubPortDevice = true; led. Channel = 0; led. Attach += Led_Attach; led. Detach += Led_Detach; led. Open (); }
private void Led_Attach (odesílatel objektů, Phidget22. Events. AttachEventArgs e) {
ledAttachedChk. Checked = true; led. State = true; ledChk. Checked = true; }
private void MagSensor_Attach (odesílatel objektů, Phidget22. Events. AttachEventArgs e) {
magSensorAttachedChk. Checked = true; magSensor. SensorType = VoltageRatioSensorType. PN_1108; magSensor. DataInterval = 16; }
private void Bottom_Attach (odesílatel objektů, Phidget22. Events. AttachEventArgs e) {
bottomAttachedChk. Checked = true; bottom. CurrentLimit = bottomCurrentLimit; bottom. Engaged = true; bottom. VelocityLimit = bottomVelocityLimit; bottom. Acceleration = bottomAccel; bottom. DataInterval = 100; }
private void Top_Attach (odesílatel objektů, Phidget22. Events. AttachEventArgs e) {
topAttachedChk. Checked = true; top. CurrentLimit = topCurrentLimit; top. Engaged = true; top. RescaleFactor = -1; top. VelocityLimit = -topVelocityLimit; top. Acceleration = -topAccel; top. DataInterval = 100; }
Během inicializace také čteme uložené informace o barvách, takže v předchozím běhu lze pokračovat.
Polohování motoru
Manipulační kód motoru se skládá z praktických funkcí pro pohyb motorů. Motory, které jsem použil, jsou 3 200 1/16 kroků na otáčku, takže jsem pro to vytvořil konstantu.
U horního motoru existují 3 polohy, do kterých chceme odeslat motor: webová kamera, otvor a polohovací magnet. K dispozici je funkce pro cestování do každé z těchto pozic:
private void nextMagnet (Boolean wait = false) {
double posn = top. Position % stepsPerRev;
top. TargetPosition += (stepsPerRev - posn);
jestli (počkej)
while (top. IsMoving) Thread. Sleep (50); }
private void nextCamera (Boolean wait = false) {
double posn = top. Position % stepsPerRev; if (posn <Properties. Settings. Default.cameraOffset) top. TargetPosition += (Properties. Settings. Default.cameraOffset - posn); else top. TargetPosition + = ((Properties. Settings. Default.cameraOffset - posn) + stepsPerRev);
pokud (počkat)
while (top. IsMoving) Thread. Sleep (50); }
private void nextHole (Boolean wait = false) {
double posn = top. Position % stepsPerRev; if (posn <Properties. Settings. Default.holeOffset) top. TargetPosition += (Properties. Settings. Default.holeOffset - posn); else top. TargetPosition + = ((Properties. Settings. Default.holeOffset - posn) + stepsPerRev);
pokud (počkat)
while (top. IsMoving) Thread. Sleep (50); }
Před zahájením běhu se horní deska zarovná pomocí magnetického senzoru. K zarovnání horní desky lze kdykoli vyvolat funkci alignMotor. Tato funkce nejprve rychle otočí desku až o 1 plnou otáčku, dokud neuvidí magnetická data nad prahovou hodnotou. Poté se trochu zazálohuje a pomalu se pohybuje vpřed a zachycuje data senzorů, jak to jde. Nakonec nastaví polohu na maximální umístění dat magnetu a resetuje posunutí polohy na 0. Maximální poloha magnetu by tedy měla být vždy na (top. Position % stepsPerRev)
Zarovnání závitu MotorThread; Boolean sawMagnet; double magSensorMax = 0; private void alignMotor () {
// Najděte magnet
top. DataInterval = top. MinDataInterval;
sawMagnet = false;
magSensor. SensorChange += magSensorStopMotor; top. VelocityLimit = -1000;
int tryCount = 0;
Zkus to znovu:
top. TargetPosition += stepsPerRev;
while (top. IsMoving &&! sawMagnet) Thread. Sleep (25);
if (! sawMagnet) {
if (tryCount> 3) {Console. WriteLine ("Align failed"); top. Engaged = false; bottom. Engaged = false; runtest = false; vrátit se; }
tryCount ++;
Console. WriteLine ("Jsme zaseknutí? Zkoušíte zálohu …"); top. TargetPosition -= 600; while (top. IsMoving) Thread. Sleep (100);
zkuste to znovu;
}
top. VelocityLimit = -100;
magData = nový seznam> (); magSensor. SensorChange += magSensorCollectPositionData; top. TargetPosition += 300; while (top. IsMoving) Thread. Sleep (100);
magSensor. SensorChange -= magSensorCollectPositionData;
top. VelocityLimit = -topVelocityLimit;
KeyValuePair max = magData [0];
foreach (pár KeyValuePair v magData) if (pair. Value> max. Value) max = pair;
top. AddPositionOffset (-max. Key);
magSensorMax = max. hodnota;
top. TargetPosition = 0;
while (top. IsMoving) Thread. Sleep (100);
Console. WriteLine ("Zarovnání se podařilo");
}
Seznam> magData;
private void magSensorCollectPositionData (odesílatel objektů, Phidget22. Events. VoltageRatioInputSensorChangeEventArgs e) {magData. Add (nový KeyValuePair (top. Position, e. SensorValue)); }
private void magSensorStopMotor (odesílatel objektů, Phidget22. Events. VoltageRatioInputSensorChangeEventArgs e) {
if (top. IsMoving && e. SensorValue> 5) {top. TargetPosition = top. Position - 300; magSensor. SensorChange -= magSensorStopMotor; sawMagnet = true; }}
Nakonec je spodní motor řízen jeho vysláním do jedné z poloh nádobky na korálky. Pro tento projekt máme 19 pozic. Algoritmus vybírá nejkratší cestu a otáčí se ve směru nebo proti směru hodinových ručiček.
private int BottomPosition {get {int posn = (int) bottom. Position % stepsPerRev; if (posn <0) posn += stepsPerRev;
return (int) Math. Round (((posn * beadCompartments) / (double) stepsPerRev));
} }
private void SetBottomPosition (int posn, bool wait = false) {
posn = posn % beadCompartments; double targetPosn = (posn * stepsPerRev) / beadCompartments;
double currentPosn = bottom. Position % stepsPerRev;
double posnDiff = targetPosn - currentPosn;
// Ponechte si to jako úplné kroky
posnDiff = ((int) (posnDiff / 16)) * 16;
if (posnDiff <= 1600) bottom. TargetPosition += posnDiff; else bottom. TargetPosition - = (stepsPerRev - posnDiff);
jestli (počkej)
while (bottom. IsMoving) Thread. Sleep (50); }
Fotoaparát
OpenCV slouží ke čtení obrázků z webové kamery. Vlákno kamery se spustí před spuštěním hlavního vlákna třídění. Toto vlákno nepřetržitě čte obrázky, vypočítává průměrnou barvu pro konkrétní oblast pomocí Mean a aktualizuje globální barevnou proměnnou. Vlákno také využívá HoughCircles, aby se pokusil detekovat buď korálek, nebo díru v horní desce, aby upřesnil oblast, na kterou se dívá pro detekci barev. Prahová a HoughCircles čísla byla stanovena metodou pokusu a omylu a do značné míry závisí na webové kameře, osvětlení a vzdálenosti.
bool runVideo = true; bool videoRunning = false; Zachycení VideoCapture; Vlákno cvThread; Detekována barva Barva; Booleovská detekce = false; int detectCnt = 0;
private void cvThreadFunction () {
videoRunning = false;
capture = new VideoCapture (selectedCamera);
pomocí (Okno okna = nové okno ("zachytit")) {
Mat image = new Mat (); Mat image2 = new Mat (); while (runVideo) {capture. Read (obrázek); if (image. Empty ()) break;
pokud (detekce)
detectCnt ++; else detectCnt = 0;
if (detekce || circleDetectChecked || showDetectionImgChecked) {
Cv2. CvtColor (image, image2, ColorConversionCodes. BGR2GREY); Mat thres = image2. Threshold ((double) Properties. Settings. Default.videoThresh, 255, ThresholdTypes. Binary); thres = thres. GaussianBlur (nový OpenCvSharp. Size (9, 9), 10);
if (showDetectionImgChecked)
image = thres;
if (detekce || circleDetectChecked) {
CircleSegment korálek = thres. HoughCircles (HoughMethods. Gradient, 2, /*thres. Rows/4*/ 20, 200, 100, 20, 65); if (bead. Length> = 1) {image. Circle (bead [0]. Center, 3, new Scalar (0, 100, 0), -1); image. Circle (korálek [0]. Center, (int) korálek [0]. Radius, nový skalární (0, 0, 255), 3); if (bead [0]. Radius> = 55) {Properties. Settings. Default.x = (decimal) bead [0]. Center. X + (decimal) (bead [0]. Radius / 2); Properties. Settings. Default.y = (desítková) kulička [0]. Center. Y - (desetinná) (kulička [0]. Radius / 2); } else {Properties. Settings. Default.x = (desítková) kulička [0]. Center. X + (desetinná) (kulička [0]. Radius); Properties. Settings. Default.y = (desetinná) kulička [0]. Center. Y - (desetinná) (kulička [0]. Radius); } Properties. Settings. Default.size = 15; Properties. Settings. Default.height = 15; } else {
CircleSegment circle = thres. HoughCircles (HoughMethods. Gradient, 2, /*thres. Rows/4*/ 5, 200, 100, 60, 180);
if (circle. Length> 1) {List xs = circle. Select (c => c. Center. X). ToList (); xs. Sort (); Seznam ys = kruhy. Vyberte (c => c. Center. Y). ToList (); ys. Sort ();
int medianX = (int) xs [xs. Count / 2];
int medianY = (int) ys [ys. Count / 2];
if (medianX> image. Width - 15)
medianX = obrázek. Šířka - 15; if (medianY> image. Height - 15) medianY = image. Height - 15;
obrázek. Kruh (medianX, medianY, 100, new Scalar (0, 0, 150), 3);
pokud (detekuje) {
Properties. Settings. Default.x = medianX - 7; Properties. Settings. Default.y = medianY - 7; Properties. Settings. Default.size = 15; Properties. Settings. Default.height = 15; }}}}}
Rect r = new Rect ((int) Properties. Settings. Default.x, (int) Properties. Settings. Default.y, (int) Properties. Settings. Default.size, (int) Properties. Settings. Default.height);
Mat beadSample = new Mat (obrázek, r);
Skalární avgColor = Cv2. Mean (beadSample); detekovaná barva = Color. FromArgb ((int) avgColor [2], (int) avgColor [1], (int) avgColor [0]);
image. Rectangle (r, new Scalar (0, 150, 0));
window. ShowImage (obrázek);
Cv2. WaitKey (1); videoRunning = true; }
videoRunning = false;
} }
soukromá prázdná kameraStartBtn_Click (odesílatel objektů, EventArgs e) {
if (cameraStartBtn. Text == "start") {
cvThread = new Thread (new ThreadStart (cvThreadFunction)); runVideo = true; cvThread. Start (); cameraStartBtn. Text = "stop"; while (! videoRunning) Thread. Sleep (100);
updateColorTimer. Start ();
} else {
runVideo = false; cvThread. Join (); cameraStartBtn. Text = "start"; }}
Barva
Nyní jsme schopni určit barvu kuličky a podle této barvy se rozhodnout, do které nádoby ji hodíme.
Tento krok závisí na porovnání barev. Chceme být schopni rozeznat barvy od sebe, abychom omezili falešně pozitivní výsledky, ale také umožnili dostatečný práh pro omezení falešných negativů. Porovnávání barev je ve skutečnosti překvapivě složité, protože způsob, jakým počítače ukládají barvy jako RGB, a způsob, jakým lidé barvy vnímají, nekorelují lineárně. Aby toho nebylo málo, je třeba vzít v úvahu také barvu světla, pod kterou je barva pozorována.
Existují komplikované algoritmy pro výpočet rozdílu barev. Používáme CIE2000, který vydává číslo blízko 1, pokud by 2 barvy byly pro člověka nerozeznatelné. K provádění těchto komplikovaných výpočtů používáme knihovnu ColorMine C#. Bylo zjištěno, že hodnota DeltaE 5 nabízí dobrý kompromis mezi falešně pozitivním a falešně negativním.
Protože často existuje více barev než kontejnerů, je poslední pozice vyhrazena jako odpadkový koš. Obecně jsem je nechal stranou, aby běžel stroj na druhý průchod.
Seznam
barvy = nový seznam (); seznam colorPanels = nový seznam (); List colorsTxts = new List (); Seznam colorCnts = nový List ();
const int numColorSpots = 18;
const int neznámýColorIndex = 18; int findColorPosition (barva c) {
Console. WriteLine ("Hledání barvy …");
var cRGB = nový Rgb ();
cRGB. R = c. R; cRGB. G = c. G; cRGB. B = c. B;
int bestMatch = -1;
dvojitá shoda Delta = 100;
pro (int i = 0; i <barvy. Count; i ++) {
var RGB = nový Rgb ();
RGB. R = barvy . R; RGB. G = barvy . G; RGB. B = barvy . B;
dvojitá delta = cRGB. Compare (RGB, nové CieDe2000Comparison ());
// dvojitá delta = deltaE (c, barvy ); Console. WriteLine ("DeltaE (" + i. ToString () + "):" + delta. ToString ()); if (delta <matchDelta) {matchDelta = delta; bestMatch = i; }}
if (matchDelta <5) {Console. WriteLine ("Found! (Posn:" + bestMatch + "Delta:" + matchDelta + ")"); vrátit bestMatch; }
if (colors. Count <numColorSpots) {Console. WriteLine ("New Color!"); barvy. Add (c); this. BeginInvoke (nová akce (setBackColor), nový objekt {colors. Count - 1}); writeOutColors (); návrat (barvy. Count - 1); } else {Console. WriteLine ("Neznámá barva!"); vrátit neznámýColorIndex; }}
Logika řazení
Funkce třídění spojuje všechny kousky a skutečně třídí korálky. Tato funkce běží ve vyhrazeném vlákně; pohyb horní desky, detekce barvy korálků, umístění do koše, ujistěte se, že horní deska zůstane zarovnaná, počítání korálků atd. Také se zastaví, když se odpadkový koš zaplní - jinak skončíme s přetékajícími korálky.
Barva vláknaTestThread; Boolean runtest = false; neplatná barvaTest () {
if (! top. Engaged)
top. Engaged = true;
if (! bottom. Engaged)
bottom. Engaged = true;
while (runtest) {
nextMagnet (true);
Thread. Sleep (100); zkuste {if (magSensor. SensorValue <(magSensorMax - 4)) alignMotor (); } catch {alignMotor (); }
nextCamera (true);
detekce = pravda;
while (detectCnt <5) Thread. Sleep (25); Console. WriteLine ("Detect Count:" + detectCnt); detekce = nepravda;
Barva c = detekována Barva;
this. BeginInvoke (nová akce (setColorDet), nový objekt {c}); int i = findColorPosition (c);
SetBottomPosition (i, true);
nextHole (true); colorCnts ++; this. BeginInvoke (nová akce (setColorTxt), nový objekt {i}); Thread. Sleep (250);
if (colorCnts [neznámýColorIndex]> 500) {
top. Engaged = false; bottom. Engaged = false; runtest = false; this. BeginInvoke (nová akce (setGoGreen), null); vrátit se; }}}
private void colourTestBtn_Click (odesílatel objektů, EventArgs e) {
if (colourTestThread == null ||! colourTestThread. IsAlive) {colourTestThread = new Thread (new ThreadStart (colourTest)); runtest = true; colourTestThread. Start (); colourTestBtn. Text = "STOP"; colourTestBtn. BackColor = Color. Red; } else {runtest = false; colourTestBtn. Text = "GO"; colourTestBtn. BackColor = Color. Green; }}
V tuto chvíli máme pracovní program. Některé kousky kódu byly z článku vynechány, takže se podívejte na zdroj, abyste ho skutečně spustili.
Druhá cena v optické soutěži
Doporučuje:
Jak vytvořit projekt třídění barev pomocí Microbit?: 4 kroky
Jak vytvořit projekt třídění barev pomocí Microbit ?: Cíle projektu Po stažení programu vidíme, že mikro: bitová LED matice ukazuje „srdce“, inicializuje servo 90 °. Když na barevný senzor položíme modré nebo žluté věci, servo otočí různé úhly, klasifikuje některé různé
Třídění barev M&M: 3 kroky
M&M Color Sorter: Na začátku tohoto projektu jsme se rozhodli automaticky třídit různé barevné bonbóny do samostatných misek efektivní rychlostí. Poprvé jsme se touto myšlenkou inspirovali, když jsme na webu viděli příspěvek https://howtomechatronics.com/projects/arduino-col
Stroj na třídění kouzelného mramoru LittleBits: 11 kroků (s obrázky)
LittleBits Magical Marble Sorting Machine: Chtěli jste někdy třídit kuličky? Pak jste mohli postavit tento stroj. Už nikdy nebudete muset míchat pytel mramoru! Je to kouzelný třídící stroj na mramor, který využívá barevný senzor od společnosti Adafruit, typ TCS34725 a Leonardo Arduino od
Stroj na třídění šroubů: 7 kroků (s obrázky)
Stroj na třídění šroubů: Jednoho dne v laboratoři (FabLab Moskva) jsem viděl svého kolegu zaneprázdněného třídením plné krabice šroubů, matic, kroužků a dalšího hardwaru. Zastavil jsem vedle něj, na vteřinu jsem to sledoval a řekl: „Pro stroj by to byla perfektní práce.“Po rychlém pohledu
Třídění barev PhantomX Pincher: 4 kroky
Třídění barev PhantomX Pincher: Úvod Tento instruktáž vytvořili 2 studenti Automation Engineering z UCN (Dánsko). Instruktáž ukazuje, jak lze pomocí PhantomX Pncher třídit krabice podle barev pomocí CMUcam5 Pixy a jejich skládáním. Tato aplikace