Мазмұны:

Робот моншақты сұрыптау: 3 қадам (суреттермен)
Робот моншақты сұрыптау: 3 қадам (суреттермен)

Бейне: Робот моншақты сұрыптау: 3 қадам (суреттермен)

Бейне: Робот моншақты сұрыптау: 3 қадам (суреттермен)
Бейне: Балаларға шабуыл жасаған үйдегі жын. #тылсымдүние #қорқыныштыоқиғалар #қорқыныштывидеолар 2024, Шілде
Anonim
Image
Image
Робот моншақтарды сұрыптау
Робот моншақтарды сұрыптау
Робот моншақтарды сұрыптау
Робот моншақтарды сұрыптау
Робот моншақтарды сұрыптау
Робот моншақтарды сұрыптау

Бұл жобада біз Perler моншақтарын түсі бойынша сұрыптайтын робот құрастыратын боламыз.

Мен әрқашан түсті сұрыптайтын робот жасағым келеді, сондықтан менің қызым Перлер бисерін жасауға қызығушылық танытқанда, мен мұны керемет мүмкіндік ретінде көрдім.

Перлер бисері көптеген моншақтарды тақтаға қойып, содан кейін оларды темірмен бірге еріту арқылы біріктірілген өнер жобаларын жасау үшін қолданылады. Сіз әдетте бұл бисерді 22 000 моншақтан тұратын түрлі -түсті пакеттерден сатып аласыз және өзіңіз қалаған түсті іздеуге көп уақыт жұмсайсыз, сондықтан оларды сұрыптау өнердің тиімділігін арттырады деп ойладым.

Мен Phidgets Inc -те жұмыс істеймін, сондықтан мен бұл жобада көбінесе Phidgets қолдандым, бірақ мұны кез келген сәйкес жабдықты қолдану арқылы жасауға болады.

1 -қадам: Жабдық

Міне, мен мұны бұрын салдым. Мен оны 100% phidgets.com сайтынан және үйдің айналасында жатқан заттармен салдым.

Фиджеттер тақтасы, қозғалтқыштар, аппараттық құралдар

  • HUB0000 - VINT Hub фиджеті
  • 1108 - магниттік сенсор
  • 2x STC1001 - 2.5A Stepper Phidget
  • 2x 3324 - 42STH38 NEMA -17 биполярлы беріліссіз қадам
  • 3x 3002 - Phidget кабелі 60 см
  • 3403 - USB2.0 4 -порттық хаб
  • 3031 - аналық шошқа құйрығы 5,5х2,1 мм
  • 3029 - 2 сымды 100 'бұралған кабель
  • 3604 - 10 мм ақ жарық диодты (10 сөмке)
  • 3402 - USB веб -камерасы

Басқа бөліктер

  • 24VDC 2.0A қуат көзі
  • Гараждан ағаш пен металл қалдықтарын шығарыңыз
  • Зип -галстук
  • Пластикалық контейнер түбімен кесілген

2 -қадам: роботты құрастырыңыз

Роботты жобалау
Роботты жобалау
Роботты жобалау
Роботты жобалау
Роботты жобалау
Роботты жобалау

Біз кірістіру бункерінен бір моншақты алатын нәрсені жобалауымыз керек, оны веб -камераның астына қойып, содан кейін оны тиісті қоқыс жәшігіне жылжытыңыз.

Бисерді алу

Мен 1 -ші бөлікті әрқайсысы бір жерде бұрғыланған дөңгелек фанераның 2 бөлігінен жасауды шештім. Төменгі бөлік бекітілген, ал жоғарғы бөлігі моншақтармен толтырылған бункердің астына айналдыра алатын қадамдық қозғалтқышқа бекітілген. Шұңқыр бункердің астында жүргенде, ол бір моншақты алады. Мен оны веб -камераның астында айналдыра аламын, содан кейін ол төменгі бөлігіндегі тесікке сәйкес келгенше айналдыра аламын, сол кезде ол құлап кетеді.

Бұл суретте мен жүйенің жұмыс істей алатынын тексеремін. Барлығы бекітілген, фанераның үстіңгі дөңгелек бөлігінен басқа, ол астындағы моторға бекітілмеген. Веб -камера әлі орнатылмаған. Мен дәл осы сәтте қозғалтқышты қосу үшін Phidget басқару тақтасын қолданамын.

Бисерді сақтау

Келесі бөлім - әр түсті ұстауға арналған қоқыс жәшігінің жүйесін жасау. Мен біркелкі бөлінген бөліктері бар дөңгелек контейнерді қолдау және айналдыру үшін төмендегі екінші сатылы қозғалтқышты қолдануды шештім. Бұл бисер түсетін тесік астындағы дұрыс бөлікті айналдыру үшін қолданыла алады.

Мен мұны картон мен скотч арқылы жасадым. Бұл жерде ең бастысы - бірізділік - әр бөлік бірдей мөлшерде болуы керек, ал барлық зат біркелкі өлшенуі керек, сондықтан ол айналып өтеді.

Моншақтарды алу бір мезгілде бір бөлікті ашатын тығыз қақпақ арқылы жүзеге асады, сондықтан моншақтарды төгуге болады.

Камера

Веб -камера үстіңгі тақтайшаның үстіне бункер мен пластинаның төменгі саңылауының арасында орнатылады. Бұл жүйеге моншақты тастамас бұрын қарауға мүмкіндік береді. Жарық диодты камераның астындағы моншақтарды жарықтандыру үшін пайдаланылады және жарықтандырудың біркелкі ортасын қамтамасыз ету үшін сыртқы жарық бұғатталады. Түсті дәл анықтау үшін бұл өте маңызды, өйткені қоршаған жарық шынымен қабылданған түсті түсіруі мүмкін.

Орынды анықтау

Жүйенің моншақты бөлгіштің айналуын анықтай алуы маңызды. Бұл іске қосу кезінде бастапқы позицияны орнату үшін, сонымен қатар қадамдық қозғалтқыштың синхрондаудан шыққанын анықтау үшін қолданылады. Менің жүйемде, моншақ кейде көтеріліп қалады, ал жүйе бұл жағдайды анықтап, оны шеше алуы үшін қажет - аздап сақтық көшірмесін жасап, агианы қолдана отырып.

Мұны шешудің көптеген жолдары бар. Мен 1108 магниттік сенсорды қолдануды шештім, оның үстінде магнит жоғарғы пластинаның шетіне салынған. Бұл маған әр айналымдағы позицияны тексеруге мүмкіндік береді. Жақсы шешім қадамдық мотордағы кодер болар еді, бірақ менде 1108 болды, сондықтан мен оны қолдандым.

Роботты аяқтаңыз

Бұл кезде бәрі ойластырылды, тексерілді. Барлығын жақсы орнатып, бағдарламалық жасақтамаға көшетін уақыт келді.

2 қадамдық қозғалтқыштар STC1001 қадам реттегіштерімен басқарылады. HUB000 - USB VINT хаб қадамдық реттегіштерді іске қосуға, сондай -ақ магниттік сенсорды оқуға және жарықдиодты жүргізуге арналған. Веб -камера мен HUB0000 екеуі де шағын USB хабына бекітілген. Қозғалтқыштарды қуаттандыру үшін 24 Вт қуат көзімен бірге 3031 шоқша мен кейбір сымдар қолданылады.

3 -қадам: кодты жазыңыз

Image
Image

Бұл жоба үшін C# және Visual Studio 2015 қолданылады. Бұл беттің жоғарғы жағындағы дереккөзді жүктеп алыңыз және оны жалғастырыңыз - негізгі бөлімдер төменде көрсетілген

Инициализация

Біріншіден, біз Phidget объектілерін құруымыз, ашуымыз және инициализациялауымыз керек. Бұл пішін жүктеу оқиғасында жасалады және Phidget тіркеуші өңдейді.

жеке жарамсыздық Form1_Load (объект жіберуші, EventArgs e) {

/ * Фиджеттерді инициализациялау және ашу */

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

төменгі. HubPort = 1;

төменгі. Attach += Bottom_Attach; төменгі. Detach += Bottom_Detach; төменгі. 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 e) {

ledAttachedChk. Checked = true; led. State = true; ledChk. Checked = true; }

MagSensor_Attach жеке жарамсыздығы (объект жіберуші, Phidget22. Events. AttachEventArgs e) {

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

private void Bottom_Attach (объект жіберуші, Phidget22. Events. AttachEventArgs e) {

bottomAttachedChk. Checked = true; bottom. CurrentLimit = bottomCurrentLimit; bottom. Engaged = true; bottom. VelocityLimit = bottomVelocityLimit; төменгі. Жеделдету = bottomAccel; төменгі. DataInterval = 100; }

жеке жарамсыздық Top_Attach (объект жіберуші, Phidget22. Events. AttachEventArgs e) {

topAttachedChk. Checked = true; top. CurrentLimit = topCurrentLimit; top. Engaged = true; top. RescaleFactor = -1; top. VelocityLimit = -topVelocityLimit; top. Akseleration = -topAccel; top. DataInterval = 100; }

Біз инициализация кезінде кез келген сақталған түсті ақпаратты оқимыз, сондықтан алдыңғы жүгіруді жалғастыруға болады.

Мотордың орналасуы

Қозғалтқышты басқару коды қозғалтқыштарды жылжытуға арналған ыңғайлылық функцияларынан тұрады. Мен қолданған қозғалтқыштар бір революцияға 3 200 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); }

private void 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 -ге ауыстырады. Осылайша, магниттің максималды орналасуы әрқашан (үстіңгі позицияда % StepPerRev) болуы керек.

Жіптерді туралауMotorThread; Бульдік аралауМагнит; қос magSensorMax = 0; жеке 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 («Туралау сәтсіз болды»); top. Engaged = жалған; төменгі. Engaged = жалған; 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 = magData [0];

foreach (magData ішіндегі KeyValuePair жұбы) if (pair. Value> max. Value) max = жұп;

top. AddPositionOffset (-max. Key);

magSensorMax = максималды мән;

top. TargetPosition = 0;

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

Console. WriteLine («Туралау сәтті болды»);

}

Тізім> magData;

жеке жарамсыз magSensorCollectPositionData (объект жіберуші, Phidget22. Events. VoltageRatioInputSensorChangeEventArgs e) {magData. Add (жаңа KeyValuePair (top. Position, e. SensorValue))); }

magSensorStopMotor жеке жарамсыздығы (объект жіберуші, Phidget22. Events. VoltageRatioInputSensorChangeEventArgs e) {

if (top. IsMoving &&. SensorValue> 5) {top. TargetPosition = top. Position - 300; magSensor. SensorChange -= magSensorStopMotor; sawMagnet = ақиқат; }}

Ақырында, төменгі қозғалтқышты моншақ контейнерінің позициясының біріне жіберу арқылы басқарады. Бұл жоба бойынша бізде 19 позиция бар. Алгоритм ең қысқа жолды таңдайды және сағат тілімен немесе сағат тіліне қарсы бұрылады.

private int BottomPosition {get {int posn = (int) bottom. Position % stepsPerRev; егер (posn <0) posn += stepsPerRev;

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

} }

SetBottomPosition жеке жарамсыздығы (int posn, bool wait = false) {

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

double currentPosn = төменгі. Позиция % stepsPerRev;

қосарланған posnDiff = targetPosn - currentPosn;

// Толық қадам ретінде сақтаңыз

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

if (posnDiff <= 1600) bottom. TargetPosition += posnDiff; else bottom. TargetPosition - = (stepsPerRev - posnDiff);

егер (күтіңіз)

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

Камера

OpenCV веб -камерадан суреттерді оқу үшін қолданылады. Камера ағыны негізгі сұрыптау жіпін бастамас бұрын іске қосылады. Бұл жіп суреттерде үнемі оқиды, Орта арқылы белгілі бір аймақтың орташа түсін есептейді және жаһандық түс айнымалысын жаңартады. Жіп сонымен қатар моншақты немесе үстіңгі тақтайдағы саңылауды анықтауға тырысады, ол түс анықтау үшін қарайтын аймақты тазартады. Шекті және HoughCircles сандары сынақ пен қателік арқылы анықталды және олар веб -камераға, жарықтандыруға және интервалға байланысты.

bool runVideo = true; bool videoRunning = false; VideoCapture түсіру; Thread cvThread; Түс анықталды Түс; Логикалық анықтау = жалған; int detectCnt = 0;

private void cvThreadFunction () {

videoRunning = жалған;

түсіру = жаңа VideoCapture (таңдалған Камера);

пайдалану (Терезе терезесі = жаңа терезе («түсіру»)) {

Mat суреті = жаңа Mat (); Mat image2 = жаңа Mat (); while (runVideo) {capture. Read (сурет); if (image. Empty ()) break;

егер (анықтау)

detectCnt ++; else detectCnt = 0;

if (анықтау || шеңберDetectChecked || 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);

if (showDetectionImgChecked)

сурет = үштік;

if (анықтау || шеңберDetectChecked) {

CircleSegment бисер = thres. HoughCircles (HoughMethods. Gradient, 2, /*thres. Rows/4*/ 20, 200, 100, 20, 65); if (бисер. Ұзындығы> = 1) {image. Circle (бисер [0]. Орталығы, 3, жаңа Скаляр (0, 100, 0), -1); сурет. Шеңбер (бисер [0]. Орталығы, (int) бисер [0]. Радиус, жаңа скаляр (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]. Радиус / 2); } else {Properties. Settings. Default.x = (ондық) бисер [0]. Center. X + (ондық) (бисер [0]. Радиус); Properties. Settings. Default.y = (ондық) бисер [0]. Center. Y - (ондық) (бисер [0]. Радиус); } Properties. Settings. Default.size = 15; Properties. Settings. Default.height = 15; } басқа {

CircleSegment шеңберлері = thres. HoughCircles (HoughMethods. Gradient, 2, /*thres. Rows/4*/ 5, 200, 100, 60, 180);

if (шеңберлер. Ұзындығы> 1) {Тізім xs = шеңберлер. таңдаңыз (c => c. Center. X). ToList (); xs. Sort (); Ys = шеңберлер тізімін таңдаңыз. (C => c. Center. Y). ToList () таңдаңыз; ys. Sort ();

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

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

егер (medianX> сурет. Ені - 15)

medianX = сурет. Ені - 15; if (medianY> image. Height - 15) medianY = image. Height - 15;

сурет. Шеңбер (medianX, medianY, 100, жаңа скаляр (0, 0, 150), 3);

егер (анықтау) {

Properties. Settings. Default.x = medianX - 7; Properties. Settings. Default.y = medianY - 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);

Mat beadSample = жаңа мат (сурет, r);

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

сурет. Тік төртбұрыш (r, жаңа скаляр (0, 150, 0));

window. ShowImage (сурет);

Cv2. WaitKey (1); videoRunning = ақиқат; }

videoRunning = жалған;

} }

жеке бос камераStartBtn_Click (объект жіберуші, EventArgs e) {

егер (cameraStartBtn. Text == «бастау») {

cvThread = жаңа ағын (жаңа ThreadStart (cvThreadFunction)); runVideo = ақиқат; cvThread. Start (); cameraStartBtn. Text = «тоқтату»; while (! videoRunning) Thread. Sleep (100);

updateColorTimer. Start ();

} басқа {

runVideo = жалған; cvThread. Join (); cameraStartBtn. Text = «бастау»; }}

Түс

Енді біз бисердің түсін анықтай аламыз және оны сол түске қарай қай контейнерге тастау керектігін шеше аламыз.

Бұл қадам түстерді салыстыруға негізделген. Біз түстерді жалған позитивті шектеу үшін ажырата білгіміз келеді, сонымен қатар жалған негативтерді шектеуге жеткілікті шекті рұқсат береміз. Түстерді салыстыру шын мәнінде таңқаларлық күрделі, себебі компьютерлердің түстерді RGB ретінде сақтауы мен адамдардың түстерді қабылдауы сызықтық байланыспайды. Жағдайды нашарлату үшін астында қаралатын жарықтың түсі де ескерілуі керек.

Түс айырмашылығын есептеудің күрделі алгоритмі бар. Біз CIE2000 қолданамыз, егер ол 2 түс адам үшін ажыратылмайтын болса, 1 -ге жақын сан шығарады. Біз бұл күрделі есептеулерді орындау үшін ColorMine C# кітапханасын қолданамыз. DeltaE 5 мәні жалған оң мен жалған теріс арасында жақсы ымыраға келуге мүмкіндік беретіні анықталды.

Контейнерлерден гөрі түстер жиі болатындықтан, соңғы орын жинауға арналған қоқыс жәшігі ретінде сақталады. Мен бұны машинаның екінші өту кезінде жұмыс істеуі үшін бөлдім.

Тізім

түстер = жаңа тізім (); тізім colorPanels = жаңа тізім (); Түс тізіміTxts = new List (); ColorCnts тізімі = жаңа тізім ();

const int numColorSpots = 18;

const int unknownColorIndex = 18; int findColorPosition (Түс c) {

Console. WriteLine («Түс табылуда …»);

var cRGB = жаңа Rgb ();

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

int bestMatch = -1;

қос сәйкестікDelta = 100;

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

var RGB = жаңа Rgb ();

RGB. R = түстер . R; RGB. G = түстер . G; RGB. B = түстер . B;

қос дельта = cRGB. Compare (RGB, жаңа CieDe2000Comparison ());

// қос дельта = deltaE (c, түстер ); Console. WriteLine («DeltaE (» + i. ToString () + «):» + delta. ToString ()); if (delta <matchDelta) {matchDelta = delta; bestMatch = i; }}

if (matchDelta <5) {Console. WriteLine («Табылды! (Posn:» + bestMatch + «Delta:» + matchDelta + «)»); bestMatch қайтару; }

if (colors. Count <numColorSpots) {Console. WriteLine («Жаңа түс!»); түстер Қосу (с); this. BeginInvoke (new Action (setBackColor), жаңа нысан {colors. Count - 1}); writeOutColors (); қайтару (түстер саны: 1); } else {Console. WriteLine («Белгісіз түс!»); unknownColorIndex қайтару; }}

Логика бойынша сұрыптау

Сұрыптау функциясы моншақтарды іс жүзінде сұрыптау үшін барлық бөліктерді біріктіреді. Бұл функция арнайы тізбекте орындалады; үстіңгі тақтаны жылжыту, моншақтың түсін анықтау, оны қоқыс жәшігіне салу, үстіңгі тақтайдың туралануын қадағалау, бисерді санау және т.б. Қоқыс жәшігі толған кезде ол да жұмысын тоқтатады - Әйтпесе біз моншақ толып кетеміз.

Thread colourTestThread; логикалық жүгіру сынағы = жалған; жарамсыз colourTest () {

егер (! жоғары. Ашулы)

top. Engaged = true;

егер (! төменгі. Ашулы)

bottom. Engaged = true;

while (жүгіру сынағы) {

nextMagnet (шын);

Thread. Sleep (100); көріңіз {if (magSensor. SensorValue <(magSensorMax - 4)) alignMotor (); } ұстау {alignMotor (); }

nextCamera (шын);

анықтау = ақиқат;

while (detectCnt <5) Thread. Sleep (25); Console. WriteLine («Detect Count:» + detectCnt); анықтау = жалған;

Түс c = foundColor;

this. BeginInvoke (жаңа әрекет (setColorDet), жаңа нысан {c}); int i = findColorPosition (c);

SetBottomPosition (i, true);

nextHole (шын); colorCnts ++; this. BeginInvoke (жаңа әрекет (setColorTxt), жаңа нысан {i}); Thread. Sleep (250);

егер (colorCnts [unknownColorIndex]> 500) {

top. Engaged = жалған; төменгі. Engaged = жалған; runtest = жалған; this. BeginInvoke (жаңа әрекет (setGoGreen), нөл); қайтару; }}}

жеке жарамсыздық colourTestBtn_Click (объект жіберуші, EventArgs e) {

if (colourTestThread == null ||! colourTestThread. IsAlive) {colourTestThread = new Thread (new ThreadStart (colourTest)); runtest = ақиқат; colourTestThread. Start (); colourTestBtn. Text = «ТОҚТА»; colourTestBtn. BackColor = Color. Red; } else {runtest = false; colourTestBtn. Text = «GO»; colourTestBtn. BackColor = Color. Green; }}

Бұл кезде бізде жұмыс бағдарламасы бар. Кейбір кодтар мақаладан тыс қалды, сондықтан оны іске қосу үшін дереккөзге қараңыз.

Оптика сайысы
Оптика сайысы

Оптика байқауының екінші жүлдесі

Ұсынылған: