Robotik Boncuk Sıralama: 3 Adım (Şəkillərlə birlikdə)
Robotik Boncuk Sıralama: 3 Adım (Şəkillərlə birlikdə)
Anonim
Image
Image
Robot muncuq çeşidlənməsi
Robot muncuq çeşidlənməsi
Robot muncuq çeşidlənməsi
Robot muncuq çeşidlənməsi
Robot muncuq çeşidlənməsi
Robot muncuq çeşidlənməsi

Bu layihədə Perler boncuklarını rənglərinə görə sıralamaq üçün bir robot quracağıq.

Həmişə bir rəng çeşidləyici robot qurmaq istəmişəm, buna görə də qızım Perler boncuk sənətinə maraq göstərəndə bunu mükəmməl bir fürsət olaraq gördüm.

Perler muncuqları, bir çox boncukları bir pegboardun üzərinə qoyaraq əridilmiş sənət layihələri yaratmaq üçün istifadə olunur və sonra bir dəmir ilə birlikdə əridir. Ümumiyyətlə bu boncukları nəhəng 22.000 boncuklu qarışıq rəngli paketlərdə alırsınız və istədiyiniz rəngi axtarmağa çox vaxt sərf edirsiniz, buna görə sıralamağın sənət səmərəliliyini artıracağını düşündüm.

Phidgets Inc -də işləyirəm, buna görə də bu layihə üçün əsasən Phidgets -dən istifadə etdim - ancaq bu hər hansı bir uyğun avadanlıqdan istifadə etməklə edilə bilər.

Addım 1: Avadanlıq

Bunu əvvəllər qurduğum budur. 100% phidgets.com -un hissələri və evin ətrafında uzanan əşyalarla tikdim.

Fidjet lövhələri, mühərriklər, avadanlıq

  • HUB0000 - VINT Hub Phidget
  • 1108 - Maqnit Sensor
  • 2x STC1001 - 2.5A Stepper Phidget
  • 2x 3324 - 42STH38 NEMA -17 Bipolyar Dişsiz Step
  • 3x 3002 - Phidget Kabeli 60 sm
  • 3403 - USB2.0 4 Portlu Hub
  • 3031 - Dişi Pigtail 5.5x2.1mm
  • 3029 - 2 tel 100 'Bükülmüş Kabel
  • 3604 - 10 mm Ağ LED (Çanta 10 ədəd)
  • 3402 - USB veb kamerası

Digər hissələr

  • 24VDC 2.0A enerji təchizatı
  • Qarajdan ağac və metal tullantıları
  • Fermuar bağları
  • Dibi kəsilmiş plastik qab

Addım 2: Robotun dizaynı

Robot dizayn edin
Robot dizayn edin
Robot dizayn edin
Robot dizayn edin
Robot dizayn edin
Robot dizayn edin

Giriş zibilxanasından tək bir boncuk ala biləcək bir şey dizayn etməliyik, veb kameranın altına yerləşdirməliyik və sonra onu müvafiq zibil qutusuna köçürməliyik.

Boncuk alma

1 -ci hissəni hər biri eyni yerdə qazılmış 2 ədəd yuvarlaq kontrplak ilə etməyə qərar verdim. Alt hissə sabitlənir və üst hissə boncuklarla dolu bir tullantı altında döndərə bilən bir pilləli motora bağlanır. Delik zibil qutusunun altından keçərkən tək bir boncuk alır. Daha sonra veb kameranın altında döndərə bilərəm və sonra alt hissədəki çuxura uyğun gələnə qədər daha da döndərə bilərəm ki, bu nöqtədən keçsin.

Bu şəkildə, sistemin işləyə biləcəyini sınayıram. Altından görünməyən bir pilləli mühərrikə yapışdırılmış üst yuvarlaq kontrplak parçası istisna olmaqla hər şey düzəldilmişdir. Veb kamera hələ quraşdırılmayıb. Bu anda mühərriki işə salmaq üçün yalnız Phidget İdarəetmə Panelindən istifadə edirəm.

Boncuk saxlama

Növbəti hissə, hər bir rəngin saxlanması üçün zibil qutusu sisteminin dizaynıdır. Aşağıdakı ikinci bir pilləli mühərrikdən istifadə edərək, bərabər bölməli bölmələri olan yuvarlaq bir qabı döndərmək qərarına gəldim. Bu, boncuğun düşəcəyi çuxurun altındakı düzgün bölməni döndərmək üçün istifadə edilə bilər.

Bunu karton və yapışqan lentdən istifadə edərək hazırladım. Burada ən vacib şey tutarlılıqdır - hər bir bölmə eyni ölçüdə olmalıdır və hər şey bərabər ağırlıqda olmalıdır ki, atlanmadan fırlansın.

Boncukların çıxarılması sıx bir qapaq vasitəsi ilə həyata keçirilir, bu da bir anda tək bir bölməni açır, beləliklə boncuklar tökülə bilər.

Kamera

Veb kamera, boşqabla alt boşqab çuxur yeri arasındakı üst lövhənin üstünə quraşdırılmışdır. Bu, sistemin boncuğu yerə qoymadan əvvəl ona baxmasına imkan verir. Kameranın altındakı boncukları işıqlandırmaq üçün bir LED istifadə olunur və ardıcıl bir işıqlandırma mühiti təmin etmək üçün ətrafdakı işıq bloklanır. Rənglərin dəqiq aşkarlanması üçün bu çox vacibdir, çünki ətrafdakı işıqlandırma həqiqətən algılanan rəngi ata bilər.

Yer Təyinatı

Sistemin boncuk ayırıcının fırlanmasını aşkar edə bilməsi vacibdir. Bu, işə salınarkən başlanğıc mövqeyini təyin etmək üçün, həm də step motorunun sinxronizasiyadan çıxdığını aşkar etmək üçün istifadə olunur. Sistemimdə, bir muncuq bəzən götürülərkən sıxışır və sistem bu vəziyyəti aşkarlaya və idarə edə bilməli idi - bir az dəstəkləyərək və agianı sınayaraq.

Bunu idarə etməyin bir çox yolu var. 1108 maqnit sensoru istifadə etməyə qərar verdim, üst plakanın kənarına bir maqnit qoyulmuşdu. Bu, hər fırlanma zamanı mövqeyi yoxlamağa imkan verir. Daha yaxşı bir həll, ehtimal ki, step motorunda bir kodlayıcı olardı, amma ətrafımda yatan 1108 var idi, buna görə də istifadə etdim.

Robotu bitirin

Bu nöqtədə hər şey hazırlandı və sınaqdan keçirildi. Hər şeyi gözəl bir şəkildə bağlamağın və yazma proqramına keçməyin vaxtı gəldi.

2 pilləli mühərriklər STC1001 pilləli idarəediciləri tərəfindən idarə olunur. HUB000 - USB VINT hubı, pilləli idarəediciləri işə salmaq, həmçinin maqnit sensoru oxumaq və LED -i idarə etmək üçün istifadə olunur. Veb kamera və HUB0000 hər ikisi də kiçik bir USB mərkəzinə bağlıdır. Mühərrikləri işə salmaq üçün 24V enerji təchizatı ilə birlikdə 3031 pigtail və bəzi tel istifadə olunur.

Addım 3: Kod yazın

Image
Image

Bu layihə üçün C# və Visual Studio 2015 istifadə olunur. Bu səhifənin yuxarısındakı mənbəni yükləyin və izləyin - əsas bölmələr aşağıda verilmişdir

Başlanğıc

Əvvəlcə Phidget obyektlərini yaratmalı, açmalı və işə salmalıyıq. Bu forma yükləmə hadisəsində edilir və Phidget işləyiciləri əlavə edir.

şəxsi boşluq Form1_Load (obyekt göndərən, EventArgs e) {

/ * Fidjetləri işə salın və açın */

top. HubPort = 0; top. Attach += Top_Attach; top. Detach += Top_Detach; top. PositionChange += Top_PositionChange; top. Open ();

alt. HubPort = 1;

alt. Attach += Bottom_Attach; alt. Detach += Bottom_Detach; alt. PositionChange += Bottom_PositionChange; alt. Open ();

magSensor. HubPort = 2;

magSensor. IsHubPortDevice = doğru; magSensor. Attach += MagSensor_Attach; magSensor. Detach += MagSensor_Detach; magSensor. SensorChange += MagSensor_SensorChange; magSensor. Open ();

led. HubPort = 5;

led. IsHubPortDevice = doğru; led. Channel = 0; led. Attach += Led_Attach; led. Detach += Led_Detach; led. Open (); }

xüsusi boşluq Led_Attach (obyekt göndərən, Phidget22. Events. AttachEventArgs e) {

ledAttachedChk. Checked = doğru; led. State = doğru; ledChk. Checked = doğru; }

xüsusi boşluq MagSensor_Attach (obyekt göndərən, Phidget22. Events. AttachEventArgs e) {

magSensorAttachedChk. Checked = doğru; magSensor. SensorType = VoltageRatioSensorType. PN_1108; magSensor. DataInterval = 16; }

şəxsi boşluq Bottom_Attach (obyekt göndərən, Phidget22. Events. AttachEventArgs e) {

bottomAttachedChk. Checked = doğru; bottom. CurrentLimit = bottomCurrentLimit; alt. Engaged = doğru; bottom. VelocityLimit = bottomVelocityLimit; alt. Sürət = bottomAccel; alt. DataInterval = 100; }

şəxsi boşluq Top_Attach (obyekt göndərən, Phidget22. Events. AttachEventArgs e) {

topAttachedChk. Checked = doğru; top. CurrentLimit = topCurrentLimit; top. Engaged = doğru; top. RescaleFactor = -1; top. VelocityLimit = -topVelocityLimit; top. Acceleration = -topAccel; top. DataInterval = 100; }

Başlanğıc zamanı qeyd olunan rəngli məlumatları da oxuyuruq, buna görə də əvvəlki işə davam etmək olar.

Motor Yerləşdirmə

Motor idarəetmə kodu, mühərrikləri hərəkət etdirmək üçün rahatlıq funksiyalarından ibarətdir. İstifadə etdiyim mühərriklər inqilabda 3, 200 1/16 addımdır, buna görə də bunun üçün bir sabit yaratdım.

Üst motor üçün motora göndərə biləcəyimiz 3 mövqe var: veb kamera, çuxur və yerləşdirmə mıknatısı. Bu vəzifələrin hər birinə səyahət etmək üçün bir funksiya var:

şəxsi boşluq nextMagnet (Boolean wait = false) {

double posn = top. Position % stepsPerRev;

top. TargetPosition += (stepsPerRev - posn);

əgər (gözlə)

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

əgər (gözlə)

while (top. IsMoving) Thread. Sleep (50); }

şəxsi boşluq 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);

əgər (gözlə)

while (top. IsMoving) Thread. Sleep (50); }

Qaçışa başlamazdan əvvəl, üst lövhə maqnit sensoru ilə hizalanır. AlignMotor funksiyası istənilən vaxt üst lövhəni hizalamaq üçün çağırıla bilər. Bu funksiya əvvəlcə boşluğu yuxarıda maqnit məlumatlarını görənə qədər lövhəni tezliklə 1 tam inqilaba çevirir. Daha sonra bir az geri çəkilir və yenə yavaş -yavaş irəliləyir, gedərkən sensor məlumatlarını tutur. Nəhayət, bu mövqeyi maksimum maqnit məlumat yerinə qoyur və mövqeyi ofsetini sıfıra sıfırlayır. Beləliklə, maksimum maqnit mövqeyi həmişə olmalıdır (üst. Mövqe % addımlarPerRev)

Mövzu alignMotorThread; Boolean sawMagnet; cüt magSensorMax = 0; şəxsi boşluq alignMotor () {

// Mıknatıs tapın

top. DataInterval = top. MinDataInterval;

sawMagnet = yalan;

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

int tryCount = 0;

yenidən cəhd elə:

top. TargetPosition += stepsPerRev;

while (top. IsMoving &&! sawMagnet) Thread. Sleep (25);

əgər (! sawMagnet) {

if (tryCount> 3) {Console. WriteLine ("Hizala alınmadı"); top. Engaged = yalan; alt. Engaged = yalan; runtest = yalan; qayıtmaq; }

tryCount ++;

Console. WriteLine ("Biz sıxışmışıqmı? Yedəkləməyə çalışırıq …"); top. TargetPosition -= 600; while (top. IsMoving) Thread. Sleep (100);

yenidən cəhd etməliyəm;

}

top. VelocityLimit = -100;

magData = yeni siyahı> (); magSensor. SensorChange += magSensorCollectPositionData; top. TargetPosition += 300; while (top. IsMoving) Thread. Sleep (100);

magSensor. SensorChange -= magSensorCollectPositionData;

top. VelocityLimit = -topVelocityLimit;

KeyValuePair max = magData [0];

foreach (magData -da KeyValuePair cütü) if (pair. Value> max. Value) max = pair;

top. AddPositionOffset (-max. Key);

magSensorMax = maksimum dəyər;

top. TargetPosition = 0;

while (top. IsMoving) Thread. Sleep (100);

Console. WriteLine ("Hizala müvəffəq oldu");

}

Siyahı> magData;

xüsusi boş magSensorCollectPositionData (obyekt göndərən, Phidget22. Events. VoltageRatioInputSensorChangeEventArgs e) {magData. Add (yeni KeyValuePair (top. Position, e. SensorValue)); }

xüsusi boş magSensorStopMotor (obyekt göndərən, Phidget22. Events. VoltageRatioInputSensorChangeEventArgs e) {

if (top. IsMoving &&. SensorValue> 5) {top. TargetPosition = top. Position - 300; magSensor. SensorChange -= magSensorStopMotor; sawMagnet = doğru; }}

Nəhayət, alt motor, boncuk konteyner mövqelərindən birinə göndərilərək idarə olunur. Bu layihə üçün 19 mövqeyimiz var. Alqoritm ən qısa yolu seçir və ya saat əqrəbi istiqamətində və ya saat yönünün əksinə çevrilir.

şəxsi int BottomPosition {get {int posn = (int) bottom. Position % stepsPerRev; əgər (posn <0) posn += stepsPerRev;

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

} }

şəxsi boşluq SetBottomPosition (int posn, bool wait = false) {

posn = posn % beadCompartments; ikiqat targetPosn = (posn * stepsPerRev) / beadCompartments;

ikiqat cərəyanPosn = aşağı. Mövqe % addımlarPerRev;

ikiqat posnDiff = targetPosn - currentPosn;

// Tam addımlar kimi saxlayın

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

əgər (posnDiff <= 1600) alt. TargetPosition += posnDiff; else bottom. TargetPosition - = (stepsPerRev - posnDiff);

əgər (gözlə)

while (bottom. IsMoving) Thread. Sleep (50); }

Kamera

OpenCV veb kameradan görüntüləri oxumaq üçün istifadə olunur. Kamera ipliyi əsas çeşidləmə ipinə başlamazdan əvvəl başlayır. Bu mövzu davamlı olaraq şəkillərdə oxuyur, Mean istifadə edərək müəyyən bir bölgə üçün ortalama bir rəng hesablayır və qlobal bir rəng dəyişənini yeniləyir. Mövzu, rəng tanıma üçün baxdığı sahəni təmizləmək üçün bir boncuk və ya üst boşqabdakı çuxuru aşkar etməyə çalışmaq üçün HoughCircles istifadə edir. Eşik və HoughCircles nömrələri sınaq və yanılma yolu ilə müəyyən edildi və veb kameradan, işıqlandırmadan və boşluqdan çox asılıdır.

bool runVideo = doğru; bool videoRunning = yalan; Video çəkmək; Mövzu cvThread; Rəng aşkar edildiRəng; Boolean aşkarlama = yalan; int detectCnt = 0;

şəxsi boşluq cvThreadFunction () {

videoRunning = yalan;

ələ keçirmə = yeni VideoCapture (seçilmişCamera);

istifadə edərək (Pəncərə pəncərəsi = yeni Pəncərə ("çəkmə")) {

Mat şəkli = yeni Mat (); Mat image2 = yeni Mat (); while (runVideo) {capture. Read (image); if (image. Empty ()) break;

əgər (aşkarlama)

detectCnt ++; başqa detectCnt = 0;

if (aşkar | | dairəDetectChecked || showDetectionImgChecked) {

Cv2. CvtColor (şəkil, görüntü2, ColorConversionCodes. BGR2GRAY); Mat eşikləri = image2. Threshold ((double) Properties. Settings. Default.videoThresh, 255, ThresholdTypes. Binary); thres = thres. GaussianBlur (yeni OpenCvSharp. Size (9, 9), 10);

əgər (showDetectionImgChecked)

şəkil = thres;

if (aşkar | | dairəDetectChecked) {

CircleSegment bead = thres. HoughCircles (HoughMethods. Gradient, 2, /*thres. Rows/4*/ 20, 200, 100, 20, 65); if (boncuk. Length> = 1) {image. Circle (boncuk [0]. Mərkəz, 3, yeni Skaler (0, 100, 0), -1); image. Circle (boncuk [0]. Mərkəz, (int) boncuk [0]. Radius, yeni Skaler (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 = (decimal) boncuk [0]. Center. Y - (decimal) (bead [0]. Radius / 2); } başqa {Properties. Settings. Default.x = (ondalık) boncuk [0]. Center. X + (decimal) (boncuk [0]. Radius); Properties. Settings. Default.y = (decimal) boncuk [0]. Center. Y - (decimal) (bead [0]. Radius); } Properties. Settings. Default.size = 15; Xüsusiyyətlər Ayarlar. Default.height = 15; } başqa {

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

əgər (dairələr. Uzunluq> 1) {Siyahı xs = dairələr. Select (c => c. Center. X). ToList (); xs. Sort (); Ys = dairələri siyahıya alın (c => c. Center. Y). ToList () seçin; ys. Sort ();

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

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

əgər (medianX> şəkil. Genişlik - 15)

medianX = şəkil. Genişlik - 15; əgər (medianY> image. Height - 15) medyanY = image. Height - 15;

image. Circle (medianX, medyanY, 100, yeni Skaler (0, 0, 150), 3);

əgər (aşkarlanır) {

Properties. Settings. Default.x = medianX - 7; Properties. Settings. Default.y = medianY - 7; Properties. Settings. Default.size = 15; Xüsusiyyətlər Ayarlar. Default.height = 15; }}}}}

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

Mat beadSample = yeni Mat (şəkil, r);

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

image. Doğrubucaq (r, yeni Skalar (0, 150, 0));

window. ShowImage (şəkil);

Cv2. WaitKey (1); videoRunning = doğru; }

videoRunning = yalan;

} }

şəxsi boş kameraStartBtn_Click (obyekt göndərən, EventArgs e) {

əgər (cameraStartBtn. Text == "başla") {

cvThread = yeni mövzu (yeni ThreadStart (cvThreadFunction)); runVideo = doğru; cvThread. Start (); cameraStartBtn. Text = "dayan"; while (! videoRunning) Thread. Sleep (100);

updateColorTimer. Start ();

} başqa {

runVideo = yalan; cvThread. Join (); cameraStartBtn. Text = "başla"; }}

Rəng

İndi bir boncukun rəngini təyin edə bilərik və onu hansı konteynerə atacağımıza əsaslanaraq qərar verə bilərik.

Bu addım rəng müqayisəsinə əsaslanır. Rəngləri yalan pozitivliyi məhdudlaşdırmaq üçün ayırmaq, həm də yalan neqativləri məhdudlaşdırmaq üçün kifayət qədər həddə icazə vermək istəyirik. Rənglərin müqayisəsi əslində təəccüblü dərəcədə mürəkkəbdir, çünki kompüterlərin rəngləri RGB olaraq saxlamaları və insanların rəngləri qəbul etmələri xətti olaraq əlaqələndirilmir. İşi daha da pisləşdirmək üçün, bir rəng altında göründüyü işığın rəngi də nəzərə alınmalıdır.

Rəng fərqini hesablamaq üçün mürəkkəb bir alqoritm var. CIE2000 istifadə edirik, əgər 2 rəng insan üçün fərqlənməyəcəksə, 1 -ə yaxın bir rəqəm çıxarır. Bu mürəkkəb hesablamaları etmək üçün ColorMine C# kitabxanasından istifadə edirik. DeltaE 5 dəyərinin yanlış pozitiv və yalan mənfi arasında yaxşı bir kompromis təqdim etdiyi aşkar edilmişdir.

Konteynerlərdən daha tez -tez daha çox rəng olduğu üçün son mövqe tutma qutusu olaraq qorunur. Ümumiyyətlə, maşınları ikinci bir keçiddə işə salmaq üçün bir kənara qoydum.

Siyahı

rənglər = yeni Siyahı (); Siyahı colorPanels = Yeni Siyahı (); Siyahı rəngləriTxts = yeni Siyahı (); Siyahı colorCnts = yeni Siyahı ();

const int numColorSpots = 18;

const int unknownColorIndex = 18; int findColorPosition (Rəng c) {

Console. WriteLine ("Rəng tapılır …");

var cRGB = yeni Rgb ();

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

int bestMatch = -1;

cüt matçDelta = 100;

for (int i = 0; i <colors. Count; i ++) {

var RGB = yeni Rgb ();

RGB. R = rənglər . R; RGB. G = rənglər . G; RGB. B = rənglər . B;

cüt delta = cRGB. Compare (RGB, yeni CieDe2000Comparison ());

// ikiqat delta = deltaE (c, rənglər ); Console. WriteLine ("DeltaE (" + i. ToString () + "):" + delta. ToString ()); if (delta <matchDelta) {matchDelta = delta; bestMatch = i; }}

if (matchDelta <5) {Console. WriteLine ("Tapıldı! (Posn:" + bestMatch + "Delta:" + matchDelta + ")"); bestMatch qayıt; }

if (colors. Count <numColorSpots) {Console. WriteLine ("Yeni Rəng!"); rənglər (c) əlavə edin; this. BeginInvoke (yeni Action (setBackColor), yeni obyekt {colors. Count - 1}); writeOutColors (); qayıt (rənglərin sayı - 1); } başqa {Console. WriteLine ("Naməlum Rəng!"); unknownColorIndex qaytar; }}

Sıralama məntiqi

Sıralama funksiyası, boncukları sıralamaq üçün bütün parçaları bir araya gətirir. Bu funksiya xüsusi bir mövzuda işləyir; üst lövhəni hərəkət etdirmək, boncuk rəngini aşkar etmək, çöp qutusuna yerləşdirmək, üst plakanın hizalı qalmasını təmin etmək, boncukları saymaq və s. Tutma qutusu dolduqda da işləməyi dayandırır - Əks halda, daşan muncuqlarla sona çatırıq.

Mövzu colourTestThread; Boolean runtest = false; void colourTest () {

əgər (! yuxarı. Nişanlı)

top. Engaged = doğru;

əgər (! alt. Nişanlı)

alt. Engaged = doğru;

while (runtest) {

nextMagnet (doğru);

Mövzu Yuxu (100); cəhd edin {if (magSensor. SensorValue <(magSensorMax - 4)) alignMotor (); } tut {alignMotor (); }

nextCamera (doğru);

aşkar = doğru;

while (detectCnt <5) Thread. Sleep (25); Console. WriteLine ("Detect Count:" + detectCnt); aşkar = yalan;

Rəng c = aşkarRəng;

this. BeginInvoke (yeni Action (setColorDet), yeni obyekt {c}); int i = findColorPosition (c);

SetBottomPosition (i, doğru);

nextHole (doğru); colorCnts ++; this. BeginInvoke (yeni Fəaliyyət (setColorTxt), yeni obyekt {i}); Mövzu Yuxu (250);

əgər (colorCnts [unknownColorIndex]> 500) {

top. Engaged = yalan; alt. Engaged = yalan; runtest = yalan; this. BeginInvoke (yeni Action (setGoGreen), null); qayıtmaq; }}}

şəxsi boşluq colourTestBtn_Click (obyekt göndərən, EventArgs e) {

if (colourTestThread == null ||! colourTestThread. IsAlive) {colourTestThread = new Thread (new ThreadStart (colourTest)); runtest = doğru; colourTestThread. Start (); colourTestBtn. Text = "DUR"; colourTestBtn. BackColor = Color. Red; } başqa {runtest = false; colourTestBtn. Text = "GO"; colourTestBtn. BackColor = Color. Green; }}

Bu nöqtədə bir iş proqramımız var. Bəzi kod bitləri məqalədən kənarda qaldı, buna görə də onu işə salmaq üçün mənbəyə baxın.

Optika Yarışması
Optika Yarışması

Optika Yarışmasında İkinci Mükafat

Tövsiyə: