Բովանդակություն:
Video: Robotic Bead Sorting: 3 քայլ (նկարներով)
2024 Հեղինակ: John Day | [email protected]. Վերջին փոփոխված: 2024-01-30 09:49
Այս նախագծում մենք կառուցելու ենք ռոբոտ ՝ Perler- ի ուլունքներն ըստ գույնի տեսակավորելու:
Ես միշտ ցանկացել եմ գույնի տեսակավորման ռոբոտ կառուցել, ուստի երբ աղջիկս հետաքրքրվեց Պերլերի ուլունքների արհեստով, ես դա դիտեցի որպես կատարյալ հնարավորություն:
Պերլերի ուլունքներն օգտագործվում են միաձուլված արվեստի նախագծեր ստեղծելու համար ՝ բազմաթիվ ուլունքներ տեղադրելով տախտակի վրա, այնուհետև դրանք հալեցնելով երկաթի հետ միասին: Դուք սովորաբար գնում եք այս ուլունքները հսկայական 22, 000 ուլունքների խառը տուփերի մեջ և շատ ժամանակ եք ծախսում ձեր ուզած գույնը որոնելու համար, ուստի ես մտածեցի, որ դրանք տեսակավորելը կբարձրացնի արվեստի արդյունավետությունը:
Ես աշխատում եմ Phidgets Inc.- ում, ուստի այս նախագծի համար հիմնականում օգտագործել եմ Phidgets- ը, բայց դա հնարավոր է անել ցանկացած հարմար սարքավորման միջոցով:
Քայլ 1: Սարքավորումներ
Ահա թե ինչ էի ես օգտագործում սա կառուցելու համար: Ես այն կառուցել եմ 100% -ով ՝ phidgets.com- ի մասերով, և այն, ինչ ես ունեի ՝ տան շուրջը պառկած:
Phidgets տախտակներ, շարժիչներ, սարքավորումներ
- HUB0000 - VINT Hub Phidget
- 1108 - Մագնիսական տվիչ
- 2x STC1001 - 2.5A Stepper Phidget
- 2x 3324 - 42STH38 NEMA -17 երկբևեռ շարժիչազրկիչ
- 3x3002 - Ֆիջեթ մալուխ 60 սմ
- 3403 - USB2.0 4 նավահանգիստային հանգույց
- 3031 - էգ խոզուկ 5.5x2.1 մմ
- 3029 - 2 մետաղալար 100 'Twisted Cable
- 3604 - 10 մմ սպիտակ LED (պայուսակ 10)
- 3402 - USB վեբ -տեսախցիկ
Այլ մասեր
- 24VDC 2.0A սնուցման աղբյուր
- Ավտոտնակից փայտ և մետաղ ջարդեք
- Zip կապեր
- Պլաստիկ տարա, որի հատակը կտրված է
Քայլ 2: Նախագծեք ռոբոտը
Մենք պետք է նախագծենք մի բան, որը կարող է մեկ բշտիկ վերցնել մուտքի վազքից, տեղադրել այն տեսախցիկի տակ, այնուհետև այն տեղափոխել համապատասխան աղբարկղ:
Ուլունքների հավաքում
Ես որոշեցի 1 -ին մասը կատարել 2 կտոր կլոր նրբատախտակով, յուրաքանչյուրը նույն տեղում փորված անցքով: Ներքևի հատվածը ամրացված է, իսկ վերին հատվածը ամրացված է սանդղակի շարժիչին, որը կարող է պտտել այն ուլունքներով լցված բալի տակ: Երբ փոսը անցնում է բուֆերի տակ, այն վերցնում է մեկ հատիկ: Այնուհետև ես կարող եմ այն պտտել վեբ -տեսախցիկի տակ, այնուհետև այն հետագայում պտտել, մինչև այն չհամապատասխանի ներքևի մասի անցքին, որի ընթացքում այն ընկնում է:
Այս նկարում ես ստուգում եմ, որ համակարգը կարող է աշխատել: Ամեն ինչ ամրագրված է, բացառությամբ նրբատախտակի վերին կլոր կտորի, որը ամրացված է տափաստանային շարժիչին ՝ ներքևից տեսանելի: Տեսախցիկը դեռ տեղադրված չէ: Ես պարզապես օգտագործում եմ Phidget Control Panel- ը `այս պահին շարժիչին դիմելու համար:
Ուլունքների պահեստավորում
Հաջորդ մասը յուրաքանչյուր գույնը պահելու համար նախատեսված աղբարկղի համակարգի ձևավորումն է: Ես որոշեցի օգտագործել երկրորդ ստորին շարժիչը ներքևում ՝ հավասարաչափ տարածված խցիկներով կլոր տարա պահելու և պտտելու համար: Սա կարող է օգտագործվել ճիշտ խցիկը պտտելու համար այն անցքի տակ, որից կաթիլը դուրս կընկնի:
Սա կառուցել եմ ստվարաթղթե և կպչուն ժապավենի միջոցով: Այստեղ ամենակարևորը հետևողականությունն է. Յուրաքանչյուր խցիկ պետք է լինի նույն չափի, և ամբողջը պետք է հավասարաչափ կշռված լինի, որպեսզի այն պտտվի առանց բաց թողնելու:
Ուլունքների հեռացումն իրականացվում է սեղմված կափարիչի միջոցով, որը միաժամանակ մերկացնում է մեկ խցիկ, այնպես որ ուլունքները կարող են թափվել:
Տեսախցիկ
Տեսախցիկը տեղադրված է գագաթի և ափսեի ստորին անցքի վայրի միջև գտնվող վերին ափսեի վրա: Սա թույլ է տալիս համակարգին նայել կաթիլը գցելուց առաջ: LED- ն օգտագործվում է տեսախցիկի տակ գտնվող ուլունքները լուսավորելու համար, իսկ շրջապատող լույսն արգելափակված է `լուսավորության հետևողական միջավայր ապահովելու համար: Սա շատ կարևոր է գույնի ճշգրիտ հայտնաբերման համար, քանի որ շրջապատող լուսավորությունն իսկապես կարող է շպրտել ընկալվող գույնը:
Տեղադրության հայտնաբերում
Համակարգի համար կարևոր է, որ կարողանա հայտնաբերել բշտիկ բաժանարարի պտույտը: Սա օգտագործվում է սկզբնական դիրքը կարգավորելու համար ՝ գործարկման ժամանակ, բայց նաև որոշելու, թե արդյոք սլաքի շարժիչը համաժամեցվել է: Իմ համակարգում, մի հատիկ երբեմն խցանում է վերցնելիս, և համակարգը պետք է կարողանար հայտնաբերել և կարգավորել այս իրավիճակը `մի փոքր պահուստավորվելով և փորձելով այլ կերպ:
Սրա հետ վարվելու շատ եղանակներ կան: Ես որոշեցի օգտագործել 1108 մագնիսական տվիչ ՝ մագնիսով, որը տեղադրված է վերին ափսեի եզրին: Սա թույլ է տալիս ինձ ստուգել յուրաքանչյուր պտույտի դիրքը: Ավելի լավ լուծում, հավանաբար, կլինի կոդավորիչը տափաստանային շարժիչի վրա, բայց ես ունեի 1108 պառկած, այնպես որ ես դա օգտագործեցի:
Ավարտեք ռոբոտը
Այս պահին ամեն ինչ մշակված է և փորձարկված: Everythingամանակն է ամեն ինչ գեղեցիկ ամրացնել և անցնել գրելու ծրագրակազմ:
Երկու սլաքային շարժիչները վարում են STC1001 ստեպեր վերահսկիչները: A HUB000 - USB VINT հանգույցը օգտագործվում է սանդղակի վերահսկիչները գործարկելու, ինչպես նաև մագնիսական տվիչը կարդալու և LED- ն վարելու համար: Տեսախցիկը և HUB0000- ը երկուսն էլ կցված են փոքր USB հանգույցին: Շարժիչները սնուցելու համար օգտագործվում է 3031 խոզուկ և որոշ մետաղալարեր ՝ 24 Վ լարման աղբյուրի հետ միասին:
Քայլ 3: Գրել կոդը
Այս նախագծի համար օգտագործվում են C# և Visual Studio 2015 ծրագրերը: Ներբեռնեք աղբյուրը այս էջի վերևում և հետևեք դրան. Հիմնական բաժինները ներկայացված են ստորև
Նախաստորագրում
Նախ, մենք պետք է ստեղծենք, բացենք և նախաստորագրենք Phidget օբյեկտները: Դա արվում է ձևի բեռնման իրադարձության մեջ, և Phidget- ը կցում է կարգավորիչներ:
մասնավոր դատարկ Form1_Load (օբյեկտ ուղարկող, EventArgs ե) {
/ * Նախաձեռնել և բացել Phidgets */
վերևում. HubPort = 0; top. Attach += Top_Attach; top. Detach += Top_Detach; top. PositionChange += Top_PositionChange; վերև: Բաց ();
ներքև. HubPort = 1;
ներքևում. Կցել += Ներքևում `կցել; ներքևում. անջատել += ներքևից ներքև. PositionChange += Bottom_PositionChange; ներքևում: Բաց ();
magSensor. HubPort = 2;
magSensor. IsHubPortDevice = ճշմարիտ; magSensor. Attach += MagSensor_Attach; magSensor. Detach += MagSensor_Detach; magSensor. SensorChange += MagSensor_SensorChange; magSensor. Open ();
led. HubPort = 5;
led. IsHubPortDevice = ճշմարիտ; led. Channel = 0; led. Attach += Led_Attach; led. Detach += Led_Detach; led. Open (); }
մասնավոր դատարկություն Led_Attach (օբյեկտ ուղարկող, Phidget22. Events. AttachEventArgs ե) {
ledAttachedChk. Checked = ճշմարիտ; led. State = true; ledChk. Checked = ճշմարիտ; }
մասնավոր դատարկություն MagSensor_Attach (օբյեկտ ուղարկող, Phidget22. Events. AttachEventArgs ե) {
magSensorAttachedChk. Checked = true; magSensor. SensorType = VoltageRatioSensorType. PN_1108; magSensor. DataInterval = 16; }
մասնավոր դատարկություն Bottom_Attach (օբյեկտ ուղարկող, Phidget22. Events. AttachEventArgs ե) {
bottomAttachedChk. Checked = ճշմարիտ; bottom. CurrentLimit = bottomCurrentLimit; ներքևում: Ներգրավված = ճշմարիտ; bottom. VelocityLimit = bottomVelocityLimit; ներքև. Արագացում = ներքև ներքևում. DataInterval = 100; }
մասնավոր դատարկություն Top_Attach (օբյեկտ ուղարկող, Phidget22. Events. AttachEventArgs ե) {
topAttachedChk. Checked = true; top. CurrentLimit = topCurrentLimit; վերևում: Ներգրավված = ճշմարիտ; վերև. RescaleFactor = -1; top. VelocityLimit = -topVelocityLimit; վերև. Արագացում = -topAccel; վերև. DataInterval = 100; }
Մենք նաև կարդում ենք ցանկացած պահված գույնի տեղեկատվություն սկզբնավորման ընթացքում, այնպես որ նախորդ վազքը կարող է շարունակվել:
Շարժիչի դիրքավորում
Շարժիչի բեռնաթափման ծածկագիրը բաղկացած է շարժիչների տեղափոխման հարմարավետության գործառույթներից: Իմ օգտագործած շարժիչները 3, 200 1/16 -րդ քայլ են մեկ պտույտի համար, ուստի դրա համար հաստատուն եմ ստեղծել:
Վերևի շարժիչի համար կան 3 դիրքեր, որոնք մենք ցանկանում ենք կարողանալ ուղարկել շարժիչին ՝ վեբ -տեսախցիկ, անցք և տեղադրման մագնիս: Այս պաշտոններից յուրաքանչյուրին մեկնելու գործառույթ կա.
private void nextMagnet (բուլյան սպասում = կեղծ) {
double posn = top. Position % stepsPerRev;
top. TargetPosition += (stepsPerRev - posn);
եթե (սպասիր)
while (top. IsMoving) Thread. Sleep (50); }
private void nextCamera (բուլյան սպասում = կեղծ) {
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);
եթե (սպասիր)
while (top. IsMoving) Thread. Sleep (50); }
մասնավոր դատարկություն nextHole (բուլյան սպասում = կեղծ) {
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);
եթե (սպասիր)
while (top. IsMoving) Thread. Sleep (50); }
Վազք սկսելուց առաջ վերին ափսեն հավասարեցվում է մագնիսական տվիչի միջոցով: AlignMotor գործառույթը կարող է ցանկացած ժամանակ կանչվել ՝ վերին ափսեի հավասարեցման համար: Այս գործառույթը նախ արագորեն թեքում է ափսեը մինչև 1 ամբողջական պտույտ, մինչև չտեսնի մագնիսի տվյալները շեմից բարձր: Այնուհետև այն մի փոքր հետ է կանգնում և նորից դանդաղ առաջ է շարժվում ՝ անընդհատ գրավելով տվիչների տվյալները: Ի վերջո, այն սահմանում է դիրքը առավելագույն մագնիսի տվյալների գտնվելու վայրի վրա և զրոյացնում դիրքը 0 -ի համար: Այսպիսով, մագնիսի առավելագույն դիրքը միշտ պետք է լինի վերևում (վերևում:
Թելի հավասարեցումMotorThread; Բուլյան սղոց Մագնիս; կրկնակի magSensorMax = 0; private void alignMotor () {
// Գտեք մագնիսը
top. DataInterval = top. MinDataInterval;
sawMagnet = կեղծ;
magSensor. SensorChange += magSensorStopMotor; top. VelocityLimit = -1000;
int tryCount = 0;
կրկին փորձել:
top. TargetPosition += stepsPerRev;
while (top. IsMoving &&! sawMagnet) Thread. Sleep (25);
եթե (! sawMagnet) {
if (tryCount> 3) {Console. WriteLine ("Հավասարեցումը ձախողվեց"); վերևում: Ներգրավված = կեղծ; ներքևում. Ներգրավված = կեղծ; runtest = կեղծ; վերադարձ; }
tryCount ++;
Console. WriteLine («Արդյո՞ք մենք խրված ենք. Փորձում ենք կրկնօրինակում …»); top. TargetPosition -= 600; while (top. IsMoving) Thread. Sleep (100);
նորից փորձել;
}
top. VelocityLimit = -100;
magData = նոր >անկ> (); magSensor. SensorChange += magSensorCollectPositionData; top. TargetPosition += 300; while (top. IsMoving) Thread. Sleep (100);
magSensor. SensorChange -= magSensorCollectPositionData;
top. VelocityLimit = -topVelocityLimit;
KeyValuePair max = mag Տվյալներ [0];
foreach (KeyValuePair զույգը magData- ում) if (pair. Value> max. Value) max = զույգ;
վերևում. Ավելացնել դիրքորոշում (-մաքս. բանալին);
magSensorMax = առավելագույն. Արժեք;
top. TargetPosition = 0;
while (top. IsMoving) Thread. Sleep (100);
Console. WriteLine ("Հավասարեցումը հաջողվեց");
}
Listանկ> magData;
private void magSensorCollectPositionData (օբյեկտ ուղարկող, Phidget22. Events. VoltageRatioInputSensorChangeEventArgs ե) {magData. Add (նոր KeyValuePair (վեր. դիրքը, e. SensorValue)); }
private void magSensorStopMotor (օբյեկտ ուղարկող, Phidget22. Events. VoltageRatioInputSensorChangeEventArgs ե) {
if (top. IsMoving && e. SensorValue> 5) {top. TargetPosition = top. Position - 300; magSensor. SensorChange -= magSensorStopMotor; sawMagnet = ճշմարիտ; }}
Ի վերջո, ներքևի շարժիչը վերահսկվում է ՝ այն ուղարկելով ուլունքների տարայի դիրքերից մեկին: Այս նախագծի համար մենք ունենք 19 հաստիք: Ալգորիթմը ընտրում է ամենակարճ ուղին և պտտվում է ժամացույցի սլաքի կամ հակառակ ուղղությամբ:
private int BottomPosition {get {int posn = (int) bottom. Position % stepsPerRev; if (posn <0) posn += stepsPerRev;
return (int) Math. Round (((posn * beadCompartments) / (double) stepsPerRev));
} }
մասնավոր դատարկություն SetBottomPosition (int posn, bool wait = false) {
posn = posn % beadCompartments; կրկնակի targetPosn = (posn * stepsPerRev) / beadCompartments;
կրկնակի ընթացիկPosn = ներքև. Դիրքը % stepsPerRev;
կրկնակի posnDiff = targetPosn - currentPosn;
// Պահեք այն որպես ամբողջական քայլեր
posnDiff = ((int) (posnDiff / 16)) * 16;
եթե (posnDiff <= 1600) ներքև. TargetPosition += posnDiff; else bottom. TargetPosition - = (stepsPerRev - posnDiff);
եթե (սպասիր)
while (bottom. IsMoving) Thread. Sleep (50); }
Տեսախցիկ
OpenCV- ն օգտագործվում է վեբ -տեսախցիկից պատկերներ կարդալու համար: Տեսախցիկի շարանը գործարկվում է հիմնական տեսակավորման շարանը սկսելուց առաջ: Այս շարանը անընդհատ կարդում է պատկերների մեջ, Միջին գույնի միջոցով հաշվարկում է որոշակի տարածաշրջանի միջին գույնը և թարմացնում է գլոբալ գույնի փոփոխականը: Թելը նաև օգտագործում է HoughCircles- ը ՝ փորձելու հայտնաբերել կամ մի բշտիկ, կամ վերին ափսեի անցքը ՝ մաքրելու այն տարածքը, որը նա փնտրում է գույնի հայտնաբերման համար: Շեմի և HoughCircles- ի թվերը որոշվել են փորձության և սխալի միջոցով և մեծապես կախված են վեբ -տեսախցիկից, լուսավորությունից և տարածությունից:
bool runVideo = true; bool videoRunning = false; VideoCapture գրավում; Թեման cvThread; Գույնը հայտնաբերվածԳույն; Բուլյան հայտնաբերում = կեղծ; int deteCnt = 0;
private void cvThreadFunction () {
videoRunning = կեղծ;
գրավում = նոր VideoCapture (ընտրված տեսախցիկ);
օգտագործելով (Window window = new Window («գրավում»)) {
Գորգի պատկեր = նոր գորգ (); Մատ պատկեր 2 = նոր գորգ (); while (runVideo) {capture. Read (պատկերը); եթե (պատկեր. Դատարկ ()) ընդմիջում;
եթե (հայտնաբերում)
deteCnt ++; այլապես deteCnt = 0;
եթե (հայտնաբերում || circleDetectChecked || showDetectionImgChecked) {
Cv2. CvtColor (պատկեր, պատկեր 2, ColorConversionCodes. BGR2GRAY); Mat thres = image2. Threshold ((double) Properties. Settings. Default.videoThresh, 255, ThresholdTypes. Binary); thres = thres. GaussianBlur (նոր OpenCvSharp. Size (9, 9), 10);
եթե (showDetectionImgChecked)
պատկեր = երեք;
եթե (հայտնաբերում || circleDetectChecked) {
CircleSegment ուլունք = 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 (bead [0]. Center, (int) bead [0]. Radius, new Scalar (0, 0, 255), 3); if (ուլունք [0]. Radius> = 55) {Properties. Settings. Default.x = (տասնորդական) ուլունք [0]. Center. X + (տասնորդական) (ուլունք [0]. Radius / 2); Properties. Settings. Default.y = (տասնորդական) ուլունք [0]. Center. Y - (տասնորդական) (ուլունք [0]. Radius / 2); } else {Properties. Settings. Default.x = (տասնորդական) ուլունք [0]. Center. X + (տասնորդական) (ուլունք [0]. Radius); Properties. Settings. Default.y = (տասնորդական) ուլունք [0]. Center. Y - (տասնորդական) (ուլունք [0]. Radius); } Properties. Settings. Default.size = 15; Properties. Settings. Default.height = 15; } ուրիշ {
CircleSegment շրջանակներ = thres. HoughCircles (HoughMethods. Gradient, 2, /*thres. Rows/4*/ 5, 200, 100, 60, 180);
եթե (շրջանակներ. Երկարություն> 1) {Listանկ xs = շրջանակներ: Ընտրեք (c => c. Center. X). ToList (); xs. Տեսակավորել (); Listանկ ys = շրջանակներ: Ընտրեք (c => c. Center. Y). ToList (); ys. Տեսակավորել ();
int medianX = (int) xs [xs. Count / 2];
int medianY = (int) ys [ys. Count / 2];
եթե (medianX> պատկեր. Լայնություն - 15)
medianX = պատկեր: Լայնություն - 15; եթե (միջին Y> պատկեր. Բարձրություն - 15) միջին Y = պատկեր: Բարձրություն - 15;
image. Circle (medianX, medianY, 100, new Scalar (0, 0, 150), 3);
եթե (հայտնաբերում) {
Properties. Settings. Default.x = medianX - 7; Properties. Settings. Default.y = միջին Y - 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 = նոր գորգ (պատկեր, r);
Scalar avgColor = Cv2. Միջին (ուլունքների օրինակ); deteColor = Color. FromArgb ((int) avgColor [2], (int) avgColor [1], (int) avgColor [0]);
պատկեր. Ուղղանկյուն (r, նոր Scalar (0, 150, 0));
window. ShowImage (պատկեր);
Cv2. WaitKey (1); videoRunning = ճշմարիտ; }
videoRunning = կեղծ;
} }
մասնավոր դատարկ տեսախցիկ StartBtn_Click (օբյեկտ ուղարկող, EventArgs ե) {
եթե (cameraStartBtn. Text == "սկսել") {
cvThread = նոր թեմա (նոր ThreadStart (cvThreadFunction)); runVideo = ճշմարիտ; cvThread. Start (); cameraStartBtn. Text = "կանգառ"; while (! videoRunning) Thread. Sleep (100);
updateColorTimer. Start ();
} ուրիշ {
runVideo = կեղծ; cvThread. Join (); cameraStartBtn. Text = "սկսել"; }}
Գույն
Այժմ մենք կարողանում ենք որոշել բշտիկի գույնը և այդ գույնի հիման վրա որոշել, թե որ տարայի մեջ այն պետք է գցել:
Այս քայլը հիմնված է գույների համեմատության վրա: Մենք ցանկանում ենք, որ կարողանանք առանձնացնել գույները ՝ սահմանափակելու կեղծ դրականը, բայց նաև թույլ ենք տալիս բավականաչափ շեմ սահմանափակել կեղծ բացասականը: Գույների համեմատությունը իրականում զարմանալիորեն բարդ է, քանի որ համակարգիչների կողմից գույները RGB- ի տեսքով պահելը և այն, թե ինչպես են մարդիկ գույներն ընկալում, գծային փոխկապակցված չեն: Ավելի վատը դարձնելու համար պետք է նաև հաշվի առնել լույսի գույնը, որի տակ դիտարկվում է գույնը:
Գոյություն ունեն բարդ ալգորիթմ ՝ գույների տարբերությունը հաշվարկելու համար: Մենք օգտագործում ենք CIE2000- ը, որը թողարկում է 1 -ի մոտ մի թիվ, եթե 2 գույները անտարբեր լինեն մարդու համար: Այս բարդ հաշվարկները կատարելու համար մենք օգտագործում ենք ColorMine C# գրադարանը: Պարզվել է, որ DeltaE- ի 5 արժեքը լավ փոխզիջում է առաջարկում կեղծ դրականի և կեղծ բացասականի միջև:
Քանի որ հաճախ ավելի շատ գույներ կան, քան տարաները, վերջին դիրքը վերապահված է որպես գավազան: Ես ընդհանրապես դրանք մի կողմ եմ դնում, որպեսզի մեքենան աշխատի երկրորդ անցման վրա:
Listանկ
գույներ = նոր ցուցակ (); ցուցակ colorPanels = նոր ցուցակ (); Colorsանկի գույներՏեքստեր = նոր ցուցակ (); Ցուցակ colorCnts = նոր ցուցակ ();
const int numColorSpots = 18;
const int unknownColorIndex = 18; int findColorPosition (գույնը գ) {
Console. WriteLine ("Գտնելով գույնը …");
var cRGB = նոր Rgb ();
cRGB. R = c. R; cRGB. G = c. G; cRGB. B = c. B;
int bestMatch = -1;
կրկնակի համընկնումԴելտա = 100;
համար (int i = 0; i <colors. Count; i ++) {
var RGB = նոր Rgb ();
RGB. R = գույներ . R; RGB. G = գույներ . G; RGB. B = գույներ . B;
կրկնակի դելտա = cRGB. Համեմատել (RGB, նոր CieDe2000Comparison ());
// կրկնակի դելտա = դելտաԷ (գ, գույներ ); Console. WriteLine ("DeltaE (" + i. ToString () + "):" + delta. ToString ()); եթե (դելտա <matchDelta) {matchDelta = դելտա; bestMatch = i; }}
if (matchDelta <5) {Console. WriteLine ("Գտնվեց! (Տեղադր." + bestMatch + "Delta:" + matchDelta + ")"); վերադարձնել bestMatch; }
if (colors. Count <numColorSpots) {Console. WriteLine («Նոր գույն»); գույներ: Ավելացնել (գ); this. BeginInvoke (նոր գործողություն (setBackColor), նոր օբյեկտ {colors. Count - 1}); writeOutColors (); վերադարձ (գույներ. Հաշիվ - 1); } else {Console. WriteLine («Անհայտ գույն»); վերադարձ անհայտ ColourIndex; }}
Տրամաբանության տեսակավորում
Տեսակավորման գործառույթը միավորում է բոլոր կտորները `իրականում տեսակավորելու ուլունքները: Այս գործառույթն աշխատում է հատուկ շարանի մեջ. վերին ափսեի տեղափոխում, բշտիկի գույնի հայտնաբերում, տեղադրում այն աղբարկղի մեջ, համոզվեք, որ վերին ափսեը հավասարեցված է, հաշվում են ուլունքները և այլն:Այն նաև դադարում է աշխատել, երբ որսալարը լցվում է: Հակառակ դեպքում մենք պարզապես հայտնվում ենք լցված ուլունքներով:
Թելի colourTestThread; Բուլյան runtest = կեղծ; void colourTest () {
եթե (! վերևում. ներգրավված է)
վերևում: Ներգրավված = ճշմարիտ;
եթե (! ներքևում. ներգրավված է)
ներքևում: Ներգրավված = ճշմարիտ;
մինչդեռ (ամենախենթ) {
nextMagnet (ճշմարիտ);
Թեման. Քուն (100); փորձեք {if (magSensor. SensorValue <(magSensorMax - 4)) alignMotor (); } catch {alignMotor (); }
հաջորդ Տեսախցիկ (ճշմարիտ);
հայտնաբերում = ճշմարիտ;
while (deteCnt <5) Թեման. Քուն (25); Console. WriteLine ("Detect Count:" + detectT); հայտնաբերում = կեղծ;
Գույն c = հայտնաբերվածԳույն;
this. BeginInvoke (նոր գործողություն (setColorDet), նոր օբյեկտ {c}); int i = findColorPosition (գ);
SetBottomPosition (i, ճշմարիտ);
nextHole (ճշմարիտ); colorCnts ++; this. BeginInvoke (նոր գործողություն (setColorTxt), նոր օբյեկտ {i}); Թեման: Քուն (250);
եթե (colorCnts [unknownColorIndex]> 500) {
վերևում: Ներգրավված = կեղծ; ներքևում. Ներգրավված = կեղծ; runtest = կեղծ; this. BeginInoke (նոր գործողություն (setGoGreen), null); վերադարձ; }}}
private void colourTestBtn_Click (օբյեկտ ուղարկող, EventArgs ե) {
եթե (colourTestThread == null ||! colourTestThread. IsAlive) {colourTestThread = նոր թեմա (նոր ThreadStart (colourTest)); runtest = ճշմարիտ; colourTestThread. Start (); colourTestBtn. Text = "ԿԱՆԳՆԵԼ"; colourTestBtn. BackColor = Color. Red; } else {runtest = կեղծ; colourTestBtn. Text = "ԳՆԵԼ"; colourTestBtn. BackColor = Color. Green; }}
Այս պահին մենք ունենք աշխատանքային ծրագիր: Կոդի որոշ հատվածներ դուրս են մնացել հոդվածից, այնպես որ, իրականում գործարկելու համար, նայեք աղբյուրին:
Երկրորդ մրցանակ Օպտիկայի մրցույթում
Խորհուրդ ենք տալիս:
Robotic Arm with Gripper: 9 քայլ (նկարներով)
Robotic Arm With Gripper. Կիտրոնի ծառերի բերքը համարվում է ծանր աշխատանք `ծառերի մեծ չափերի և նաև այն շրջանների տաք կլիմայի պատճառով, որտեղ կիտրոնի ծառեր են տնկվում: Ահա թե ինչու մեզ այլ բան է պետք, որը կօգնի գյուղատնտեսության ոլորտի աշխատողներին ավելի հեշտությամբ ավարտին հասցնել իրենց աշխատանքը
Moslty եռաչափ տպված Robotic Arm, որը նմանակում է տիկնիկային վերահսկիչին. 11 քայլ (նկարներով)
Moslty 3D տպված Robotic Arm That Mimics Puppet Controller. Ես ինժեներական ճարտարագիտության ուսանող եմ Հնդկաստանից և սա My Undergrad աստիճանի նախագիծն է: Այս նախագիծը կենտրոնացած է ցածր գնով ռոբոտային թևի մշակման վրա, որը հիմնականում 3D տպված է և ունի 5 DOF 2 մատով բռնիչ Ռոբոտային ձեռքը վերահսկվում է
Arduino Controlled Robotic Biped: 13 քայլ (նկարներով)
Arduino Controlled Robotic Biped. Ինձ միշտ հետաքրքրել են ռոբոտները, հատկապես այն տեսակները, որոնք փորձում են ընդօրինակել մարդկային գործողությունները: Այս հետաքրքրությունը ստիպեց ինձ փորձել նախագծել և մշակել ռոբոտացված երկոտանի, որը կարող էր ընդօրինակել մարդու քայլելն ու վազելը: Այս Ուղեցույցում ես ձեզ ցույց կտամ
ASL Robotic Hand (ձախ) ՝ 9 քայլ (նկարներով)
ASL Robotic Hand (Ձախ). Այս կիսամյակի նախագիծը պետք է ստեղծեր եռաչափ տպագիր ձախ ձեռքը, որն ունակ է դասարանային պայմաններում ցուցադրել ամերիկյան ժեստերի լեզվի այբուբենը խուլ և լսողության խնդիրներ ունեցող մարդկանց համար: Ամերիկյան ժեստերի լեզուների ցուցադրման մատչելիությունը
DIY Arduino Robotic Arm, Քայլ առ քայլ ՝ 9 քայլ
DIY Arduino Robotic Arm, Քայլ առ քայլ. Այս ձեռնարկը սովորեցնում է ձեզ, թե ինչպես ինքնուրույն կառուցել ռոբոտ -բազուկ