Evlenme teklifi yapan robot yapma fikri nereden çıktı derseniz, kız arkadaşıma bir mühendis ile evlenmenin nasıl olduğunu taa baştan göstermek istedim arkadaşlar. 🙂 Dolayısıyla bu yazının benim için çok ayrı bir yeri var. Çünkü bir ömür yaşamak istediğim kişiye adayacağım bir paylaşım. Gelelim çok uzatmadan robotun nasıl yapıldığına. Belki bir kaç modifiye ile sizde kendi evlenme teklifi tarzınızı oluşturabilirsiniz.
Bu yazı yazan robot paralel robot ailesine mensup olup iki kolu bulunmaktadır. Hatırlarsanız daha önce 3 kollusunu yapmıştık (Bkz. Parelel Robot – Giriş). Bir yazı tahtası üzerine iki kolun birleştiği noktaya bir servo motor daha koyarak kalem ve silgiyi hareket ettirebilen bir mekanizmaya sahiptir. Üçüncü servo motor aynı zamanda kalemin tahta üzerinden kalkmasını bu sayede kesikli çizgiler oluşturabilmenizi sağlamaktadır.
Çift Kol Paralel Robot Kinematik Denklem Hesaplanması
Mekanizmadan anlayabileceğiniz üzere yazıyı oluşturabilmeniz için ihtiyacınız olan sadece 2 adet kolun açı bilgisi. Bir fonksiyon düşünün x ve y koordinatlarını giriyorsunuz size açı bilgilerini çıkarıyor. Buna ek olarak kalem servosuna ne zaman aşağı yukarı kalkacağı bilgisini veriyorsunuz.
Kinematik denklemini incelersek iki çember kesişmesi formülünü kullanacağız. Daha önce bu konuda paylaşım yaptığım için detaya girmiyorum (Bkz. İki çember Kesisim Noktası Nasıl Bulunur).
Mekanizmada kollar birbirlerine eş ölçüde oldukları için hesaplarımız sadeleşmiştir. Aşağıda çizdiğim mekanizmanın yazdığınız yazıya göre x ve y kordinatlarının oluşturduğu sol ve sağ kol açılarını görebilirsiniz.
Robotun yazmanızı istediğiniz yazıyı tek tek bu şekilde üzerinden geçerek sol ve sağ açı değerlerini çıkarabilirsiniz. Bu yöntem zahmet isteyen bir yöntemdir. Bunun yerine yazılım zekanızı kullanmanızı tavsiye ederim. Kinematik denklemin nasıl oluşturulduğunu aşağıda hem excelde hem de visual studio c# ortamında bulabilirsiniz.
S harfini Excel’ de x ve y koordinatlarını girerek daha önceki çember kesişmesi formülleriyle açı değerlerini çıkarabilirsiniz. Burada tek yapmanız gereken bir yazılım kullanarak x ve y koordinatlarını üretmeniz sizin açınızdan daha sağlıklı olacaktır.
Evlenme Teklifi Yapan Robot C# Yazılımı
Eğer aynı mekanizmayı C# dilinde denemek istiyorsanız aşağıdaki kodu inceleyebilirsiniz. C# programında çizgi çizme metotlarıyla istediğiniz şekilde mekanizmalar tasarlayabilirsiniz. Bir form açın ve sadece iki tane label ekleyin bunlar açı bilgilerini ekrana basmamıza yarayacaktır.
Yukarıdaki formda gördüğünüz çizgileri mouse_click olayı ile form ekranına tıkladığınızda mouse’ un x ve y pozisyon bilgisini alıp açı bilgilerini üretip iki çember kesişmesi formülü ile kol çizgilerini çıkaran kod ile üretilmektedir.
MouseClick Olayı ve Paint özelliği için designer.cs dosyanızdaki form1 özelliklerine aşağıki iki satırı ekleyin. Direk form üzerinde de ekleyebilrisiniz daha önce görmüştük. Bilmeyenler için tekrar yazayım istedim.
// // Form1 // this.Paint += new System.Windows.Forms.PaintEventHandler(this.Form1_Paint); this.MouseClick += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseClick);
Ana program kodu aşağıdadır.
// kaizen40.com using System; using System.Drawing; using System.Windows.Forms; namespace ParalelRobotMouseKontrol { public partial class Form1 : Form { PointF mouse_pos; private bool draw_enable = false; PointF Intersection1, Intersection2; //int NumIntersections = 0; // kol uzunlukları gerçekte iki katı burada yarım olarak pixel cisinden yazılmıştır (1 pixel = 0.264583 mm) private int L1 = 316, L2 = 483, R1 = 316, R2 = 483; // L2 ve R2 aynı uzunlukta olmalı yoksa formül yanlış hesaplar // kol mafsal noktaları private float L1x1 = 278, L1y1 = 555, R1x1 = 555, R1y1 = 555; // L1y1 ve R1y1 aynı hizada olmalı public Form1() { InitializeComponent(); } private void Form1_Paint(object sender, PaintEventArgs e) { Pen blackPen = new Pen(Color.Black, 3); Pen redPen = new Pen(Color.Red, 3); // kalem tanımlandı Pen bluePen = new Pen(Color.Blue, 3); // kalem tanımlandı e.Graphics.DrawRectangle(blackPen, 0, 0, 833, 555); // çalışam alanı çerçevesini çiz if (draw_enable == true) { draw_enable = false; // sol ve sağ kol noktaları float cx0 = mouse_pos.X, cy0 = mouse_pos.Y, radius0 = L2, // üst daire cx1 = L1x1, cy1 = L1y1, radius1 = L1, // sol daire cx2 = R1x1, cy2 = R1y1, radius2 = R1; // sağ daire // kesişmeyi hesapla FindCircleCircleIntersections(cx0, cy0, radius0, cx1, cy1, radius1, out Intersection1, out Intersection2); // sol kol çizgileri çiz e.Graphics.DrawLine(bluePen, cx0, cy0, Intersection2.X, Intersection2.Y); e.Graphics.DrawLine(redPen, cx1, cy1, Intersection2.X, Intersection2.Y); double SolKolAci = (Math.Atan((cy1 - Intersection2.Y) / (cx1 - Intersection2.X)) * 180 / Math.PI); if (SolKolAci > 0) { LabelSolKolAci.Text = (90 - SolKolAci).ToString(); } else { LabelSolKolAci.Text = (-1 * (SolKolAci) + 90).ToString(); } //üst kol açısını hesapla double SolUstKolAci = (Math.Atan((Intersection2.Y - cy0) / (cx0 - Intersection2.X)) * 180 / Math.PI); // +45 derece ye göre kalem konumlanmıştır, 127 pixel 141 dpi de 67 mm yapar float x1 = cx0 + Convert.ToSingle(127 * Math.Cos(DegToRad(Convert.ToInt32(SolUstKolAci + 45)))); float y1 = cy0 - Convert.ToSingle(127 * Math.Sin(DegToRad(Convert.ToInt32(SolUstKolAci + 45)))); e.Graphics.DrawLine(redPen, cx0, cy0, x1, y1); // sağ kol değişkenlerini ata cx1 = cx2; cy1 = cy2; radius1 = radius2; FindCircleCircleIntersections(cx0, cy0, radius0, cx1, cy1, radius1, out Intersection1, out Intersection2); // sağ kol çizgileri çiz e.Graphics.DrawLine(bluePen, cx0, cy0, Intersection1.X, Intersection1.Y); e.Graphics.DrawLine(redPen, cx1, cy1, Intersection1.X, Intersection1.Y); double SagKolAci = (Math.Atan((cy1 - Intersection1.Y) / (cx1 - Intersection1.X)) * 180 / Math.PI); if (SagKolAci > 0) { LabelSagKolAci.Text = (90 - SagKolAci).ToString(); } else { LabelSagKolAci.Text = (-1 * (SagKolAci) + 90).ToString(); } } DrawPoint(e.Graphics, mouse_pos, Brushes.HotPink, Pens.Red); redPen.Dispose(); blackPen.Dispose(); bluePen.Dispose(); } private double DegToRad(int deg) { return Math.PI * deg / 180; // dereceyi radyana çevir } private int FindCircleCircleIntersections( float cx0, float cy0, float radius0, float cx1, float cy1, float radius1, out PointF intersection1, out PointF intersection2) { // Find the distance between the centers. float dx = cx0 - cx1; // üst dairenin merkezi: cx0, float dy = cy0 - cy1; double dist = Math.Sqrt(dx * dx + dy * dy); // See how many solutions there are. if (dist > radius0 + radius1) { // No solutions, the circles are too far apart. intersection1 = new PointF(float.NaN, float.NaN); intersection2 = new PointF(float.NaN, float.NaN); return 0; } else if (dist < Math.Abs(radius0 - radius1)) { // No solutions, one circle contains the other. intersection1 = new PointF(float.NaN, float.NaN); intersection2 = new PointF(float.NaN, float.NaN); return 0; } else if ((dist == 0) && (radius0 == radius1)) { // No solutions, the circles coincide. intersection1 = new PointF(float.NaN, float.NaN); intersection2 = new PointF(float.NaN, float.NaN); return 0; } else { // Find a and h. double a = (radius0 * radius0 - radius1 * radius1 + dist * dist) / (2 * dist); double h = Math.Sqrt(radius0 * radius0 - a * a); // Find P2. double cx2 = cx0 + a * (cx1 - cx0) / dist; double cy2 = cy0 + a * (cy1 - cy0) / dist; // Get the points P3. intersection1 = new PointF((float)(cx2 + h * (cy1 - cy0) / dist), (float)(cy2 - h * (cx1 - cx0) / dist)); intersection2 = new PointF((float)(cx2 - h * (cy1 - cy0) / dist), (float)(cy2 + h * (cx1 - cx0) / dist)); // See if we have 1 or 2 solutions. if (dist == radius0 + radius1) { MessageBox.Show("Teğet geçti"); return 1; } /* TxtPoints.Text = "1. Kesişim X: " + Intersection1.X + "\r\n" + "1. Kesişim Y: " + Intersection1.Y + "\r\n\r\n" + "2. Kesişim X: " + Intersection2.X + "\r\n" + "2. Kesişim Y: " + Intersection2.Y + "\r\n"; */ return 2; } } private void Form1_MouseClick(object sender, MouseEventArgs e) { mouse_pos.X = e.X; mouse_pos.Y = e.Y; draw_enable = true; Invalidate(); // yeniden çiz } private void DrawPoint(Graphics gr, PointF pt, Brush brush, Pen pen) { const int RADIUS = 3; gr.FillEllipse(brush, pt.X - RADIUS, pt.Y - RADIUS, 2 * RADIUS, 2 * RADIUS); gr.DrawEllipse(pen, pt.X - RADIUS, pt.Y - RADIUS, 2 * RADIUS, 2 * RADIUS); } } }
Burada bir noktaya dikkat etmek gerekiyor. Mekanizmaya koyduğunuz servo motorların konumu. Ben bu motorları birbirine bakacak şekilde koyduğum için sol ve sağ ko laçı değerleri ter olmaktadır. Dolayısıyla ufak bir if-else koşulu ile bu sorunu çözdüm. Sizde kendi yapacağınız mekanizmaya göre farklılık olabilir en son hesapladığınız açı değerini servoya gönderilebilecek şekilde ayarlamayı unutmayın.
Eğer açı 0′ dan büyükse 180 ile topla değilse aynısı göm. Bu eklentiyi C# kodunda da farketmişsinizdir.
if (SagKolAci > 0) { LabelSagKolAci.Text = (90 - SagKolAci).ToString(); } else { LabelSagKolAci.Text = (-1 * (SagKolAci) + 90).ToString(); }
Peki işimiz bitti mi? Hayır 🙂 O halde bu açı bilgilerini ne yapacağız. Tabiki de bir arduino kartı üzerinden servo motorlarımızı süreceğiz. (Bkz. Servo Kontrolü Arduino)
Evlenme Teklif Yapan Robot Arduino Programı
Gelelim Arduino’ya, ilk olarak Uno bağlamıştım ama rom belleği yetmedi. O yüzden Mega bağlayarak ihtiyacım olan belleğe kavştum. Eğer bellek ile sorun yaşarsanız sd kart bağlayarak oradan açı bilgilerini okutabilirsiniz. 3 adet motor ve bir adet de parmak izi okuyucu bağlantısı yaptım. Unutmayın evlenme tekflifi yapıyorsunuz sizden başkası bu teklifi yapamasın diye parmak izimi arduino’ ya yükledim, bu şekilde tetikleme yapıyorum. 🙂
(Bkz. Arduino Parmak izi okuyucu kullanımı)
Arduino bağlantı şeması
Arduino Programı
/* web : www.kaizen40.com tarih: 28.08.2018 yazan: Remzi ŞAHİNOĞLU board: MEGA bu program biricik sevgilime evlenme teklifi etmek için yaptığım robotun elektronik devresindeki arduino programıdır. 3 adet servo motorların robot kol pozisyonlarını başlangçta ayarlamak için yazılmıştır. Arduino 9.pin : Kalem servosu, myservo[0] 10.pin : Sağ Kol servosu, myservo[1] 11.pin : Sol Kol servosu, myservo[2] */ #include <Adafruit_Fingerprint.h> // On Leonardo/Micro or others with hardware serial, use those! #0 is green wire, #1 is white // uncomment this line: // #define mySerial Serial1 // For UNO and others without hardware serial, we must use software serial... // pin #2 is IN from sensor (GREEN wire) // pin #3 is OUT from arduino (WHITE wire) // comment these two lines if using hardware serial //#include <SoftwareSerial.h> //SoftwareSerial mySerial(2, 3); Adafruit_Fingerprint finger = Adafruit_Fingerprint(&Serial1); #include <Servo.h> Servo myservo[3]; // istenilen servo objesini oluştur int r_pos[50][3] = { // S {94,158,90}, {94,158,90}, {94,158,90}, {94,158,90}, {94,158,90}, {94,158,90}, {94,158,90}, {94,158,90}, {94,158,90}, {94,158,90}, {94,158,120}, {94,159,120}, {94,159,120}, {93,160,120}, {93,161,120}, {94,162,120}, {94,162,120}, {94,163,120}, {94,164,120}, {95,164,120}, {95,165,120}, {96,165,120}, {97,165,120}, {98,165,120}, {99,165,120}, {99,165,120}, {100,164,120}, {100,163,120}, {100,163,120}, {100,162,120}, {100,161,120}, {100,161,120}, {100,160,120}, {100,159,120}, {100,158,120}, {101,158,120}, {101,157,120}, {103,156,120}, {104,156,120}, {105,157,120}, {106,158,120}, {107,158,120}, {107,159,120}, {107,160,120}, {107,161,120}, {107,162,120}, {107,163,120}, {106,164,120}, {106,164,90}, {106,164,90} // diğer harflere bu şekilde devam edebilirsiniz }; String inputString; boolean r_start, s_start, c_start; void setup() { Serial.begin(9600); while (!Serial); // For Yun/Leo/Micro/Zero/... delay(100); Serial.println("\n\nAdafruit finger detect test"); // set the data rate for the sensor serial port finger.begin(57600); if (finger.verifyPassword()) { Serial.println("Found fingerprint sensor!"); } else { Serial.println("Did not find fingerprint sensor :("); while (1) { delay(1); } } finger.getTemplateCount(); Serial.print("Sensor contains "); Serial.print(finger.templateCount); Serial.println(" templates"); Serial.println("Waiting for valid finger..."); pinMode(s_buton, INPUT_PULLUP); // servoların bağlı olduğu pinler tanımlanıyor myservo[0].attach(11); // 3.servo, sol kol myservo[1].attach(10); // 2.servo, sağ kol myservo[2].attach(9); // 1.servo, kalem servosu delay(500); myservo[2].write(90); // başlangıçtaki açı bilgisini servoya ver myservo[1].write(90); myservo[0].write(90); delay(500); } void loop() { getFingerprintIDez(); delay(50); //don't ned to run this at full speed. inputString = ""; while(Serial.available()) { char inChar = (char)Serial.read(); if(inChar == 'r') // soru yaz komutu geldiğinde { r_start = true; } } if (r_start == true) // "benimle evlenir misin?" yazısı yazılsın { r_start = false; for (int i = 0; i < 50; i++) // pozisyonlara sırayla git { for (int j = 0; j < 3; j++) { myservo[j].write(r_pos[i][j]); } //Serial.println((String)r_pos[i][0] + "," + (String)r_pos[i][1] + "," + (String)r_pos[i][2]); delay(75); } } } uint8_t getFingerprintID() { uint8_t p = finger.getImage(); switch (p) { case FINGERPRINT_OK: Serial.println("Image taken"); break; case FINGERPRINT_NOFINGER: Serial.println("No finger detected"); return p; case FINGERPRINT_PACKETRECIEVEERR: Serial.println("Communication error"); return p; case FINGERPRINT_IMAGEFAIL: Serial.println("Imaging error"); return p; default: Serial.println("Unknown error"); return p; } // OK success! p = finger.image2Tz(); switch (p) { case FINGERPRINT_OK: Serial.println("Image converted"); break; case FINGERPRINT_IMAGEMESS: Serial.println("Image too messy"); return p; case FINGERPRINT_PACKETRECIEVEERR: Serial.println("Communication error"); return p; case FINGERPRINT_FEATUREFAIL: Serial.println("Could not find fingerprint features"); return p; case FINGERPRINT_INVALIDIMAGE: Serial.println("Could not find fingerprint features"); return p; default: Serial.println("Unknown error"); return p; } // OK converted! p = finger.fingerFastSearch(); if (p == FINGERPRINT_OK) { Serial.println("Found a print match!"); } else if (p == FINGERPRINT_PACKETRECIEVEERR) { Serial.println("Communication error"); return p; } else if (p == FINGERPRINT_NOTFOUND) { Serial.println("Did not find a match"); return p; } else { Serial.println("Unknown error"); return p; } // found a match! Serial.print("Found ID #"); Serial.print(finger.fingerID); Serial.print(" with confidence of "); Serial.println(finger.confidence); return finger.fingerID; } // returns -1 if failed, otherwise returns ID # int getFingerprintIDez() { uint8_t p = finger.getImage(); if (p != FINGERPRINT_OK) return -1; p = finger.image2Tz(); if (p != FINGERPRINT_OK) return -1; p = finger.fingerFastSearch(); if (p != FINGERPRINT_OK) return -1; // found a match! Serial.print("Found ID #"); Serial.print(finger.fingerID); Serial.print(" with confidence of "); Serial.println(finger.confidence); r_start = true; return finger.fingerID; }
Aşağıdaki resimde uno kartı gözüküyor ama daha sonradan mega taktım bağlantı yerleri parmak izi okuyucu dışında aynı. Parmak izi okuyucuyu Arduino koduna bakarsanız Serial1′ e bağlandığını görürsünüz.
Mekanik Montajı
Plexiglass parçalarını reklamcıda kestirdim. Dİğer parçaları 3D yazıcıdan bastım. Kendi yaptığım 3D yazıcıyı da vakit bulunca nasıl yapıldığını yazacağım. Mafsalları nano rulmanlar ile M3 civatalar kullanarak birbirine bağladım. Görüşmek üzere.
2 yorum
Aydın Gürbüz · 12 Eylül 2019 11:47 tarihinde
Bu kadar zahmete girdiğine göre gerçekten çok sevdiğin birisiyle evlenmişsin. Ömür boyu mutluluklar dilerim. 🙂
Remzi Şahinoğlu · 27 Eylül 2019 08:46 tarihinde
Teşekkür ederim.