10. hafta / 14
Robotlar için bilgisayarlı görü
Araçlar: Python (OpenCV), bu platform, Webots kamerası
Bu haftanın sonunda
- İğne deliği (pinhole) kamera modelini ve kalibrasyonun neden önemli olduğunu açıklamak
- Nesneleri HSV'de renge göre ayırmak ve konumlarını bir denetim hatasına çevirmek
- Şerit çizgilerini ve işaretçileri (ArUco) bulup gezinmede kullanmak
Ders notları
Kamera, bir robotun taşıyabileceği en zengin ve kullanması en zor sensördür: 640×480'lik renkli bir görüntü neredeyse bir milyon sayıdır ve saniyede otuz kez gelir. Robot görüsü bu sayıları birkaç kullanışlı büyüklüğe çevirir: çizgi nerede, hedef nerede, işaretçi ne kadar uzakta.
İğne deliği modeli
Kamera çerçevesindeki bir 3B nokta (X, Y, Z) şu piksele düşer (u, v):
fx, fy (piksel cinsinden odak uzaklığı) ve cx, cy (ana nokta) birlikte iç parametre matrisini oluşturur. Satranç tahtası deseniyle kalibrasyon bunları ve mercek bozulmasını kestirir. Kalibrasyon yapılmazsa görüntüden ölçülen mesafeler yanlış olur.
Renge göre ayırma (segmentasyon)
RGB rengi ve parlaklığı birbirine karıştırır; bir gölge üç kanalı da değiştirir. HSV ise renk tonunu (hangi renk), doygunluğu ve parlaklığı ayırır; bu yüzden bir ton aralığı nesneyi değişen ışıkta da bulur. Akış: bulanıklaştır → HSV → eşikle (maske) → küçük lekeleri silmek için morfolojik açma → en büyük kontur → ağırlık merkezi. Ağırlık merkezinin görüntü merkezinden yatay sapması bir PID için hata sinyalidir: buna görsel servo (visual servoing) denir.
Şerit tespiti
- Aracın önünde bir ilgi bölgesi (ROI) kırp.
- Kenarları bul (Canny) ya da şerit rengini eşikle.
- Çizgi (Hough dönüşümü) ya da polinom uydur; çoğu zaman önce kuş bakışı perspektif dönüşümü yapılır.
- Şerit merkezinin yanal sapması ve yön hatası direksiyon denetleyicisini sürer; tıpkı 1. haftadaki çizgi izleyen gibi, ama IR sensör yerine kamerayla.
İşaretçiler (fiducial markers)
ArUco ve AprilTag basılı kare kodlardır. OpenCV onları bulur ve kalibre edilmiş bir kamerayla tam 3B pozlarını verir. Drone'lar bunları hassas iniş için, robot kollar nesnenin yerini bulmak için kullanır.
Görü nerede çalışır?
Klasik görü bir Raspberry Pi'de çalışır; derin ağlar bir hızlandırıcı ister (11. hafta). Akıllı kamera modülleri (HuskyLens, Pixy2, Grove Vision AI) tespiti kartın üzerinde yapar ve UART ya da I²C üzerinden yalnız koordinat gönderir, böylece bir Uno bile onları kullanabilir. ESP32-CAM görüntüyü Wi-Fi ile daha güçlü bir bilgisayara aktarır.
Derinlemesine
Görüş alanından odak uzaklığına
Kalibrasyondan önce piksel cinsinden odak uzaklığını yatay görüş alanından (HFOV) ve görüntü genişliği W'dan tahmin edebilirsiniz:
Raspberry Pi Camera v2'nin HFOV değeri yaklaşık 62,2°'dir; 640 piksel genişlikte fx ≈ 320 / tan(31,1°) ≈ 530 piksel olur. Ardından dama tahtası kalibrasyonu gerçek değeri ve bozulma (distortion) katsayılarını verir.
Çözümlü örnek: tek görüntüden mesafe ve yön
10 cm'lik bir ArUco işaretçisi fx = 600 piksel ile 40 piksel genişliğinde görünüyor. Benzer üçgenlerden
Tek bir piksellik ölçüm hatası önemlidir: 39 pikselde aynı formül 1,54 m verir. Duyarlılık dZ ≈ Z² / (fx Wreal) = 2,25 / 60 ≈ piksel başına 0,04 m'dir ve mesafenin karesiyle büyür; hassas iniş yapan dronların alçaldıkça daha küçük bir işaretçiye geçmesinin nedeni budur. cx = 320 piksel ve fx = 530 piksel iken u = 400 pikseldeki bir hedefin yönü şudur:
6. haftanın yön PID'sine ham piksel farkını değil ψ'yi verin: böylece kamera çözünürlüğünü değiştirdiğinizde kazançlar değişmez.
Görüntünün nerede işleneceğini veri hızı belirler
Saniyede 30 karede ham 640 × 480 RGB, 640 × 480 × 3 × 30 ≈ 27,6 MB/s eder; 1280 × 720'de yaklaşık 83 MB/s'dir. İkisi de bir mikrodenetleyiciden veya tipik bir Wi-Fi bağlantısından geçmez; bu yüzden ESP32-CAM genellikle yaklaşık on kat daha küçük olan JPEG kareler gönderir, Raspberry Pi 4B veya Jetson ise kareleri kameranın yanında işler ve yalnızca sonuçları gönderir.
Uygulamada HSV
- OpenCV renk tonunu (hue) 0 ile 179 arasında saklar (derecenin yarısı). Kırmızı iki uçta birden durur, bu yüzden VEYA ile birleştirilmiş iki aralık ister: yaklaşık 0 ile 10 ve 170 ile 179.
- Doygunluğu düşük veya parlaklığı çok düşük piksellerin renk tonu anlamsızdır; örneğin S > 80 ve V > 50 şartı koyun.
- Mümkünse otomatik pozlamayı ve otomatik beyaz dengesini kilitleyin; yoksa robot bir pencereye döndükçe eşikleriniz kayar.
Kuş bakışı görünüm ve işaretçi konumu
Bilinen dört zemin noktası ve pikselleri 3 × 3'lük bir homografi tanımlar (cv2.getPerspectiveTransform). Dönüşümden sonra bir piksel sabit bir zemin mesafesine, örneğin 1 cm'ye karşılık gelir; böylece şerit kayması doğrudan metreye çevrilir. Bu yalnızca düz zeminde geçerlidir. ArUco için solvePnP, dört köşeden ve kalibre edilmiş iç parametrelerden 6B konumu (pose) çıkarır. Küçük veya neredeyse tam karşıdan görülen işaretçiler, kareler arasında yer değiştiren neredeyse eşit iyilikte iki konum verebilir; daha büyük işaretçiler, işaretçi panoları veya AprilTag'ler bunu azaltır.
Simülasyon
Renk ve şerit ayırma
Yapay bir kamera görüntüsü HSV'ye çevrilir ve eşiklenerek maskeye dönüşür. Maskenin ağırlık merkezi (ya da şerit merkezi) bir direksiyon hatasına dönüşür. Aralıkları ayarla, ışığı değiştir, topu sürükle, sonra döngüyü P denetleyiciyle kapat.
Kod
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
lo, hi = np.array([0, 120, 70]), np.array([10, 255, 255]) # HSV'de kırmızı aralığı
while True:
ok, frame = cap.read()
if not ok:
break
mask = cv2.inRange(cv2.cvtColor(frame, cv2.COLOR_BGR2HSV), lo, hi)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, np.ones((5, 5), np.uint8))
cnts, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if cnts:
c = max(cnts, key=cv2.contourArea)
M = cv2.moments(c)
if M["m00"] > 500:
cx, cy = int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"])
err = cx - frame.shape[1] // 2 # PID'e ver -> dönüş hızı
cv2.circle(frame, (cx, cy), 8, (0, 255, 0), -1)
cv2.putText(frame, f"error {err}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
cv2.imshow("tracking", frame)
if cv2.waitKey(1) == 27:
breakimport cv2
import numpy as np
def lane_offset(frame):
h, w = frame.shape[:2]
roi = frame[int(h * 0.6):, :] # aracın önündeki yol
gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(cv2.GaussianBlur(gray, (5, 5), 0), 60, 150)
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 40, minLineLength=40, maxLineGap=20)
if lines is None:
return None
xs = [x for l in lines for x in (l[0][0], l[0][2])]
left = [x for x in xs if x < w / 2]
right = [x for x in xs if x >= w / 2]
if not left or not right:
return None
centre = (max(left) + min(right)) / 2
return (centre - w / 2) / (w / 2) # -1 .. +1, direksiyon PID'ine verSık yapılan hatalar
- OpenCV'nin görüntüleri BGR olarak yüklediğini unutup COLOR_RGB2HSV ile dönüştürmek; tüm renk eşikleri yanlış olur.
- OpenCV renk tonunu 0 ile 179 arasında saklarken derece cinsinden (0 ile 360) aralıklar kullanmak.
- Kırmızıyı tek bir renk tonu aralığıyla bölütlemek ve nesnenin yarısını kaybetmek.
- Otomatik pozlamayı ve beyaz dengesini açık bırakmak; ayarlanmış eşikler ışık değişince çalışmaz.
- Metrik mesafeleri veya işaretçi konumlarını kalibre edilmemiş, tahmini iç parametrelerle hesaplamak.
- Ham piksel farkını PID hatası olarak kullanıp sonra kamera çözünürlüğünü değiştirmek; tüm kazançlar sessizce ölçek değiştirir.
Yazılım mühendisliği bağlantısı
Görüntü işleme kodu mantık kadar veriye de bağlıdır, bu yüzden veriye dayalı testler ister. Depoda küçük bir gerçek veya simüle kare kümesini (tests/images/), her biri için beklenen sonuçla birlikte saklayın: şerit kayması, işaretçi kimliği ve konumu, hedef ağırlık merkezi.
- Fikstür testleri (fixture tests), işlem hattını saklanan her görüntü üzerinde çalıştırır ve beklenen değerlerle bir tolerans içinde karşılaştırır.
- Sürüm kontrolünde yapılandırma: HSV eşikleri ve kamera kalibrasyon dosyası (
camera_info.yaml) koddaki sihirli sayılar değil, girdilerdir. - Performans bütçesi: CI'da kare başına milisaniyeyi ölçün ve işlem hattı referans makinede örneğin 20 ms'yi aşarsa başarısız kılın.
- Saf görüntü işlemeyi kamera G/Ç'sinden ayırın; böylece aynı fonksiyon testlerde, Webots'ta ve robotta çalışır.
Dönem projesi uygulaması: zor durumları (gölgeler, virajlar, açılı duran bir işaretçi) kapsayan on Webots karesi yakalayın, beklenen çıktıları etiketleyin ve bunları fikstür testi olarak ekleyin.
Laboratuvar görevi
Önce tarayıcıdaki renk izleme simülasyonunu çalıştır, sonra OpenCV izleyicisini kendi kameranla: seçtiğin bir nesne için HSV aralığını ayarla ve hatayı 10 Hz'de yazdır. Webots'ta proje aracına bir kamera ekle ve lane_offset() fonksiyonunun verdiği şerit sapmasını her karede yazdır.
Kendini sına
Bir kamerada f_x = 800 piksel. 0,20 m genişliğindeki bir nesne 50 piksel genişliğinde görünüyor. Nesne ne kadar uzaktadır?
OpenCV'nin HSV uzayında kırmızı bir nesne nasıl bölütlenmelidir?
Bir hedef u = 400 pikselde, c_x = 320 piksel ve f_x = 530 piksel. Optik eksene göre yönü nedir?
Saniyede 30 kare veren 1280 × 720 RGB bir kameranın ham veri hızı nedir?
Bu hafta dönem projesinde
- Otonom kara aracı: Şerit takibi. lane_offset() + PID ile bir tur; sapma grafiği teslim.
- Otonom drone görevi: İşaretçi tespiti. Kamera görüntüsünden ArUco pozu; piksel hatası ve tahmini mesafe kaydı.
- Görsel kılavuzlu robot kol: Küp tespiti ve homografi. Renk ayırma + homografiyle küplerin dünya konumu; hata tablosu.
Ödev
Şerit sapmasını 6. haftadaki PID sınıfına bağla ve Webots aracının şeritten çıkmadan pistte bir tur atmasını sağla. Kısa bir video ve sapmanın zamana göre grafiğini teslim et.