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.

Evlenme Teklifi Yapan Robot

Evlenme Teklifi Yapan Robot

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.

Yazar Robot Fonksiyon

Yazar Robot Fonksiyon

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

Çemberlerin Kesişmesi

Çemberlerin Kesişmesi

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.

Çizgisel Tasarım

Çizgisel Tasarım

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.

Excel'de Açı Hesaplama Formülü

Excel’de Açı Hesaplama Formülü

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.

Evlenme Teklifi Eden Robot Yazılımı

Evlenme Teklifi Eden Robot Yazılımı

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.

Yazar Robot Servo Konumu

Yazar Robot Servo Konumu

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ı

Yazar Robot Arduino Şeması

Yazar Robot Arduino Ş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.

Evlenme Teklifi Yapan Robot

Genel Görünüş

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.

3D Printer Parçaları

3D Printer Parçaları

Mafsal Bağlantısı

Mafsal Bağlantısı

Evlenme Teklifi Yapan Robot Çalışma Videosu


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. 🙂

Bir yanıt yazın

Avatar placeholder

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Bu site, istenmeyenleri azaltmak için Akismet kullanıyor. Yorum verilerinizin nasıl işlendiği hakkında daha fazla bilgi edinin.