Robotic Bead Sorting: 3 քայլ (նկարներով)
Robotic Bead Sorting: 3 քայլ (նկարներով)
Anonim
Image
Image
Ռոբոտային ուլունքների տեսակավորում
Ռոբոտային ուլունքների տեսակավորում
Ռոբոտային ուլունքների տեսակավորում
Ռոբոտային ուլունքների տեսակավորում
Ռոբոտային ուլունքների տեսակավորում
Ռոբոտային ուլունքների տեսակավորում

Այս նախագծում մենք կառուցելու ենք ռոբոտ ՝ 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: Գրել կոդը

Image
Image

Այս նախագծի համար օգտագործվում են 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; }}

Այս պահին մենք ունենք աշխատանքային ծրագիր: Կոդի որոշ հատվածներ դուրս են մնացել հոդվածից, այնպես որ, իրականում գործարկելու համար, նայեք աղբյուրին:

Օպտիկայի մրցույթ
Օպտիկայի մրցույթ

Երկրորդ մրցանակ Օպտիկայի մրցույթում

Խորհուրդ ենք տալիս: