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

Projeler · Proje 5

3B nokta bulutunu kümeleme

Robotun LiDAR taramasında nesneleri etiket görmeden ayırmak: k-ortalamalar, DBSCAN ve PCA

Senaryo. Depoda duran bir robotun döner LiDAR’ı çevresini 360° tarar ve bir nokta bulutu üretir: zemindeki halkalar, bir koli yığını, bir varil, bir insan, bir palet ve bir dolap; havada da birkaç sahte yansıma. Robot hangi noktanın hangi nesneye ait olduğunu bilmez. Çarpmadan dolaşmak için önce bu noktaları nesnelere ayırması gerekir.

Amaç. Beş nesnenin beşini de ayrı birer küme olarak bulmak: her nesnenin noktalarının en az %90’ı tek bir kümede, o kümenin de en az %90’ı o nesne olmalı. Sahte yansımalar hiçbir nesneye katılmamalı.

3B sahne yükleniyor…

tarama: 0 / 1640 nokta
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. Tarama: ışın döndükçe noktalar açı sırasıyla gelir. Her nokta yalnız üç sayıdır (x, y, z); renk, etiket ya da nesne adı yok.
  2. Zemin ayıklama: 0,08 m’nin altındaki her nokta zemin sayılır ve atılır. Bulutun yarısından fazlası zemindir; atılmazsa bütün nesneleri birbirine bağlayan dev bir küme olur.
  3. PCA (isteğe bağlı): kalan noktaların kovaryans matrisi çıkarılır, özvektörleri en çok yayılan yönlerdir. Bu sahnede ilk iki yön yatay düzleme yakındır; yükseklik en az bilgiyi taşır ve atılır. Bulut gözünün önünde iki boyuta yassılır.
  4. k-ortalamalar: k merkez seçilir, her nokta en yakın merkeze katılır, merkez kendi noktalarının ortalamasına taşınır; hiçbir atama değişmeyene kadar. Kötü bir başlangıç iki nesneyi tek kümede birleştirebilir; bu yüzden 10 başlangıç denenir ve en sıkısı oynatılır. Her nokta bir kümeye girmek zorundadır, sahte yansımalar da.
  5. DBSCAN: eps yarıçaplı kürede en az 6 komşusu olan nokta çekirdektir. Küme bir çekirdekten başlar ve çekirdekten çekirdeğe genişler. Hiçbir kümeye ulaşmayan nokta gürültüdür; küme sayısını kimse söylemez.
  6. Ölçüm: her nesne en çok örtüştüğü kümeyle eşleşir. Tablo satırlarda gerçek nesneleri, sütunlarda bulunan kümeleri sayar; köşegen ne kadar doluysa ayırma o kadar iyidir.

Model

Yükseklik eşiğiyle zemin ayıklama, sonra iki kümeleme algoritmasından biri: k-ortalamalar (k-ortalamalar++ başlangıcı, Lloyd turları) ya da DBSCAN (yoğunluk tabanlı, eps yarıçapı, en az 6 komşu). İkisi de kursun 2B derslerinin 3B genellemesidir. PCA açıkken kovaryans matrisinin özvektörleri bulunur ve nokta bulutu en çok değişen iki yöne izdüşürülür; kümeleme bu iki sayı üzerinde yapılır.

Kullanılan dersler: k-means: etiketsiz gruplar · DBSCAN: yoğunluğa göre kümeler · Temel bileşen analizi (PCA)

Kendin dene

  1. Algoritmayı DBSCAN bırak, eps’i 0,35’ten 0,15’e indir. Komşu kürelerin içi boşalır: her nesne parçalara bölünür, 20’den fazla küçük küme ve 160’tan fazla gürültü noktası çıkar, bulunan nesne sayısı 0’a düşer. Sonra eps’i 1’e çıkar: nesneler yine ayrı kalır çünkü aralarında metrelerce boşluk var, ama sahte yansımaların bir kısmı nesnelere yapışır.
  2. Algoritmayı k-ortalamalar yap (algo 0) ve k = 5 ile beş nesnenin beşini de bul. Sonra k’yı 3 yap: komşu nesneler aynı kümede birleşir ve en fazla 2 nesne bulunur. k-ortalamalar gürültü bilmez; her sahte yansıma bir nesneye eklenir.
  3. PCA’yı aç (pca 1). İlk iki bileşen varyansın %98’inden fazlasını taşır; bulut yassılır ve DBSCAN aynı eps ile yine beş nesneyi de bulur. Üç sayı yerine iki sayı yetti, çünkü bu depoda nesneler üst üste değil yan yana duruyor.

Python karşılığı

nokta_bulutu.pypython
  1. import numpy as np
  2. tarama = np.genfromtxt('lidar-3b.csv', delimiter=',', skip_header=1)[:, :3] # (N, 3) nokta, metre: x ileri, y sol, z yukarı
  3. P = tarama[tarama[:, 2] > 0.08] # zemini at
  4. mu = P.mean(0)
  5. val, vec = np.linalg.eigh(np.cov((P - mu).T)) # kovaryansın özvektörleri
  6. val, vec = val[::-1], vec[:, ::-1] # büyükten küçüğe
  7. print("açıklanan varyans", val / val.sum())
  8. Z = (P - mu) @ vec[:, :2] # PCA: 3 sayıdan 2 sayıya
  9. def kmeans(X, k, rng):
  10. C = X[rng.choice(len(X), k, replace=False)] # rastgele merkezler
  11. while True:
  12. g = ((X[:, None] - C) ** 2).sum(2).argmin(1) # en yakın merkez
  13. C2 = np.array([X[g == j].mean(0) for j in range(k)]) # grubun ortalaması
  14. if np.allclose(C, C2): return g
  15. C = C2
  16. def dbscan(X, eps=0.35, min_pts=6):
  17. D = ((X[:, None] - X) ** 2).sum(2) <= eps ** 2 # eps küresindeki komşular
  18. core = D.sum(1) >= min_pts # çekirdek noktalar
  19. g, k = np.full(len(X), -1), 0 # -1: gürültü
  20. for i in np.flatnonzero(core):
  21. if g[i] != -1: continue
  22. g[i], kuyruk = k, [i] # yeni küme
  23. while kuyruk:
  24. j = kuyruk.pop()
  25. if not core[j]: continue # sınır noktası genişlemez
  26. for t in np.flatnonzero(D[j] & (g == -1)):
  27. g[t] = k; kuyruk.append(t)
  28. k += 1
  29. return g
  30. etiket = dbscan(P) # ya da kmeans(Z, 5, np.random.default_rng(0))
  31. def siluet(X, g): # ortalama siluet skoru (gürültü hariç)
  32. X, g = X[g >= 0], g[g >= 0]
  33. L = np.unique(g)
  34. if len(L) < 2: return 0.0
  35. D = np.sqrt(((X[:, None] - X) ** 2).sum(2))
  36. ort = np.stack([D[:, g == l].sum(1) / np.maximum(1, (g == l).sum() - (g == l)) for l in L], 1) # her kümeye ortalama uzaklık
  37. a = ort[np.arange(len(g)), np.searchsorted(L, g)] # kendi kümesi
  38. b = np.where(L[None] == g[:, None], np.inf, ort).min(1) # en yakın diğer küme
  39. return float(np.mean((b - a) / np.maximum(a, b)))
  40. history = {'eps': [], 'clusters': [], 'noise_share': [], 'silhouette': []} # geçmiş: eps taraması
  41. for e in (0.2, 0.3, 0.35, 0.4, 0.5, 0.6):
  42. g = dbscan(P, eps=e)
  43. history['eps'].append(e); history['clusters'].append(int(g.max() + 1))
  44. history['noise_share'].append(float((g < 0).mean())); history['silhouette'].append(siluet(P, g))
  45. for row in zip(*history.values()): print(' '.join(f'{k}={v:.3f}' for k, v in zip(history, row))) # tablo
  46. import csv
  47. with open('egitim_gecmisi.csv', 'w', newline='') as f: # geçmişi CSV'ye yaz
  48. csv.writer(f).writerows([list(history), *zip(*history.values())])
  49. try:
  50. import matplotlib.pyplot as plt # varsa grafik çiz
  51. fig, (a1, a2) = plt.subplots(1, 2, figsize=(9, 3.5))
  52. a1.plot(history['eps'], history['clusters'], 'o-'); a1.set_xlabel('eps'); a1.set_ylabel('clusters')
  53. a2.scatter(P[:, 0], P[:, 1], c=etiket, s=4, cmap='tab10'); a2.set_aspect('equal') # kümeler (üstten görünüş)
  54. plt.show()
  55. except ImportError:
  56. print('matplotlib yok: grafik atlandı')
  • Depo taraması (3B nokta bulutu)lidar-3b.csv · 1640 satır · 4 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.

nokta_bulutu.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ç