Სარჩევი:

Robotic Bead დახარისხება: 3 ნაბიჯი (სურათებით)
Robotic Bead დახარისხება: 3 ნაბიჯი (სურათებით)

ვიდეო: Robotic Bead დახარისხება: 3 ნაბიჯი (სურათებით)

ვიდეო: Robotic Bead დახარისხება: 3 ნაბიჯი (სურათებით)
ვიდეო: ფილმები ქართულად / მიმიფურთხებია თქვენი საფლავებისთვის 3 2024, ნოემბერი
Anonim
Image
Image
Robotic Bead დახარისხება
Robotic Bead დახარისხება
Robotic Bead დახარისხება
Robotic Bead დახარისხება
Robotic Bead დახარისხება
Robotic Bead დახარისხება

ამ პროექტში ჩვენ ვაშენებთ რობოტს, რომელიც პერლერის მძივებს ფერის მიხედვით დაალაგებს.

მე ყოველთვის მინდოდა შემექმნა ფერის დასალაგებელი რობოტი, ასე რომ, როდესაც ჩემი ქალიშვილი დაინტერესდა პერლერის მძივების დამზადებით, მე ეს დავინახე როგორც შესანიშნავი შესაძლებლობა.

პერლერის მძივები გამოიყენება შერწყმული ხელოვნების პროექტების შესაქმნელად, მრავალი მძივის დაფაზე დაფარვით, შემდეგ კი მათ რკინასთან ერთად დნობისას. თქვენ ჩვეულებრივ ყიდულობთ ამ მძივებს გიგანტური 22,000 მძივის შერეული ფერის პაკეტებში და დიდ დროს ატარებთ თქვენთვის სასურველი ფერის მოსაძებნად, ამიტომ ვფიქრობდი, რომ მათი დახარისხება გაზრდის ხელოვნების ეფექტურობას.

მე ვმუშაობ Phidgets Inc- ში, ამიტომ მე ძირითადად Phidgets გამოვიყენე ამ პროექტისთვის - მაგრამ ეს შეიძლება გაკეთდეს ნებისმიერი შესაფერისი ტექნიკის გამოყენებით.

ნაბიჯი 1: აპარატურა

აი რას ვიყენებდი ამის ასაშენებლად. მე ავაშენე ის 100% -ით phidgets.com– ის ნაწილებით და ნივთებით, რაც მე მქონდა სახლში.

ფიჯეტების დაფები, მოტორსი, ტექნიკა

  • HUB0000 - VINT Hub Phidget
  • 1108 - მაგნიტური სენსორი
  • 2x STC1001 - 2.5A სტეპერი ფიჯეტი
  • 2x 3324 - 42STH38 NEMA -17 ბიპოლარული გადაცემათა კოლოფი
  • 3x3002 - ფიჯეტის კაბელი 60 სმ
  • 3403 - USB2.0 4 პორტიანი კერა
  • 3031 - ქალი ღორი 5,5x2,1 მმ
  • 3029 - 2 მავთული 100 'Twisted Cable
  • 3604 - 10 მმ თეთრი LED (ჩანთა 10)
  • 3402 - USB ვებკამერა

სხვა ნაწილები

  • 24VDC 2.0A კვების ბლოკი
  • შეაგროვეთ ხე და ლითონი ავტოფარეხიდან
  • Zip კავშირები
  • პლასტმასის კონტეინერი, რომლის ქვედა ნაწილი შეწყვეტილია

ნაბიჯი 2: შეიმუშავეთ რობოტი

შექმენით რობოტი
შექმენით რობოტი
შექმენით რობოტი
შექმენით რობოტი
შექმენით რობოტი
შექმენით რობოტი

ჩვენ უნდა შევქმნათ ისეთი რამ, რამაც შეიძლება ერთი მძივი აიღოს შეყვანის ბუდედან, განათავსოს ის ვებკამერის ქვეშ და შემდეგ გადაიტანოს შესაბამის ურნაში.

მძივის პიკაპი

მე გადავწყვიტე 1 ნაწილი გამეკეთებინა 2 ცალი მრგვალი პლაივუდით, თითოეულს ერთსა და იმავე ადგილას გაბურღული ხვრელით. ქვედა ნაჭერი ფიქსირდება, ხოლო ზედა ნაწილი მიმაგრებულია სტეპერ ძრავზე, რომელსაც შეუძლია მისი ბრუნვა მძივებით სავსე ხუნდის ქვეშ. როდესაც ხვრელი მიდის ბუდის ქვეშ, ის იღებს ერთ მძივს. შემდეგ შემიძლია მისი გადაბრუნება ვებკამერის ქვეშ და შემდეგ შემობრუნება მანამ, სანამ არ ემთხვევა ქვედა ნაწილში არსებულ ხვრელს, რომლის დროსაც იგი იშლება.

ამ სურათზე, მე ვამოწმებ, რომ სისტემას შეუძლია მუშაობა. ყველაფერი ფიქსირდება პლაივუდის ზედა მრგვალი ნაწილის გარდა, რომელიც მიმაგრებულია სტეპერ ძრავზე ქვემოდან. ვებკამერა ჯერ არ არის დამონტაჟებული. მე უბრალოდ ვიყენებ Phidget Control Panel– ს, რომ ამ ეტაპზე მოტორზე გადავიდე.

მძივის შესანახი

შემდეგი ნაწილი არის ბინის სისტემის შემუშავება თითოეული ფერის შესანახად. მე გადავწყვიტე გამოვიყენო მეორე სტეპერიანი ძრავა მრგვალი კონტეინერის მხარდასაჭერად და როტაციისთვის თანაბრად განლაგებული კუპეებით. ეს შეიძლება გამოყენებულ იქნას სწორი განყოფილების გადასატრიალებლად იმ ხვრელის ქვეშ, საიდანაც მძივი ამოვარდება.

მე ავაშენე ეს მუყაოს და წებოვანი ლენტის გამოყენებით. ყველაზე მნიშვნელოვანი აქ არის თანმიმდევრულობა - თითოეული კუპე უნდა იყოს ერთი და იგივე ზომის და მთელი ნივთი უნდა იყოს თანაბრად შეწონილი ისე რომ ტრიალებს გამოტოვების გარეშე.

მძივის ამოღება ხორციელდება მჭიდროდ მორგებული სახურავის საშუალებით, რომელიც ერთდროულად აჩვენებს ერთ ნაწილს, ასე რომ მძივები შეიძლება გადმოიყაროს.

კამერა

ვებკამერა დამონტაჟებულია ზედა ფირფიტაზე ბუდესა და ქვედა ფირფიტის ხვრელს შორის. ეს საშუალებას აძლევს სისტემას შეხედოს მძივს, სანამ ის ჩააგდებს. LED გამოიყენება კამერის ქვეშ მძივების გასანათებლად და გარემოს შუქი დაბლოკილია, რათა უზრუნველყოს თანმიმდევრული განათების გარემო. ეს ძალიან მნიშვნელოვანია ფერის ზუსტი გამოვლენისთვის, რადგან გარე განათებას ნამდვილად შეუძლია გადააგდოს აღქმული ფერი.

მდებარეობის გამოვლენა

მნიშვნელოვანია, რომ სისტემამ შეძლოს მძივის გამყოფი როტაციის გამოვლენა. ეს გამოიყენება საწყისი პოზიციის დასაყენებლად დაწყებისას, მაგრამ ასევე იმის დასადგენად, არის თუ არა სტეპერიანი ძრავა სინქრონიზებული. ჩემს სისტემაში, მძივი ზოგჯერ იჭრება დაკრეფის დროს და სისტემას უნდა შეეძლოს ამ სიტუაციის გამოვლენა და მართვა - ცოტათი უკან დახევით და აგიანის მცდელობით.

უამრავი გზა არსებობს ამის მოსაგვარებლად. მე გადავწყვიტე გამოვიყენო 1108 მაგნიტური სენსორი, მაგნიტი ჩადგმული ზედა ფირფიტის კიდეზე. ეს მაძლევს საშუალებას გადამოწმდეს პოზიცია ყოველ ბრუნვაზე. უკეთესი გამოსავალი ალბათ იქნება სტეპერი ძრავის კოდირება, მაგრამ მე მქონდა 1108 იქვე, ასე რომ გამოვიყენე.

დაასრულეთ რობოტი

ამ ეტაპზე, ყველაფერი შემუშავებულია და შემოწმებულია. დროა ყველაფერი ლამაზად დაამონტაჟო და წერო პროგრამულ უზრუნველყოფაზე.

2 სტეპერ ძრავას მართავს STC1001 სტეპერი კონტროლერი. HUB000 - USB VINT კერა გამოიყენება სტეპერ კონტროლერების გასაშვებად, ასევე მაგნიტური სენსორის წასაკითხად და LED- ის მართვისთვის. ვებკამერა და HUB0000 ორივე მიმაგრებულია პატარა USB კერაზე. 3031 პიგტეილი და ზოგიერთი მავთული გამოიყენება 24V კვების ბლოკთან ერთად ძრავების დასაძრავად.

ნაბიჯი 3: დაწერე კოდი

Image
Image

C# და Visual Studio 2015 გამოიყენება ამ პროექტისათვის. ჩამოტვირთეთ წყარო ამ გვერდის ზედა ნაწილში და მიჰყევით მას - ძირითადი სექციები მოცემულია ქვემოთ

ინიციალიზაცია

პირველ რიგში, ჩვენ უნდა შევქმნათ, გავხსნათ და დავიწყოთ ფიჯეტის ობიექტები. ეს კეთდება ფორმის დატვირთვის შემთხვევაში და ფიჯეტი ანიჭებს დამმუშავებლებს.

პირადი სიცარიელე Form1_Load (ობიექტის გამგზავნი, EventArgs e) {

/ * ფიჯიტების ინიციალიზაცია და გახსნა */

top. HubPort = 0; ზედა. მიმაგრება += ზედა_დამაგრება; ზედა. გათიშვა += ზედა_მოხსნა; top. PositionChange += Top_PositionChange; ზედა. ღია ();

ქვედა. HubPort = 1;

ქვედა. მიმაგრება += ქვედა_დამაგრება; ქვედა. გათიშვა += ქვედა_მოხსნა; bottom. 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. არხი = 0; led. Attach += Led_Attach; led. Detach += Led_Detach; ხელმძღვანელობდა. ღია (); }

პირადი სიცარიელე Led_Attach (ობიექტის გამგზავნი, Phidget22. Events. AttachEventArgs ე) {

ledAttachedChk. Checked = ჭეშმარიტი; led. State = true; ledChk. Checked = ჭეშმარიტი; }

პირადი სიცარიელე MagSensor_Attach (ობიექტის გამგზავნი, Phidget22. Events. AttachEventArgs ე) {

magSensorAttachedChk. Checked = ჭეშმარიტი; magSensor. SensorType = VoltageRatioSensorType. PN_1108; magSensor. DataInterval = 16; }

პირადი სიცარიელე Bottom_Attach (ობიექტის გამგზავნი, Phidget22. Events. AttachEventArgs ე) {

bottomAttachedChk. Checked = ჭეშმარიტი; bottom. CurrentLimit = ქვედაCurrentLimit; ქვედა. ჩართული = ჭეშმარიტი; bottom. VelocityLimit = ქვედაVelocityLimit; bottom. Acceleration = bottomAccel; ბოლოში. DataInterval = 100; }

პირადი სიცარიელე Top_Attach (ობიექტის გამგზავნი, Phidget22. Events. AttachEventArgs ე) {

topAttachedChk. Checked = ჭეშმარიტი; top. CurrentLimit = topCurrentLimit; ზედა. ჩართული = ჭეშმარიტი; ზედა. RescaleFactor = -1; top. VelocityLimit = -topVelocityLimit; ზედა. აჩქარება = -topAccel; ზედა. DataInterval = 100; }

ჩვენ ასევე ვკითხულობთ ნებისმიერ შენახულ ფერთა ინფორმაციას ინიციალიზაციის დროს, ასე რომ წინა რგოლის გაგრძელება შეიძლება.

საავტომობილო პოზიციონირება

საავტომობილო დამუშავების კოდი შედგება მოხერხებულობის ფუნქციებისგან ძრავების გადაადგილებისთვის. ძრავები, რომლებსაც ვიყენებ, არის 3, 200 1/16 ნაბიჯი თითო რევოლუციისთვის, ამიტომ მე შევქმენი მუდმივი ამისათვის.

ზედა ძრავისთვის არის 3 პოზიცია, რომლის გაგზავნაც გვსურს ძრავზე: ვებკამერა, ხვრელი და პოზიციონირების მაგნიტი. არსებობს ფუნქცია თითოეულ ამ პოზიციაზე მოგზაურობისთვის:

კერძო სიცარიელე შემდეგი მაგნიტი (ლოგიკური ლოდინი = ყალბი) {

ორმაგი პოზიცია = ზედა. პოზიცია % ნაბიჯებიPerRev;

top. TargetPosition += (stepsPerRev - posn);

თუ (დაელოდე)

while (top. IsMoving) თემა. ძილი (50); }

პირადი სიცარიელე შემდეგი კამერა (ლოგიკური ლოდინი = ყალბი) {

ორმაგი პოზიცია = ზედა. პოზიცია % ნაბიჯებიPerRev; 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) თემა. ძილი (50); }

პირადი სიცარიელე nextHole (ლოგიკური ლოდინი = ყალბი) {

ორმაგი პოზიცია = ზედა. პოზიცია % ნაბიჯებიPerRev; 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) თემა. ძილი (50); }

სირბილის დაწყებამდე ზედა ფირფიტა გასწორებულია მაგნიტური სენსორის გამოყენებით. AlignMotor ფუნქცია შეიძლება გამოვიძახოთ ნებისმიერ დროს ზედა ფირფიტის გასწორების მიზნით. ეს ფუნქცია პირველად სწრაფად ბრუნავს ფირფიტას 1 სრულ რევოლუციამდე, სანამ არ დაინახავს მაგნიტის მონაცემებს ბარიერის ზემოთ. შემდეგ ის ოდნავ უკან იხევს და კვლავ ნელა წინ მიიწევს, რაც სენსორის მონაცემებს იჭერს. დაბოლოს, ის ადგენს პოზიციას მაქსიმალური მაგნიტის მონაცემების ადგილმდებარეობას და აღადგენს პოზიციას ოფსეტურიდან 0. ამრიგად, მაქსიმალური მაგნიტის პოზიცია ყოველთვის უნდა იყოს (ზედა. პოზიცია % ნაბიჯები PerRev)

ძაფის გასწორება MotorThread; ლოგიკური ხერხი მაგნიტი; ორმაგი magSensorMax = 0; private 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 ("გასწორება ვერ მოხერხდა"); ზედა. ჩართული = ყალბი; ქვედა. ჩართული = ყალბი; runtest = ყალბი; დაბრუნების; }

tryCount ++;

Console. WriteLine ("ჩვენ დავრჩით? ვცდილობთ სარეზერვო…"); top. TargetPosition -= 600; while (top. IsMoving) თემა. ძილი (100);

ისევ სცადე;

}

top. VelocityLimit = -100;

magData = ახალი სია> (); magSensor. SensorChange += magSensorCollectPositionData; top. TargetPosition += 300; while (top. IsMoving) თემა. ძილი (100);

magSensor. SensorChange -= magSensorCollectPositionData;

top. VelocityLimit = -topVelocityLimit;

KeyValuePair max = magData [0];

foreach (KeyValuePair წყვილი magData) if (pair. Value> max. Value) max = წყვილი;

ზედა. დამატება პოზიციონირება (-max. Key);

magSensorMax = max. Value;

top. TargetPosition = 0;

while (top. IsMoving) თემა. ძილი (100);

Console. WriteLine ("გასწორება წარმატებულია");

}

სია> magData;

private void magSensorCollectPositionData (ობიექტის გამგზავნი, Phidget22. Events. VoltageRatioInputSensorChangeEventArgs ე) {magData. Add (ახალი KeyValuePair (ზედა. პოზიცია, e. SensorValue)); }

private void magSensorStopMotor (ობიექტის გამგზავნი, Phidget22. Events. VoltageRatioInputSensorChangeEventArgs ე) {

if (top. IsMoving && e. SensorValue> 5) {top. TargetPosition = top. Position - 300; magSensor. SensorChange -= magSensorStopMotor; sawMagnet = ჭეშმარიტი; }}

დაბოლოს, ქვედა ძრავა კონტროლდება მძივის კონტეინერის ერთ -ერთ პოზიციაზე გაგზავნით. ამ პროექტისთვის ჩვენ გვაქვს 19 პოზიცია. ალგორითმი ირჩევს უმოკლეს გზას და ბრუნავს ან საათის ისრის მიმართულებით ან ისრის საწინააღმდეგოდ.

private int BottomPosition {მიიღეთ {int posn = (int) bottom.position % stepsPerRev; if (posn <0) posn += stepsPerRev;

return (int) მათემატიკა. მრგვალი (((posn * beadCompartments) / (ორმაგი) ნაბიჯები PerRev));

} }

პირადი სიცარიელე SetBottomPosition (int posn, bool wait = false) {

posn = posn % bead კუპეები; Double targetPosn = (posn * stepsPerRev) / beadCompartments;

ორმაგი მიმდინარეPosn = ქვედა. პოზიცია % ნაბიჯებიPerRev;

ორმაგი posnDiff = targetPosn - currentPosn;

// შეინახეთ როგორც სრული ნაბიჯები

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

თუ (posnDiff <= 1600) ქვედა. TargetPosition += posnDiff; სხვა ქვედა. TargetPosition - = (stepsPerRev - posnDiff);

თუ (დაელოდე)

while (bottom. IsMoving) თემა. ძილი (50); }

კამერა

OpenCV გამოიყენება ვებკამერიდან სურათების წასაკითხად. კამერის ძაფი იწყება დაწყებული დახარისხების ძაფის დაწყებამდე. ეს თემა განუწყვეტლივ კითხულობს სურათებს, ითვლის საშუალო ფერს კონკრეტული რეგიონისთვის Mean- ის გამოყენებით და განაახლებს გლობალური ფერის ცვლადს. ძაფი ასევე იყენებს HoughCircles- ს, რათა აღმოაჩინოს ან მძივი, ან ხვრელი ზედა ფირფიტაზე, რათა დახვეწოს ის ტერიტორია, რომელსაც იგი ეძებს ფერის გამოვლენის მიზნით. ბარიერისა და HoughCircles- ის რიცხვები განისაზღვრა ცდისა და შეცდომის საშუალებით და დიდად არის დამოკიდებული ვებკამერაზე, განათებაზე და ინტერვალზე.

bool runVideo = true; bool videoRunning = false; ვიდეო გადაღება; თემა cvThread; ფერი გამოვლენილი ფერი; ლოგიკური გამოვლენა = ყალბი; int detectnt = 0;

პირადი void cvThreadFunction () {

videoRunning = ყალბი;

გადაღება = ახალი ვიდეო გადაღება (არჩეული კამერა);

გამოყენებით (ფანჯრის ფანჯარა = ახალი ფანჯარა ("გადაღება")) {

ხატის სურათი = ახალი მატი (); მატ სურათი 2 = ახალი მატი (); while (runVideo) {capture. Read (სურათი); თუ (სურათი. ცარიელი ()) შესვენება;

თუ (გამოვლენა)

deteCnt ++; სხვაგან deteCnt = 0;

თუ (გამოვლენა || წრედიდექტოლდება || შოუდეტექციამომოწმებულია) {

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

თუ (showDetectionImgChecked)

სურათი = thres;

თუ (გამოვლენა || circleDetectChecked) {

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, new Scalar (0, 100, 0), -1); სურათი. წრე (მძივი [0]. ცენტრი, (int) მძივი [0]. რადიუსი, ახალი სკალარი (0, 0, 255), 3); if (bead [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]. რადიუსი); Properties. Settings. Default.y = (ათობითი) მძივი [0]. Center. Y - (ათობითი) (მძივი [0]. Radius); } 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. დალაგება (); სია ys = წრეები. აირჩიეთ (c => c. Center. Y). ToList (); ys. დალაგება ();

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

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

თუ (მედიანა X> სურათი. სიგანე - 15)

მედიანა X = სურათი. სიგანე - 15; თუ (მედიანა> გამოსახულება. სიმაღლე - 15) მედიანა Y = სურათი. სიმაღლე - 15;

სურათი. წრე (მედიანა X, მედიანა Y, 100, ახალი სკალარი (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 = new Rect ((int) Properties. Settings. Default.x, (int) Properties. Settings. Default.y, (int) Properties. Settings. Default.size, (int) Properties. Settings. Default.height);

Mat beadSample = ახალი ხალიჩა (სურათი, r);

სკალარული avgColor = Cv2. საშუალო (beadSample); აღმოჩენილია ფერი = ფერი. ფერიდან ((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 = "გაჩერება"; ხოლო (! ვიდეო გაშვება) თემა. ძილი (100);

updateColorTimer. Start ();

} სხვა {

runVideo = ყალბი; cvThread. Join (); cameraStartBtn. Text = "დაწყება"; }}

ფერი

ახლა ჩვენ შეგვიძლია განვსაზღვროთ მძივის ფერი და ამ ფერის საფუძველზე გადავწყვიტოთ რომელ კონტეინერში ჩავაგდოთ იგი.

ეს ნაბიჯი ემყარება ფერის შედარებას. ჩვენ გვსურს შეგვეძლოს ერთმანეთისგან განვასხვავოთ ფერები ცრუ პოზიტივის შეზღუდვისათვის, მაგრამ ასევე დავუშვათ საკმარისი ბარიერი ზღუდავს ცრუ უარყოფითს. ფერების შედარება მართლაც გასაკვირი რთულია, რადგან ის, თუ როგორ ინახავს კომპიუტერები ფერებს RGB- ს სახით და როგორ აღიქვამენ ადამიანები ფერებს, არ არის კორელაციაში. უარესი რომ იყოს, მხედველობაში უნდა იქნას მიღებული სინათლის ფერი, რომლის ქვეშაც განიხილება ფერი.

არსებობს ფერადი განსხვავების გამოთვლის რთული ალგორითმი. ჩვენ ვიყენებთ CIE2000- ს, რომელიც გამოაქვს რიცხვს 1 -ის მახლობლად, თუ 2 ფერი ადამიანისთვის განურჩეველი იქნება. ჩვენ ვიყენებთ ColorMine C# ბიბლიოთეკას ამ რთული გამოთვლების გასაკეთებლად. აღმოჩნდა, რომ DeltaE 5 არის კარგი კომპრომისი ცრუ დადებითსა და ცრუ უარყოფითს შორის.

ვინაიდან ხშირად კონტეინერებზე უფრო მეტი ფერია, ბოლო პოზიცია დაცულია როგორც კაჩალის ურნა. მე საერთოდ გამოვტოვე ისინი, რომ გაუშვა მანქანა მეორე პასზე.

სია

ფერები = ახალი სია (); სია colorPanels = ახალი სია (); სიის ფერებიTxts = ახალი სია (); სია 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;

ორმაგი მატჩი დელტა = 100;

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

var RGB = ახალი Rgb ();

RGB. R = ფერები . R; RGB. G = ფერები . G; RGB. B = ფერები . B;

ორმაგი დელტა = cRGB. შეადარეთ (RGB, ახალი CieDe2000Comparison ());

// ორმაგი დელტა = deltaE (c, ფერები ); Console. WriteLine ("DeltaE (" + i. ToString () + "):" + delta. ToString ()); თუ (დელტა <matchDelta) {matchDelta = დელტა; bestMatch = i; }}

if (matchDelta <5) {Console. WriteLine ("ნაპოვნია! (პოზიცია:" + bestMatch + "დელტა:" + მატჩი დელტა + ")"); დაბრუნება bestMatch; }

if (colors. Count <numColorSpots) {Console. WriteLine ("ახალი ფერი!"); ფერები. დამატება (გ); this. BeginInvoke (ახალი მოქმედება (setBackColor), ახალი ობიექტი {colors. Count - 1}); writeOutColors (); დაბრუნება (ფერები. რაოდენობა - 1); } else {Console. WriteLine ("უცნობი ფერი!"); დაბრუნება უცნობიColorIndex; }}

ლოგიკის დახარისხება

დახარისხების ფუნქცია აერთიანებს ყველა ნაწილს რეალურად დასალაგებლად მძივები. ეს ფუნქცია გადის ერთგულ თემაში; ზედა ფირფიტის გადატანა, მძივის ფერის გამოვლენა, ყუთში მოთავსება, დარწმუნდით, რომ ზედა ფირფიტა შეესაბამება, მძივების დათვლა და ა.ის ასევე წყვეტს მუშაობას, როდესაც ქოთნის ყუთი სავსე ხდება - წინააღმდეგ შემთხვევაში ჩვენ უბრალოდ ვამთავრებთ მძივების გადავსებას.

თემა colourTestThread; ლოგიკური runtest = ყალბი; void colourTest () {

თუ (! ზედა. ჩართული)

ზედა. ჩართული = ჭეშმარიტი;

თუ (! ქვედა. ჩართული)

ქვედა. ჩართული = ჭეშმარიტი;

ხოლო (ყველაზე მაგარი) {

შემდეგი მაგნიტი (ჭეშმარიტი);

თემა ძილი (100); სცადეთ {if (magSensor. SensorValue <(magSensorMax - 4)) alignMotor (); } catch {alignMotor (); }

შემდეგი კამერა (ჭეშმარიტი);

გამოვლენა = ჭეშმარიტი;

while (deteCnt <5) თემა. ძილი (25); Console. WriteLine ("გამოვლენის რაოდენობა:" + detectnt); გამოვლენა = ყალბი;

ფერი c = აღმოჩენილი ფერი;

this. BeginInvoke (ახალი მოქმედება (setColorDet), ახალი ობიექტი {გ}); int i = findColorPosition (გ);

SetBottomPosition (i, ჭეშმარიტი);

nextHole (ჭეშმარიტი); colorCnts ++; this. BeginInvoke (ახალი მოქმედება (setColorTxt), ახალი ობიექტი {i}); თემა ძილი (250);

if (colorCnts [unknownColorIndex]> 500) {

ზედა. ჩართული = ყალბი; ქვედა. ჩართული = ყალბი; runtest = ყალბი; this. BeginInvoke (ახალი მოქმედება (setGoGreen), null); დაბრუნების; }}}

პირადი სიცარიელე colourTestBtn_Click (ობიექტის გამგზავნი, EventArgs e) {

if (colourTestThread == null ||! colourTestThread. IsAlive) {colourTestThread = ახალი თემა (ახალი ThreadStart (colourTest)); runtest = ჭეშმარიტი; colourTestThread. Start (); colourTestBtn. Text = "STOP"; colourTestBtn. BackColor = ფერი. წითელი; } else {runtest = ყალბი; colourTestBtn. Text = "GO"; colourTestBtn. BackColor = ფერი. მწვანე; }}

ამ ეტაპზე ჩვენ გვაქვს სამუშაო პროგრამა. კოდის რამდენიმე ნაწილი დარჩა სტატიის გარეთ, ასე რომ გადახედეთ წყაროს რეალურად გასაშვებად.

ოპტიკის კონკურსი
ოპტიკის კონკურსი

მეორე პრიზი ოპტიკის კონკურსში

გირჩევთ: