Përmbajtje:

Renditja e rruazave robotike: 3 hapa (me fotografi)
Renditja e rruazave robotike: 3 hapa (me fotografi)

Video: Renditja e rruazave robotike: 3 hapa (me fotografi)

Video: Renditja e rruazave robotike: 3 hapa (me fotografi)
Video: Dies ist ein Ausbilder, der sich als regulärer Pilates-Kurs anmeldet. 2024, Korrik
Anonim
Image
Image
Renditja e rruazave robotike
Renditja e rruazave robotike
Renditja e rruazave robotike
Renditja e rruazave robotike
Renditja e rruazave robotike
Renditja e rruazave robotike

Në këtë projekt, ne do të ndërtojmë një robot për të renditur rruaza Perler sipas ngjyrës.

Unë gjithmonë kam dashur të ndërtoj një robot të klasifikimit të ngjyrave, kështu që kur vajza ime u interesua për punimin e rruazave Perler, e pashë këtë si një mundësi të përsosur.

Rruazat Perler përdoren për të krijuar projekte të shkrira arti duke vendosur shumë rruaza në një dërrasë dhe pastaj duke i shkrirë ato së bashku me një hekur. Ju në përgjithësi i blini këto rruaza në pako gjigante 22, 000 rruaza me ngjyra të përziera dhe kaloni shumë kohë duke kërkuar ngjyrën që dëshironi, kështu që mendova se renditja e tyre do të rriste efikasitetin e artit.

Unë punoj për Phidgets Inc. kështu që kam përdorur kryesisht Phidgets për këtë projekt - por kjo mund të bëhet duke përdorur çdo pajisje të përshtatshme.

Hapi 1: Pajisje kompjuterike

Këtu është ajo që kam përdorur për të ndërtuar këtë. Unë e ndërtova atë 100% me pjesë nga phidgets.com, dhe gjëra që kisha të shtrira rreth shtëpisë.

Bordet e Phidgets, Motorët, Pajisjet

  • HUB0000 - VINT Hub Phidget
  • 1108 - Sensor magnetik
  • 2x STC1001 - 2.5A Stepper Phidget
  • 2x 3324 - 42STH38 NEMA -17 Stepper Bipolar Gearless
  • 3x 3002 - Kabllo Phidget 60cm
  • 3403 - USB2.0 4 -Port Hub
  • 3031 - Bishti Femër 5.5x2.1mm
  • 3029 - 2 tela 100 'Kabllo e përdredhur
  • 3604 - LED të bardhë 10 mm (Qese prej 10)
  • 3402 - Webcam USB

Pjesë të tjera

  • Furnizimi me energji 24VDC 2.0A
  • Hidhni dru dhe metal nga garazhi
  • Lidhëse zip
  • Enë plastike me pjesën e poshtme të prerë

Hapi 2: Dizajnoni Robotin

Dizajnoni Robotin
Dizajnoni Robotin
Dizajnoni Robotin
Dizajnoni Robotin
Dizajnoni Robotin
Dizajnoni Robotin

Ne duhet të hartojmë diçka që mund të marrë një rruazë të vetme nga hinka hyrëse, ta vendosim atë nën kamerën në internet dhe më pas ta zhvendosim në koshin e duhur.

Marrje rruaza

Vendosa të bëj pjesën e parë me 2 copë kompensatë të rrumbullakët, secila me një vrimë të shpuar në të njëjtin vend. Pjesa e poshtme është e fiksuar, dhe pjesa e sipërme është e bashkangjitur në një motor stepper, i cili mund ta rrotullojë atë nën një enë të mbushur me rruaza. Kur vrima udhëton nën plesht, ajo merr një rruazë të vetme. Më pas mund ta rrotulloj nën kamerën e uebit, dhe pastaj të rrotullohem më tej derisa të përputhet me vrimën në pjesën e poshtme, në të cilën pikë bie.

Në këtë foto, unë jam duke testuar që sistemi mund të funksionojë. Çdo gjë është e fiksuar përveç pjesës së sipërme të kompensatës, e cila është ngjitur në një motor stepper jashtë pamjes nën të. Kamera në internet nuk është montuar ende. Unë thjesht po përdor Panelin e Kontrollit Phidget për t'u kthyer në motor në këtë pikë.

Magazinimi i rruazave

Pjesa tjetër është dizajnimi i sistemit të koshit për mbajtjen e secilës ngjyrë. Vendosa të përdor një motor stepper të dytë më poshtë për të mbështetur dhe rrotulluar një enë të rrumbullakët me ndarje të vendosura në mënyrë të barabartë. Kjo mund të përdoret për të rrotulluar ndarjen e duhur nën vrimën nga e cila do të bjerë rruaza.

Unë e ndërtova këtë duke përdorur karton dhe shirit ngjitës. Gjëja më e rëndësishme këtu është qëndrueshmëria - çdo ndarje duhet të jetë me të njëjtën madhësi, dhe e gjithë gjëja duhet të peshohet në mënyrë të barabartë në mënyrë që të rrotullohet pa kapërcyer.

Heqja e rruazave realizohet me anë të një kapaku të shtrënguar i cili ekspozon një ndarje të vetme në të njëjtën kohë, kështu që rruazat mund të derdhen jashtë.

Kamera

Kamera e uebit është montuar mbi pllakën e sipërme midis pikës dhe vendndodhjes së vrimës së pllakës së poshtme. Kjo i lejon sistemit të shikojë rruazën para se ta lëshojë atë. Një LED përdoret për të ndriçuar rruazat nën kamerë, dhe drita e ambientit është e bllokuar, në mënyrë që të sigurojë një mjedis ndriçimi të qëndrueshëm. Kjo është shumë e rëndësishme për zbulimin e saktë të ngjyrave, pasi ndriçimi i ambientit me të vërtetë mund të heqë ngjyrën e perceptuar.

Zbulimi i vendndodhjes

Importantshtë e rëndësishme që sistemi të jetë në gjendje të zbulojë rrotullimin e ndarësit të rruazave. Kjo përdoret për të vendosur pozicionin fillestar kur filloni, por gjithashtu për të zbuluar nëse motori stepper ka dalë nga sinkronizimi. Në sistemin tim, një rruazë nganjëherë bllokohet gjatë marrjes, dhe sistemi duhej të ishte në gjendje të zbulonte dhe trajtonte këtë situatë - duke u mbështetur pak dhe duke provuar një agian.

Ka shumë mënyra për ta trajtuar këtë. Vendosa të përdor një sensor magnetik 1108, me një magnet të ngulitur në buzë të pllakës së sipërme. Kjo më lejon të verifikoj pozicionin në çdo rrotullim. Një zgjidhje më e mirë ndoshta do të ishte një kodues në motorin stepper, por unë kisha një 1108 të shtrirë përreth kështu që e përdor atë.

Përfundoni Robotin

Në këtë pikë, gjithçka është përpunuar dhe testuar. It'sshtë koha për të montuar gjithçka bukur dhe për të kaluar në programin e shkrimit.

2 motorët stepper po drejtohen nga kontrolluesit stepper STC1001. Një shpërndarës HUB000 - USB VINT përdoret për drejtimin e kontrolluesve të hapave, si dhe për leximin e sensorit magnetik dhe drejtimin e LED. Webcam dhe HUB0000 janë bashkangjitur të dy në një shpërndarës të vogël USB. Një gropë 3031 dhe disa tela përdoren së bashku me një furnizim me energji 24V për të fuqizuar motorët.

Hapi 3: Shkruani kodin

Image
Image

C# dhe Visual Studio 2015 përdoren për këtë projekt. Shkarkoni burimin në krye të kësaj faqe dhe ndiqni së bashku - pjesët kryesore janë të përshkruara më poshtë

Fillimi

Së pari, ne duhet të krijojmë, hapim dhe inicializojmë objektet Phidget. Kjo bëhet në ngjarjen e ngarkimit të formës, dhe mbajtësit e bashkëngjitjes së Phidget -it.

zbrazëti private Form1_Load (dërguesi i objektit, EventArgs e) {

/ * Filloni dhe hapni Phidgets */

lart. HubPort = 0; lartë. Attach += Top_Attach; lart. Detach += Top_Detach; lart. PositionChange += Top_PositionChange; lart. Hapur ();

fund. HubPort = 1;

fund. Attach += Bottom_Attach; fund. Detach += Bottom_Detach; fund. PositionChange += Bottom_PositionChange; poshtë. Hapur ();

magSensor. HubPort = 2;

magSensor. IsHubPortDevice = e vërtetë; magSensor. Attach += MagSensor_Attach; magSensor. Detach += MagSensor_Detach; magSensor. SensorChange += MagSensor_SensorChange; magSensor. Hapur ();

led. HubPort = 5;

led. IsHubPortDevice = e vërtetë; led. Kanal = 0; led. Attach += Led_Attach; led. Detach += Led_Detach; led. Hapur (); }

zbrazëtira private Led_Attach (dërguesi i objektit, Phidget22. Events. AttachEventArgs e) {

ledAttachedChk. Checked = e vërtetë; udhëhequr. Shteti = i vërtetë; ledChk. Checked = e vërtetë; }

zbrazëti private MagSensor_Attach (dërguesi i objektit, Phidget22. Events. AttachEventArgs e) {

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

zbrazëti private Bottom_Attach (dërguesi i objektit, Phidget22. Events. AttachEventArgs e) {

bottomAttachedChk. Checked = e vërtetë; fund. CurrentLimit = fundCurrentLimit; fund. Angazhuar = e vërtetë; fund. VelocityLimit = fundVelocityLimit; fund. Acceleration = fundAccel; fund. DataInterval = 100; }

zbrazëtira private Top_Attach (dërguesi i objektit, Phidget22. Events. AttachEventArgs e) {

topAttachedChk. Checked = e vërtetë; top. CurrentLimit = topCurrentLimit; lart. Angazhuar = e vërtetë; lart. RescaleFactor = -1; top. VelocityLimit = -topVelocityLimit; lart. Acceleration = -topAccel; lart. DataInterval = 100; }

Ne gjithashtu lexojmë në çdo informacion të ruajtur të ngjyrave gjatë fillimit, kështu që një ekzekutim i mëparshëm mund të vazhdojë.

Pozicionimi i motorit

Kodi i trajtimit të motorit përbëhet nga funksione komoditeti për lëvizjen e motorëve. Motorët që kam përdorur janë 3, 200 1/16 hapa për revolucion, kështu që krijova një konstante për këtë.

Për motorin e sipërm, ka 3 pozicione që duam të jemi në gjendje t'i dërgojmë në motor: kamerën në internet, vrimën dhe magnetin e pozicionimit. Ekziston një funksion për të udhëtuar në secilën nga këto pozicione:

zbrazëti private tjetërMagnet (Pritje Boolean = false) {

dyshe posn = krye. Pozicioni % hapaPerRev;

lart. TargetPosition += (hapatPerRev - posn);

nëse (prit)

ndërsa (lart. IsMoving) Thread. Fleep (50); }

private void nextCamera (Pritje Boolean = false) {

dyshe posn = krye. Pozicioni % hapaPerRev; if (posn <Properties. Settings. Default.cameraOffset) top. TargetPosition += (Properties. Settings. Default.cameraOffset - posn); tjetër top. TargetPosition + = ((Properties. Settings. Default.cameraOffset - posn) + stepsPerRev);

nëse (prit)

ndërsa (lart. IsMoving) Thread. Fleep (50); }

void privat nextHole (Pritje Boolean = false) {

dyshe posn = krye. Pozicioni % hapaPerRev; if (posn <Properties. Settings. Default.holeOffset) top. TargetPosition += (Properties. Settings. Default.holeOffset - posn); tjetër top. TargetPosition + = ((Properties. Settings. Default.holeOffset - posn) + stepsPerRev);

nëse (prit)

ndërsa (lart. IsMoving) Thread. Fleep (50); }

Para fillimit të vrapimit, pllaka e sipërme rreshtohet duke përdorur sensorin magnetik. Funksioni alignMotor mund të thirret në çdo kohë për të rreshtuar pllakën e sipërme. Ky funksion së pari e kthen shpejt pllakën në 1 rrotullim të plotë derisa të shohë të dhënat e magnetit mbi një prag. Pastaj mbështetet pak dhe ecën përpara ngadalë përsëri, duke kapur të dhënat e sensorit ndërsa shkon. Së fundi, ai vendos pozicionin në vendndodhjen maksimale të të dhënave të magnetit dhe rivendos pozicionin e kompensuar në 0. Kështu, pozicioni i magnetit maksimal duhet të jetë gjithmonë në (lart. Pozicioni % hapaPerRev)

Thread alignMotorThread; sharrë BooleanMagnet; dyfish magSensorMax = 0; void privat alignMotor () {

// Gjeni magnetin

lartë. DataInterval = krye. MinDataInterval;

sawMagnet = false;

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

int tryCount = 0;

Provo përsëri:

top. TargetPosition += hapatPerRev;

ndërsa (lart. IsMoving &&! sawMagnet) Thread. Fleep (25);

nëse (! sawMagnet) {

if (tryCount> 3) {Console. WriteLine ("Rreshtimi dështoi"); lart. Angazhuar = false; fundi. Angazhuar = false; runtest = false; kthim; }

tryCount ++;

Console. WriteLine ("A kemi ngecur? Po provojmë një kopje rezervë …"); lart. TargetPosition -= 600; ndërsa (lart. IsMoving) Thread. Sleep (100);

u përpoqa përsëri;

}

lart. VelocityLimit = -100;

magData = Lista e re> (); magSensor. SensorChange += magSensorCollectPositionData; lart. TargetPosition += 300; ndërsa (lart. IsMoving) Thread. Sleep (100);

magSensor. SensorChange -= magSensorCollectPositionData;

top. VelocityLimit = -topVelocityLimit;

KeyValuePair max = magData [0];

foreach (Çifti KeyValuePair në magData) nëse (palë. Vlera> max. Vlera) max = palë;

lart. AddPositionOffset (-max. Çelësi);

magSensorMax = max. Vlera;

top. TargetPosition = 0;

ndërsa (lart. IsMoving) Thread. Sleep (100);

Console. WriteLine ("Rreshtimi arriti");

}

Lista> magData;

private void magSensorCollectPositionData (objekti dërgues, Phidget22. Events. VoltageRatioInputSensorChangeEventArgs e) {magData. Add (i ri KeyValuePair (lart. Pozicioni, e. SensorValue)); }

private void magSensorStopMotor (dërguesi i objektit, Phidget22. Events. VoltageRatioInputSensorChangeEventArgs e) {

if (lart. IsMoving && e. SensorValue> 5) {top. TargetPosition = krye. Pozicioni - 300; magSensor. SensorChange -= magSensorStopMotor; sawMagnet = e vërtetë; }}

Së fundmi, motori i poshtëm kontrollohet duke e dërguar atë në një nga pozicionet e enës së rruazës. Për këtë projekt, ne kemi 19 pozicione. Algoritmi po zgjedh rrugën më të shkurtër, dhe kthehet ose në drejtim të akrepave të orës ose në të kundërt.

int int BottomPosition {merrni {int posn = (int) fund. Pozicioni % hapaPerRev; nëse (posn <0) posn += hapatPerRev;

kthimi (int) Math. Round (((posn * beadCompartments) / (double) stepsPerRev));

} }

zbrazëti private SetBottomPosition (int posn, bool wait = false) {

posn = posn % beadCompartments; target i dyfishtëPosn = (posn * stepsPerRev) / beadCompartments;

rrymë e dyfishtëPosn = poshtë. Pozicioni % hapaPerRev;

dyfishtë posnDiff = targetPosn - currentPosn;

// Mbani atë si hapa të plotë

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

nëse (posnDiff <= 1600) fund. TargetPosition += posnDiff; tjetër fund. TargetPosition - = (hapatPerRev - posnDiff);

nëse (prit)

ndërsa (poshtë. IsMoving) Thread. Fleep (50); }

Kamera

OpenCV përdoret për të lexuar imazhe nga kamera në internet. Fije e kamerës fillon para fillimit të fijes kryesore të renditjes. Ky fije lexon vazhdimisht në imazhe, llogarit një ngjyrë mesatare për një rajon të caktuar duke përdorur Mean dhe përditëson një ndryshore globale të ngjyrave. Fije gjithashtu përdor HoughCircles për të provuar të zbulojë ose një rruaza, ose vrimë në pllakën e sipërme, për të përsosur zonën që po shikon për zbulimin e ngjyrave. Numrat e pragut dhe HoughCircles u përcaktuan përmes provës dhe gabimit dhe varen shumë nga kamera në internet, ndriçimi dhe hapësira.

bool runVideo = e vërtetë; bool videoRunning = false; Regjistrimi i videove; Fije cvThread; Ngjyra e zbuluarNgjyra; Zbulimi Boolean = false; int deteCnt = 0;

private void cvThreadFunction () {

videoRunning = false;

kapje = VideoCapture e re (Kamera e zgjedhur);

duke përdorur (Dritarja e dritares = dritare e re ("kapja")) {

Imazhi i mat = mat i ri (); Imazhi mat2 = Mat i ri (); ndërsa (runVideo) {capture. Lexoni (imazhin); nëse (imazhi. Bosh ()) pushim;

nëse (zbulimi)

deteCnt ++; tjetër deteCnt = 0;

nëse (zbulimi || rrethDetectChecked || showDetectionImgChecked) {

Cv2. CvtColor (imazhi, imazhi2, ColorConversionCodes. BGR2GRAY); Mat thres = image2. Thresh ((double) Properties. Settings. Default.videoThresh, 255, ThresholdTypes. Binary); thres = thres. GaussianBlur (OpenCvSharp i ri. Madhësia (9, 9), 10);

nëse (showDetectionImgChecked)

imazh = thres;

nëse (zbulimi || rrethDetectChecked) {

CircleSegment bead = thres. HoughCircles (HoughMethods. Gradient, 2, /*thres. Rows/4*/ 20, 200, 100, 20, 65); if (bead. Length> = 1) {image. Circle (bead [0]. Center, 3, Scalar i ri (0, 100, 0), -1); imazh. Rrethi (rruaza [0]. Qendra, (int) rruaza [0]. Radius, Scalar i ri (0, 0, 255), 3); nëse (rruaza [0]. Radius> = 55) {Properties. Settings. Default.x = (dhjetore) rruaza [0]. Center. X + (dhjetore) (sferë [0]. Radius / 2); Properties. Settings. Default.y = (dhjetore) bead [0]. Center. Y - (decimal) (bead [0]. Radius / 2); } else {Properties. Settings. Default.x = (dhjetore) rruaza [0]. Center. X + (dhjetore) (sferë [0]. Radius); Properties. Settings. Default.y = (dhjetore) bead [0]. Center. Y - (decimal) (bead [0]. Radius); } Properties. Settings. Default.size = 15; Properties. Settings. Default.height = 15; } tjetër {

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

if (qarqet. Gjatesia> 1) {Lista xs = rrathët. Select (c => c. Center. X). ToList (); xs. Sort (); Lista ys = qarqet. Zgjidhni (c => c. Center. Y). ToList (); ys. Sort ();

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

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

nëse (medianX> imazhi. Gjerësia - 15)

mesatarX = imazh. Gjerësia - 15; nëse (mesatarY> imazh. Lartësia - 15) mesatareY = imazh. Lartësia - 15;

imazh. Rrethi (mesatarX, mesatarY, 100, i ri Scalar (0, 0, 150), 3);

nëse (zbulimi) {

Properties. Settings. Default.x = mesatareX - 7; Properties. Settings. Default.y = mesatarY - 7; Properties. Settings. Default.size = 15; Properties. Settings. Default.height = 15; }}}}}

Rect r = Rect ((int) Properties. Settings. Default.x, (int) Properties. Settings. Default.y, (int) Properties. Settings. Default.size, (int) Properties. Settings. Default.height);

Rruaza matSempull = Mat i ri (imazhi, r);

AvgCalor skalar = Cv2. Do të thotë (mostër rruaza); zbuluarColor = Color. FromArgb ((int) avgColor [2], (int) avgColor [1], (int) avgColor [0]);

imazh. Drejtkëndësh (r, Scalar i ri (0, 150, 0));

dritare. ShfaqImage (imazh);

Cv2. WaitKey (1); videoRunning = e vërtetë; }

videoRunning = false;

} }

kamera private e pavlefshmeStartBtn_Click (dërguesi i objektit, EventArgs e) {

nëse (cameraStartBtn. Text == "fillo") {

cvThread = fije e re (fillimi i ri i temës (cvThreadFunction)); runVideo = e vërtetë; cvThread. Start (); cameraStartBtn. Text = "ndal"; ndërsa (! videoRunning) Thread. Fleep (100);

updateColorTimer. Start ();

} tjetër {

runVideo = false; cvThread. Hoin (); cameraStartBtn. Text = "fillo"; }}

Ngjyrë

Tani, ne jemi në gjendje të përcaktojmë ngjyrën e një rruaza, dhe të vendosim bazuar në atë ngjyrë në cilën enë do ta hedhim.

Ky hap mbështetet në krahasimin e ngjyrave. Ne duam të jemi në gjendje të dallojmë ngjyrat për të kufizuar pozitivët e rremë, por gjithashtu të lejojmë prag të mjaftueshëm për të kufizuar negativët e rremë. Krahasimi i ngjyrave është në të vërtetë çuditërisht kompleks, sepse mënyra se si kompjuterët ruajnë ngjyrat si RGB dhe mënyra se si njerëzit i perceptojnë ngjyrat nuk lidhen në mënyrë lineare. Për t'i bërë gjërat më keq, duhet të merret parasysh edhe ngjyra e dritës nën të cilën shihet një ngjyrë.

Ekzistojnë algoritme të ndërlikuara për llogaritjen e ndryshimit të ngjyrave. Ne përdorim CIE2000, i cili nxjerr një numër afër 1 nëse 2 ngjyra do të ishin të padallueshme për një njeri. Ne po përdorim bibliotekën ColorMine C# për të bërë këto llogaritje të komplikuara. Një vlerë DeltaE prej 5 është gjetur të ofrojë një kompromis të mirë midis pozitivit të rremë dhe negativit të rremë.

Meqenëse shpesh ka më shumë ngjyra sesa kontejnerë, pozicioni i fundit është i rezervuar si një kosh katallash. Në përgjithësi i lashë mënjanë këto për të funksionuar edhe pse makina në një kalim të dytë.

Listë

ngjyra = Lista e re (); Lista e ngjyrave Panel = Lista e re (); Ngjyrat e listësTxts = Lista e re (); Lista e ngjyraveCnts = Lista e re ();

const int numColorSpots = 18;

const int unknownColorIndex = 18; int findColorPosition (Ngjyra c) {

Console. WriteLine ("Gjetja e ngjyrës …");

var cRGB = Rgb e re ();

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

int bestMatch = -1;

ndeshje e dyfishteDelta = 100;

për (int i = 0; i <ngjyra. Numërimi; i ++) {

var RGB = Rgb e re ();

RGB. R = ngjyrat . R; RGB. G = ngjyrat . G; RGB. B = ngjyrat . B;

delta e dyfishtë = cRGB. Krahaso (RGB, krahasim i ri CieDe2000 ());

// delta e dyfishtë = deltaE (c, ngjyrat ); Console. WriteLine ("DeltaE (" + i. ToString () + "):" + delta. ToString ()); if (delta <matchDelta) {matchDelta = delta; bestMatch = i; }}

if (matchDelta <5) {Console. WriteLine ("U gjet! (Posn:" + bestMatch + "Delta:" + matchDelta + ")"); kthehu bestMatch; }

if (colors. Count <numColorSpots) {Console. WriteLine ("Ngjyra e re!"); ngjyrat. Shto (c); this. FeginInvoke (Veprimi i ri (setBackColor), objekt i ri {colors. Count - 1}); writeOutColors (); kthimi (ngjyrat. Numri - 1); } else {Console. WriteLine ("Ngjyra e panjohur!"); kthimi i panjohurColorIndex; }}

Renditja e logjikës

Funksioni i renditjes bashkon të gjitha pjesët për të renditur në të vërtetë rruaza. Ky funksion funksionon në një fije të dedikuar; lëvizja e pllakës së sipërme, zbulimi i ngjyrës së rruazës, vendosja e saj në një kosh, sigurimi që pllaka e sipërme të qëndrojë e rreshtuar, numërimi i rruazave, etj. Gjithashtu ndalon së funksionuari kur koshi i mbushjes të mbushet - Përndryshe ne thjesht përfundojmë me rruaza të tejmbushura.

Ngjyra e fijesTestThread; runtest Boolean = false; void colourTest () {

nëse (! lartë. Angazhuar)

lart. Angazhuar = e vërtetë;

nëse (! poshtë. Angazhuar)

fund. Angazhuar = e vërtetë;

ndërsa (më e çmendur) {

nextMagnet (e vërtetë);

Fije. Fjet (100); provo {if (magSensor. SensorValue <(magSensorMax - 4)) alignMotor (); } catch {alignMotor (); }

kamera tjetër (e vërtetë);

zbulimi = i vërtetë;

ndërsa (deteCnt <5) Thread. Fleep (25); Console. WriteLine ("Detect Count:" + deteCnt); zbulimi = i rremë;

Ngjyra c = zbuluarNgjyra;

this. FeginInvoke (Veprim i ri (setColorDet), objekt i ri {c}); int i = findColorPosition (c);

SetBottomPosition (i, e vërtetë);

nextHole (e vërtetë); colorCnts ++; this. FeginInvoke (Veprimi i ri (setColorTxt), objekt i ri {i}); Fije. Gjumi (250);

nëse (colorCnts [unknownColorIndex]> 500) {

lart. Angazhuar = false; fundi. Angazhuar = false; runtest = false; this. FeginInvoke (Veprimi i ri (setGoGreen), null); kthim; }}}

ngjyra e zbrazët privateTestBtn_Click (dërguesi i objektit, EventArgs e) {

if (colourTestThread == null ||! colourTestThread. IsAlive) {colourTestThread = fije e re (e re ThreadStart (colourTest)); runtest = e vërtetë; colourTestThread. Start (); colourTestBtn. Text = "STOP"; colourTestBtn. BackColor = Ngjyra. E kuqe; } else {runtest = false; colourTestBtn. Text = "SHKO"; colourTestBtn. BackColor = Color. Green; }}

Në këtë pikë, ne kemi një program pune. Disa pjesë të kodit u lanë jashtë artikullit, kështu që hidhini një sy burimit për ta ekzekutuar atë në të vërtetë.

Konkursi i Optikës
Konkursi i Optikës

Çmimi i dytë në Konkursin e Optikës

Recommended: