3D Eşleştirme, Robotik

Mavis, Teknodrom firması ile başlattığı robot yönlendirme ve parça bulma yakalama ayıklama çalışmalarına yeni bir boyut ekledi. Robotun yakalayacağı parçaları öğrenebilmesi için son derece pratik bir arayüz geliştirildi.

Sistemin Çalışması

Öncelikle kameranın dış etkenlerden (değişken ışık kaynakları, gün ışığı vb.) etkilenmemesi için düzgün aydınlatılmış bir ortam oluşturulur. Bu ortam robotun çalışmasını engellemeyecek şekilde yapılmalıdır.

1. İşlem olarak, kameraya yakalayacağı parçalar öğretilir. Her parça farklı bir yerinden tutulmak istenebilir. Tutma noktası parçanın belirli köşeleri, uçları gibi daha önceden tahmin edilemeyen bir yer olabilir. Robot operatörü, parçanın ideal tutma noktasını,  kameralı görüntü işleme yazılımında bir değişiklik istemeden (programlamaya ihtiyaç olmadan) kolayca gösterebilmelidir. (Programa  tutma noktası öğretilmelidir) Programda hiç değişiklik yapılmadan son derece kolay bir arabirimde (UI) parçalar sisteme tanıtılabilmelidir. Mavis’in kendi geliştirdiği sistem bu sorunu en kolay şekilde çözmüştür.

Menüden “yeni parça öğretme” işlemi seçildiğinde, kameranın altında bulunan parçanın resmi ekranda belirir. “Öğretme İşlemine Başlamak İstiyor musunuz?” sorusuna Evet dendiğinde, interaktif bir şekilde tutma noktası kullanıcıdan alınır.

Öğretilecek parçanın en ideal parça olmasına özen gösterilir. Buna rağmen ekranda parça sınır çizgileri belirlenir ve kenar iyileştirme algoritmaları ile çapak ve pürüzler ön işlemden geçirilerek temizlenir.

Bu noktada sistem parçanın modellenmiş ve temizlenmiş halini model kodu ile kaydeder. Herhangi bir görüntü işleme programı ile (Paint, Photoshop vb.) bu modellenmiş fotoğraf üzerinde daha da hassas çalışmalar yapılabilir. (Filtreleme, yuvarlatma, kenarları düzleştirme vb.)

Üstelik, tüm bu işlemler gerçek parça resmi yerine parçanın direk CAD çizimi baz alınarak ta yapılabilir.

Program çalışma anında, öğretilen parçayı tanıyacak ve tutma noktasının koordinatlarını ve açılarını robota iletecektir.

Sistem tüm robotlarla haberleşebilecek altyapıya göre tasarlanmıştır. Testler ve uyarlamalar Teknodrom firmasında Motoman Robot kullanılarak yapılmıştır. Haberleşme olarak seri port ve dijital Input/output modülü kullanılmıştır. Ethernet haberleşme için de arayüz geliştirilmiştir. Sistem Fanuc, kuka gibi robotlar ile de çalışabilecek şekildedir. Test ve geliştirmeler; Teknodrom firmasından Emrah Hünerlitürkoğlu, Mavis firmasından Mustafa Sarı ve Hüseyin Çelik tarafından gerçekleştirilmiştir.

Mavis Targovishte’de

Mavis Yapay Görme Ekibi, Bulgaristan Targovishte deki bir tesiste, kameralı kontrol kurulumu için yerinde çalışmalar yaptı. 1 gün boyunca süren çalışmalarda, üretim esnasında ve laboratuvar ortamında birçok farklı tip ürün üzerinde çalışılarak sonuca varıldı. Yapılan çalışmalar, proje sahibine rapor olarak sunulacaktır. Proje gerçeklenme aşamasında, daha detaylı bilgi verilecektir.

Mavis, Bugün Bulgaristanda kurulumunu yaptığı bir diğer proje olan robot kontrollü kameralı kontrol sisteminin kontrol ve iyileştirme çalışmalarına devam etmektedir.

Mavis İzmir’de

Mavis, Kameralı Yaprak Kontrolü makinasının montaj ve ilk testleri için İzmirde.

14 Haziran itibariyle, İzmirde sıcaklık : 34 derece.  Öğle saatlerinde 40 a yaklaşacağı söyleniyor.

(Müşteri Bilgi Gizliliği politikamız gereği, yapılan çalışma ve teknolojisi hakkında daha fazla bilgi verilmemiştir)

VYP ye Yeni Bir Standart Kontrol

Mavis VYP Kameralı Kontrol Yazılımı standart kontrollerine yeni bir tane daha eklendi : Parça Yönelimi.

Mavis VYP de, kameralı kontroller 3 gruba ayrılır.

  • Standart Kontroller
  • Projeye Özgü Kontroller
  • Gelişmiş Kontroller

Bir projenin sadece standart kontroller kullanılarak gerçeklenmesi en ideal durumdur. Projeye özgü kontroller bazı durumlarda kaçınılmaz olabilir. VYP, standart kontrol setini zenginleştirerek Projeye özgü kontrolleri olabildiğince minimuma indirgemiştir.

Standart kontrol kullanmanın avantajları

  • Kod yazımına gerek kalmaz
  • Defalarca test edilmiş olduğundan hatalara açık değildir
  • İyi Dökümante edilmiş ve eğitimlerde anlatılmış olduğundan kullanıcılar kolay uyum sağlar
  • Modüler / parametrik  olduğundan zahmetsizce uyarlanabilir
  • Güncelleştirmeler veya arayüz iyileştirmelerinden var olan tüm kurulumlar ek işlem yapılmaksızın yükseltilmiş olur

Parça Yönelimi :

Bu kontrol, sıkça karşılaştığımız “ters yönde takılmış” parçaları yakalamaya yarar. Kullanıcı arayüzü oldukça basittir.

Zorunlu kullanıcı seçimleri

  • bağlantı noktası (_juncPoint)
  • parçanın doğrultusu (_partDirection) Yatay / Dikey
  • parçanın beklenilen yönelimi (_expectingOrientation) Sol / Sağ / Üst / Alt

Opsiyonel kullanıcı seçimleri

  • Yaklaşma dörtgeninin kenar uzunluğu (_juncHeight) Varsayılan Değer : 10

Böylece sadece 3 seçimle birlikte kullanıcı parçanın olması gereken yönü sisteme kolayca bildirmiş olur.

(VYP Parça Yönelimi İçeren Kontrolleri Çalıştırıyor)

VYP Ekranında %800 zoom edildiğinde, parçanın yönelimi görülüyor.

Yönelim kullanıcının beklediği doğrultuda ise yeşil ok, aksi halde kırmızı ok (ters istikamete) ekranda belirecektir.


Yeni kontrolün C# kodlaması :

 

		private bool CheckDirection(JobDefs job)
		{

			if (masterFailed)
			{
				DrawCurrentRoi(job, false);
				return false;
			}

			HObject ho_ImageReduced;
			HObject ho_Region, ho_SelectedRegions, ho_Open, ho_Rect;
			HTuple hv_R1, hv_C1, hv_R2, hv_C2;

			bool retval = false;

			// Initialize local and output iconic variables 
			HOperatorSet.GenEmptyObj(out ho_ImageReduced);
			HOperatorSet.GenEmptyObj(out ho_Region);
			HOperatorSet.GenEmptyObj(out ho_SelectedRegions);
			HOperatorSet.GenEmptyObj(out ho_Open);
			HOperatorSet.GenEmptyObj(out ho_Rect);

			ho_ImageReduced.Dispose();
			if (imagesource.ReduceJobDomain(job, out ho_ImageReduced))
			{
				ho_Region.Dispose();
				imagesource.ApplyThreshold(job, ho_ImageReduced, out ho_Region);

				if (job.ValDouble("area") > 0)
				{

					ho_SelectedRegions.Dispose();
					imagesource.ApplySelection(job, ho_Region, out ho_SelectedRegions);

					int openR = job.VarInt32("OpeningRadius", 1);
					ho_Open.Dispose();
					HOperatorSet.OpeningCircle(ho_SelectedRegions, out ho_Open, openR);

					imagesource.DisplayObj(ho_Open, "blue");

					// Yaklaşma Bölgesini oluşturalım
					HOperatorSet.SmallestRectangle1(ho_Open, out hv_R1, out hv_C1, out hv_R2, out hv_C2);
					int tutamac = job.VarInt32("_juncPoint", 3); //L R U D
					int rHeight = job.VarInt32("_juncHeight", 10); // Yaklaşma Dörtgeninin Yüksekliği
					int yonelim = job.VarInt32("_partDirection", 0); // Horizontal, Vertical
					int beklenenYon = job.VarInt32("_expectingOrientation", 0); // L R U D

					if (tutamac == 0) // Soldan tutturulmuş				
						HOperatorSet.GenRectangle1(out ho_Rect, hv_R1, hv_C1-rHeight, hv_R2, hv_C1);
					else if (tutamac == 1) // Sağdan tutturulmuş				
						HOperatorSet.GenRectangle1(out ho_Rect, hv_R1, hv_C2, hv_R2, hv_C2 + rHeight);
					else if (tutamac == 2) // Üstten tutturulmuş				
						HOperatorSet.GenRectangle1(out ho_Rect, hv_R1 - rHeight, hv_C1, hv_R1, hv_C2);
					else if (tutamac == 3) // Alttan tutturulmuş				
						HOperatorSet.GenRectangle1(out ho_Rect, hv_R2, hv_C1, hv_R2 + rHeight, hv_C2);

					imagesource.SetColor("yellow");

					ho_ImageReduced.Dispose();
					HOperatorSet.ReduceDomain(imagesource.Image, ho_Rect, out ho_ImageReduced);

					ho_Region.Dispose();
					if (imagesource.ApplyThreshold(job, ho_ImageReduced, out ho_Region))
					{
						double yRow = job.ValDouble("row"); // yaklaşma Row
						double yCol = job.ValDouble("column"); // yaklaşma col

						HOperatorSet.DispCross(imagesource.DisplayHandle, yRow, yCol, 12, 0);

						if (yonelim == 0) // Yatay bir parça ise
						{
							double pC1 = TupleToDbl(hv_C1);
							double pC2 = TupleToDbl(hv_C2);

							double lFark = Math.Abs(pC1 - yCol); // merkez ile sol noktanın farkı
							double rFark = Math.Abs(pC2 - yCol); // merkez ile sağ noktanın farkı

							//imagesource.SetColor("blue");
							if (beklenenYon == 0) // Soldan bekleniyor
							{
								retval = lFark > rFark; // Solda bekleniyordu, geldi.
								imagesource.SetColor(retval ? "green" : "red");
								if (retval)
									HOperatorSet.DispArrow(imagesource.DisplayHandle, yRow, yCol, yRow, hv_C1, 1);
								else
									HOperatorSet.DispArrow(imagesource.DisplayHandle, yRow, yCol, yRow, hv_C2, 1);

							}
							else if (beklenenYon == 1) // Sağdan bekleniyor
							{
								retval = rFark > lFark;
								imagesource.SetColor(retval ? "green" : "red");								
								if (retval)
									HOperatorSet.DispArrow(imagesource.DisplayHandle, yRow, yCol, yRow, hv_C2, 1);
								else
									HOperatorSet.DispArrow(imagesource.DisplayHandle, yRow, yCol, yRow, hv_C1, 1);
							}
						} 
						else  // Dikey Parça
						{
							double pR1 = TupleToDbl(hv_R1);
							double pR2 = TupleToDbl(hv_R2);

							double uFark = Math.Abs(pR1 - yRow); // merkez ile üst noktanın farkı
							double dFark = Math.Abs(pR2 - yRow); // merkez ile alt noktanın farkı

							if (beklenenYon == 2) // Üstte bekleniyor
							{
								retval = uFark > dFark;
								imagesource.SetColor(retval ? "green" : "red");
								if (retval)
									HOperatorSet.DispArrow(imagesource.DisplayHandle, yRow, yCol, hv_R1, yCol, 1);
								else
									HOperatorSet.DispArrow(imagesource.DisplayHandle, yRow, yCol, hv_R2, yCol, 1);
							}
							else if (beklenenYon == 3) // Altta bekleniyor
							{
								retval = dFark > uFark;
								imagesource.SetColor(retval ? "green" : "red");
								if (retval)
									HOperatorSet.DispArrow(imagesource.DisplayHandle, yRow, yCol, hv_R2, yCol, 1);
								else
									HOperatorSet.DispArrow(imagesource.DisplayHandle, yRow, yCol, hv_R1, yCol, 1);
							}

						}
						PutLog(job, retval, "Parça Beklenen Yönde " + (retval ? "Geldi" : "Gelmedi"));
					}

				}
				else RedLog(job, "Seçili Bölg Bulunmamadı!");
			}
			else RedLog(job, "ROI indirgenemedi!");

			ho_ImageReduced.Dispose();
			ho_Region.Dispose();
			ho_SelectedRegions.Dispose();

			int compela = job.VarInt32("Complement", 0);
			if (compela == 1) retval = !retval;

			if (!retval)
			{
				DrawCurrentRoi(job, retval);
				int masterJob = job.VarInt32("MasterJob", 0);
				if (masterJob == 1)
					masterFailed = true;
			}

			return retval;
		}

Tek Lokasyonda 10 Farklı Kameralı Kontrol İstasyonu


Mavis, Volkswagen yan sanayilerinden Kros Otomotiv Kütahya fabrikasında, 10. kameralı kontrol istasyonunu kurmaya hazırlanıyor.

Fabrika genelindeki kameralı kontrol istasyonlarımıza ilişkin ortalama rakamlar…

  • Çalışan 7, Yapım aşamasında 3 istasyon mevcuttur
  • Her bir istasyonda ortalama 4 kamera bulunmaktadır
  • En az kamera sayısı olan istasyonda 2 kamera bulunmaktadır
  • En çok kameralı istasyon ise 7 kameralıdır
  • Her bir istasyonda ortalama 4 farklı çeşit (tip) ürün kontrol edilmektedir.
  • Her istasyon 3 vardiya çalışmaktadır

Böylece Kros Otomotiv 10 farklı istasyon ile en çok kurulum yaptığımız yer olmuştur. Kros Otomotiv’i, B/S/H/ ve Renault ve Derby izlemektedir.

Kros Otomotiv’de kurulu istasyonlarımızdan görüntüler…

Bir görevi belirli bir zaman sonra çalıştırma

Bir görevi belirli ve kesin bir süre sonra çalıştırmak gerekirse ne yapılır?

En basit yöntem, bir Timer kurmak ve bu Timer ın interval değerini belirli değere set ederek TimerTick olayında görevin çalışmasını tetiklemektir.  Daha komplex uygulamalarda bu yöntemi kullanmak zahmetli ya da sorunlu olabilir (Genelde de olur).

Böyle durumlarda thread kullanmak ve thread çalışması için belirli bir zaman atamak daha profesyonel bir yaklaşımdır. .NET halihazırda thread tetiklenmesi, bekletilmesi, sonlandırılması vb. işlemler için güçlü API desteği sunar.

Kameralı Kontrol uygulamamız, yakaladığı hatalı parçayı bir t süre sonra üflemek suretiyle konveyörden atacak olsun.

		public void UfleP(int t)
		{
			System.Threading.Thread.Sleep(t);
			SetDigitalOutput(1);
		}

gibi bir kod çalıştırıldığında, sistem t süresi kadar bekleyecek sonra dijital output set etmek suretiyle üfleme yapacaktır. Bu kod, bekleme süresinde (Sleep) sistemi uyutacağından, görüntü işleme uygulamalarında akla bile getirilmemelidir. Yapılması gereken, bu fonksiyonu çağıran yapıyı bir thread haline getirmek, thread i start edip unutmak, thread kendi içinde beklese bile ana programın bundan hiç etkilenmemesini sağlamaktır. Thread çalışırken bekleyecek, görevini yapacak ve en nihayetinde kendi halinde  sonlanacaktır (Terminate). Tüm bu olan bitenden ne ana programın, ne de programcını haberdar olması bile gerekmez. (Tam ideal durum. Yaz ve unut. Hiçbir Timer bu kadar kullanışlı olamazdı.)

Burada bir sorun, thread için parametre geçirmektir. Normal thread çağırırken parametre kullanımına izin vermez. Parametre kullanımı için ParameterizedThreadStart kullanmak gerekir.

Yapıyı yeniden kodlarsak;

		public void UfleP(object t)
		{
			int iSleep = (int)t;
			System.Threading.Thread.Sleep(iSleep);
			SetDigitalOutput(1);
		}

şekline dönüştürerek (parametrenin object türünden olduğuna dikkat edin. Dolayısıyla birden fazla parametre vermek, dizi geçmek vb. object türünün sağladığı sınırsız avantajlar burada kullanılabilir…)

		private void PickAfterMilliSecond(int msAfter)
		{
			System.Threading.ParameterizedThreadStart starter = new System.Threading.ParameterizedThreadStart(UfleP);
			new System.Threading.Thread(starter).Start(msAfter);
		}

gibi bir yapı ile, Parametrik Thread yapımızı programda istediğimiz yerden çağırabiliriz. Dertsiz tasasız çalışacaktır…

Delphi içinden uEye Kamera Kullanımı

iDS uEye kameralar, son derece güçlü bir SDK ile birlikte gelmektedirler.  Güçlü yanlarını genel başlıklar altında toplarsak

  • 32 bit ve 64 bit işletim sistemleri için %100 destek
  • Tüm platformlar için %100 destek (Native Windows, .NET, Linux, Web)
  • ActiveX, OCX bileşenleri olarak kullanabilme
  • Native .NET kütüphanesi
  • Tüm görüntü işleme arabirimleri (HALCON, neurocheck, cognex…)
  • Birçok derlenmiş ve çalışan örnek uygulamalar

vb. sayılabilir.

.NET ortamında uygulama geliştirmek için, C# ve onun güçlü IDE si VS yi kesinlikle tavsiye ederim. Buna rağmen, Delphi ile uygulama geliştirmenin cazibesi de hala devam etmektedir. Bunları da kendime göre kısaca sıralarsam;

  • En hızlı Native Win32 uygulama
  • Delphi 1 den itibaren standartlaşmış güçlü ve oturmuş IDE
  • ObjectPascal dilinin getirdiği güç artı kolaylık
  • Birçok VCL, hazır kod, library vb. vb.
  • Açıklanamayan Delphi bağlılığı/sevgisi

Delphi 1 den Delphi 2010 versiyonuna kadar Delphi kullanmış biri olarak, laf açılmışken susmam son derece zor olduğundan, lafı burada keserek, Delphi içinden uEye kameralarının kullanımına geçiyorum.

1. Öncelikle uEye Driverları ve programları Full olarak kurulur. (www.ueyesetup.com)

2. uEye Demo programı ile kameranın düzgün olarak çalıştığı test edilir.

3. Şimdi Delphi Çalıştırılır (Resimler Delphi2009 programından alınmıştır. Delphi5 ten itibaren aynı mantık geçerli olduğundan tüm versiyonlar için burada anlatılanlar geçerli olacaktır)

ilk iş olarak, diğer tüm ActiveX bileşenlerinde olduğu gibi, uEye ActiveX bileşeni Delphi içinden yeni bir component olarak install edilir.

Bunun için Component ana menüsünden, “Import Component” menü öğesi seçilir. Gelen ekranda “Import ActiveX Control” radio button işaretlenir ve aşağıdaki ekran gelir.

Bu ekranda uEyeCam ActiveX Control bulunur ve istenilen Delphi component paletine yüklenir. (Ben Additional sekmesini seçtim. ActiveX ya da System gibi palet ler daha okunaklı görünebilir. İStediğinizi seçebilirsiniz zaten, dilerseniz yeni bir palet te oluşturabilirsiniz)

Sonraki ekranda Create New Unit” seçerek ilerledim ve nihayetinde Deplhi bana yeni ActiveX bileşenimin Additional paletine başarıyla yüklendiğini söyledi.

Artık delphi içinden kullanıma hazırım.

Normal form tasarlar gibi çalışıyorum artık. Additional paletinden uEye ActiveX bileşenini formun üzerine koydum ve istediğim gibi ölçeklendirdim.

sonrasında işime yarayacak bazı butonlar koydum. Kamera Aç, Kapat, Resmi Farklı Kaydet vb. gibi.

“Yükle” butonuna

uEyeCam1.InitCamera(0);

kodunu ekliyorum. Ve daha fazla kod yazmadan hemen F9 a basıyorum ve ta taaa… Yine Delphi hızı ve mucizesi ekranda beliriyor…

Dilersem, fotoğrafı farklı kaydetmek için;

uEyeCam1.SaveImage(”);

kodunu kullanabilirim. Parametee olarak ” kullanırsam, kaydedeceğim yeri seçmem için Save Dialog görüntülenecektir. Belirli bir yere kaydetmek istiyorsam, bunu parantez içinde belirtmem yeterlidir.

Kamera ile işim bittiğinde (Formu kapatırken vs.)

uEyeCam1.ExitCamera;

kodunu çağırmam yeterli olacaktır.

Hepsi bu kadar kolay…

uEye ActiveX bileşeninin yüzlerce fonksiyonu mevcuttur ve bunlar Help kısmında son derece detaylı açıklanmıştır. İhtiyaç duyulabilecek hemen her fonksiyon gerçek bir Alman mühendisliği inceliğiyle kodlanmıştır. Programcıya son derece kolay bir kodlama işi kalmış, geliştirilen uygulama diğer Delphi uygulamalarında olduğu gibi, “En Hızlı” ve “En Şık” olarak harddisk te yerini almıştır.

Bir tüyo : Ekranda karşınıza çıkan default iDS uEye görüntüsünden hoşlanmıyorsanız, kendi marka ve logonuzu içeren bitmap dosyayı C:\Windows\System32 klasörüne uEyeCamOcx.bmp ismiyle kaydedin 😉

Uygulama başlar başlamaz sizi bu ekran karşılayacaktır.

Mavis Kütahya’da

Yeni kameralı kontrol masaları kurulumları ve eski kurulu sistemlere bakım amacıyla Mavis teknik personeli bugün Kütahya Kros Otomotivde bulunmaktadır.

Yapılacak Çalışmalar

  • Barkod yazıcıların yeni etiket formlarının uyarlanması
  • Yeni 2 kameralı kontrol masasının montajı ve devreye alınması
  • Eski hatlarda bakım ve kontrol yapılması

Arçelik Çamaşır Makinesi Soket Kontrolü

Mavis, Arçelik Çayırova kampüsündeki çamaşır makinası üretim tesislerinde kameralı kontrol uygulamalarını devreye aldı.

Yapılan Kontroller

  • Soket Varlık Kontrolü
  • Soket Pozisyon Kontrolü

Program, üretim hattında %100 kontrol esasına göre çalışmaktadır. Hatalı üretim görüldüğünde sesli ve görsel ikaz ile operatörü bilgilendirmektedir.

Soket hatalı takılmış (Tam yerine oturmamış)

Soketler düzgün olarak takılmış

Projenin Zorlukları :

  • Takılan soketlerin ve zeminin beyaz renkte olması
  • Soketin yerine tam oturmaması durumunda aradaki farkı algılamanın zor olması