Okullar için Robotik ve Mühendislik · anaokulundan liseye

Projeler · Proje 1

Otonom araç: pisti kendi kendine öğrenen sürücü

Beş mesafe sensörü, küçük bir sinir ağı ve nöroevrim

Senaryo. Bir robot yarışı için kapalı bir pist: yaklaşık 22 metreye 13 metrelik dalgalı bir halka (bir tur 57 metre), yol 1,3 metre genişliğinde. Her aracın önünde beş mesafe sensörü var (LiDAR’ın en basit hâli); yol kenarına olan uzaklığı ölçerler. Kimse araca nasıl direksiyon kıracağını göstermez; elimizde yalnızca bir ölçüt var: pistte ne kadar ilerledi.

Amaç. Bir aracın pisti yoldan çıkmadan iki tam tur dönmesi.

3B sahne yükleniyor…

nesil 1 · 30/30 araç yolda · en iyi %0 tur
Sürükle: döndür · iki parmak / tekerlek: yakınlaş

Eğitim paneli

Eğitim çalıştırılıyor…

Model bu sayfada, tarayıcında sıfırdan eğitilir (sabit tohum: aynı ayarlarla her seferinde aynı sonuç). Eğitim her karede küçük parçalar hâlinde yapılır; sekme ya da sahne görünmezken durur.

Adımlar

  1. Sensörler: her karede beş ışın −60°, −30°, 0°, 30° ve 60° yönünde yol kenarına kadar uzatılır; uzaklık 5 metreye bölünür. Altıncı girdi aracın hızıdır.
  2. Ağ: altı sayı gizli katmandan geçer, iki sayı çıkar: direksiyon (−1 sol, +1 sağ) ve gaz (hız 1 ile 4 m/s arası).
  3. Nesil: 30 araç aynı anda başlar. Yoldan çıkan, geri giden ya da 3 saniye ilerleyemeyen araç elenir. Herkes elenince ya da 60 saniye dolunca nesil biter.
  4. Uygunluk: aracın orta çizgi boyunca ulaştığı en uzak nokta (metre). Sıralamada en iyi iki araç olduğu gibi kalır.
  5. Yeni nesil: geri kalan 28 araç, iyi araçlardan birinin kopyasıdır; her ağırlık %30 olasılıkla “mutasyon” kadar gauss gürültüsü alır. Kötü bozulmalar elenir, iyiler kalır.

Model

Her aracın beyni 6 → 6 → 2 nöronlu bir sinir ağı (sinir ağı dersindeki ağın aynısı, tanh eğrileriyle). Ağırlıklar gradyan inişiyle değil nöroevrimle öğrenilir: en iyi araçlar kopyalanır, kopyaların ağırlıklarına küçük gauss gürültüsü eklenir. Doğru cevap olmadığı için geri yayılım kullanılamaz; ödül yalnızca nesil sonunda gelir (Q-öğrenme dersindeki gibi).

Kullanılan dersler: Küçük bir sinir ağı · Aktivasyon fonksiyonları · Q-öğrenme: şarj istasyonunu bulan robot

Kendin dene

  1. Mutasyonu 0 yap ve eğit. Kopyalar asıllarının aynısı olur, evrim durur: rekor ilk nesilde ne ise orada kalır ve hiçbir araç tur atamaz.
  2. Popülasyonu 30’dan 10’a indir. Varsayılan tohumla ilk tam tur 6. nesil yerine 16. nesilde gelir; 6 araçla 80 nesilde bile gelmez. Az araç, her nesilde az deneme demektir.
  3. Gizli nöronu 6’dan 2’ye indir: ilk tam tur 16. nesle kalır. 12 yap: 4. nesilde gelir. Sonra tohumu değiştir; ilk 20 tohumun hepsinde 10 nesil içinde bir araç iki tur atar.

Python karşılığı

otonom_arac.pypython
  1. import numpy as np
  2. C = np.genfromtxt('pist-orta-cizgi.csv', delimiter=',', skip_header=1) # orta çizgi: 300 nokta (x, z), m
  3. n, YARI, H = len(C), 0.65, 0.1 # yol yarı genişliği, ızgara hücresi (m)
  4. xs = np.arange(C[:, 0].min() - 1, C[:, 0].max() + 1, H)
  5. zs = np.arange(C[:, 1].min() - 1, C[:, 1].max() + 1, H)
  6. IDX = np.full((len(zs), len(xs)), -1) # hücre başına en yakın orta çizgi noktası, yol dışı −1
  7. for i, z in enumerate(zs):
  8. d = (xs[:, None] - C[:, 0]) ** 2 + (z - C[:, 1]) ** 2
  9. j = d.argmin(1)
  10. IDX[i] = np.where(d[np.arange(len(xs)), j] < YARI ** 2, j, -1)
  11. def hucre(x, z): # konumların hücre değeri (yol mu, nerede)
  12. c, r = ((x - xs[0]) / H).astype(int), ((z - zs[0]) / H).astype(int)
  13. ok = (c >= 0) & (r >= 0) & (c < len(xs)) & (r < len(zs))
  14. out = np.full(x.shape, -1)
  15. out[ok] = IDX[r[ok], c[ok]]
  16. return out
  17. ACILAR = np.radians([-60, -30, 0, 30, 60]) # beş mesafe sensörü
  18. def isinlar(x, z, h): # bütün araçların ışınları birlikte
  19. a = h[:, None] + ACILAR
  20. d, acik = np.full(a.shape, 5.0), np.ones(a.shape, bool)
  21. for s in np.arange(0.25, 5.01, 0.25): # 25 cm adımlarla ilerle
  22. yol = hucre(x[:, None] + np.cos(a) * s, z[:, None] + np.sin(a) * s) >= 0
  23. d[acik & ~yol] = s - 0.125 # ilk yol dışı nokta: kenara uzaklık
  24. acik &= yol
  25. return d
  26. N, rng = 30, np.random.default_rng(1) # 30 araç
  27. W1, b1 = rng.normal(0, 6 ** -.5, (N, 6, 6)), np.zeros((N, 6)) # her aracın ağı: 6 → 6 → 2
  28. W2, b2 = rng.normal(0, 6 ** -.5, (N, 6, 2)), np.zeros((N, 2))
  29. def sur(W1, b1, W2, b2, dt=0.05): # bir nesil: hepsi aynı anda sürer
  30. x, z = np.full(N, C[0, 0]), np.full(N, C[0, 1])
  31. h, v = np.full(N, np.arctan2(C[1, 1] - C[0, 1], C[1, 0] - C[0, 0])), np.ones(N)
  32. son, ilerleme, en_iyi, canli = np.zeros(N, int), np.zeros(N), np.zeros(N), np.ones(N, bool)
  33. for t in range(600): # en fazla 30 saniye
  34. g = np.c_[isinlar(x, z, h) / 5, v / 4] # girdiler: 5 ışın, hız
  35. o = np.tanh(np.einsum('nj,njk->nk', np.tanh(np.einsum('ni,nij->nj', g, W1) + b1), W2) + b2) # direksiyon, gaz
  36. v = np.where(canli, v + (2.5 + 1.5 * o[:, 1] - v) * 0.15, 0) # hız 1–4 m/s
  37. h += 2.5 * o[:, 0] * dt
  38. x, z = x + np.cos(h) * v * dt, z + np.sin(h) * v * dt
  39. k = hucre(x, z)
  40. canli &= k >= 0 # yoldan çıkan elenir
  41. adim = (k - son + n // 2) % n - n // 2 # orta çizgide kaç nokta ilerledi
  42. ilerleme = np.where(canli, ilerleme + adim, ilerleme); son = np.where(canli, k, son)
  43. canli &= ilerleme > en_iyi - 10 # geri giden de elenir
  44. en_iyi = np.maximum(en_iyi, ilerleme)
  45. if not canli.any(): break
  46. return en_iyi / n # uygunluk: tur cinsinden en uzak nokta
  47. history = {'generation': [], 'best': [], 'mean': [], 'median': []} # eğitim geçmişi: nesil başına uygunluk (tur)
  48. for nesil in range(1, 13):
  49. puan = sur(W1, b1, W2, b2)
  50. sira = np.argsort(-puan)
  51. for k, v in zip(history, (nesil, puan.max(), puan.mean(), np.median(puan))): history[k].append(v if isinstance(v, int) else float(v))
  52. print(f"nesil {nesil:2d}: en iyi {puan.max():.2f} tur, ortalama {puan.mean():.2f}")
  53. if puan.max() >= 1: print("bir araç tam tur attı"); break
  54. eb = np.r_[sira[:2], sira[(rng.random(N - 2) ** 2 * 12).astype(int)]] # en iyi iki aynen, kalanlar iyilerin kopyası
  55. W1, b1, W2, b2 = [w[eb] + rng.normal(0, .3, w[eb].shape) * (rng.random(w[eb].shape) < .3) * (np.arange(N) >= 2).reshape(-1, *[1] * (w.ndim - 1)) for w in (W1, b1, W2, b2)] # mutasyon (ilk ikiye dokunma)
  56. for k in history: print(f'{k:<12}{history[k][-1]:>10.3f}') # son ölçümler tablosu
  57. import csv
  58. with open('egitim_gecmisi.csv', 'w', newline='') as f: # geçmişi CSV'ye yaz
  59. csv.writer(f).writerows([list(history), *zip(*history.values())]) # satır başına bir nesil
  60. try:
  61. import matplotlib.pyplot as plt # varsa grafik çiz
  62. for k in ('best', 'mean', 'median'): plt.plot(history['generation'], history[k], label=k)
  63. plt.xlabel('generation'); plt.ylabel('laps'); plt.legend(); plt.show()
  64. except ImportError:
  65. print('matplotlib yok: grafik atlandı')
  • Yarış pisti: orta çizgipist-orta-cizgi.csv · 300 satır · 2 sütun
    Veri setini indir
  • Sürücü sensör kayıtlarıalıştırmasurucu-kayitlari.csv · 400 satır · 8 sütun
    Veri setini indir
Kodu tarayıcıda çalıştırPython · NumPy · matplotlib · Pyodide

Python ve NumPy bu sayfada, tarayıcında çalışır (Pyodide). Kodu değiştirip yeniden çalıştırabilirsin; dersin veri setleri çalışma klasöründe hazır. Kod matplotlib kullanırsa grafikler çıktının altında görünür, yazdığı dosyalar (ör. egitim_gecmisi.csv) indirilebilir.

otonom_arac.pypython

Çıktı

Çıktı burada görünecek.
Esc
  • PanelAna sayfaSayfa
  • ModüllerTüm modüllerSayfa
  • ParçalarParça ansiklopedisiSayfa
  • Mühendislik yoluBüyükler için robotik: 128 parça, 7 seviyeSayfa
  • Robotik dersi (ESOF 323)Üniversite robotik dersi, 14 haftaSayfa
  • Drone dersiBüyükler için: parça, simülasyon, yazılım; 10 üniteSayfa
  • Blok atölyesiSerbest blok programlamaAraç
  • Devre laboratuvarıSanal Arduino ve devreAraç
  • Robot arenasıSanal robot sürüşüAraç
  • OyunlarBloklarla 8 öğretici oyunAraç
  • Mikro Kart atölyesiSanal LED kartı bloklarla programlaAraç
  • Robot atölyesiMikro Kart'ı robota tak, bloklarla görev yapAraç