جدول المحتويات:

فرز الخرزة الروبوتية: 3 خطوات (بالصور)
فرز الخرزة الروبوتية: 3 خطوات (بالصور)

فيديو: فرز الخرزة الروبوتية: 3 خطوات (بالصور)

فيديو: فرز الخرزة الروبوتية: 3 خطوات (بالصور)
فيديو: 💍استمتعواسترخي أثناء فرز الخرز في صناديق💍NOOB vs PRO - Bead Sort 2024, يوليو
Anonim
Image
Image
فرز الخرزة الروبوتية
فرز الخرزة الروبوتية
فرز الخرزة الروبوتية
فرز الخرزة الروبوتية
فرز الخرزة الروبوتية
فرز الخرزة الروبوتية

في هذا المشروع ، سنقوم ببناء روبوت لفرز خرز Perler حسب اللون.

كنت أرغب دائمًا في بناء روبوت لفرز الألوان ، لذلك عندما اهتمت ابنتي بصياغة خرزة بيرلر ، رأيت هذا كفرصة مثالية.

تُستخدم حبات Perler لإنشاء مشاريع فنية مدمجة عن طريق وضع العديد من الخرزات على لوح مشابك ، ثم صهرها معًا بمكواة. عادة ما تشتري هذه الخرزات في عبوات ألوان مختلطة 22000 خرزة عملاقة ، وتقضي الكثير من الوقت في البحث عن اللون الذي تريده ، لذلك اعتقدت أن فرزها سيزيد من كفاءة الفن.

أنا أعمل في شركة Phidgets Inc. لذلك استخدمت في الغالب Phidgets لهذا المشروع - ولكن يمكن القيام بذلك باستخدام أي جهاز مناسب.

الخطوة 1: الأجهزة

إليكم ما اعتدت على بناء هذا. لقد قمت ببنائه بنسبة 100٪ بأجزاء من موقع phidgets.com ، وأشياء كنت أمتلكها حول المنزل.

لوحات ، محركات ، هاردوير Phidgets

  • HUB0000 - VINT Hub Phidget
  • 1108 - مستشعر مغناطيسي
  • عدد 2 STC1001 - 2.5A Stepper Phidget
  • 2x 3324 - 42STH38 NEMA-17 ثنائي القطب جيرليس ستيبر
  • 3x 3002 - كابل فيدجيت 60 سم
  • 3403 - محور USB2.0 4 منافذ
  • 3031 - أنثى ضفيرة 5.5x2.1 ملم
  • 3029 - 2 سلك 100 'كبل مجدول
  • 3604-10 مم أبيض LED (كيس من 10)
  • 3402 - كاميرا ويب USB

الأجزاء الأخرى

  • 24VDC 2.0A امدادات الطاقة
  • خردة الخشب والمعدن من المرآب
  • العلاقات البريدية
  • وعاء بلاستيك بجزء سفلي مقطوع

الخطوة الثانية: تصميم الروبوت

صمم الروبوت
صمم الروبوت
صمم الروبوت
صمم الروبوت
صمم الروبوت
صمم الروبوت

نحتاج إلى تصميم شيء يمكنه أخذ حبة واحدة من قادوس الإدخال ، ووضعها تحت كاميرا الويب ، ثم نقلها إلى الحاوية المناسبة.

حبة بيك اب

قررت أن أقوم بالجزء الأول بقطعتين من الخشب الرقائقي المستدير ، كل منها به ثقب محفور في نفس المكان. تم إصلاح القطعة السفلية ، والقطعة العلوية متصلة بمحرك متدرج ، والذي يمكنه تدويرها أسفل قادوس مليء بالخرز. عندما ينتقل الثقب أسفل القادوس ، فإنه يلتقط حبة واحدة. يمكنني بعد ذلك تدويرها أسفل كاميرا الويب ، ثم تدويرها مرة أخرى حتى تتطابق مع الفتحة الموجودة في القطعة السفلية ، وعند هذه النقطة تسقط من خلالها.

في هذه الصورة ، أختبر أن النظام يمكن أن يعمل. تم إصلاح كل شيء باستثناء الجزء العلوي المستدير من الخشب الرقائقي ، والذي يتم توصيله بمحرك متدرج بعيدًا عن الأنظار تحته. لم يتم تثبيت كاميرا الويب بعد. أنا فقط أستخدم لوحة تحكم Phidget لأنتقل إلى المحرك في هذه المرحلة.

تخزين حبة

الجزء التالي هو تصميم نظام السلة لحفظ كل لون. قررت استخدام محرك متدرج ثانٍ أدناه لدعم وتدوير حاوية مستديرة بأجزاء متباعدة بشكل متساوٍ. يمكن استخدام هذا لتدوير الحجرة الصحيحة أسفل الفتحة التي ستسقط منها الخرزة.

لقد صنعت هذا باستخدام الورق المقوى وشريط لاصق. أهم شيء هنا هو الاتساق - يجب أن تكون كل مقصورة بنفس الحجم ، ويجب أن يكون كل شيء موزونًا بشكل متساوٍ بحيث يدور دون تخطي.

تتم إزالة الخرزة عن طريق غطاء محكم يفضح حجرة واحدة في كل مرة ، بحيث يمكن سكب الخرزات.

الة تصوير

يتم تثبيت كاميرا الويب فوق اللوحة العلوية بين القادوس وموقع فتحة اللوحة السفلية. يسمح هذا للنظام بالنظر إلى الخرزة قبل إسقاطها. يتم استخدام LED لإضاءة الخرز الموجود أسفل الكاميرا ، ويتم حظر الإضاءة المحيطة ، من أجل توفير بيئة إضاءة متسقة. هذا مهم جدًا لاكتشاف اللون بدقة ، حيث يمكن للإضاءة المحيطة أن تتخلص من الألوان المتصورة.

كشف الموقع

من المهم أن يكون النظام قادرًا على اكتشاف دوران فاصل الحبيبات. يستخدم هذا لإعداد الموضع الأولي عند بدء التشغيل ، ولكن أيضًا لاكتشاف ما إذا كان محرك السائر قد خرج عن المزامنة. في نظامي ، تتكدس الخرزة أحيانًا أثناء التقاطها ، ويحتاج النظام إلى أن يكون قادرًا على اكتشاف هذا الموقف والتعامل معه - عن طريق النسخ الاحتياطي قليلاً ومحاولة agian.

هناك الكثير من الطرق للتعامل مع هذا. قررت استخدام مستشعر مغناطيسي 1108 ، مع مغناطيس مدمج في حافة اللوحة العلوية. هذا يسمح لي بالتحقق من الموقف في كل دورة. من المحتمل أن يكون الحل الأفضل هو برنامج تشفير على محرك السائر ، لكن كان لدي 1108 موجودًا في الجوار لذا استخدمت ذلك.

قم بإنهاء الروبوت

في هذه المرحلة ، تم عمل واختبار كل شيء. حان الوقت لتركيب كل شيء بشكل جيد والانتقال إلى برامج الكتابة.

يتم تشغيل محركي السائر بواسطة وحدات تحكم STC1001. يتم استخدام HUB000 - USB VINT hub لتشغيل وحدات التحكم في السائر ، بالإضافة إلى قراءة المستشعر المغناطيسي وقيادة مؤشر LED. تم توصيل كل من كاميرا الويب و HUB0000 بمحور USB صغير. يتم استخدام ضفيرة 3031 وبعض الأسلاك جنبًا إلى جنب مع مصدر طاقة 24 فولت لتشغيل المحركات.

الخطوة 3: اكتب الرمز

Image
Image

يتم استخدام C # و Visual Studio 2015 لهذا المشروع. قم بتنزيل المصدر في الجزء العلوي من هذه الصفحة وتابع على طول - الأقسام الرئيسية موضحة أدناه

التهيئة

أولاً ، يجب إنشاء كائنات Phidget وفتحها وتهيئتها. يتم ذلك في حدث تحميل النموذج ومعالجات إرفاق Phidget.

Form1_Load باطل خاص (مرسل الكائن ، EventArgs e) {

/ * تهيئة وفتح Phidgets * /

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

bottom. HubPort = 1 ،

أسفل. إرفاق + = إرفاق سفلي ؛ bottom. Detach + = Bottom_Detach ؛ bottom. PositionChange + = Bottom_PositionChange ؛ bottom. Open () ،

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 = صحيح ؛ led. State = صحيح ؛ ledChk. Checked = صحيح ؛ }

MagSensor_Attach باطل خاص (مرسل الكائن ، Phidget22. Events. AttachEventArgs e) {

magSensorAttachedChk. Checked = صحيح ؛ magSensor. SensorType = VoltageRatioSensorType. PN_1108 ، magSensor. DataInterval = 16 ، }

Bottom_Attach باطل خاص (مرسل الكائن ، Phidget22. Events. AttachEventArgs e) {

bottomAttachedChk. Checked = صحيح ؛ bottom. CurrentLimit = bottomCurrentLimit ؛ bottom. Engaged = صحيح ؛ bottom. VelocityLimit = bottomVelocityLimit ؛ bottom. Acceleration = bottomAccel ؛ bottom. DataInterval = 100 ؛ }

Top_Attach باطل خاص (مرسل الكائن ، Phidget22. Events. AttachEventArgs e) {

topAttachedChk. Checked = صحيح ؛ top. CurrentLimit = topCurrentLimit ؛ top. Engaged = صحيح ؛ top. RescaleFactor = -1 ؛ top. VelocityLimit = -topVelocityLimit ؛ top. Acceleration = -topAccel ؛ top. DataInterval = 100 ؛ }

نقرأ أيضًا في أي معلومات لون محفوظة أثناء التهيئة ، بحيث يمكن متابعة التشغيل السابق.

تموضع المحرك

يتكون رمز التعامل مع المحرك من وظائف ملائمة لتحريك المحركات. المحركات التي استخدمتها هي 3 ، 200 1/16 خطوة لكل ثورة ، لذلك قمت بإنشاء ثابت لهذا.

بالنسبة للمحرك العلوي ، هناك 3 أوضاع نريد أن نتمكن من إرسالها إلى المحرك: كاميرا الويب ، والفتحة ، ومغناطيس تحديد المواقع. هناك وظيفة للسفر إلى كل من هذه المواقف:

private void nextMagnet (Boolean wait = false) {

مزدوج posn = top. Position٪ stepsPerRev ؛

top. TargetPosition + = (stepsPerRev - posn) ؛

إذا (انتظر)

بينما (الأعلى يتحرك) الخيط. النوم (50) ؛ }

private void nextCamera (Boolean wait = false) {

مزدوج 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) ؛

إذا (انتظر)

بينما (الأعلى يتحرك) الخيط. النوم (50) ؛ }

private void nextHole (Boolean wait = false) {

مزدوج 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) ؛

إذا (انتظر)

بينما (الأعلى يتحرك) الخيط. النوم (50) ؛ }

قبل بدء الجري ، تتم محاذاة اللوحة العلوية باستخدام المستشعر المغناطيسي. يمكن استدعاء وظيفة alignMotor في أي وقت لمحاذاة اللوحة العلوية. تقوم هذه الوظيفة أولاً بتحويل اللوحة بسرعة إلى دورة كاملة واحدة حتى ترى بيانات المغناطيس أعلى من العتبة. ثم يقوم بالنسخ الاحتياطي قليلاً ويتحرك للأمام مرة أخرى ببطء ، ويلتقط بيانات المستشعر أثناء تقدمه. أخيرًا ، يقوم بتعيين الموضع على أقصى موقع لبيانات المغناطيس ، ويعيد تعيين إزاحة الموضع إلى 0. وبالتالي ، يجب أن يكون موضع المغناطيس الأقصى دائمًا في (top. Position٪ stepsPerRev)

محاذاة الخيطموتورثريد ؛ منشار منطقي مغناطيس ؛ magSensorMax مزدوج = 0 ؛ محاذاة باطلة خاصة () {

// ابحث عن المغناطيس

top. DataInterval = top. MinDataInterval ؛

sawMagnet = خطأ ؛

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

int tryCount = 0 ؛

حاول مجددا:

top. TargetPosition + = stepsPerRev ؛

بينما (الجزء العلوي يتحرك &&! sawMagnet) الخيط. النوم (25) ؛

إذا (! sawMagnet) {

إذا (tryCount> 3) {Console. WriteLine ("فشل المحاذاة") ؛ top. Engaged = خطأ ؛ bottom. Engaged = false ؛ runtest = خطأ ؛ إرجاع؛ }

tryCount ++ ؛

Console. WriteLine ("هل نحن عالقون؟ جرب نسخة احتياطية …")؛ top. TargetPosition - = 600 ؛ بينما (الأعلى يتحرك) الخيط. النوم (100) ؛

اذهب إلى المحاولة ؛

}

top. VelocityLimit = -100 ؛

magData = قائمة جديدة> () ؛ magSensor. SensorChange + = magSensorCollectPositionData ؛ top. TargetPosition + = 300 ؛ بينما (الأعلى يتحرك) الخيط. النوم (100) ؛

magSensor. SensorChange - = magSensorCollectPositionData ؛

top. VelocityLimit = -topVelocityLimit ؛

KeyValuePair max = magData [0] ،

foreach (زوج KeyValuePair في magData) if (pair. Value> max. Value) max = pair ؛

top. AddPositionOffset (-max. Key) ؛

magSensorMax = max. Value ؛

top. TargetPosition = 0 ؛

بينما (الأعلى يتحرك) الخيط. النوم (100) ؛

Console. WriteLine ("نجحت المحاذاة") ؛

}

قائمة> magData ؛

magSensorCollectPositionData (مرسل الكائن ، Phidget22. Events. VoltageRatioInputSensorChangeEventArgs e) {magData. Add (new KeyValuePair (top. Position، e. SensorValue)) ؛ }

magSensorStopMotor باطل خاص (مرسل الكائن ، Phidget22. Events. VoltageRatioInputSensorChangeEventArgs e) {

إذا (top. IsMoving && e. SensorValue> 5) {top. TargetPosition = top. Position - 300 ؛ magSensor. SensorChange - = magSensorStopMotor ؛ sawMagnet = صحيح ؛ }}

أخيرًا ، يتم التحكم في المحرك السفلي عن طريق إرساله إلى أحد مواضع حاوية الحبيبات. لهذا المشروع ، لدينا 19 وظيفة. تختار الخوارزمية أقصر مسار ، وتستدير إما في اتجاه عقارب الساعة أو عكس اتجاه عقارب الساعة.

خاص 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 ؛ مزدوج targetPosn = (posn * stepsPerRev) / BeadCompartments ؛

double currentPosn = bottom. Position٪ stepsPerRev ؛

مزدوج posnDiff = targetPosn - currentPosn ؛

// احتفظ بها كخطوات كاملة

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

إذا (posnDiff <= 1600) bottom. TargetPosition + = posnDiff ؛ else bottom. TargetPosition - = (stepsPerRev - posnDiff) ؛

إذا (انتظر)

بينما (القاع يتحرك) الخيط. النوم (50) ؛ }

الة تصوير

يستخدم OpenCV لقراءة الصور من كاميرا الويب. يبدأ خيط الكاميرا قبل بدء سلسلة الفرز الرئيسي. هذا الخيط يقرأ باستمرار في الصور ويحسب متوسط اللون لمنطقة معينة باستخدام المتوسط ويقوم بتحديث متغير اللون العام. يستخدم الخيط أيضًا HoughCircles لمحاولة اكتشاف حبة ، أو ثقب في اللوحة العلوية ، لتحسين المنطقة التي يبحث عنها لاكتشاف اللون. تم تحديد أرقام العتبة و HoughCircles من خلال التجربة والخطأ ، وتعتمد بشكل كبير على كاميرا الويب والإضاءة والتباعد.

bool runVideo = true ؛ منطقي videoRunning = false ؛ التقاط فيديو الموضوع cvThread ؛ تم الكشف عن اللون الكشف المنطقي = خطأ ؛ الباحث عن الباحث = 0 ؛

وظيفة cvThreadFunction الخاصة () {

videoRunning = خطأ ؛

التقاط = فيديو جديد (كاميرا مختارة) ؛

باستخدام (Window window = new Window ("capture")) {

صورة حصيرة = حصيرة جديدة () ؛ Mat image2 = حصيرة جديدة () ؛ while (runVideo) {capture. Read (صورة) ؛ إذا كسر (image. Empty ()) ؛

إذا (الكشف)

DiscoverCnt ++ ؛ آخر detectiveCnt = 0 ؛

إذا (الكشف عن || CircleDetectChecked || showDetectionImgChecked) {

Cv2. CvtColor (صورة ، صورة 2 ، ColorConversionCodes. BGR2GRAY) ؛ مات thres = image2. Threshold ((مزدوج) 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) ؛ إذا (حبة.الطول> = 1) {image. Circle (حبة [0]. مركز ، 3 ، عددي جديد (0 ، 100 ، 0) ، -1) ؛ image. Circle (حبة [0]. المركز ، (int) حبة [0]. Radus ، عددي جديد (0 ، 0 ، 255) ، 3) ؛ إذا (حبة [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 circles = thres. HoughCircles (HoughMethods. Gradient، 2، /*thres. Rows/4*/ 5، 200، 100، 60، 180) ؛

if (circles. Length> 1) {List xs = circles. Select (c => c. Center. X). ToList () ؛ xs. Sort () ، اكتب ys = circles. Select (c => c. Center. Y). ToList () ؛ ys. Sort () ،

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

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

إذا (الوسيط> صورة. العرض - 15)

الوسيط X = الصورة العرض - 15 ؛ إذا (medianY> image. Height - 15) medianY = image. Height - 15 ؛

image. Circle (الوسيط X ، الوسيط Y ، 100 ، 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 = خصائص Rect ((int) new. Settings. Default.x ،

(int) Properties. Settings. Default.y، (int) Properties. Settings. Default.size، (int) Properties. Settings. Default.height) ؛

عينة حبة حصيرة = حصيرة جديدة (صورة ، ص) ؛

عددي avgColor = Cv2. Mean (حبة عينة) ؛ الكشف عن اللون = Color. FromArgb ((int) avgColor [2] ، (int) avgColor [1] ، (int) avgColor [0]) ؛

image. Rectangle (r ، Scalar جديد (0 ، 150 ، 0)) ؛

window. ShowImage (صورة) ؛

Cv2. WaitKey (1) ، videoRunning = صحيح ؛ }

videoRunning = خطأ ؛

} }

cameraStartBtn_Click (مرسل الكائن ، EventArgs e) {

إذا (cameraStartBtn. Text == "بدء") {

cvThread = خيط جديد (ThreadStart جديد (cvThreadFunction)) ؛ runVideo = صحيح ؛ cvThread. Start () ، cameraStartBtn. Text = "توقف" ؛ بينما (! videoRunning) Thread. Sleep (100) ؛

updateColorTimer. Start () ،

} آخر {

runVideo = خطأ ؛ cvThread. Join () ، cameraStartBtn. Text = "بدء" ؛ }}

لون

الآن ، نحن قادرون على تحديد لون حبة ، ونقرر بناءً على ذلك اللون أي حاوية نضعها فيها.

تعتمد هذه الخطوة على مقارنة الألوان. نريد أن نكون قادرين على التمييز بين الألوان للحد من النتائج الإيجابية الخاطئة ، ولكن أيضًا نسمح بحد كافٍ للحد من السلبيات الخاطئة. تعتبر مقارنة الألوان في الواقع أمرًا معقدًا بشكل مدهش ، لأن الطريقة التي تخزن بها أجهزة الكمبيوتر الألوان على هيئة RGB ، والطريقة التي يدرك بها البشر الألوان لا ترتبط خطيًا. لجعل الأمور أسوأ ، يجب أيضًا مراعاة لون الضوء الذي يتم عرض اللون تحته.

هناك خوارزمية معقدة لحساب اختلاف اللون. نحن نستخدم CIE2000 ، الذي ينتج رقمًا قريبًا من 1 إذا كان لا يمكن تمييز لونين للإنسان. نحن نستخدم مكتبة ColorMine C # لإجراء هذه الحسابات المعقدة. تم العثور على قيمة DeltaE البالغة 5 لتقديم حل وسط جيد بين الإيجابية الزائفة والسلبية الزائفة.

نظرًا لوجود عدد أكبر من الألوان ثم الحاويات ، يتم حجز الموضع الأخير كصندوق تجميع. عادةً ما أضع هذه الأشياء جانبًا للتشغيل من خلال الآلة في تمريرة ثانية.

قائمة

الألوان = قائمة جديدة () ؛ قائمة colorPanels = قائمة جديدة () ؛ قائمة colorTxts = قائمة جديدة () ، قائمة colorCnts = قائمة جديدة () ؛

const int numColorSpots = 18 ؛

const int unknownColorIndex = 18 ؛ int findColorPosition (اللون c) {

Console. WriteLine ("Finding color…")؛

var cRGB = new Rgb () ؛

cRGB. R = c. R ؛ cRGB. G = c. G ؛ cRGB. B = c. B ؛

int bestMatch = -1 ؛

مباراة مزدوجة دلتا = 100 ؛

لـ (int i = 0 ؛ i <colours. Count ؛ i ++) {

var RGB = new Rgb () ؛

RGB. R = ألوان . R ؛ RGB. G = ألوان . G ؛ RGB. B = ألوان . B ؛

دلتا مزدوجة = cRGB. Compare (RGB ، جديد CieDe2000Comparison ()) ؛

// double delta = deltaE (ج ، ألوان ) ؛ Console. WriteLine ("DeltaE (" + i. ToString () + "):" + delta. ToString ())؛ إذا (delta <matchDelta) {matchDelta = delta ؛ bestMatch = أنا ؛ }}

إذا (matchDelta <5) {Console. WriteLine ("Found! (Posn:" + bestMatch + "Delta:" + matchDelta + ")")؛ إرجاع أفضل تطابق ؛ }

if (colors. Count <numColorSpots) {Console. WriteLine ("New Color!")؛ الألوان إضافة (ج) ، this. BeginInvoke (new Action (setBackColor) ، كائن جديد {colors. Count - 1}) ؛ writeOutColors () ، العودة (عدد الألوان - 1) ؛ } else {Console. WriteLine ("Unknown Color!")؛ إرجاع unknownColorIndex ؛ }}

منطق الفرز

تجمع وظيفة الفرز بين كل القطع لفرز الخرز بالفعل. تعمل هذه الوظيفة في سلسلة مخصصة ؛ تحريك اللوحة العلوية ، والكشف عن لون الخرزة ، ووضعها في سلة ، والتأكد من بقاء اللوحة العلوية في محاذاة ، وعد الخرزات ، إلخ.كما أنه يتوقف عن العمل عندما يمتلئ صندوق catchall - وإلا فإننا سننتهي بالخرز الفائض.

colourTestThread؛ Boolean runtest = false؛ اختبار لون باطل () {

إذا (! top. Engaged)

top. Engaged = صحيح ؛

إذا (! bottom. Engaged)

bottom. Engaged = صحيح ؛

بينما (runtest) {

nextMagnet (صحيح) ؛

خيط النوم (100) ؛ جرب {if (magSensor. SensorValue <(magSensorMax - 4)) alignMotor () ؛ } catch {alignMotor ()؛ }

nextCamera (صحيح) ؛

كشف = صحيح ؛

بينما (detectCnt <5) Thread. Sleep (25) ؛ Console. WriteLine ("Detect Count:" + detectCnt) ؛ كشف = خطأ ؛

اللون ج = تم الكشف عن اللون ؛

this. BeginInvoke (new Action (setColorDet)، new object {c})؛ int i = findColorPosition (c) ؛

SetBottomPosition (أنا ، صحيح) ؛

nextHole (صحيح) ؛ colorCnts ++ ؛ this. BeginInvoke (new Action (setColorTxt)، new object {i}) ؛ خيط النوم (250) ؛

إذا (colorCnts [unknownColorIndex]> 500) {

top. Engaged = خطأ ؛ bottom. Engaged = false ؛ runtest = خطأ ؛ this. BeginInvoke (new Action (setGoGreen)، null) ؛ إرجاع؛ }}}

لون باطل خاص TestBtn_Click (مرسل الكائن ، EventArgs e) {

if (colourTestThread == null ||! colourTestThread. IsAlive) {colourTestThread = new Thread (new ThreadStart (colourTest)) ؛ runtest = صحيح ؛ colourThread. Start () ، colourTestBtn. Text = "STOP" ، colourTestBtn. BackColor = Color. Red ، } else {runtest = false؛ colourTestBtn. Text = "GO" ، colourTestBtn. BackColor = Color. Green ، }}

في هذه المرحلة ، لدينا برنامج عمل. تم ترك بعض أجزاء التعليمات البرمجية خارج المقالة ، لذا ألقِ نظرة على المصدر لتشغيله بالفعل.

مسابقة البصريات
مسابقة البصريات

الجائزة الثانية في مسابقة البصريات

موصى به: