Görevli Otonom: Kendi Başına İş Yapan Robot
Eller boşken bir iş biter mi? (10 dk)
Şimdiye kadar otonomda robotuna yalnızca gezdirdin: ileri git, dön, dur. Ama gerçek bir görev sadece gezmek değildir. Bir robot bir yere gider, orada bir iş yapar, sonra başka yere geçer. Videodaki robot tam bunu yaptı: gitti, mekanizmasıyla yükü sardı, geri döndü. Hem de kimse dokunmadan, tek bir Start ile.
Bunu yapabilmen için iki şeyi zaten biliyorsun. Mekanizma motorunu sürüşteyken RB ve LB butonlarıyla çalıştırmıştın. Otonom fazlarını da kurmuştun: robot bir sayıya bakıp "hangi adımdayım" diye karar veriyordu. Bugün bu ikisini birleştiriyoruz. Fazların içine, sürüş satırlarının yanına mekanizma satırlarını da koyacağız. Robot bir fazda ilerleyecek, başka bir fazda mekanizmasını çevirecek.
Bir de yeni bir alışkanlık kuracağız. Sürüşte robot senin gözünün önünde ve elinin altındaydı; bir şey ters gidince çubuğu bırakırdın. Otonomda robot elinin dışında. Peki ikinci fazda takıldıysa, bunu nereden anlarsın? İşte burada telemetri devreye girer: robot hangi fazda olduğunu ekrana yazacak, sen de dışarıdan onun kafasının içini okuyacaksın.
Tartış: Robota "git, yükü sar, dön" dedirtmek istiyorsun. Sence bu üç iş aynı anda mı olmalı, yoksa sırayla mı? Bir arkadaşınla konuş.
Fazın içine mekanizmayı koy (20 dk)
Elimizde tanıdık bir faz makinesi var: robot bir faz sayısı tutar, o fazın işini yapar, süre dolunca bir sonrakine geçer. Şimdiye kadar her fazda yalnızca surSasi çağırdın, yani sadece sürdün. Bugün her fazın içine bir de mekanizma satırı ekliyoruz.
Aşağıdaki kod, mekanizma motoru takılı robotun tam programı. Sürüş 16-19 pinlerinde; mekanizma 21 ve 22 pinlerinde. Robot havada, tekerler dönmekte serbest olsun. Kodu olduğu gibi yükle:
#define PROBOT_WIFI_AP_SSID "RobotAdi"
#define PROBOT_WIFI_AP_PASSWORD "sifre1234"
#define PROBOT_WIFI_AP_CHANNEL 1
#include <probot.h>
const int SOL_RPWM = 16, SOL_LPWM = 17; // sol motorun iki sinyal pini
const int SAG_RPWM = 18, SAG_LPWM = 19; // sag motorun iki sinyal pini
const int MEK_RPWM = 21, MEK_LPWM = 22; // mekanizma motorunun 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)); }
}
// hazir yardimci: sasiyi arcade mantigiyla surer
void surSasi(float ileri, float donus) {
surMotor(SOL_RPWM, SOL_LPWM, ileri + donus);
surMotor(SAG_RPWM, SAG_LPWM, ileri - donus);
}
int faz = 0;
unsigned long fazBasi = 0;
void robotInit() {
pinMode(SOL_RPWM, OUTPUT); pinMode(SOL_LPWM, OUTPUT);
pinMode(SAG_RPWM, OUTPUT); pinMode(SAG_LPWM, OUTPUT);
pinMode(MEK_RPWM, OUTPUT); pinMode(MEK_LPWM, OUTPUT);
}
void robotEnd() {
surSasi(0.0f, 0.0f);
surMotor(MEK_RPWM, MEK_LPWM, 0.0f);
}
void teleopInit() {}
void teleopLoop() {
auto js = probot::io::joystick_api::makeDefault();
surSasi(js.getLeftY(), js.getRightX());
if (js.getRB()) surMotor(MEK_RPWM, MEK_LPWM, 0.6f); // basiliyken ileri sar
else if (js.getLB()) surMotor(MEK_RPWM, MEK_LPWM, -0.6f); // basiliyken geri sar
else surMotor(MEK_RPWM, MEK_LPWM, 0.0f); // birakinca dur
delay(20);
}
void autonomousInit() {
faz = 0;
fazBasi = millis();
}
void autonomousLoop() {
unsigned long gecen = millis() - fazBasi;
probot::clearTelemetry();
probot::printf("faz: %d\n", faz);
probot::printf("gecen: %d ms\n", (int)gecen);
if (faz == 0) { // 2 sn ileri git
surSasi(0.6f, 0.0f);
surMotor(MEK_RPWM, MEK_LPWM, 0.0f);
if (gecen > 2000) { faz = 1; fazBasi = millis(); }
} else if (faz == 1) { // 1.5 sn mekanizmayi sar
surSasi(0.0f, 0.0f);
surMotor(MEK_RPWM, MEK_LPWM, 0.6f);
if (gecen > 1500) { faz = 2; fazBasi = millis(); }
} else if (faz == 2) { // 0.8 sn sola don
surSasi(0.0f, -0.5f);
surMotor(MEK_RPWM, MEK_LPWM, 0.0f);
if (gecen > 800) { faz = 3; fazBasi = millis(); }
} else { // dur ve bekle
surSasi(0.0f, 0.0f);
surMotor(MEK_RPWM, MEK_LPWM, 0.0f);
}
delay(20);
}
Yüklemeden önce tahmin et. Bu robot Start'a basınca ne yapacak? Kaç ayrı iş sırayla olacak, hangi sırayla? Bir kağıda "önce... sonra... sonra..." diye yaz.
Bağlan ve çalıştır. Telefonu robotun RobotAdi ağına bağla, tarayıcıya http://192.168.4.1 yaz. Driver Station'da otonom fazını aç, süresini en az 8 saniye yap. Init'e, sonra Start'a bas. Şimdi hem robota hem de sağ paneldeki telemetriye bak.
Gözle: robot önce ileri gidiyor ve ekranda faz: 0 yazıyor. Sonra duruyor, mekanizma dönmeye başlıyor, ekran faz: 1 oluyor. Sonra dönüyor, faz: 2. En sonunda duruyor, faz: 3. Robotun her hareketi ekranda bir sayıya karşılık geliyor. Tahminin tuttu mu?
Ne yaptık: görev rutini ve teşhis (7 dk)
Az önce kurduğun şeyin adı bir görev rutini: robotun tek başına, sırayla yaptığı bir işler dizisi. Rutinin yapı taşı faztır. Faz, "robot şu an hangi adımda" sorusunun cevabıdır; kodda bir int faz sayısıyla tutulur. Her adım bittiğinde faz bir artar ve robot sonraki işe geçer.
Bugünkü yeni şey, fazın içine iki tür satır koymandı: bir sürüş satırı (surSasi) ve bir mekanizma satırı (surMotor(MEK_RPWM...)). Her faz bu ikisine de bir değer verir. Faz 0'da robot ilerler ama mekanizma durur (0.0f). Faz 1'de robot durur ama mekanizma döner. Yani her faz, iki motor grubuna aynı anda "sen ne yapacaksın" der. Bu yüzden faz 1'de mekanizmayı çalıştırırken sürüşe de 0.0f yazdık: söylemezsen robot bir önceki hızda gitmeye devam eder.
İşte "git, sar, dön" rutininin iskeleti bu:
| Faz | Sürüş | Mekanizma | Anlamı |
|---|---|---|---|
| 0 | ileri 0.6 | 0 | Görev noktasına git |
| 1 | 0 | sar 0.6 | Yükü sar (ya da bırak) |
| 2 | dön -0.5 | 0 | Geri yöne dön |
| 3 | 0 | 0 | Dur, görev bitti |
İkinci yeni şey telemetri oldu. Otonomda robot elinin dışında çalışır; bir şey ters gidince onu durdurup soramazsın. Bu yüzden robot her turda önce ekranı temizler (probot::clearTelemetry()), sonra hangi fazda olduğunu yazar. Robot ikinci adımda takıldıysa ekranda faz: 1de donup kaldığını görürsün. Bu, dışarıdan robotun kafasının içini okumaktır; buna teşhis denir. İyi bir robotçu robotun yanlış davrandığını görünce tahmin etmez, telemetriye bakar.
Bir güvenlik hatırlatması: adımlar arasında delay(2000) gibi uzun beklemeler kullanmadık. Bunun yerine geçen zamanı millis ile ölçtük. Çünkü döngü uzun süre bloke olursa güvenlik devreye girer ve robotu durdurur; beklemeyi hep zaman farkıyla yaparız.
Kendi rutinini kur (15 dk)
Şimdi bu rutini senin görevine göre değiştireceksin. Her değişiklikten sonra yükle, yerde küçük bir saha kur (bant, kutu, şişe) ve robotu gerçekten çalıştır. Telemetriyi hep açık tut.
- Süreleri sahana uydur. Robotun kutuya tam ulaşması için faz 0 süresini ayarla. Kutu uzaksa
if (gecen > 2000)içindeki2000i büyüt; yakınsa küçült. Dönüşü çeyrek tur yapmak için faz 2'deki800ü değiştir. Her seferinde çalıştır ve gözle. - Sar yerine bırak yaptır. Şu an faz 1 mekanizmayı
+0.6file ileri sarıyor. Yükü bırakmak için o satırı-0.6fyap. Robot artık noktaya gidip yükü boşaltacak. Yön hangi işi yapıyor, denemeden bilemezsin; dene ve gör. - Bir adım daha ekle: geri gel. Şu an robot dönüp duruyor. Dönüşten sonra başlangıca dönmesi için yeni bir faz ekle. Faz 2'nin sonunu
faz = 3yerine yeni adıma bağla ve son bloğu şöyle yap:
Artık rutinin: git, sar, dön, geri gel, dur. Beş adımlık kendi görevin.} else if (faz == 3) { // 2 sn geri git surSasi(0.6f, 0.0f); surMotor(MEK_RPWM, MEK_LPWM, 0.0f); if (gecen > 2000) { faz = 4; fazBasi = millis(); } } else { // dur surSasi(0.0f, 0.0f); surMotor(MEK_RPWM, MEK_LPWM, 0.0f); } - Ekrana bir satır daha koy. Robotun ne yaptığını daha net görmek için telemetriye kendi mesajını ekle. Faz kontrollerinin hemen üstüne değil, mesela her fazın içine kısa bir açıklama yazabilirsin. Basit yol: en üstteki telemetri bloğunun altına şunu ekle:
Robot sarma adımına gelince ekranda ek satırı gör. Kendi teşhis mesajını yazdın.if (faz == 1) probot::printf("mekanizma calisiyor\n");
Mekanizmanın uçta zorlanmasına dikkat et. Motor bir uca dayanıp zorlanırsa iner gibi ses çıkarır, çektiği akım artar ve sigorta atabilir. Faz 1 süresini mekanizma ucuna dayanmadan bitecek kadar kısa tut. Uçta zorlama yok.
Ders sonu kontrolü (7 dk)
Kendi cümlelerinle cevapla:
- Bir fazın içine hem sürüş hem mekanizma satırı koydun ve robot ikisini doğru sırayla yaptı mı?
- "git, sar, dön" rutininde hangi faz hangi işi yapıyor, üçünü de söyleyebiliyor musun?
- Robot ikinci adımda takılsaydı, bunu ekrandan nasıl anlardın?
- Faz 1'de sürüşe neden
0.0fyazmak zorundayız, bir cümleyle açıkla. - Adımlar arasında neden
delay(2000)kullanmıyoruz?
Günlük hayat bağı. Bulaşık makinesi de bir görev rutini çalıştırır: önce su alır, sonra ısıtır, sonra yıkar, sonra boşaltır, en son kurutur. Her adım bitince bir sonrakine geçer; sen başında beklemezsin. Üstündeki ekran hangi programda olduğunu yazar. Senin robotunun telemetrisi de tam o ekran gibi çalışıyor: dışarıdan içeride ne olduğunu okumanı sağlıyor.
Erken bitirdiysen
- Sar sonra bırak. Rutine iki mekanizma adımı koy: bir noktada yükü sar (
+0.6f), başka bir noktada bırak (-0.6f). Robot bir yerden alıp başka yere taşısın. - Faz sayacı ekrana. Telemetriye toplam kaç faz geçtiğini yazdır.
gecenin yanına, robotun kaçıncı adımda olduğunu daha okunur göstermenin bir yolunu düşün; ipucu,fazsayısı zaten sende var.