Satura rādītājs:

Robotu lodīšu šķirošana: 3 soļi (ar attēliem)
Robotu lodīšu šķirošana: 3 soļi (ar attēliem)

Video: Robotu lodīšu šķirošana: 3 soļi (ar attēliem)

Video: Robotu lodīšu šķirošana: 3 soļi (ar attēliem)
Video: ОЯПОНИЛАСЬ! 10 вещей, которые я ПОЛЮБИЛА живя в Японии 10 лет 2024, Jūlijs
Anonim
Image
Image
Robotu lodīšu šķirošana
Robotu lodīšu šķirošana
Robotu lodīšu šķirošana
Robotu lodīšu šķirošana
Robotu lodīšu šķirošana
Robotu lodīšu šķirošana

Šajā projektā mēs veidosim robotu, lai šķirotu Perlera krelles pēc krāsas.

Es vienmēr esmu gribējis izveidot krāsu šķirošanas robotu, tāpēc, kad mana meita ieinteresējās par Pērlera krelles veidošanu, es to uztvēru kā lielisku iespēju.

Perlera krelles tiek izmantotas, lai izveidotu kausētus mākslas projektus, novietojot daudzas krelles uz dēļa un pēc tam izkausējot tās kopā ar gludekli. Jūs parasti pērkat šīs krelles milzu 22 000 pērlīšu jauktu krāsu iepakojumos un pavadāt daudz laika, meklējot vēlamo krāsu, tāpēc domāju, ka to šķirošana paaugstinātu mākslas efektivitāti.

Es strādāju uzņēmumā Phidgets Inc., tāpēc šim projektam galvenokārt izmantoju Phidgets, taču to varēja izdarīt, izmantojot jebkuru piemērotu aparatūru.

1. darbība. Aparatūra

Lūk, ko es izmantoju, lai to izveidotu. Es to uzbūvēju 100%, izmantojot detaļas no vietnes phidgets.com, un lietas, kas man bija ap māju.

Fidžeta dēļi, motori, aparatūra

  • HUB0000 - VINT Hub Phidget
  • 1108 - magnētiskais sensors
  • 2x STC1001 - 2.5A Stepper Phidget
  • 2x 3324 - 42STH38 NEMA -17 bipolārais pārnesumu pārnesums
  • 3x 3002 - Phidget kabelis 60cm
  • 3403 - USB2.0 4 portu centrmezgls
  • 3031 - sieviešu bize 5,5x2,1 mm
  • 3029 - 2 vadu 100 'savīts kabelis
  • 3604 - 10 mm balta LED (soma 10)
  • 3402 - USB tīmekļa kamera

Citas daļas

  • 24VDC 2.0A barošanas avots
  • Metāllūžņi no metāla un garāžas
  • Zip saites
  • Plastmasas trauks ar nogrieztu dibenu

2. darbība: projektējiet robotu

Projektējiet robotu
Projektējiet robotu
Projektējiet robotu
Projektējiet robotu
Projektējiet robotu
Projektējiet robotu

Mums ir jāizstrādā kaut kas tāds, kas no ievades tvertnes var paņemt vienu lodīti, novietot to zem tīmekļa kameras un pēc tam pārvietot atbilstošajā tvertnē.

Pērļu savākšana

Es nolēmu veikt 1. daļu ar 2 apaļa saplākšņa gabaliem, katrs ar caurumu, kas izurbts tajā pašā vietā. Apakšējais gabals ir fiksēts, un augšējais gabals ir piestiprināts pie pakāpiena motora, kas to var pagriezt zem piltuves, kas piepildīta ar krellēm. Kad caurums pārvietojas zem piltuves, tas uztver vienu lodīti. Pēc tam es varu to pagriezt zem tīmekļa kameras un pēc tam vēl pagriezt, līdz tas sakrīt ar caurumu apakšējā daļā, un tad tas izkrīt.

Šajā attēlā es pārbaudu, vai sistēma var darboties. Viss ir fiksēts, izņemot augšējo apaļo saplākšņa gabalu, kas apakšā ir piestiprināts pie pakāpiena motora. Tīmekļa kamera vēl nav uzstādīta. Es tikai izmantoju Phidget vadības paneli, lai šajā brīdī ieslēgtu motoru.

Pērļu uzglabāšana

Nākamā daļa ir tvertņu sistēmas izstrāde katras krāsas turēšanai. Es nolēmu izmantot zemāk esošo otro pakāpju motoru, lai atbalstītu un pagrieztu apaļu trauku ar vienmērīgi izvietotiem nodalījumiem. To var izmantot, lai pagrieztu pareizo nodalījumu zem cauruma, no kura lodīte izkritīs.

Es to izveidoju, izmantojot kartonu un līmlenti. Vissvarīgākais šeit ir konsekvence - katram nodalījumam jābūt vienāda izmēra, un visam jābūt vienmērīgi nosvērtam, lai tas grieztos, neizlaižot.

Pērļu noņemšana tiek veikta, izmantojot cieši pieguļošu vāku, kas vienlaikus atklāj vienu nodalījumu, tāpēc lodītes var izliet.

Kamera

Tīmekļa kamera ir uzstādīta virs augšējās plāksnes starp piltuvi un apakšējās plāksnes atveres vietu. Tas ļauj sistēmai apskatīt lodītes pirms tās nomešanas. Gaismas diodes tiek izmantotas, lai apgaismotu lodītes zem kameras, un apkārtējā gaisma tiek bloķēta, lai nodrošinātu nemainīgu apgaismojuma vidi. Tas ir ļoti svarīgi precīzai krāsu noteikšanai, jo apkārtējais apgaismojums patiešām var izmest uztverto krāsu.

Atrašanās vietas noteikšana

Sistēmai ir svarīgi noteikt lodītes atdalītāja rotāciju. To izmanto, lai iestatītu sākotnējo pozīciju palaišanas laikā, kā arī lai noteiktu, vai soļu motors ir izkļuvis no sinhronizācijas. Manā sistēmā krelles paņemšanas laikā dažreiz iestrēgst, un sistēmai bija jāspēj noteikt un rīkoties šajā situācijā - nedaudz dublējot un mēģinot.

Ir daudz veidu, kā to risināt. Es nolēmu izmantot 1108 magnētisko sensoru ar magnētu, kas iestrādāts augšējās plāksnes malā. Tas ļauj man pārbaudīt pozīciju katrā rotācijā. Labāks risinājums, iespējams, būtu kodētājs uz soļu motora, bet man bija 1108, kas atrodas apkārt, tāpēc es to izmantoju.

Pabeidz robotu

Šajā brīdī viss ir izstrādāts un pārbaudīts. Ir pienācis laiks visu labi uzstādīt un pāriet uz rakstīšanas programmatūru.

2 soļu motorus vada STC1001 pakāpju kontrolieri. HUB000 - USB VINT centrmezgls tiek izmantots, lai palaistu pakāpju kontrolierus, kā arī lasītu magnētisko sensoru un vadītu LED. Tīmekļa kamera un HUB0000 ir pievienoti nelielam USB centrmezglam. Motoru barošanai tiek izmantots 3031 bize un daži vadi kopā ar 24 V barošanas avotu.

3. darbība: uzrakstiet kodu

Image
Image

Šim projektam tiek izmantoti C# un Visual Studio 2015. Lejupielādējiet avotu šīs lapas augšdaļā un sekojiet tam - galvenās sadaļas ir izklāstītas zemāk

Inicializācija

Pirmkārt, mums ir jāizveido, jāatver un jāinicializē Phidget objekti. Tas tiek darīts veidlapas ielādes notikumā un Phidget pielikumu apstrādātājos.

private void Form1_Load (objekta sūtītājs, EventArgs e) {

/ * Inicializēt un atvērt Phidgets */

top. HubPort = 0; top. Attach += Top_Attach; top. Detach += Top_Detach; top. PositionChange += Top_PositionChange; augšā. Atvērt ();

apakšā. HubPort = 1;

bottom. Attach += Bottom_Attach; bottom. Detach += Bottom_Detach; bottom. PositionChange += Bottom_PositionChange; apakšā. Atvērt ();

magSensor. HubPort = 2;

magSensor. IsHubPortDevice = true; magSensor. Attach += MagSensor_Attach; magSensor. Detach += MagSensor_Detach; magSensor. SensorChange += MagSensor_SensorChange; magSensor. Open ();

vadīja. HubPort = 5;

led. IsHubPortDevice = true; vadīja. Kanāls = 0; led. Attach += Led_Attach; led. Detach += Led_Detach; led. Open (); }

private void Led_Attach (objekta sūtītājs, Phidget22. Events. AttachEventArgs e) {

ledAttachedChk. Checked = true; led. State = taisnība; ledChk. Checked = true; }

private void MagSensor_Attach (objekta sūtītājs, Phidget22. Events. AttachEventArgs e) {

magSensorAttachedChk. Checked = true; magSensor. SensorType = VoltageRatioSensorType. PN_1108; magSensor. DataInterval = 16; }

private void Bottom_Attach (objekta sūtītājs, Phidget22. Events. AttachEventArgs e) {

bottomAttachedChk. Checked = true; bottom. CurrentLimit = bottomCurrentLimit; apakšā. Engaged = true; bottom. VelocityLimit = bottomVelocityLimit; bottom. Acceleration = bottomAccel; apakšā. DataInterval = 100; }

private void Top_Attach (objekta sūtītājs, Phidget22. Events. AttachEventArgs e) {

topAttachedChk. Checked = true; top. CurrentLimit = topCurrentLimit; augšā. Engaged = true; top. RescaleFactor = -1; top. VelocityLimit = -topVelocityLimit; augšā. Paātrinājums = -topAccel; top. DataInterval = 100; }

Mēs arī lasām visu saglabāto krāsu informāciju inicializācijas laikā, tāpēc var turpināt iepriekšējo darbību.

Motora pozicionēšana

Motora apstrādes kods sastāv no ērtām funkcijām motoru pārvietošanai. Motori, kurus izmantoju, ir 3, 200 1/16 soļi uz vienu apgriezienu, tāpēc es šim nolūkam izveidoju konstanti.

Augšējam motoram ir 3 pozīcijas, kuras mēs vēlamies nosūtīt uz motoru: tīmekļa kamera, caurums un pozicionēšanas magnēts. Ir funkcija ceļošanai uz katru no šīm pozīcijām:

private void nextMagnet (Būla gaidīšana = nepatiesa) {

double posn = top. Position % stepsPerRev;

top. TargetPosition += (stepsPerRev - posn);

ja (pagaidiet)

kamēr (top. IsMoving) Thread. Sleep (50); }

private void nextCamera (Būla gaidīšana = nepatiesa) {

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);

ja (pagaidiet)

kamēr (top. IsMoving) Thread. Sleep (50); }

private void nextHole (Būla gaidīšana = nepatiesa) {

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);

ja (pagaidiet)

kamēr (top. IsMoving) Thread. Sleep (50); }

Pirms skriešanas uzsākšanas augšējā plāksne ir izlīdzināta, izmantojot magnētisko sensoru. Funkciju alignMotor var izsaukt jebkurā laikā, lai izlīdzinātu augšējo plāksni. Šī funkcija vispirms ātri pagriež plāksni līdz 1 pilnam apgriezienam, līdz redz magnēta datus virs sliekšņa. Pēc tam tas nedaudz dublē un atkal lēnām virzās uz priekšu, uztverot sensora datus. Visbeidzot, tas nosaka pozīciju uz maksimālo magnēta datu atrašanās vietu un atiestata pozīcijas nobīdi uz 0. Tādējādi maksimālajai magnēta pozīcijai vienmēr jābūt (augšpusē. Pozīcija % soļiPerRev)

Thread alignMotorThread; Būla zāģisMagnets; dubultā magSensorMax = 0; private void alignMotor () {

// Atrodi magnētu

top. DataInterval = top. MinDataInterval;

sawMagnet = nepatiess;

magSensor. SensorChange += magSensorStopMotor; top. VelocityLimit = -1000;

int tryCount = 0;

mēģini vēlreiz:

top. TargetPosition += stepsPerRev;

kamēr (top. IsMoving &&! sawMagnet) Thread. Sleep (25);

ja (! sawMagnet) {

if (tryCount> 3) {Console. WriteLine ("Izlīdzināšana neizdevās"); augšā. Engaged = false; apakšā. Iesaistīts = nepatiess; runtest = nepatiess; atgriešanās; }

tryCount ++;

Console. WriteLine ("Vai mēs esam iestrēguši? Mēģinām dublēt …"); top. TargetPosition -= 600; kamēr (top. IsMoving) Thread. Sleep (100);

atkal izmēģināt;

}

top. VelocityLimit = -100;

magData = jauns saraksts> (); magSensor. SensorChange += magSensorCollectPositionData; top. TargetPosition += 300; kamēr (top. IsMoving) Thread. Sleep (100);

magSensor. SensorChange -= magSensorCollectPositionData;

top. VelocityLimit = -topVelocityLimit;

KeyValuePair max = magData [0];

foreach (KeyValuePair pāris magData) if (pair. Value> max. Value) max = pāris;

top. AddPositionOffset (-max. Key);

magSensorMax = max. Value;

top. TargetPosition = 0;

kamēr (top. IsMoving) Thread. Sleep (100);

Console. WriteLine ("Izlīdzināšana izdevās");

}

Saraksts> magData;

private void magSensorCollectPositionData (objekta sūtītājs, Phidget22. Events. VoltageRatioInputSensorChangeEventArgs e) {magData. Add (jauns KeyValuePair (top. Position, e. SensorValue)); }

private void magSensorStopMotor (objekta sūtītājs, Phidget22. Events. VoltageRatioInputSensorChangeEventArgs e) {

if (top. IsMoving && e. SensorValue> 5) {top. TargetPosition = top. Position - 300; magSensor. SensorChange -= magSensorStopMotor; sawMagnet = taisnība; }}

Visbeidzot, apakšējo motoru kontrolē, nosūtot to uz vienu no lodītes konteinera pozīcijām. Šim projektam mums ir 19 amati. Algoritms izvēlas īsāko ceļu un griežas pulksteņrādītāja virzienā vai pretēji.

private int BottomPosition {get {int posn = (int) bottom. Position % stepsPerRev; ja (posn <0) posn += soļiPerRev;

return (int) Math. Round ((((posn * beadCompartments) / (dubultie) soļiPerRev));

} }

private void SetBottomPosition (int posn, bool wait = false) {

posn = posn % beadCompartments; dubults mērķisPosn = (posn * stepsPerRev) / beadCompartments;

dubultā strāvaPosn = apakšā. Pozīcija % soļiPerRev;

double posnDiff = targetPosn - pašreizējaisPosn;

// Saglabājiet to kā pilnu darbību

posnDiff = ((int) (posnDiff / 16)) * 16;

ja (posnDiff <= 1600) bottom. TargetPosition += posnDiff; cits apakšā. TargetPosition - = (stepsPerRev - posnDiff);

ja (pagaidiet)

kamēr (apakšā. IsMoving) Thread. Sleep (50); }

Kamera

OpenCV tiek izmantots attēlu lasīšanai no tīmekļa kameras. Kameras pavediens tiek sākts pirms galvenā šķirošanas pavediena uzsākšanas. Šis pavediens nepārtraukti lasa attēlos, aprēķina vidējo krāsu konkrētam reģionam, izmantojot vidējo, un atjaunina globālu krāsu mainīgo. Vītne izmanto arī HoughCircles, lai mēģinātu noteikt lodītes vai augšējās plāksnes caurumu, lai precizētu apgabalu, kurā tas meklē krāsu noteikšanai. Slieksnis un HoughCircles skaitļi tika noteikti, izmantojot izmēģinājumus un kļūdas, un tie lielā mērā ir atkarīgi no tīmekļa kameras, apgaismojuma un atstarpēm.

bool runVideo = true; bool videoRunning = false; VideoCapture uztveršana; Vītne cvThread; Krāsa konstatētaKrāsa; Būla noteikšana = nepatiesa; int atklātCnt = 0;

private void cvThreadFunction () {

videoRunning = nepatiess;

uztveršana = jauna VideoCapture (izvēlēta kamera);

izmantojot (loga logs = jauns logs ("uztveršana")) {

Mat attēls = jauns Mat (); Mat attēls2 = jauns Mat (); while (runVideo) {capture. Read (attēls); ja (attēls. Tukšs ()) pārtraukums;

ja (atklāj)

detectCnt ++; cits atklātCnt = 0;

if (atklājot || circleDetectChecked || showDetectionImgChecked) {

Cv2. CvtColor (attēls, attēls2, ColorConversionCodes. BGR2GRAY); Mat thres = image2. Threshold ((double) Properties. Settings. Default.videoThresh, 255, ThresholdTypes. Binary); thres = thres. GaussianBlur (jauns OpenCvSharp. Size (9, 9), 10);

ja (showDetectionImgChecked)

attēls = thres;

if (atklājot || circleDetectChecked) {

CircleSegment bead = thres. HoughCircles (HoughMethods. Gradient, 2, /*thres. Rows/4*/ 20, 200, 100, 20, 65); if (bead. Length> = 1) {image. Circle (lodīte [0]. Center, 3, new Scalar (0, 100, 0), -1); attēls.aplis (lodīte [0]. centrs, (int) pērle [0]. rādiuss, jauns skalārs (0, 0, 255), 3); ja (lodīte [0]. Radius> = 55) {Properties. Settings. Default.x = (decimāldaļa) lodītes [0]. Centrs. X + (decimāls) (pērle [0]. Radius / 2); Properties. Settings. Default.y = (decimāls) lodītes [0]. Centrs. Y - (decimālskaitlis) (lodītes [0]. Radius / 2); } cits {Properties. Settings. Default.x = (decimāls) lodītes [0]. Center. X + (decimāls) (lodītes [0]. Radius); Properties. Settings. Default.y = (decimāls) lodītes [0]. Centrs. Y - (decimālskaitlis) (lodītes [0]. Radius); } Properties. Settings. Default.size = 15; Properties. Settings. Default.height = 15; } vēl {

CircleSegment apļi = thres. HoughCircles (HoughMethods. Gradient, 2, /*thres. Rows/4*/ 5, 200, 100, 60, 180);

ja (apļi. Garums> 1) {Saraksts xs = apļi. Atlasiet (c => c. Center. X). ToList (); xs. Kārtot (); Saraksts ys = apļi. Atlasiet (c => c. Centrs. Y). ToList (); ys. Kārtot ();

int medianX = (int) xs [xs. Count / 2];

int medianY = (int) ys [ys. Count / 2];

ja (medianX> attēls. Platums - 15)

medianX = attēls. Platums - 15; ja (medianY> attēls. Augstums - 15) medianY = attēls. Augstums - 15;

attēls. aplis (medianX, medianY, 100, new Scalar (0, 0, 150), 3);

ja (nosaka) {

Properties. Settings. Default.x = mediānaX - 7; Properties. Settings. Default.y = mediāna Y - 7; Properties. Settings. Default.size = 15; Properties. Settings. Default.height = 15; }}}}}

Taisnība 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 = jauns Mat (attēls, r);

Skalārs avgColor = Cv2. Mean (beadSample); detectColor = Color. FromArgb ((int) avgColor [2], (int) avgColor [1], (int) avgColor [0]);

attēls. Taisnstūris (r, jauns skalārs (0, 150, 0));

window. ShowImage (attēls);

Cv2. WaitKey (1); videoRunning = taisnība; }

videoRunning = nepatiess;

} }

private void cameraStartBtn_Click (objekta sūtītājs, EventArgs e) {

ja (cameraStartBtn. Text == "start") {

cvThread = new Thread (jauns ThreadStart (cvThreadFunction)); runVideo = taisnība; cvThread. Start (); cameraStartBtn. Text = "apstāties"; kamēr (! videoRunning) Thread. Sleep (100);

updateColorTimer. Start ();

} vēl {

runVideo = nepatiess; cvThread. Join (); cameraStartBtn. Text = "sākt"; }}

Krāsa

Tagad mēs varam noteikt lodītes krāsu un, pamatojoties uz šo krāsu, izlemt, kurā traukā to ievietot.

Šis solis balstās uz krāsu salīdzinājumu. Mēs vēlamies spēt atšķirt krāsas, lai ierobežotu viltus pozitīvos, bet arī atļaut pietiekami daudz sliekšņa, lai ierobežotu viltus negatīvus. Krāsu salīdzināšana patiesībā ir pārsteidzoši sarežģīta, jo veids, kādā datori uzglabā krāsas kā RGB, un veids, kā cilvēki uztver krāsas, nav savstarpēji saistīti. Lai pasliktinātu situāciju, jāņem vērā arī gaismas krāsa, ar kuru tiek skatīta krāsa.

Krāsu atšķirību aprēķināšanai ir sarežģīts algoritms. Mēs izmantojam CIE2000, kas izdala skaitli, kas ir tuvu 1, ja 2 krāsas cilvēkam nebūtu atšķiramas. Šo sarežģīto aprēķinu veikšanai mēs izmantojam ColorMine C# bibliotēku. Ir konstatēts, ka DeltaE vērtība 5 piedāvā labu kompromisu starp viltus pozitīvu un viltus negatīvu.

Tā kā bieži ir vairāk krāsu nekā konteineru, pēdējā vieta tiek rezervēta kā tvertne. Es parasti tos atstāju malā, lai palaistu mašīnu otrajā piegājienā.

Saraksts

krāsas = jauns saraksts (); saraksta colorPanels = jauns saraksts (); Saraksta krāsasTxts = jauns Saraksts (); Saraksta colorCnts = jauns saraksts ();

const int numColorSpots = 18;

const int nezināmsColorIndex = 18; int findColorPosition (krāsa c) {

Console. WriteLine ("Atrast krāsu …");

var cRGB = jauns Rgb ();

cRGB. R = c. R; cRGB. G = c. G; cRGB. B = c. B;

int bestMatch = -1;

dubultā spēleDelta = 100;

par (int i = 0; i <krāsas. skaits; i ++) {

var RGB = jauns Rgb ();

RGB. R = krāsas . R; RGB. G = krāsas . G; RGB. B = krāsas . B;

dubultā delta = cRGB. Compare (RGB, jauns CieDe2000Comparison ());

// dubultā delta = deltaE (c, krāsas ); Console. WriteLine ("DeltaE (" + i. ToString () + "):" + delta. ToString ()); ja (delta <matchDelta) {matchDelta = delta; bestMatch = i; }}

if (matchDelta <5) {Console. WriteLine ("Atrasts! (Posn:" + bestMatch + "Delta:" + matchDelta + ")"); atgriezt bestMatch; }

if (colors. Count <numColorSpots) {Console. WriteLine ("Jauna krāsa!"); krāsas. Pievienot (c); this. BeginInvoke (jauna darbība (setBackColor), jauns objekts {colors. Count - 1}); writeOutColors (); atgriešanās (krāsas. Skaits - 1); } else {Console. WriteLine ("Nezināma krāsa!"); atgriezt unknownColorIndex; }}

Šķirošanas loģika

Šķirošanas funkcija apvieno visus gabalus, lai faktiski kārtotu krelles. Šī funkcija darbojas īpašā pavedienā; augšējās plāksnes pārvietošana, lodītes krāsas noteikšana, ievietošana tvertnē, pārliecināšanās, ka augšējā plāksne paliek izlīdzināta, saskaitot krelles utt. Tas pārstāj darboties arī tad, kad ķeksīša tvertne kļūst pilna - pretējā gadījumā mēs vienkārši nonākam pārpildītās krellēs.

Vītnes krāsa colorTestThread; Būla runtest = false; void colourTest () {

ja (! top. Engaged)

augšā. Engaged = true;

ja (! apakšā. Iesaistīts)

apakšā. Engaged = true;

kamēr (runtest) {

nextMagnet (taisnība);

Vītne. Miega (100); mēģināt {if (magSensor. SensorValue <(magSensorMax - 4)) alignMotor (); } catch {alignMotor (); }

nextCamera (taisnība);

atklāšana = taisnība;

kamēr (atklātCnt <5) Vītne. Miega (25); Console. WriteLine ("Detect Count:" + detectCnt); atklāšana = nepatiesa;

Krāsa c = atklātaKrāsa;

this. BeginInvoke (jauna darbība (setColorDet), jauns objekts {c}); int i = atrastColorPosition (c);

SetBottomPosition (i, taisnība);

nextHole (taisnība); colorCnts ++; this. BeginInvoke (jauna darbība (setColorTxt), jauns objekts {i}); Vītne. Miega (250);

ja (colorCnts [unknownColorIndex]> 500) {

augšā. Engaged = false; apakšā. Iesaistīts = nepatiess; runtest = nepatiess; this. BeginInvoke (jauna darbība (setGoGreen), null); atgriešanās; }}}

private void colourTestBtn_Click (objekta sūtītājs, EventArgs e) {

if (colourTestThread == null ||! colourTestThread. IsAlive) {colourTestThread = new Thread (new ThreadStart (colourTest)); runtest = taisnība; colourTestThread. Start (); colourTestBtn. Text = "STOP"; colourTestBtn. BackColor = Krāsa. Sarkana; } cits {runtest = false; colourTestBtn. Text = "IET"; colourTestBtn. BackColor = Krāsa. Zaļa; }}

Šobrīd mums ir darba programma. Daži koda biti netika iekļauti rakstā, tāpēc apskatiet avotu, lai to faktiski palaistu.

Optikas konkurss
Optikas konkurss

Otrā balva optikas konkursā

Ieteicams: