IMA Juno’yu Kıpırdatmak
IMA Juno denilen ve bluetooth üzerinden kontrol edilebilen aşağıdaki 2 tekerli şirin robotun STL dosyası ve Arduino kodu kendi sitesinde sunulmaktadır. Tekerleri ve şasisi dahil plastik aksamının tamamı 3 boyutlu yazıcıdan alınan bu robotun Google Play Store’daki kendi Android uygulaması bluetooth eşleşmesi yapılmasına rağmen Juno’yu algılamadı ve dolayısıyla çalıştırmadı.
Bu çalışmada Juno’yu hareket ettirebilmek için “Arduino ile Bluetooth Kontrollü Araba Uygulaması” Juno’ya göre revize edildi. Bluetooth kontrollü araba ile Juno arasındaki donanımsal en büyük fark Juno’da DC motor yerine 360 derecelik servo motor kullanılmasıydı. Bunun için Arduino kodunun servo motorlara göre tekrardan düzenlenmesi gerekti. Servo motorların kullanımı ile ilgili ufak bir araştırma ile bilinmesi gereken temel noktalardan bazıları şöyle sıralanabilir:
- Servo kütüphanesi write() fonksiyonu değeri; standart servoda milin dönme açısını belirlerken 360 derecelik servoda milin dönme hızını belirler.
- write() fonksiyonu değeri 90 ise 360’lık servo hareket etmez.
- write() fonksiyonu değeri 0-90 arası ise mil bir yöne, 90-180 arası ise diğer yöne hareket eder. 0 ve 180 servonun en hızlı döndüğü uç değerlerdir.
- Servo.h kütüphanesi koda eklendiğinde Arduino Uno’da 9. ve 10. pinlerin PWM özelliği servo motorun bu pinlere bağlanıp bağlanmadığına bakılmaksızın pasif hale getirilir.
- FS90R’nin (360’lık servo) yüksüz 4.8V’de çektiği akım 100mA iken 6V’da 120mA’dir.
- Servolar fazla akım çekebileceğinden bir ve ikiden fazla sürülmesi gerektiğinde ayrı bir kaynaktan (Arduino’nun +5V pininden değil) beslenmesi gerekmektedir.
IMA Juno için devre tasarımı aşağıdaki gibi değiştirildi:
Revize edilmiş Arduino kodumuz ise şöyledir:
/*** IMA Juno Alternative ***/ #include <Servo.h> Servo solServo, sagServo; /*Servo veri pinleri*/ int solServoPin = 9; //Sol servo motor veri pini int sagServoPin = 10; //Sağ servo motor veri pini String satir = ""; //Seri porttan okunan satir int yon = 0; //Gelen yön verisi int hiz = 0; //Gelen hiz verisi int semiColonPos = 0; //Noktalı virgül pozisyonu int solServoHizi = 0; //Sol servo motor hizi int sagServoHizi = 0; //Sağ servo motor hizi /*Ledler*/ int solLed = 4; int sagLed = 5; int arkaSolLed = 6; int arkaSagLed = 7; void setup() { solServo.attach(solServoPin); sagServo.attach(sagServoPin); pinMode(solLed, OUTPUT); pinMode(sagLed, OUTPUT); pinMode(arkaSolLed, OUTPUT); pinMode(arkaSagLed, OUTPUT); digitalWrite(solLed, HIGH); digitalWrite(sagLed, HIGH); digitalWrite(arkaSolLed, HIGH); digitalWrite(arkaSagLed, HIGH); Serial.begin(9600); //Zaman aşımı süresi appinventor timer süresi (15ms) //ile uyumlu hale getiriliyor Serial.setTimeout(15); } void loop() { if (Serial.available()>0) { satir = Serial.readString(); //15ms zaman aşımı /* * Gönderilen veri Yön;Hız formatındadır. * Yön için: 1-> Sağa, 2->Sola, 3->İleri, 4->Geri, 0->Dur * Hız için değer aralığı 0..90'dır. */ semiColonPos = satir.indexOf(';'); yon = satir.substring(0,semiColonPos).toInt(); hiz = satir.substring(semiColonPos+1).toInt(); solServoHizi = yon == 4 ? 90 - hiz : 90 + hiz; sagServoHizi = yon == 4 ? 90 + hiz : 90 - hiz; switch (yon) { case 1: //Sağa dön solServo.write(solServoHizi); sagServo.write(90); Serial.print("Sağa dönüyorum"); break; case 2: //Sola dön solServo.write(90); sagServo.write(sagServoHizi); Serial.print("Sola dönüyorum"); break; case 3: //İleri solServo.write(solServoHizi); sagServo.write(sagServoHizi); Serial.print("İleri gidiyorum"); break; case 4: //Geri solServo.write(solServoHizi); sagServo.write(sagServoHizi); Serial.print("Geri gidiyorum"); break; case 0: //Dur solServo.write(90); sagServo.write(90); Serial.print("Durdum"); break; } ledYak(); } } void ledYak() { switch (yon) { case 1: //Sağa dön digitalWrite(solLed, LOW); digitalWrite(sagLed, HIGH); digitalWrite(arkaSolLed, LOW); digitalWrite(arkaSagLed, LOW); break; case 2: //Sola dön digitalWrite(solLed, HIGH); digitalWrite(sagLed, LOW); digitalWrite(arkaSolLed, LOW); digitalWrite(arkaSagLed, LOW); break; case 3: //İleri digitalWrite(solLed, LOW); digitalWrite(sagLed, LOW); digitalWrite(arkaSolLed, LOW); digitalWrite(arkaSagLed, LOW); break; case 4: //Geri digitalWrite(solLed, LOW); digitalWrite(sagLed, LOW); digitalWrite(arkaSolLed, LOW); digitalWrite(arkaSagLed, LOW); break; case 0: //Dur digitalWrite(solLed, LOW); digitalWrite(sagLed, LOW); digitalWrite(arkaSolLed, HIGH); digitalWrite(arkaSagLed, HIGH); break; } }
Juno’nun tekerlerinin ikisinin de aynı yönde dönmesi için sol ve sağ servoların hız değerlerinin birbirinin tersi yönde olacak şekilde hesaplanmış olduğuna dikkat edelim.
Android tarafında ise Appinventor kodunda çok ufak 2 değer değişikliği yapılmıştır. Bluetooth kontrollü arabada analogWrite() için 0-255 arası hız değeri gönderilirken Juno’da 0-90 arası hız değeri gönderilmektedir. Ayrıca hız artışı 15’ten 5’e düşürülmüştür. Bunun haricinde Appinventor Android uygulama bileşenleri ve Android uygulamasının çalışması “Bluetooth Kontrollü Araba Uygulaması – 2. Kısım” ile tamamen aynıdır. İlgili yazıdan Android uygulamasının çalışması incelenebilir.
Uygulama arayüzü ve bileşenleri şöyledir:
Appinventor kod blokları aşağıdaki gibidir (değişiklikler kırmızı daire ile gösterilmiştir):
Uygulama görüntüleri:
Uygulama videosu:
APK dosyası linki: Dosyayı indirmek için buraya tıklayınız.
Uygulamayı pdf formatında indirmek için buraya tıklayınız.
Kaynakça
https://www.exploremaking.com
https://www.thingiverse.com/thing:1720394
https://github.com/exploremaking/Juno/blob/master/Juno.ino
https://www.arduino.cc/en/Reference/Servo
https://www.arduino.cc/en/Reference/ServoWrite
http://ctc-dev.verkstad.cc/en/course-literature/continuous-rotation-servo/
https://core-electronics.com.au/continuous-rotation-micro-servo-fs90r.html
https://media.digikey.com/pdf/Data%20Sheets/Adafruit%20PDFs/2442_Web.pdf
Arduino ve Python ile Basit Radar Uygulaması – 2. Kısım
Arduino ve Python ile basit radar uygulamasının ilk kısmında ölçülen mesafe bilgileri; ilgili açı bilgisi ile beraber seri porta gönderilmişti. Bu kısımda ise seri porta gönderilen bilgilerin Python ile okunup parse edilerek (ayrıştırılarak) grafiğe dönüştürülmesi anlatılmaktadır.
Arduino tarafında seri porta gönderilen veriler şöyle görülmektedir:
Verileri parse edip grafikleştiren Python kaynak kodumuz ise şu şekildedir:
# -*- coding: utf-8 -*- import serial, turtle #Arduino'nun bilgisayara bağlı olduğu seri porta bağlan ser = serial.Serial(port='COM6', baudrate=9600, timeout=0) print("connected to: " + ser.portstr) #Grafik çizmek için turtle nesnesinin özellikleri turtle.speed(0) turtle.pensize(1) turtle.pencolor("black") turtle.fillcolor("green") #Mesafe ve açı bilgilerini yazmak için #ikinci turtle nesnesi ve özellikleri trt2 = turtle.Turtle() trt2.speed(0) trt2.penup() trt2.hideturtle() #Mesafe bilgilerini grafikleştirmeden önce #ekrana uzaklıklara ait temsili #yarım daireleri çiz def yarimDaireCiz(): turtle.clear() turtle.pencolor("black") turtle.penup() turtle.goto(0,-20) turtle.write("0") turtle.goto(100,-20) turtle.write("10cm") turtle.goto(200,-20) turtle.write("20cm") turtle.goto(300,-20) turtle.write("30cm") turtle.goto(400,-20) turtle.write("40cm") turtle.goto(-100,-20) turtle.write("10cm") turtle.goto(-200,-20) turtle.write("20cm") turtle.goto(-300,-20) turtle.write("30cm") turtle.goto(-400,-20) turtle.write("40cm") turtle.penup() turtle.home() turtle.forward(400) turtle.seth(90) turtle.pendown() turtle.begin_fill() turtle.circle(400,180) turtle.end_fill() turtle.seth(0) turtle.penup() turtle.home() turtle.forward(300) turtle.seth(90) turtle.pendown() turtle.circle(300,180) turtle.seth(0) turtle.penup() turtle.home() turtle.forward(200) turtle.seth(90) turtle.pendown() turtle.circle(200,180) turtle.seth(0) turtle.penup() turtle.home() turtle.forward(100) turtle.seth(90) turtle.pendown() turtle.circle(100,180) turtle.seth(0) #Seri porttan okunup parse edilen #aci ve mesafe bilgilerini #cizgiye dönüştür def cizgiCiz(aci, mesafe): turtle.penup() turtle.home() turtle.seth(aci) turtle.forward(mesafe*10) # 1cm = 10birim turtle.pencolor("red") turtle.pendown() turtle.forward(400-mesafe*10) #Parse edilen aci ve mesafe bilgilerini #ekrana yaz def mesafeYaz(aci, mesafe): trt2.clear() trt2.goto(-50,-40) trt2.write("Açı: "+str(aci)+" - Mesafe: "+str(mesafe)+ " cm") #Seri porttan okunan bilgileri parse et def veriAyikla(veri): veriler = veri.split(";") aci = veriler[0].split(":") mesafe = veriler[1].split(":") aci = int(aci[1]) mesafe = int(mesafe[1]) mesafeYaz(str(aci),str(mesafe)) if mesafe>=40 or mesafe == 0: mesafe = 41 if aci == 0 or aci == 180: yarimDaireCiz() cizgiCiz(aci, mesafe) #Seri porttan sürekli veri oku line = "" while True: for c in ser.read(): if c == '\n': veriAyikla(line) line = "" break else: line += c ser.close()
Programda öncelikle Arduino’nın veri gönderdiği -uygulamamızda USB’den bilgisayara bağlı olan- porta bağlanıyoruz. (Port bilgilerini ve baudrate değerini uygun şekilde değiştirmelisiniz)
Turtle modülü kullanımı kolay ve basit çizim işlemlerinde kullanılabilen bir python modülür. Programda iki tane turtle nesnesi kullanılmıştır. “turtle” mesafe bilgilerine göre çizgi çizmek için, “trt2” ise mesafe ve açı bilgilerini ekrana yazmak için kullanılan nesnedir. Seri port bağlantısının ardından turtle nesneleri ve özellikleri tanımlanmıştır.
yarimDaireCiz() fonksiyonu ekrana uzaklıklara ait temsili yarım daireler çizmek için kullanılmaktadır. Radar uygulama alanı 40 cm olarak düşünülmüştür. 1 cm 10 birim baz alınarak yarım daireler bu fonksiyon ile ekrana çizilmektedir. Gelen açı değeri her 0 ve 180 derece olduğunda yarimDaireCiz() program içinden çağırılmaktadır. Önce ekran temizlenmekte ardından yarım daireler çizilmektedir. Fonksiyon bir nevi arka plan oluşturmaktadır.
cizgiCiz() fonksiyonu ise parse edilen mesafe ve açı bilgilerini parametre olarak almaktadır. Bu verilerle ekrana istenilen açıda gelen mesafe bilgisi bazlı çizgi çizmektedir. “turtle” nesnesi çizgi çizmeden gelen mesafeye göre önce ileri gitmekte (santim başına 10 birim) ardından 40cm’den kalan mesafe kadar kırmızı çizgi çizmektedir.
Programda yer alan diğer 2 fonksiyondan biri mesafeYaz() ve diğeri ise veriAyikla() fonksiyonudur. mesafeYaz() fonksiyonu parametre olarak gönderilen açı ve mesafe bilgilerini “trt2” nesnesiyle ekrana yazmaktadır.
veriAyikla() fonksiyonu ise seri porttan okunup kendisine gönderilen veriyi parse etmektedir. Öncelikle “;” ardından “:” karakterinden ayrıştırma yaparak açı ve mesafe bilgilerini elde eder. Ekrana uzun çizgi çizilmemesi ve radar uygulama alanı 40 cm düşünüldüğü için mesafe 40’tan fazla ise 40’a eşitlenmektedir. Mesafeye göre çizgi çizilmeden önce de (cizgiCiz() fonksiyonu çalıştırılmadan önce) açı değeri 0 veya 180 ise yarimDaireCiz() bu fonksiyon içinden çağırılmaktadır.
Program akışı, seri port bağlantısı ve turtle nesnelerinin özelliklerinin tanımlanmasının ardından sürekli olarak seri porttan 1 byte veri okuma işlemi ile devam eder. Okunan karakter satır sonu (\n) değilse devamlı “line” değişkeninin sonuna eklenir. Satır sonu karakteri gönderilmişse (açı ve mesafe bilgisi artık tamamlanmışsa) “line” değişkeninde birleştirilmiş veri parse edilmesi için veriAyikla() fonksiyonuna gönderilir. Önce açı ve mesafe bilgileri elde edilir. Sonrada açı ve mesafe bazlı çizgi çizilir. Döngü kesintisiz devam eder.
Uygulama ekran görüntüsü şöyledir:
Kaynakça
https://elinux.org/Serial_port_programming
https://stackoverflow.com/questions/16077912/python-serial-how-to-use-the-read-or-readline-function-to-read-more-than-1-char
http://pyserial.readthedocs.io/en/latest/shortintro.html
https://docs.python.org/2/library/turtle.html