Hata avı: telemetriyle teşhis koy
Bu modül boyunca robotunun içindeki sayıları ekrana koymayı öğrendin. Çubuğun değerini gördün, kendi hız çarpanını ekrana yazdın, çubuğu bıraktığında değerin nasıl tam sıfıra oturduğunu izledin. Şimdi o beceriyi gerçek bir işe koşacaksın. Çünkü telemetrinin en güçlü olduğu an, bir şey ters gittiği andır.
Bugün öğretmenin sana bilerek bozulmuş üç kod verecek. Üçü de yüklenir, üçü de çalışır, ama üçü de yanlış davranır. Görevin onları tek tek tamir etmek değil önce; görevin teşhis koymak. Bir doktor gibi: önce ölç, sonra tedavi et.
Kanca: iki dakikada iki yanlış tahmin (10 dk)
Videodaki öğrenciye dikkat et. Robotu ileri gitmiyor diye önce koda bakıp bir satırı tahminle değiştirdi. Olmadı. İkinci kez tahmin etti. Yine olmadı. İki değişiklik, iki yanlış tahmin, sıfır ilerleme. Sonra bir şey yaptı: koda bakmayı bıraktı, ekrana baktı. Panelde çubuğun değeri 0.80 yazıyordu, ama motora giden değer -0.40 idi. Artı girmiş, eksi çıkmış. Bunu görünce hangi satırı düzelteceğini saniyede anladı.
Sınıfa sor, cevaplasınlar:
- Robotun neden ters gittiğini bakarak anlamaya çalışmak mı hızlı, yoksa değeri ölçüp artının nerede eksiye döndüğünü görmek mi?
- Bir araba çalışmadığında usta ne yapar: kaputun altına bakıp parçaları rastgele mi söker, yoksa önce bir cihaz bağlayıp ölçüm mü alır?
Kesin cevabı şimdi vermiyoruz. Elin üç bozuk koda değince ikisini de kendin göreceksin. Aklında tek cümle kalsın: tahmin etme, ölç.
Ana etkinlik: üç bozuk kodu avla (20 dk)
Öğretmenin sana üç dosya verecek: Bozuk 1, Bozuk 2, Bozuk 3. Üçünün de içinde tanıdığın diferansiyel sürüş kodu ve bir de telemetri paneli var. Panel her turda önce ekranı temizler, sonra dört değeri yazar: çubuğun ileri değeri, çubuğun dönüş değeri, sol motora giden değer ve sağ motora giden değer.
İşte üç dosyanın da ortak telemetri bloğu; robotun içinde ne olup bittiğini sana bu dört satır anlatacak:
probot::clearTelemetry(); // once ekrani temizle
probot::printf("solY (ileri cubugu): %.2f\n", ileri);
probot::printf("sagX (donus cubugu): %.2f\n", donus);
probot::printf("sol motor: %.2f\n", sol);
probot::printf("sag motor: %.2f\n", sag);
%.2f, o değeri iki ondalık basamakla yazar (örneğin 0.80). clearTelemetry her turun başında ekranı siler; yoksa panel eski yazılarla dolar. Bu iki kalıbı geçen derslerde kurdun; bugün onları bir teşhis cihazı gibi kullanıyorsun.
İşte Bozuk 1'in tamamı. Yükle, robotu yükseltiye koy (tekerlekler boşta dönsün), bağlan ve dene:
#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 surus motoru
const int SAG_RPWM = 18, SAG_LPWM = 19; // sag surus motoru
const float HIZ_CARPANI = 0.5f; // hizi yariya indiren carpan
// 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(SOL_RPWM, OUTPUT); pinMode(SOL_LPWM, OUTPUT);
pinMode(SAG_RPWM, OUTPUT); pinMode(SAG_LPWM, OUTPUT);
}
void robotEnd() {
surMotor(SOL_RPWM, SOL_LPWM, 0); surMotor(SAG_RPWM, SAG_LPWM, 0);
}
void teleopInit() {}
void teleopLoop() {
auto js = probot::io::joystick_api::makeDefault();
float ileri = js.getLeftX(); // ileri hareketi buradan okuyor
float donus = js.getRightX();
float sol = (ileri + donus) * HIZ_CARPANI;
float sag = (ileri - donus) * HIZ_CARPANI;
surMotor(SOL_RPWM, SOL_LPWM, sol);
surMotor(SAG_RPWM, SAG_LPWM, sag);
probot::clearTelemetry();
probot::printf("solY (ileri cubugu): %.2f\n", ileri);
probot::printf("sagX (donus cubugu): %.2f\n", donus);
probot::printf("sol motor: %.2f\n", sol);
probot::printf("sag motor: %.2f\n", sag);
delay(20);
}
void autonomousInit() {}
void autonomousLoop() { delay(20); }
Belirti: Sol çubuğu ileri itiyorsun, robot kımıldamıyor. Ama çubuğu yana ittiğinde motorlar canlanıyor. Panele bak: çubuğu ileri iterken solY satırı 0.80 gösteriyor olabilir ama sol motor ve sag motor 0.00'da takılı. İleri çubuğunun değeri motorlara hiç ulaşmıyor. İşte teşhis: kod ileri hareketi yanlış eksenden okuyor. Aradaki fark bu üç bozuk kodun her birinde farklı bir iz bırakır.
Şimdi öğretmenin sana Bozuk 2 ve Bozuk 3'ü de versin. İkisi de yukarıdaki kodun neredeyse aynısı; yalnızca teleopLoop'un ilk üç satırı farklı. Diğer her şey (telemetri paneli dahil) birebir aynı. İşte o farklı satırlar:
Bozuk 2 (bu üç satır dışında her şey aynı):
float ileri = -js.getLeftY(); // ileri degeri boyle hesaplaniyor
float donus = js.getRightX();
float sol = (ileri + donus) * HIZ_CARPANI;
Bozuk 3 (bu üç satır dışında her şey aynı):
float ileri = js.getLeftY();
float donus = js.getRightX();
float sag = (ileri - donus); // sag motora giden deger boyle hesaplaniyor
Üçünü de sırayla yükle, sür ve paneli oku. Cevabı koda bakarak tahmin etme; önce robotu sür, panelde değerin nasıl davrandığını gör, sonra karar ver. Şu tabloyu kağıda çiz ve doldur:
| Bozuk kod | Robot ne yapıyor? | Panelde ne görüyorsun? | Teşhis (sorun ne?) |
|---|---|---|---|
| Bozuk 1 | |||
| Bozuk 2 | |||
| Bozuk 3 |
İpucu olarak her birinde ne arayacağını söyleyeyim, gerisini panel anlatır: Bozuk 1'de çubuğun değeri motorlara ulaşıyor mu? Bozuk 2'de çubuğu ileri itince motor değeri artı mı çıkıyor, eksi mi? Bozuk 3'te robotu dümdüz ileri sürerken (dönüş yokken) sol motor ile sag motor eşit mi?
Kavram köprüsü: her hatanın bir izi vardır (7 dk)
Üç bozuk kodu avladın. Şimdi ne yaptığının adını koyalım. Aslında her seferinde aynı yöntemi kullandın: robotun içindeki bir sayıyı görünür yaptın ve onu beklediğin değerle karşılaştırdın. Beklentiyle gerçek arasındaki fark, sana hatanın yerini gösterdi. Buna teşhis denir ve üç hatanın üçü de panelde farklı bir iz bıraktı:
- Ölü değer (yanlış eksen). Bozuk 1'de çubuğun değeri panelde canlıydı ama motora giden değer sıfırda ölüydü. Bir yerde canlı olan sayı, olması gereken yere ulaşmıyordu. Bu, değerin yanlış kaynaktan okunduğunun izidir. Kod
getLeftXokuyordu; oysa ileri hareketgetLeftY'de. - Ters işaret. Bozuk 2'de çubuğu ileri ittin,
solYartı çıktı ama motor değeri eksiydi. Elin bir yöne, robot ters yöne. Girişle çıkış arasında işaret dönmüş. İz buydu; sebep de baştaki o küçük eksi. - Eşit olması gerekenler eşit değil (unutulmuş çarpan). Bozuk 3'te robotu dümdüz sürerken
sol motorilesag motorfarklı çıktı; biri diğerinin iki katıydı. Dönüş yokken ikisi eşit olmalıydı. Fark, bir tarafta bir işlemin unutulduğunun izidir. Sağ motorda* HIZ_CARPANIçarpanı düşmüştü.
Gördüğün gibi robotun garip davranışı belirsiz bir "çalışmıyor" değildi. Her birinin panelde net bir parmak izi vardı. Kod satırlarını rastgele değiştirip denemek yerine, hangi değerin yanlış olduğunu ölçtün ve doğruca oraya gittin. Bir mühendisin sorun giderme yolu budur: tahmin etme, ölç.
Uygulama: düzelt, sonra bir tuzak kur (15 dk)
İki bölüm. İlkini herkes yapar, ikincisi bir arkadaşınla.
1) Üçünü de düzelt (7 dk). Şimdi teşhisi tedaviye çevir. Her bozuk kodda tek bir şeyi düzelt, yükle ve panelde düzeldiğini doğrula:
- Bozuk 1:
js.getLeftX()yerinejs.getLeftY()yaz. Artık ileri çubuğu motorlara ulaşır. - Bozuk 2: baştaki eksiyi kaldır,
float ileri = js.getLeftY();olsun. Artık ileri itince motor değeri artı çıkar. - Bozuk 3: sağ satırın sonuna unutulan çarpanı geri koy:
float sag = (ileri - donus) * HIZ_CARPANI;. Artık dümdüz giderken iki motor eşit.
Her düzeltmeden sonra koda değil panele bak: değer artık beklediğin gibi mi? Düzelttiğini gözünle görmeden bir sonrakine geçme.
2) Bir arkadaşına tuzak kur (8 dk). Şimdi rolü değiştir. Çalışan kodunu al ve içine kendin küçük bir hata koy: bir işareti çevir, bir ekseni değiştir, ya da bir çarpanı sil. Sonra kodu bir arkadaşına ver, ama ona ne bozduğunu söyleme. O da telemetri panelini kullanarak hatanı avlasın. Sonra sıra sende: onun tuzağını sen avla.
Bunu yaparken şunu fark edeceksin: hatayı gizlemek kolay, ama panel onu ele veriyor. İyi bir telemetri paneli, kodun içinde saklanan hatayı dışarı çıkarır. Bir arkadaşın bulması ne kadar hızlı olduysa, panelin o kadar iyi demektir.
Pekiştirme ve değerlendirme (6 dk)
Kodu kapat, kağıda ya da sözlü cevapla:
- "Tahmin etme, ölç" ne demek? Bir robot ters gidince ilk yaptığın şey ne olmalı?
- Panelde çubuğun değeri
0.75ama o değer motora hiç ulaşmıyorsa, hangi türden bir hata arıyorsun? - Çubuğu ileri ittiğinde motor değeri eksi çıkıyorsa hataya ne ad verirsin?
- Robotu dümdüz sürerken sol ve sağ motor değerleri farklıysa, bu sana neyi düşündürür?
- Telemetri panelin olmasaydı bu üç hatayı bulmak nasıl olurdu? Neden daha yavaş olurdu?
Günlük hayat bağı: bir doktor ateşin var mı diye yüzüne bakıp tahmin etmez; termometre koyar, ölçer, sayıya bakar. Bir araba ustası motoru rastgele sökmez; bir cihaz bağlar, sensörlerin değerini okur ve arızayı sayıdan bulur. Sen de bugün aynısını yaptın. Robotunun telemetri paneli, senin termometren ve arıza cihazın. Bir şey ters gittiğinde artık tahmin etmene gerek yok; robota kendi değerini sorup cevabı okuyabiliyorsun.
Erken bitirene ek görev: kendi uyarı satırın (opsiyonel)
İyi bir teşhis paneli sadece sayıları göstermez; bir şey ters gidince seni uyarır. Kendi uyarını ekle.
Fikir şu: robotu dümdüz sürerken iki motor eşit olmalı. Farkı hesaplayıp panele yaz, üstelik fark büyükse bir uyarı bas. Telemetri bloğunun sonuna şunu ekle:
float fark = sol - sag; // iki motor arasindaki fark
probot::printf("fark: %.2f\n", fark);
if (fark > 0.05f || fark < -0.05f) {
probot::println(">> DIKKAT: motorlar esit degil");
}
}
Yükle ve dene. Robotu dümdüz sürerken (dönüş yokken) fark neredeyse sıfır olmalı ve uyarı çıkmamalı. Şimdi bilerek Bozuk 3'ün hatasını geri koy: bir motorun çarpanını sil. Panelde fark büyür ve >> DIKKAT satırı belirir. Kendi kurduğun panel, kendi hatanı sana bağırarak söyler.
Meydan okuma: 0.05f eşiğini neden seçtik? Çok küçük yaparsan (0.001f) ne olur, çok büyük yaparsan (0.5f) ne olur? Bir arkadaşınla tartış: bir uyarı ne zaman gereğinden çok bağırır, ne zaman gereken uyarıyı kaçırır?