Yazılım: dört motoru tek formülle sür
Bir önceki modülde kumandayı koda bağladın: sol çubuğu ileri gitmeye, sağ çubuğu dönmeye. İki motor, iki satır. Şimdi dört motorun var ve robot yalnız ileri-geri gitmiyor; burnunu hiç çevirmeden yana da kayabiliyor. Peki dört tekeri aynı anda nasıl yönetirsin? Bu derste tek bir formül kuracağız ve o formülü ezberlemeden, kendi elinle bir tabloyu doldurarak çıkaracağız.
Kanca: yana kayan robot (10 dk)
Videoyu izledikten sonra düşün: robot sağa kayarken burnu hep aynı yöne bakıyor, yani dönmüyor. Ama dört tekerin de dönmesi lazım. Sınıfa sor:
- Robot sağa kayarken dört tekerin hepsi aynı yöne mi dönüyor, yoksa bazıları ileri bazıları geri mi?
- Bir teker ileri, komşusu geri dönerse robot neden savrulmuyor da düz yana gidiyor?
Kesin cevabı şimdi vermiyoruz. Elin kodun başına geçtiğinde bunu kendin bulacaksın. Şunu aklında tut: mecanum tekerin üstündeki eğik makaralar, tekerin ittiği kuvveti 45 derece yana çevirir. Dört tekerin kuvvetini doğru işaretlerle toplayınca robot istediğin yöne süzülür.
Ana etkinlik: katkı tablosunu sen doldur (20 dk)
Kod yazmadan önce mantığı kuracağız. Kumandadan üç ayrı sayı okuyacağız, her biri -1 ile +1 arasında:
- ileri: sol çubuğu yukarı ittikçe +, aşağı çektikçe -.
- yan: sol çubuğu sağa ittikçe +, sola çektikçe - (yana kayma).
- donus: sağ çubuğu sağa ittikçe + (robotun kendi ekseninde dönmesi).
Her tekerin hızı bu üç sayının toplamıdır. Tek fark: her teker için işaretler değişir. İşaretleri ezberlemeyeceğiz; her hareketi tek tek düşünüp bulacağız.
Kağıda şu tabloyu çiz ve boş kutuları doldur. Her kutuya o teker o harekette ileri (+) mi yoksa geri (-) mi dönmeli, onu yaz. Robotu elinle it, tekerin hangi yöne dönmesi gerektiğini gözünde canlandır.
| Tekerlek | İleri giderken | Sağa kayarken | Sağa dönerken |
|---|---|---|---|
| Ön sol (FL) | ? | ? | ? |
| Ön sağ (FR) | ? | ? | ? |
| Arka sol (BL) | ? | ? | ? |
| Arka sağ (BR) | ? | ? | ? |
Doldururken üç ipucu:
- İleri giderken dört tekerin de ileri dönmesi gerekir. Bu sütun kolay: hepsi +.
- Sağa dönerken (robot kendi etrafında saat yönünde) sol taraftaki tekerler ileri, sağ taraftakiler geri döner. Tıpkı bir tankın dönmesi gibi.
- Sağa kayarken en zor sütun bu. Mecanum makaraları yüzünden çapraz duran tekerler birlikte çalışır: ön sol ile arka sağ bir yöne, ön sağ ile arka sol öbür yöne döner. Robotu masada elinle sağa kaydırmaya çalış, tekerlerin nasıl döndüğünü hisset.
Doldurduktan sonra sonuçlarını yan masayla karşılaştır. Doğru tablo şöyle:
| Tekerlek | İleri | Sağa kayma | Sağa dönüş | Formül |
|---|---|---|---|---|
| Ön sol (FL) | + | + | + | ileri + yan + donus |
| Ön sağ (FR) | + | - | - | ileri - yan - donus |
| Arka sol (BL) | + | - | + | ileri - yan + donus |
| Arka sağ (BR) | + | + | - | ileri + yan - donus |
Son sütundaki formüller, doldurduğun işaretlerin toplamından başka bir şey değil. İşte mecanum sürüşün kalbi bu dört satır. Bir sonraki adımda aynısını koda dökeceğiz.
Kavram köprüsü: formül ve normalizasyon (7 dk)
Dört formül satırını kurdun. Ama bir sorun var. Diyelim ki hem tam ileri (ileri = 1) hem tam sağa kayma (yan = 1) istiyorsun. Ön sol teker için formül 1 + 1 + 0 = 2 verir. Oysa bir motor en fazla 1 kadar hız üretebilir; 2 diye bir şey yok. Motor tavana çarpar, diğer tekerler orantısız kalır, robot istediğin yöne gitmez.
Çözüm normalizasyon: dört değerin en büyüğünü bul, eğer 1'i aşıyorsa dört değeri de o sayıya böl. Böylece en büyük değer tam tam 1 olur, geri kalanlar aynı oranda küçülür. Oranlar korunduğu için robot yönü bozulmaz; sadece hepsi birlikte hız tavanına sığar. Aşmıyorsa hiçbir şey yapmayız.
Bunu bir cümleyle aklında tut: Normalizasyon, dört tekerin oranını bozmadan en hızlı olanı sınıra oturtur.
Şimdi bu iki fikri (dört formül + normalizasyon) düz koda dökelim. Kod, geçen modülde tanıdığın iskeletin aynısı: pin çiftleri, surMotor yardımcısı ve altı hook. Yeni olan tek şey teleopLoop içindeki dört formül ve normalizasyon bloğu.
#define PROBOT_WIFI_AP_SSID "RobotAdi"
#define PROBOT_WIFI_AP_PASSWORD "sifre1234"
#define PROBOT_WIFI_AP_CHANNEL 1
#include <probot.h>
const int FL_RPWM = 10, FL_LPWM = 11; // on sol motorun iki sinyal pini
const int FR_RPWM = 12, FR_LPWM = 13; // on sag motorun iki sinyal pini
const int BL_RPWM = 14, BL_LPWM = 15; // arka sol motorun iki sinyal pini
const int BR_RPWM = 16, BR_LPWM = 17; // arka sag motorun iki sinyal pini
// hazir yardimci: motoru hizina gore surer (-1 geri, 0 dur, +1 ileri)
void surMotor(int rpwm, int lpwm, float hiz) {
hiz = constrain(hiz, -1.0f, 1.0f);
if (hiz > 0) { analogWrite(rpwm, (int)(hiz * 255)); analogWrite(lpwm, 0); }
else { analogWrite(rpwm, 0); analogWrite(lpwm, (int)(-hiz * 255)); }
}
void robotInit() {
pinMode(FL_RPWM, OUTPUT); pinMode(FL_LPWM, OUTPUT);
pinMode(FR_RPWM, OUTPUT); pinMode(FR_LPWM, OUTPUT);
pinMode(BL_RPWM, OUTPUT); pinMode(BL_LPWM, OUTPUT);
pinMode(BR_RPWM, OUTPUT); pinMode(BR_LPWM, OUTPUT);
}
void robotEnd() {
surMotor(FL_RPWM, FL_LPWM, 0); surMotor(FR_RPWM, FR_LPWM, 0);
surMotor(BL_RPWM, BL_LPWM, 0); surMotor(BR_RPWM, BR_LPWM, 0);
}
void teleopInit() {}
void teleopLoop() {
auto js = probot::io::joystick_api::makeDefault();
float ileri = js.getLeftY();
float yan = js.getLeftX();
float donus = js.getRightX();
// dort formul satiri: katki tablosundan turettigin isaretler
float fl = ileri + yan + donus;
float fr = ileri - yan - donus;
float bl = ileri - yan + donus;
float br = ileri + yan - donus;
// normalizasyon: en buyuk deger 1'i asarsa hepsini ayni orana bol
float enb = max(max(abs(fl), abs(fr)), max(abs(bl), abs(br)));
if (enb > 1.0f) { fl /= enb; fr /= enb; bl /= enb; br /= enb; }
surMotor(FL_RPWM, FL_LPWM, fl);
surMotor(FR_RPWM, FR_LPWM, fr);
surMotor(BL_RPWM, BL_LPWM, bl);
surMotor(BR_RPWM, BR_LPWM, br);
delay(20);
}
void autonomousInit() {}
void autonomousLoop() { delay(20); }
Kodu yükle. Robotu yükseltiye koy, dört teker havada dönsün. Bağlantı akışı her zamanki gibi: telefonu robotun WiFi ağına bağla, tarayıcıda http://192.168.4.1 adresini aç, Driver Station çıkar, kumandayı telefona bağlayıp bir tuşuna bas, Init ve Start. Sol çubukla ileri-geri ve yana kayma, sağ çubukla dönüş. Montaj yönüne göre bazı motorlar ters dönebilir; bunu bir sonraki derste, o motorun RPWM ve LPWM sabitlerini kodda yer değiştirerek düzelteceksin. Şimdilik dört tekerin de bir şekilde döndüğünü görmen yeterli.
Uygulama: kodu kendine göre değiştir (15 dk)
Sırayla dene. Her değişiklikten sonra kodu yeniden yükle ve farkı gözle.
- Eksenleri yer değiştir. Şu an dönüş sağ çubukta, kayma sol çubukta. İkisini takas et:
yandeğişkeninejs.getRightX(),donusdeğişkeninejs.getLeftX()ver. Hangi eşleme sana daha doğal geliyor? Karar senin; beğenmezsen geri al. - Kaymayı %70'e kıs. Yana kayma genelde ileri gitmekten daha verimsizdir ve robot kayarken zorlanır. Kayma eksenini yüzde yetmişe indir:
float yan = js.getLeftX() * 0.7f;yaz. Aynı çubuk hareketiyle daha yumuşak bir kayma alacaksın. - RB ile yavaş mod taşı. Önceki modülde RB tuşuna basınca robotu yavaşlatan modu yazmıştın. Aynısını buraya taşı: RB basılıysa üç eksenin hepsini aynı çarpanla küçült.
İpucu:
float carpan = js.getRB() ? 0.4f : 1.0f; float ileri = js.getLeftY() * carpan; float yan = js.getLeftX() * carpan; float donus = js.getRightX() * carpan;Üç ekseni birlikte küçülttüğün için robot yön değiştirmez, sadece her şeyi ağırdan alır. Hassas hizalama görevlerinde işine yarayacak.
Pekiştirme ve değerlendirme (6 dk)
Kısa öz-kontrol. Tabloyu kapat, kağıda bak:
- Katkı tablosundan mecanum formülünün dört satırını bakmadan yazabiliyor musun?
- "Normalizasyon ne işe yarar?" sorusuna tek cümleyle cevap ver.
- Yana kayarken ön sol ile ön sağ tekerin işaretleri neden farklı? Videodaki kayan robotu hatırla.
- En az bir değiştirme görevini (eksen takası, %70 kısma ya da RB yavaş mod) robotta çalışır halde gördün mü?
Günlük hayat bağı: mecanum sürüş, dar depolarda çalışan taşıma robotlarında ve bazı forkliftlerde kullanılır. Robotun manevra yapmak için önce dönüp sonra ilerlemesi gerekmez; olduğu yerde yana süzülüp rafın önüne geçer. Senin yazdığın dört satır, o robotların da kalbindeki mantık.
Erken bitirene ek görev: tekrarı azalt (opsiyonel)
teleopLoop içindeki dört surMotor satırına bak. Hepsi neredeyse aynı: pin çifti değişiyor, mantık aynı. Bir de robotInit ve robotEnd içinde benzer tekrarlar var. Aynı şeyi tekrar tekrar yazmak yerine, kendi fonksiyonunu yazabilirsin. Fonksiyon, bir işi bir kez tanımlayıp adıyla defalarca çağırmanı sağlar.
Dört motoru tek çağrıyla süren bir yardımcı yaz. Şablon:
// dort tekeri tek seferde surer
void surDort(float fl, float fr, float bl, float br) {
surMotor(FL_RPWM, FL_LPWM, fl);
surMotor(FR_RPWM, FR_LPWM, fr);
surMotor(BL_RPWM, BL_LPWM, bl);
surMotor(BR_RPWM, BR_LPWM, br);
}
Sonra teleopLoop sonundaki dört satırın yerine tek satır yaz:
surDort(fl, fr, bl, br);
Aynı mantığı robotEnd için de dene: surDort(0, 0, 0, 0); tek satırı, dört durdurma satırının yerini tutar. Kodun kısaldı, ama davranışı hiç değişmedi. İyi bir fonksiyon tam bunu yapar: tekrarı toplar, kodu okunur kılar. İleride pin çiftini değiştirmen gerekse tek yerde değiştirirsin.
Meydan okuma: Sol çubuk tam yukarıdayken (ileri = 1, diğerleri 0) normalizasyon devreye girer mi, girmez mi? Önce tahmin et, sonra enb değerini js telemetrisiyle ekrana bastırıp kontrol et.