Evlenme Teklifi Yapan Robot Nasıl Yapılır :)
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.
1 2 3 4 5 | // // 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | // 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.
1 2 3 4 5 6 7 8 | 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ı
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | /* 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.


Bu kadar zahmete girdiğine göre gerçekten çok sevdiğin birisiyle evlenmişsin. Ömür boyu mutluluklar dilerim. 🙂
Teşekkür ederim.