Rehberler · 13 dk okuma

Engelden Kaçan Robot Yapımı: Arduino ve HC-SR04 ile Adım Adım

Engelden kaçan robot yapımı: servo üzerinde HC-SR04 ile tarama, L298N ve L293D bağlantısı, dur-geri çekil-dön algoritması, IR sensör ve Bluetooth kodu.

Engelden kaçan robot, önündeki duvarı, sandalyeyi ya da kutuyu çarpmadan fark edip kendine yeni bir yol bulan, kendi kendine hareket eden bir robot arabadır. En yaygın yapıda bir Arduino Uno, bir HC-SR04 ultrasonik mesafe sensörü, sensörü sağa sola çeviren bir SG90 servo motor, iki redüktörlü DC motor ve bir L298N (ya da L293D) motor sürücü kullanılır. Robot düz ilerler, bir engele yaklaşınca durur, biraz geri çekilir, başını iki yana çevirip ölçüm yapar ve en açık yöne dönerek yoluna devam eder.

Bu rehberde Arduino robot araba yazısında kurduğumuz 2WD arabayı, aynı pin düzenini koruyarak otonom bir robota dönüştürüyoruz. Bağlantı tablolarını, mesafe eşiklerinin nasıl hesaplandığını, üç aşamada büyüyen kodu, sensörün göremediği yüzeyleri ve bu açıkları kızılötesi sensörlerle nasıl kapatacağını, son olarak da telefondan Bluetooth ile manuel moda geçmeyi adım adım anlatıyoruz. Aynı robotu ESP32 ve Raspberry Pi Pico ile sürmek için gereken kodlar da yazının sonunda.

HC-SR04 ultrasonik mesafe sensörü
HC-SR04: T yazan dönüştürücü 40 kHz'lik ses gönderir, R yazan yankıyı dinler. Engelden kaçan robotun gözü budur; yaklaşık 15 derecelik dar bir açıyla 2–400 cm arasını ölçer.

Engelden kaçan robot nasıl çalışır?

Her otonom robot aynı üç adımlık döngüyü saniyede onlarca kez tekrarlar: algıla, karar ver, hareket et. Algılama işini HC-SR04 yapar: kulağın duyamadığı 40 kHz'lik kısa bir ses gönderir ve yankının dönme süresini ölçer; mikrosaniye cinsinden süreyi 58'e bölünce mesafeyi santimetre olarak bulursun. Karar işini Arduino yapar ve ölçülen mesafeyi senin belirlediğin eşiklerle karşılaştırır. Hareket işini ise L298N ile motorlar üstlenir. Sensör bir servonun üzerinde durduğu için robot, gövdesini çevirmeden sola ve sağa "bakabilir"; bu da sabit sensörlü robotlara göre çok daha isabetli dönüş kararları verir. Sensörün çalışma ilkesinin ayrıntıları HC-SR04 Mesafe Sensörü yazısında.

ParçaGöreviNot
Arduino Uno (ya da Nano)Beyin: ölçer, karar verir, motorlara komut gönderirESP32 ve Pico da kullanılabilir
HC-SR04Göz: öndeki engelin uzaklığını ölçer5 V ile çalışır, 2–400 cm
SG90 servoBoyun: sensörü sola ve sağa çevirirSensör plastik bir aparatla servo başlığına takılır
L298N ya da L293D shieldKas: motorlara yön ve hız verirArduino pini motor akımını veremez
2 redüktörlü TT motor ve sarhoş tekerDiferansiyel sürüşDönüş, iki tekerleğin zıt yönde dönmesiyle yapılır
2 adet 18650 pil (7,4 V)Enerji9 V kare pil yetmez
2 adet IR engel sensörü (isteğe bağlı)Köşe ve yan engelleri yakalarFC-51 gibi LM393'lü modüller
HC-05 (isteğe bağlı)Telefondan manuel kontrolYalnızca Android ile çalışır
Arduino Uno
Arduino Uno
L298N
L298N
TT motor
TT motor
18650 Li-ion
18650 Li-ion

Güç beslemesi: servo robotu resetlemesin

Engelden kaçan robotta motorlara bir de servo eklendiği için güç dağıtımı, sıradan bir robot arabadan daha önemlidir. SG90 dururken yaklaşık 10 mA çeker; dönmeye başladığı anda akımı 100–250 mA'e çıkar, zorlandığında ise yarım ampere yaklaşır. Bu ani akım, motorların kalkış akımıyla aynı ana denk gelirse Arduino'nun 5 V hattı bir anlığına çöker ve kart yeniden başlar. Çözüm basit: motorlar pilden L298N üzerinden, Arduino ise pilden VIN üzerinden beslenir; servo, HC-SR04, IR sensörler ve HC-05 Arduino'nun 5V pinini paylaşır ve servonun besleme uçlarının yanına 470 µF'lık bir kondansatör konur. Servo dönerken bile 5 V hattındaki toplam yük yarım amperin altında kalır; bu yük kısa süreli olduğu için Uno'nun regülatörü onu taşır. Pil ömrünü kendi değerlerinle hesaplamak için Pil ömrü aracını kullanabilirsin.

BölümNereden beslenir?Tipik akım
2 TT motorL298N 12V klemensi ← 2 adet 18650 (7,4 V)Sürüşte 0,3–0,6 A, kalkışta 2 A'e kadar
Arduino UnoVIN ← pilin artısı (anahtardan sonra)Yaklaşık 50 mA
SG90 servoArduino 5V + 470 µF kondansatörDururken ~10 mA, dönerken 100–250 mA
HC-SR04Arduino 5VYaklaşık 15 mA
2 IR sensörArduino 5VHer biri 20–40 mA
HC-05Arduino 5VEşleşme ararken 30–40 mA, bağlıyken yaklaşık 10 mA
Tüm GND'leri (pil eksisi, L298N, Arduino, servo, sensörler) birbirine bağla; ortak toprak olmazsa sensör anlamsız mesafeler okur ve servo titrer. Servoyu motorlarla aynı ince jumper kablodan besleme. MG90S ya da MG996R gibi daha güçlü bir servo kullanacaksan onu Arduino'dan değil, 5 V'a ayarlanmış ayrı bir düşürücü modülden (ör. LM2596) besle.

Bağlantı şeması: Arduino Uno, ESP32 ve Pico

Motor pinleri robot araba rehberindekiyle aynıdır; böylece hazır arabanı sökmeden, yalnızca sensör kafasını ekleyerek bu projeye geçebilirsin. Hız pinlerinin 5 ve 6'da olması bilinçli bir seçimdir: Uno'da Servo kütüphanesi Timer1'i kullandığı için 9 ve 10 numaralı pinlerdeki PWM'i devre dışı bırakır; motor hızları bu pinlerde olsaydı servo takıldığı anda hız kontrolü bozulurdu. HC-SR04 ve IR sensörler A0–A3'e bağlanır. Uno'nun analog pinleri gerektiğinde 14–19 numaralı dijital pinler gibi de çalışır ve bu seçim aynı kodu L293D shield ile kullanmayı kolaylaştırır, çünkü shield bu pinlere dokunmaz. Kartının pin düzenini Pinout aracında görebilirsin.

BağlantıArduino UnoESP32Raspberry Pi Pico
L298N ENA (sol motor hızı)D5GPIO 25GP15
L298N IN1, IN2D7, D8GPIO 27, 33GP14, GP13
L298N ENB (sağ motor hızı)D6GPIO 26GP10
L298N IN3, IN4D9, D4GPIO 32, 4GP12, GP11
Servo sinyal (turuncu)D3GPIO 13GP16
Servo + (kırmızı), − (kahverengi)5V, GNDL298N 5V, GNDL298N 5V, GND
HC-SR04 TRIGA0GPIO 18GP17
HC-SR04 ECHOA1 (doğrudan)GPIO 19, 1 kΩ / 2 kΩ bölücüyleGP18, 1 kΩ / 2 kΩ bölücüyle
HC-SR04 VCC5VL298N 5VL298N 5V
IR sol OUT, IR sağ OUTA2, A3GPIO 23, 22GP19, GP20
IR sensör VCC5V3V33V3
HC-05 TXD, RXDD10, D11 (RXD bölücüyle)Gerekmez (dahili Bluetooth)GP1, GP0 (UART0)
Kartın beslemesiVIN ← pil (anahtardan sonra)5V (VIN) ← L298N 5VVSYS ← L298N 5V
HC-SR04'ün ECHO pini 5 V'luk sinyal verir; ESP32 ve Pico'nun pinleri ise 3,3 V'tur ve 5 V'a dayanıklı değildir. ECHO → 1 kΩ → karta giden nokta → 2 kΩ → GND şeklinde bir gerilim bölücü kur: 5 V × 2 / 3 ≈ 3,3 V. TRIG için bölücü gerekmez; 3,3 V'luk sinyal sensörü tetiklemeye yeter. ESP32 ve Pico'da kart, servo ve HC-SR04 aynı L298N 5V çıkışını paylaştığı için bu küçük regülatörün yaklaşık 0,5 A'lik sınırına yaklaşılabilir; resetlenme görürsen servoyu ayrı bir 5 V kaynaktan besle.

Sensör kafasını monte et ve test et

SG90 mikro servo motor
SG90: 9 gramlık mikro servo; 4,8 V'ta 60 dereceyi yaklaşık 0,1 saniyede döner. HC-SR04'ü taşıyan plastik aparat servonun başlığına vidalanır.
  1. Servoyu başlığı takılı değilken bağla ve aşağıdaki test kodunu yükle. Kod her taramanın sonunda servoyu 90 dereceye getirip 2 saniye bekler; bu sırada USB kablosunu çıkar, servo olduğu yerde kalır. Başlığı, sensör tam öne bakacak şekilde mile tak; böylece 90 derece "tam ileri" anlamına gelir.
  2. HC-SR04'ü plastik aparatla ya da sıcak silikonla başlığa sabitle. Sensör kabloları servo dönerken gerilmeyecek kadar uzun olmalı; 140 derecelik tarama sırasında kablonun şasiye takılması sık görülen bir arızadır.
  3. Sensörü yerden 8–12 cm yükseğe ve gövdenin en ön noktasına yakın yerleştir. Çok alçakta durursa yerden ve halının dokusundan yankı alır; çok yüksekte durursa alçak engelleri göremez.
  4. Sensörü 3–5 derece yukarı eğik monte et. Tam yatay duran ve alçakta kalan bir sensör, özellikle pürüzlü zeminlerde yerden dönen yankıyı engel sanabilir.
  5. Seri Monitör'ü 9600 baud ile aç. Elini sensörün soluna tuttuğunda 125 ve 160 derecelik satırlarda küçük değerler görüyorsan sol taraf büyük açılardır; tersi oluyorsa gelişmiş koddaki SERVO_DIR değerini -1 yap, ilk kodda ise LEFT_ANGLE ile RIGHT_ANGLE değerlerinin yerini değiştir.
Arduino (C++)
1#include <Servo.h>
2
3const int SERVO_PIN = 3;
4const int TRIG = A0, ECHO = A1;        // analog pinler dijital pin olarak / analog pins used as digital pins
5Servo head;
6
7int pingCm() {
8  digitalWrite(TRIG, LOW);  delayMicroseconds(2);
9  digitalWrite(TRIG, HIGH); delayMicroseconds(10);   // 10 µs tetik darbesi / 10 µs trigger pulse
10  digitalWrite(TRIG, LOW);
11  unsigned long t = pulseIn(ECHO, HIGH, 25000UL);    // en fazla 25 ms bekle (~4,3 m) / wait at most 25 ms (~4.3 m)
12  return t / 58;                                     // 0 = yankı yok / 0 = no echo
13}
14
15void setup() {
16  Serial.begin(9600);
17  pinMode(TRIG, OUTPUT);
18  pinMode(ECHO, INPUT);
19  head.attach(SERVO_PIN);
20  head.write(90);                                    // önce ortala / center first
21  delay(1000);
22}
23
24void loop() {
25  const int angles[] = {20, 55, 90, 125, 160};
26  for (int a : angles) {
27    head.write(a);
28    delay(400);                                      // servo yerine otursun / let the servo settle
29    Serial.print(a);
30    Serial.print(F(" derece / degrees: "));
31    Serial.print(pingCm());
32    Serial.println(F(" cm"));
33  }
34  Serial.println();
35  head.write(90);
36  delay(2000);
37}

Her satırda bir açı ve o yöndeki mesafe görünür. Sensörün önünde yaklaşık 4 metre içinde hiçbir şey yoksa 0 cm okursun: pulseIn() 25 ms içinde yankı gelmezse 0 döndürür. Ana kodda 0'ı "önü açık" sayacağız, ama kablosu çıkmış ya da TRIG ile ECHO'su ters bağlanmış bir sensör de çoğu zaman 0 okur. Bu yüzden robotu yere koymadan önce elini 10, 30 ve 60 cm'ye tutup değerlerin gerçekten değiştiğini gör; test edilmeden çalıştırılan robot duvara hiç yavaşlamadan gider.

Karar algoritması: dur, geri çekil, en açık yöne dön

Robotun davranışını mesafeye göre bölgelere ayırmak, kodu hem basit hem güvenilir yapar. Önünde engel yokken tam hızla ilerler, engel yaklaştıkça yavaşlar, durma eşiğine geldiğinde durur ve tarama yapar. Tarama sırasında sensör 70 ve 35 derece sola, 35 ve 70 derece sağa bakar; her yönde üç ölçüm alınır ve ortancası kullanılır. Robot en uzun mesafeyi veren yöne, o açı kadar döner. Hiçbir yön açık değilse, örneğin bir köşeye girdiyse, biraz geri gelip 180 derece döner.

Ölçülen mesafeBölgeRobotun yaptığı
60 cm'den uzak (ya da yankı yok)SerbestTam hızla ileri (PWM 200)
25–60 cmYaklaşmaMesafeyle orantılı yavaşlar (PWM 200'den 130'a)
12–25 cmEngelDurur, ölçümü doğrular, iki yana tarar, en açık yöne döner
12 cm'den yakınÇok yakınÖnce 350 ms geri çekilir, sonra tarar
Bütün yönler 35 cm'den yakınÇıkmazGeri gelir ve 180 derece döner

Bu eşikler rastgele seçilmedi. Durma mesafesi üç payın toplamıdır: tepki payı (iki ölçüm arasında alınan yol), fren payı (motorlar kesildikten sonra robotun kayması) ve dönüş payı (robot yerinde dönerken burnunun engele değmemesi). 6 V alan TT motorlu bir araba yaklaşık 0,5–0,7 m/s hızla gider ve 60 ms'lik ölçüm aralığında 3–4 cm yol alır. Fren payı yaklaşık 5 cm, 15 cm genişliğinde yerinde dönen bir gövde için dönüş payı da 10 cm civarındadır. Toplam yaklaşık 18–20 cm eder; üstüne birkaç santimetrelik güvenlik payı eklenince STOP_CM = 25 iyi bir başlangıç olur. Robotun daha hızlıysa ya da zemin kayganse eşiği 30–35 cm'ye çıkar. Dar koridorlarda ise çok büyük bir eşik, robotun sürekli durup taramasına yol açar; son ayarı kendi pistinde yap.

Adım adım kod 1: ilk engelden kaçan robot

İlk sürüm algoritmanın iskeletidir: ileri git, 25 cm'de dur, geri çekil, sola ve sağa bak, daha açık tarafa dön. Motor fonksiyonu drive(sol, sağ) robot araba rehberindekinin aynısıdır ve -255 ile 255 arasında değer alır; artı ileri, eksi geri demektir. Kodu yükledikten sonra önce arabayı tekerlekleri boşta kalacak şekilde bir kutunun üstüne koy ve elini sensöre yaklaştırarak durma ve dönme davranışını izle; her şey doğruysa robotu yere indir.

Arduino (C++)
1#include <Servo.h>
2
3const int ENA = 5, IN1 = 7, IN2 = 8;            // sol motor / left motor
4const int ENB = 6, IN3 = 9, IN4 = 4;            // sağ motor / right motor
5const int SERVO_PIN = 3;
6const int TRIG = A0, ECHO = A1;
7
8const int STOP_CM = 25;                         // bu mesafede dur / stop at this distance
9const int SPEED = 170;                          // düz gidiş hızı / cruising speed
10const int TURN = 170;                           // dönüş hızı / turning speed
11const int LEFT_ANGLE = 160, RIGHT_ANGLE = 20;   // ters ise yer değiştir / swap if reversed
12
13Servo head;
14
15void drive(int left, int right) {               // her biri -255 … 255 / each -255 … 255
16  digitalWrite(IN1, left > 0);  digitalWrite(IN2, left < 0);
17  digitalWrite(IN3, right > 0); digitalWrite(IN4, right < 0);
18  analogWrite(ENA, abs(left));
19  analogWrite(ENB, abs(right));
20}
21
22int pingCm() {
23  digitalWrite(TRIG, LOW);  delayMicroseconds(2);
24  digitalWrite(TRIG, HIGH); delayMicroseconds(10);
25  digitalWrite(TRIG, LOW);
26  unsigned long t = pulseIn(ECHO, HIGH, 25000UL);
27  if (t == 0) return 400;                       // yankı yok: önü açık say / no echo: treat as open
28  return t / 58;
29}
30
31int lookAt(int angle) {
32  head.write(angle);
33  delay(400);                                   // servo dönsün ve dursun / let the servo turn and settle
34  return pingCm();
35}
36
37void setup() {
38  int pins[] = {ENA, IN1, IN2, ENB, IN3, IN4, TRIG};
39  for (int p : pins) pinMode(p, OUTPUT);
40  pinMode(ECHO, INPUT);
41  drive(0, 0);
42  head.attach(SERVO_PIN);
43  head.write(90);
44  delay(3000);                                  // yere koyman için süre / time to put it on the floor
45}
46
47void loop() {
48  int front = pingCm();
49  if (front > STOP_CM) {
50    drive(SPEED, SPEED);                        // yol açık: ileri / path clear: forward
51    delay(60);                                  // ölçümler arası 60 ms / 60 ms between readings
52    return;
53  }
54
55  drive(0, 0);                                  // 1) dur / 1) stop
56  delay(150);
57  drive(-SPEED, -SPEED);                        // 2) biraz geri çekil / 2) back off a little
58  delay(300);
59  drive(0, 0);
60
61  int left = lookAt(LEFT_ANGLE);                // 3) iki yana bak / 3) look both ways
62  int right = lookAt(RIGHT_ANGLE);
63  head.write(90);
64  delay(300);
65
66  if (left < STOP_CM && right < STOP_CM) {      // iki yan kapalı: geri dön / both blocked: turn around
67    drive(-TURN, TURN);
68    delay(700);
69  } else if (left > right) {                    // 4) daha açık tarafa dön / 4) turn to the more open side
70    drive(-TURN, TURN);
71    delay(350);
72  } else {
73    drive(TURN, -TURN);
74    delay(350);
75  }
76  drive(0, 0);
77  delay(100);
78}

Bu kod işini görür ama birkaç zayıflığı vardır: tek bir hatalı ölçüm robotu boşuna durdurur, robot engele hep aynı hızla yaklaşır, yalnızca iki yöne bakar ve dönüş süreleri açıya göre ayarlanmaz. Ayrıca delay() ile beklediği için tarama sırasında hiçbir şeye tepki veremez. Bekleme mantığının ayrıntıları millis() ve delay() yazısında; sonraki sürüm bu sorunların çoğunu çözüyor.

Adım adım kod 2: filtre, kademeli yavaşlama, IR ve Bluetooth

Gelişmiş sürüm aynı bağlantılarla çalışır; IR sensörleri ve HC-05'i henüz takmadıysan da sorun olmaz. IR girişleri INPUT_PULLUP ile tanımlandığı için takılı olmayan bir sensör "engel yok" (HIGH) okunur; HC-05 yoksa robot zaten otomatik modda başlar. Kodun her parçasının ne yaptığını aşağıdaki tabloda bulabilirsin.

Arduino (C++)
1#include <Servo.h>
2#include <SoftwareSerial.h>
3
4// Pinler / Pins
5const int ENA = 5, IN1 = 7, IN2 = 8;     // sol motor / left motor
6const int ENB = 6, IN3 = 9, IN4 = 4;     // sağ motor / right motor
7const int SERVO_PIN = 3;
8const int TRIG = A0, ECHO = A1;
9const int IR_LEFT = A2, IR_RIGHT = A3;   // LOW = engel (takılı değilse HIGH) / LOW = obstacle (HIGH if not fitted)
10SoftwareSerial bt(10, 11);               // RX, TX → HC-05 TXD, RXD (RXD bölücüyle / RXD via divider)
11
12// Ayarlar / Settings
13const int BACK_CM = 12;                  // daha yakınsa geri çekil / closer: back off first
14const int STOP_CM = 25;                  // dur ve tara / stop and scan
15const int SLOW_CM = 60;                  // yavaşlamaya başla / start slowing down
16const int GUARD_CM = 15;                 // manuel modda çarpışma koruması / collision guard in manual mode
17const int MAX_CM = 400;
18const int CRUISE = 200, SLOW = 130, TURN = 170;   // PWM hızları / PWM speeds
19const int SERVO_CENTER = 90;             // sensör öne bakmıyorsa düzelt / adjust if not facing ahead
20const int SERVO_DIR = 1;                 // +70 sola bakmıyorsa -1 yap / set to -1 if +70 doesn't look left
21const float MS_PER_DEG = 4.0;            // derece başına ms, kalibre et / ms per degree, calibrate
22const unsigned long BT_TIMEOUT = 600;    // manuelde komut kesilirse dur / stop if commands stop
23
24Servo head;
25bool autoMode = true;
26int manualLeft = 0, manualRight = 0, manualSpeed = 200;
27unsigned long lastCmd = 0, lastPing = 0;
28
29void drive(int left, int right) {        // her biri -255 … 255 / each -255 … 255
30  left = constrain(left, -255, 255);
31  right = constrain(right, -255, 255);
32  digitalWrite(IN1, left > 0);  digitalWrite(IN2, left < 0);
33  digitalWrite(IN3, right > 0); digitalWrite(IN4, right < 0);
34  analogWrite(ENA, abs(left));
35  analogWrite(ENB, abs(right));
36}
37
38int pingCm() {
39  digitalWrite(TRIG, LOW);  delayMicroseconds(2);
40  digitalWrite(TRIG, HIGH); delayMicroseconds(10);
41  digitalWrite(TRIG, LOW);
42  unsigned long t = pulseIn(ECHO, HIGH, 25000UL);  // ~4,3 m zaman aşımı / ~4.3 m timeout
43  return t == 0 ? MAX_CM : (int)(t / 58);          // yankı yok = açık alan / no echo = open space
44}
45
46int distanceCm() {                       // 3 ölçümün ortancası / median of 3 readings
47  int a = pingCm(); delay(60);
48  int b = pingCm(); delay(60);
49  int c = pingCm();
50  return max(min(a, b), min(max(a, b), c));
51}
52
53bool irBlocked(int pin) {
54  return digitalRead(pin) == LOW;
55}
56
57int lookAt(int offset) {                 // + sol, - sağ (derece) / + left, - right (degrees)
58  head.write(SERVO_CENTER + SERVO_DIR * offset);
59  delay(300);                            // servo yerine otursun / let the servo settle
60  return distanceCm();
61}
62
63void turnBy(int deg) {                   // + sola, - sağa / + left, - right
64  if (deg > 0) drive(-TURN, TURN);
65  else drive(TURN, -TURN);
66  delay((unsigned long)(abs(deg) * MS_PER_DEG));
67  drive(0, 0);
68  delay(100);
69}
70
71void avoid() {
72  drive(0, 0);                           // 1) dur / 1) stop
73  delay(120);
74  int front = distanceCm();              // ortancayla doğrula / confirm with the median
75  bool sides = irBlocked(IR_LEFT) || irBlocked(IR_RIGHT);
76  if (front >= STOP_CM && !sides) return;          // yanlış alarm / false alarm
77
78  if (front < BACK_CM || sides) {        // 2) çok yakın: geri çekil / 2) too close: back off
79    drive(-TURN, -TURN);
80    delay(350);
81    drive(0, 0);
82  }
83
84  const int offsets[] = {70, 35, -35, -70};        // 3) tara / 3) scan
85  int bestOffset = 0, bestCm = 0;
86  for (int off : offsets) {
87    int cm = lookAt(off);
88    if (cm > bestCm) { bestCm = cm; bestOffset = off; }
89  }
90  head.write(SERVO_CENTER);
91  delay(200);
92
93  if (bestCm < STOP_CM + 10) {           // her yön kapalı: çıkmaz / every way blocked: dead end
94    drive(-TURN, -TURN);
95    delay(400);
96    turnBy(180);
97  } else {
98    turnBy(bestOffset);                  // 4) en açık yöne dön / 4) turn to the most open direction
99  }
100}
101
102void autoStep() {
103  if (millis() - lastPing < 60) return;  // 60 ms'de bir ölç / measure every 60 ms
104  lastPing = millis();
105  int d = pingCm();
106  bool l = irBlocked(IR_LEFT), r = irBlocked(IR_RIGHT);
107
108  if (d < STOP_CM || (l && r)) {
109    avoid();
110  } else if (l) {                        // sol köşede engel: sağa kaç / front-left: dodge right
111    drive(TURN, -TURN);
112    delay(150);
113  } else if (r) {                        // sağ köşede engel: sola kaç / front-right: dodge left
114    drive(-TURN, TURN);
115    delay(150);
116  } else if (d < SLOW_CM) {
117    int s = map(d, STOP_CM, SLOW_CM, SLOW, CRUISE);   // yaklaştıkça yavaşla / slow down as you get closer
118    drive(s, s);
119  } else {
120    drive(CRUISE, CRUISE);
121  }
122}
123
124void manualStep() {
125  if (millis() - lastCmd > BT_TIMEOUT) {           // komut yok: dur / no commands: stop
126    manualLeft = 0;
127    manualRight = 0;
128  }
129  static int front = MAX_CM;
130  if (millis() - lastPing >= 60) {
131    lastPing = millis();
132    front = pingCm();
133  }
134  bool forward = manualLeft > 0 && manualRight > 0;
135  if (forward && front < GUARD_CM) drive(0, 0);    // çarpışma koruması / collision guard
136  else drive(manualLeft, manualRight);
137}
138
139void handleBluetooth() {
140  while (bt.available()) {
141    char c = bt.read();
142    Serial.println(c);                   // gelen harfi göster / show the incoming letter
143    lastCmd = millis();
144    if (c == 'A') {                      // otomatik mod / auto mode
145      autoMode = true;
146      continue;
147    }
148    if (c == 'M') {                      // manuel mod / manual mode
149      autoMode = false;
150      manualLeft = manualRight = 0;
151      drive(0, 0);
152      head.write(SERVO_CENTER);
153      continue;
154    }
155    if (autoMode) continue;              // otomatikte yön harflerini yok say / ignore in auto mode
156    int s = manualSpeed;
157    switch (c) {
158      case 'F': manualLeft = s;  manualRight = s;  break;   // ileri / forward
159      case 'B': manualLeft = -s; manualRight = -s; break;   // geri / backward
160      case 'L': manualLeft = -s; manualRight = s;  break;   // yerinde sola / spin left
161      case 'R': manualLeft = s;  manualRight = -s; break;   // yerinde sağa / spin right
162      case 'S': manualLeft = 0;  manualRight = 0;  break;   // dur / stop
163      default:
164        if (c >= '0' && c <= '9') manualSpeed = map(c - '0', 0, 9, 100, 240);   // hız kademesi / speed level
165    }
166  }
167}
168
169void setup() {
170  int outs[] = {ENA, IN1, IN2, ENB, IN3, IN4, TRIG};
171  for (int p : outs) pinMode(p, OUTPUT);
172  drive(0, 0);
173  pinMode(ECHO, INPUT);
174  pinMode(IR_LEFT, INPUT_PULLUP);
175  pinMode(IR_RIGHT, INPUT_PULLUP);
176  Serial.begin(9600);
177  bt.begin(9600);                        // HC-05 veri modu hızı / HC-05 data mode baud rate
178  head.attach(SERVO_PIN);
179  head.write(SERVO_CENTER);
180  delay(3000);                           // yere koyman için süre / time to put it on the floor
181}
182
183void loop() {
184  handleBluetooth();
185  if (autoMode) autoStep();
186  else manualStep();
187}
Fonksiyon ya da ayarNe yapar?
pingCm()Tek ölçüm yapar; 25 ms içinde yankı gelmezse 400 cm (açık alan) döndürür
distanceCm()60 ms arayla 3 ölçüm alır ve ortancasını verir; tek bir sıçrayan değeri eler
autoStep()60 ms'de bir ölçer; mesafeye ve IR'lere göre ilerler, yavaşlar, kaçar ya da avoid() çağırır
avoid()Durur, ölçümü doğrular, gerekirse geri çekilir, 4 yöne bakar ve en açık yöne döner
turnBy(derece)Robotu verilen açı kadar yerinde döndürür; süre MS_PER_DEG ile hesaplanır
manualStep()Telefondan gelen yönü uygular; ileri giderken 15 cm'de durur, 600 ms komut gelmezse durur
SERVO_CENTER, SERVO_DIRSensörün tam öne baktığı açı ve sol tarafın yönü
STOP_CM, SLOW_CM, BACK_CMDurma, yavaşlama ve geri çekilme eşikleri

Ortanca (medyan) filtre, engelden kaçan robot için ortalamadan daha iyidir: 24, 26 ve 400 cm gibi üç ölçümün ortalaması 150 cm çıkar ve robotu yanıltır, ortanca ise 26 cm'yi seçer. Konuyu daha derin incelemek istersen sensör verisi filtreleme yazısına bakabilirsin. Otomatik ve manuel mod ayrımı da aslında küçük bir durum makinesidir; bu yaklaşımı Arduino durum makinesi rehberinde ayrıntılı anlatıyoruz. Kodu yükledikten sonra robotu kendi odana göre şu sırayla ayarla:

  1. Servo merkezi: Robot düz bir duvara dik dururken sensör tam karşıya bakmıyorsa SERVO_CENTER değerini 85 ya da 95 gibi küçük adımlarla değiştir.
  2. Dönüş süresi: setup()'ın sonuna geçici olarak turnBy(360); satırını ekle ve robotun tam bir tur atıp atmadığını izle. 360 yerine 300 derece döndüyse MS_PER_DEG değerini 4,0 × 360 / 300 = 4,8 yap. Bu değer pile ve zemine göre değiştiği için ayarı dolu pille, robotun gezeceği zeminde yap.
  3. Durma eşiği: Robotu duvara doğru gönder ve durduğu yerde burnunun duvara uzaklığını ölç. 10 cm'den az kalıyorsa STOP_CM değerini 5 cm artır.
  4. Yavaş hız: SLOW değeri motorların kalkış eşiğinin altında kalırsa robot yaklaşma bölgesinde durup kalır; bu durumda SLOW değerini 150'ye çıkar.

HC-SR04'ün kör noktaları: yumuşak yüzeyler, eğik duvarlar ve ince engeller

Ultrasonik sensör, sesi geri yansıtan yüzeyleri iyi görür; ama her engel sesi sensöre geri göndermez. Üretici, iyi sonuç için hedefin en az 0,5 m² büyüklüğünde ve olabildiğince düz olmasını önerir. Gerçek bir odada ise robotun önüne perde, koltuk, sandalye ayağı ve duvar köşeleri çıkar. Ses, bilardo topu gibi geldiği açıyla yansır: duvara dik bakan sensör güçlü bir yankı alır, ama bakış açısı dikten yaklaşık 30–40 dereceden fazla saparsa ses başka yöne seker ve hiç geri dönmez. Bu durumda pulseIn() 0 döndürür ve kod önü açık sanır. Robotların duvara sürtünerek çarpmasının en sık sebebi budur.

Engel ya da yüzeyNeden görülmez?Çözüm
Perde, koltuk, minder, kıyafet, hayvan tüyüYumuşak ve gözenekli yüzey sesi emer, yankı çok zayıf dönerYakın mesafe için IR sensör ekle, robotu yavaş sür
Duvara eğik yaklaşmakSes başka yöne yansır, yankı sensöre dönmezÖn köşelere dışa açılı IR sensör ekle, tarama açılarını sık tut
Sandalye ve masa ayağı, ince direkYansıtan yüzey çok küçük, yankı zayıfHızı düşür, ortanca filtre kullan, yanlara IR ekle
Sensörün altında kalan alçak engel (kablo, eşik, terlik)Ses konisinin altında kalırÖn tampona aşağı bakan bir IR sensör ya da mikro anahtar ekle
Masa kenarı, merdiven boşluğuUltrasonik sensör öne bakar, zemindeki boşluğu görmezZemine bakan TCRT5000 ile boşluk algılama
2 cm'den yakın nesnelerSensörün ölü bölgesi: yankı, ses gönderimi bitmeden dönerRobotu 12 cm'de geri çekilecek şekilde ayarla
Köşeler ve dar koridorlarSes iki duvar arasında sekip geç döner, mesafe uzun görünürOrtanca filtre ve birden fazla açıdan ölçüm
Bir sensörün göremediğini başka bir sensörle kapatmak, tek sensörü kusursuz hâle getirmeye çalışmaktan her zaman daha etkilidir. Ama ilk iş olarak hızı düşür: 0,3 m/s ile giden bir robot, aynı hatalı ölçümle 0,7 m/s'deki kadar sert çarpmaz ve ikinci bir ölçüme zaman kalır. Farklı mesafe sensörlerinin güçlü ve zayıf yanlarını Arduino mesafe sensörleri karşılaştırması yazısında topladık.

IR sensörlerle güçlendirme

Ultrasonik ve kızılötesi (IR) sensörler birbirinin eksiğini kapatır. FC-51 gibi IR engel sensörleri bir kızılötesi LED ile bir alıcıdan oluşur; LM393 karşılaştırıcısı yansıyan ışık yeterince güçlüyse çıkışı LOW yapar ve kart üzerindeki LED yanar. Menzili trimpotla yaklaşık 2–30 cm arasında ayarlanır ama mesafe ölçmez, yalnızca "engel var" ya da "yok" der. İki IR sensörü robotun ön köşelerine, dışa doğru 30–45 derece açıyla yerleştirirsen ultrasonik sensörün dar konisinin dışında kalan sandalye ayaklarını ve eğik duvarları yakalarsın. Gelişmiş kodda sol IR tetiklenince robot kısa bir an sağa, sağ IR tetiklenince sola kaçar; ikisi birden tetiklenirse önünde engel varmış gibi durup tarar.

ÖzellikHC-SR04 (ultrasonik)FC-51 (IR engel)TCRT5000 (IR yansıma)
ÇıktıMesafe (cm)Var / yok (dijital)Analog değer ve dijital çıkış
Menzil2–400 cmYaklaşık 2–30 cm (ayarlanabilir)Yaklaşık 1–15 mm
Algılama açısıYaklaşık 15°Yaklaşık 35°Çok dar
Zayıf olduğu durumYumuşak, eğik ve ince yüzeylerSiyah ve mat yüzeyler, güneş ışığıYükseklik değişimi, güneş ışığı
İyi gördüğüCam, siyah nesneler, büyük düz yüzeylerKumaş, köşeler, yakın engellerZemin rengi ve masa kenarı
Robottaki yeriServoyla dönen ana gözÖn köşelerÖn tamponun altı, zemine bakar
TCRT5000 kızılötesi yansıma sensörü
TCRT5000: zemine 5–10 mm yakınlıkta, aşağı bakacak şekilde takılınca masa kenarını ve merdiven boşluğunu fark eder; yansıma kesildiği anda robot durup geri çekilebilir.

Masa üstünde gezen robotlar için en tehlikeli engel aslında boşluktur. Ön tamponun altına zemine bakan bir TCRT5000 modülü takarsan zemin altından kaybolduğu anda bunu fark edersin. Modülün dijital çıkışını Uno'da boş kalan D2'ye bağla; zemin görünmediğinde çıkışın hangi seviyeye geçtiğini önce Seri Monitör'de gör, sonra autoStep() fonksiyonunun en başında bu pini oku ve zemin kaybolduysa motorları hemen durdurup robotu geri çek. Bu kontrol mesafe ölçümünden önce yapılmalıdır; düşmek, çarpmaktan her zaman daha pahalıdır.

Bluetooth ile manuel moda geçiş

Otonom robotu bazen elle sürmek istersin: onu bir odadan diğerine götürmek, bir engelin önüne getirip tepkisini denemek ya da sadece oynamak için. Gelişmiş koddaki HC-05 bağlantısı bunu sağlar. Telefon A harfini gönderince robot otomatik moda, M harfini gönderince manuel moda geçer. Manuel modda robot araba rehberindeki kumanda harfleri (F, B, L, R, S) kullanılır, ama bir farkla: robot ileri giderken önünde 15 cm'den yakın bir engel görürse senin komutuna rağmen durur. Bu çarpışma koruması, otonom robotun sensörlerinden manuel sürüşte de yararlanmanı sağlar. HC-05'in eşleştirme adımları ve AT komutları HC-05 Bluetooth Modülü yazısında.

HarfGöreviHangi modda?
AOtomatik moda geçHer iki modda
MManuel moda geç, robotu durdurHer iki modda
F / Bİleri / geriManuel
L / RYerinde sola / sağa dönManuel
SDurManuel
0–9Hız kademesi (PWM 100–240)Manuel
  1. HC-05'in TXD pinini D10'a, RXD pinini 1 kΩ / 2 kΩ bölücü üzerinden D11'e, VCC'yi 5V'a, GND'yi GND'ye bağla.
  2. Android telefonunun Bluetooth ayarlarında HC-05'i 1234 (olmazsa 0000) şifresiyle eşleştir.
  3. Play Store'dan bir Bluetooth araba kumandası uygulaması kur ve HC-05'e bağlan. Arduino'yu USB'ye bağlı tutup Seri Monitör'ü 9600 baud ile açarsan kod, telefondan gelen her harfi ekrana yazar.
  4. Çoğu kumanda uygulamasında far ya da korna gibi fazladan tuşlar bulunur ve her biri ayrı bir harf gönderir. Bu harfleri Seri Monitör'de gör ve koddaki 'A' ile 'M' yerine yaz; tuşlarını kendin belirlemek istersen App Inventor ile Arduino Bluetooth rehberiyle kendi kumandanı yapabilirsin.
  5. USB kablosunu çıkar ve anahtarı aç; robot 3 saniye sonra otomatik modda yola çıkar. Manuel moda geçip telefondan sür.
Uno'da SoftwareSerial bir bayt alırken diğer kesmeleri yaklaşık 1 ms bekletir; bu sırada Servo kütüphanesinin darbeleri uzayabilir ve sensör kafası hafifçe seğirebilir. Robotun davranışını bozmaz, ama rahatsız ediyorsa ESP32'nin dahili Bluetooth'una geçebilirsin. HC-05 ve HC-06 iPhone ile çalışmaz; iPhone için BLE kullanan bir modül ya da BLE ile programlanmış bir ESP32 gerekir.

L293D motor shield ile kurulum

L293D motor sürücü shield
L293D motor shield: Arduino Uno'nun üstüne takılır; M1–M4 klemenslerine dört DC motor, köşedeki başlıklara iki servo bağlanır.

Kablo karmaşasını sevmiyorsan L298N yerine Arduino Uno'nun üstüne doğrudan takılan L293D motor shield'i (Adafruit Motor Shield V1 ve klonları) kullanabilirsin. Shield dört DC motor sürebilir, iki servo başlığı vardır ve servoları Arduino'nun 5 V hattından besler. Motorlar Arduino pinlerine doğrudan değil, kart üzerindeki 74HC595 kaydırmalı yazmacı üzerinden bağlıdır; bu yüzden onları Kütüphane Yöneticisi'nden kuracağın Adafruit Motor Shield library (V1 için olan, V2 değil) ile, yani AFMotor ile sürersin. Shield D3, D4, D5, D6, D7, D8, D11 ve D12'yi motorlar, D9 ve D10'u servolar için kullanır; A0–A5, D2 ve D13 boş kalır. HC-SR04 ve IR sensörler zaten A0–A3'te olduğu için yalnızca motor kodunu, servo pinini ve HC-05 pinlerini değiştirmen yeter.

AyarL298N ileL293D shield ile
Motor bağlantısıOUT1–OUT4 klemensleriM1 (sol) ve M2 (sağ) klemensleri
Motor kodudrive() içinde digitalWrite ve analogWritedrive() içinde AFMotor
SERVO_PIN3Servoyu taktığın başlığa göre 9 ya da 10
HC-05D10, D11A4 (RX) ve A5 (TX): SoftwareSerial bt(A4, A5)
Kanal başına akım2 A sürekli0,6 A sürekli, 1,2 A anlık
Motor beslemesi12V klemensiEXT_PWR klemensi; PWR jumper'ı takılıysa Arduino da aynı kaynaktan beslenir
Arduino (C++)
1// L293D shield: ENA–IN4 tanımlarını ve setup()'taki pinMode listesinden bu pinleri sil, drive() fonksiyonunu aşağıdakiyle değiştir
2// L293D shield: delete the ENA–IN4 definitions, remove them from the pinMode list in setup() and replace drive() with the one below
3// Ayrıca / Also: SERVO_PIN = 9 ya da / or 10, SoftwareSerial bt(A4, A5)
4#include <AFMotor.h>                     // Adafruit Motor Shield V1 kütüphanesi / library
5
6AF_DCMotor leftMotor(1);                 // M1 klemensi / M1 terminal
7AF_DCMotor rightMotor(2);                // M2 klemensi / M2 terminal
8
9void drive(int left, int right) {        // her biri -255 … 255 / each -255 … 255
10  leftMotor.setSpeed(min(abs(left), 255));
11  rightMotor.setSpeed(min(abs(right), 255));
12  leftMotor.run(left > 0 ? FORWARD : (left < 0 ? BACKWARD : RELEASE));
13  rightMotor.run(right > 0 ? FORWARD : (right < 0 ? BACKWARD : RELEASE));
14}

L293D'nin kanal başına 0,6 A'lik sınırı, TT motorlu bir 2WD robotun normal sürüşü için yeterlidir; ama tekerlek bir engele dayanıp sıkıştığında motor 1 A'in üzerinde akım çekebilir ve entegre hızla ısınır. Tarama algoritmasının robotu engele dayamadan durdurması bu yüzden shield'in de ömrünü uzatır. Shield'in jumper ayarları ve dört motorlu araba kodu L293D Motor Shield yazısında, L298N'in iç yapısı ise L298N ile DC Motor Kontrolü rehberinde.

ESP32 ile engelden kaçan robot

ESP32'de iki önemli fark var. Birincisi, Arduino'nun Servo kütüphanesi ESP32'de çalışmaz; servo sinyalini kartın PWM birimi olan LEDC ile doğrudan üretiyoruz: 50 Hz'lik sinyalde 0,544–2,4 ms'lik darbe 0–180 dereceye karşılık gelir. Motorlar 1 kHz'te, servo 50 Hz'te çalıştığı için önce motor kanalları, sonra servo kanalı açılır; böylece servo kendi zamanlayıcısını kullanır. İkincisi, ESP32'nin içinde klasik Bluetooth olduğu için HC-05'e gerek kalmaz; robot telefonda "BlueGrays-Robot" adıyla görünür ve yön ile mod harfleri Uno sürümüyle aynıdır. Kod, ESP32 Arduino çekirdeğinin 3.x sürümü içindir (ledcAttach ve ledcWrite pin numarasıyla çalışır) ve klasik Bluetooth gerektirdiği için ESP32-S3 ile ESP32-C3'te derlenmez.

Arduino (C++)
1// ESP32 Arduino çekirdeği 3.x / ESP32 Arduino core 3.x
2#include "BluetoothSerial.h"
3
4const int ENA = 25, IN1 = 27, IN2 = 33;   // sol motor / left motor
5const int ENB = 26, IN3 = 32, IN4 = 4;    // sağ motor / right motor
6const int SERVO_PIN = 13;
7const int TRIG = 18, ECHO = 19;           // ECHO 1 kΩ / 2 kΩ bölücüyle / ECHO via a 1 kΩ / 2 kΩ divider
8const int IR_LEFT = 23, IR_RIGHT = 22;    // LOW = engel / LOW = obstacle
9
10const int BACK_CM = 12, STOP_CM = 25, SLOW_CM = 60, GUARD_CM = 15, MAX_CM = 400;
11const int CRUISE = 200, SLOW = 130, TURN = 170;
12const int SERVO_CENTER = 90, SERVO_DIR = 1;
13const float MS_PER_DEG = 4.0;             // kalibre et / calibrate
14
15BluetoothSerial bt;
16bool autoMode = true;
17int manualLeft = 0, manualRight = 0;
18unsigned long lastCmd = 0, lastPing = 0;
19
20void drive(int left, int right) {         // her biri -255 … 255 / each -255 … 255
21  left = constrain(left, -255, 255);
22  right = constrain(right, -255, 255);
23  digitalWrite(IN1, left > 0);  digitalWrite(IN2, left < 0);
24  digitalWrite(IN3, right > 0); digitalWrite(IN4, right < 0);
25  ledcWrite(ENA, abs(left));
26  ledcWrite(ENB, abs(right));
27}
28
29void servoWrite(int angle) {              // 0–180° → 544–2400 µs
30  angle = constrain(angle, 0, 180);
31  uint32_t us = map(angle, 0, 180, 544, 2400);
32  ledcWrite(SERVO_PIN, us * 16384 / 20000);   // 14 bit, 20 ms periyot / 14-bit, 20 ms period
33}
34
35int pingCm() {
36  digitalWrite(TRIG, LOW);  delayMicroseconds(2);
37  digitalWrite(TRIG, HIGH); delayMicroseconds(10);
38  digitalWrite(TRIG, LOW);
39  unsigned long t = pulseIn(ECHO, HIGH, 25000UL);
40  return t == 0 ? MAX_CM : (int)(t / 58);
41}
42
43int distanceCm() {                        // 3 ölçümün ortancası / median of 3 readings
44  int a = pingCm(); delay(60);
45  int b = pingCm(); delay(60);
46  int c = pingCm();
47  return max(min(a, b), min(max(a, b), c));
48}
49
50bool irBlocked(int pin) { return digitalRead(pin) == LOW; }
51
52void turnBy(int deg) {                    // + sola, - sağa / + left, - right
53  if (deg > 0) drive(-TURN, TURN);
54  else drive(TURN, -TURN);
55  delay((unsigned long)(abs(deg) * MS_PER_DEG));
56  drive(0, 0);
57  delay(100);
58}
59
60void avoid() {
61  drive(0, 0);
62  delay(120);
63  int front = distanceCm();
64  bool sides = irBlocked(IR_LEFT) || irBlocked(IR_RIGHT);
65  if (front >= STOP_CM && !sides) return;            // yanlış alarm / false alarm
66  if (front < BACK_CM || sides) {                    // geri çekil / back off
67    drive(-TURN, -TURN);
68    delay(350);
69    drive(0, 0);
70  }
71  const int offsets[] = {70, 35, -35, -70};
72  int bestOffset = 0, bestCm = 0;
73  for (int off : offsets) {
74    servoWrite(SERVO_CENTER + SERVO_DIR * off);
75    delay(300);
76    int cm = distanceCm();
77    if (cm > bestCm) { bestCm = cm; bestOffset = off; }
78  }
79  servoWrite(SERVO_CENTER);
80  delay(200);
81  if (bestCm < STOP_CM + 10) {                       // çıkmaz / dead end
82    drive(-TURN, -TURN);
83    delay(400);
84    turnBy(180);
85  } else {
86    turnBy(bestOffset);
87  }
88}
89
90void setup() {
91  int outs[] = {IN1, IN2, IN3, IN4, TRIG};
92  for (int p : outs) pinMode(p, OUTPUT);
93  pinMode(ECHO, INPUT);
94  pinMode(IR_LEFT, INPUT_PULLUP);
95  pinMode(IR_RIGHT, INPUT_PULLUP);
96  ledcAttach(ENA, 1000, 8);               // motor PWM: 1 kHz, 8 bit
97  ledcAttach(ENB, 1000, 8);
98  ledcAttach(SERVO_PIN, 50, 14);          // servo: 50 Hz, 14 bit
99  drive(0, 0);
100  servoWrite(SERVO_CENTER);
101  bt.begin("BlueGrays-Robot");            // telefonda görünecek ad / name shown on the phone
102  delay(3000);
103}
104
105void loop() {
106  while (bt.available()) {
107    char c = bt.read();
108    lastCmd = millis();
109    if (c == 'A') autoMode = true;                                  // otomatik / auto
110    else if (c == 'M') {                                            // manuel / manual
111      autoMode = false;
112      manualLeft = manualRight = 0;
113      servoWrite(SERVO_CENTER);
114    }
115    else if (!autoMode) {
116      if (c == 'F')      { manualLeft = 200;  manualRight = 200; }  // ileri / forward
117      else if (c == 'B') { manualLeft = -200; manualRight = -200; } // geri / backward
118      else if (c == 'L') { manualLeft = -200; manualRight = 200; }  // sola / left
119      else if (c == 'R') { manualLeft = 200;  manualRight = -200; } // sağa / right
120      else if (c == 'S') { manualLeft = 0;    manualRight = 0; }    // dur / stop
121    }
122  }
123
124  if (millis() - lastPing < 60) return;               // 60 ms'de bir / every 60 ms
125  lastPing = millis();
126  int d = pingCm();
127
128  if (!autoMode) {
129    if (millis() - lastCmd > 600) manualLeft = manualRight = 0;     // komut yok: dur / no command: stop
130    bool forward = manualLeft > 0 && manualRight > 0;
131    if (forward && d < GUARD_CM) drive(0, 0);                       // çarpışma koruması / collision guard
132    else drive(manualLeft, manualRight);
133    return;
134  }
135
136  bool l = irBlocked(IR_LEFT), r = irBlocked(IR_RIGHT);
137  if (d < STOP_CM || (l && r)) avoid();
138  else if (l) { drive(TURN, -TURN); delay(150); }
139  else if (r) { drive(-TURN, TURN); delay(150); }
140  else if (d < SLOW_CM) { int s = map(d, STOP_CM, SLOW_CM, SLOW, CRUISE); drive(s, s); }
141  else drive(CRUISE, CRUISE);
142}

Raspberry Pi Pico (MicroPython) ile engelden kaçan robot

Raspberry Pi Pico'da MicroPython'un time_pulse_us() fonksiyonu Arduino'daki pulseIn() gibi çalışır; yankı gelmezse negatif bir değer döndürdüğü için bunu 400 cm, yani açık alan sayıyoruz. Servo için 50 Hz'lik PWM ve darbe genişliğini nanosaniye olarak alan duty_ns() kullanılır. GP16'daki servo ile GP15 ve GP10'daki motor hız pinleri farklı PWM dilimlerinde olduğu için birbirinin frekansını bozmaz. Hızlar bu kez yüzde olarak verilir. Pico'da Bluetooth olmadığından manuel mod istersen HC-05'i robot araba rehberindeki gibi UART0'a (GP0, GP1) bağlayabilirsin.

MicroPython
1from machine import Pin, PWM, time_pulse_us
2import time
3
4ena = PWM(Pin(15)); ena.freq(1000)          # sol motor hızı / left motor speed
5enb = PWM(Pin(10)); enb.freq(1000)          # sağ motor hızı / right motor speed
6in1, in2 = Pin(14, Pin.OUT), Pin(13, Pin.OUT)
7in3, in4 = Pin(12, Pin.OUT), Pin(11, Pin.OUT)
8servo = PWM(Pin(16)); servo.freq(50)        # servo sinyali, 50 Hz / servo signal, 50 Hz
9trig = Pin(17, Pin.OUT, value=0)
10echo = Pin(18, Pin.IN)                      # 1 kΩ / 2 kΩ bölücü üzerinden / via a 1 kΩ / 2 kΩ divider
11ir_left = Pin(19, Pin.IN, Pin.PULL_UP)      # 0 = engel / 0 = obstacle
12ir_right = Pin(20, Pin.IN, Pin.PULL_UP)
13
14STOP_CM, BACK_CM = 25, 12
15CRUISE, TURN = 75, 65                       # hız yüzdesi / speed in percent
16MS_PER_DEG = 4                              # kalibre et / calibrate
17SERVO_CENTER = 90                           # öne bakmıyorsa düzelt / adjust if not facing ahead
18
19def drive(left, right):                     # -100 … 100 (%)
20    in1.value(left > 0); in2.value(left < 0)
21    in3.value(right > 0); in4.value(right < 0)
22    ena.duty_u16(int(min(abs(left), 100) * 655))
23    enb.duty_u16(int(min(abs(right), 100) * 655))
24
25def head(offset):                           # + sol, - sağ / + left, - right
26    angle = max(0, min(180, SERVO_CENTER + offset))
27    servo.duty_ns((544 + angle * 1856 // 180) * 1000)   # 0,544–2,4 ms darbe / 0.544–2.4 ms pulse
28
29def ping_cm():
30    trig.value(1); time.sleep_us(10); trig.value(0)     # 10 µs tetik / 10 µs trigger
31    t = time_pulse_us(echo, 1, 25000)
32    return 400 if t < 0 else t // 58                    # negatif = yankı yok / negative = no echo
33
34def distance_cm():                          # 3 ölçümün ortancası / median of 3 readings
35    r = []
36    for _ in range(3):
37        r.append(ping_cm())
38        time.sleep_ms(60)
39    return sorted(r)[1]
40
41def turn_by(deg):                           # + sola, - sağa / + left, - right
42    if deg > 0:
43        drive(-TURN, TURN)
44    else:
45        drive(TURN, -TURN)
46    time.sleep_ms(int(abs(deg) * MS_PER_DEG))
47    drive(0, 0)
48    time.sleep_ms(100)
49
50def avoid():
51    drive(0, 0)
52    time.sleep_ms(120)
53    front = distance_cm()
54    sides = not ir_left.value() or not ir_right.value()
55    if front >= STOP_CM and not sides:
56        return                              # yanlış alarm / false alarm
57    if front < BACK_CM or sides:            # geri çekil / back off
58        drive(-TURN, -TURN); time.sleep_ms(350); drive(0, 0)
59    best_off, best_cm = 0, 0
60    for off in (70, 35, -35, -70):          # tara / scan
61        head(off)
62        time.sleep_ms(300)
63        cm = distance_cm()
64        if cm > best_cm:
65            best_off, best_cm = off, cm
66    head(0)
67    time.sleep_ms(200)
68    if best_cm < STOP_CM + 10:              # çıkmaz / dead end
69        drive(-TURN, -TURN); time.sleep_ms(400)
70        turn_by(180)
71    else:
72        turn_by(best_off)
73
74drive(0, 0)
75head(0)
76time.sleep(3)                               # yere koyman için süre / time to put it down
77while True:
78    d = ping_cm()
79    left_hit, right_hit = not ir_left.value(), not ir_right.value()
80    if d < STOP_CM or (left_hit and right_hit):
81        avoid()
82    elif left_hit:
83        drive(TURN, -TURN); time.sleep_ms(150)   # sağa kaç / dodge right
84    elif right_hit:
85        drive(-TURN, TURN); time.sleep_ms(150)   # sola kaç / dodge left
86    else:
87        drive(CRUISE, CRUISE)
88    time.sleep_ms(60)                        # ölçümler arası / between readings

Engelden kaçan robot ile proje fikirleri

  • Radar ekranı: Taramada ölçtüğün açı ve mesafeleri seri porttan gönderip bilgisayarda yeşil bir radar ekranı çiz; ayrıntılar Arduino Radar Projesi yazısında.
  • Çizgi ve engel: Robot çizgi izleyen robot gibi pistte ilerlerken önüne çıkan kutunun etrafından dolaşıp çizgiye geri dönsün; yarışmalarda sık görülen bir görevdir.
  • Duvar takip eden robot: Sensörü sürekli 60 derece sağa çevirip duvara sabit 20 cm uzaklıkta ilerle; labirent çözmenin ilk adımıdır.
  • Ekranlı robot: Mesafeyi ve modu küçük bir OLED ekranda göster; hata ayıklama çok kolaylaşır.
  • Süpürge robotu prototipi: Rastgele yön seçimi, çarpma anahtarları ve boşluk sensörüyle evde dolaşan bir temizlik robotu taslağı yap.
  • Akıllı baston: Aynı mesafe ölçme mantığını görme engelliler için titreşimli bir uyarıya dönüştür; bkz. Arduino Akıllı Baston.
  • Okul ve yarışma projesi: Engel parkuru, süre tutma ve otomatik ya da manuel mod gösterisiyle sun; fikirler için TÜBİTAK Arduino proje fikirleri yazısına göz at.

Sık yapılan hatalar ve çözümleri

SorunOlası nedenÇözüm
Robot duvara hiç durmadan çarpıyorSensör 0 okuyor (kablo kopuk, TRIG ve ECHO ters) ve kod bunu açık alan sayıyorTest kodunu çalıştır, elinle 10–60 cm arasını dene, TRIG ve ECHO pinlerini kontrol et
Duvara eğik yaklaşınca sürtüyorSes eğik yüzeyden başka yöne yansıyorÖn köşelere IR sensör ekle, hızı düşür
Durup tarama yaparken Arduino resetleniyorServo ve motor akımı 5 V hattını çökertiyorServoya 470 µF kondansatör, dolu 18650 piller; gerekirse servoyu ayrı 5 V kaynaktan besle
Robot sebepsiz yere durup tarıyorTek bir hatalı ölçüm ya da zeminden gelen yankıSensörü 8–12 cm yükseğe al ve biraz yukarı eğ; ortanca doğrulamayı kullan
Robot kapalı tarafa dönüyorServo montajı ters, sol ve sağ karışmışSERVO_DIR değerini -1 yap
Dönüşler çok kısa ya da çok uzunMS_PER_DEG kalibre edilmemiş ya da pil zayıfturnBy(360) ile ölç, dolu pille yeniden ayarla
Köşede sağa sola dönüp duruyorİki yan da eşiğe yakın, karar sürekli değişiyorÇıkmaz eşiğini (STOP_CM + 10) büyüt ya da son dönülen yönü tercih ettir
Sensör kafası seğiriyorServo beslemesi zayıf ya da SoftwareSerial kesmeleriServo kablosunu kısalt, kondansatör ekle, gereksiz sık Bluetooth komutundan kaçın
Sorunun kaynağını bulmanın en hızlı yolu, robotu USB'ye bağlı tutup tekerlekler havadayken çalıştırmak ve pingCm() değerlerini Seri Monitör'e yazdırmaktır. Değerler mantıklıysa sorun mekanikte ya da güç tarafındadır. Arduino'nun sık sık yeniden başlamasının diğer sebepleri için Arduino Kendini Resetliyor yazısına bakabilirsin.

Engelden kaçan robot; sensör okuma, filtreleme, karar verme ve motor kontrolünü tek projede birleştirdiği için robotiğe geçişin en iyi adımıdır. Önce ilk kodla temel davranışı gör, sonra gelişmiş sürümle eşikleri kendi odana göre ayarla, en son IR sensörler ve Bluetooth ile robotunu güçlendir. BlueGrays uygulamasında engelden kaçan robot, L298N ile Bluetooth kontrollü araba, HC-SR04 ile mesafe ölçme ve park sensörü projelerini devre şemaları ve kodlarıyla adım adım bulabilirsin.

Sıkça sorulan sorular

Engelden kaçan robot için hangi malzemeler gerekir?

Temel bir engelden kaçan robot için Arduino Uno, HC-SR04 ultrasonik sensör, SG90 servo, L298N ya da L293D motor sürücü, iki redüktörlü DC motorlu 2WD şasi kiti ve iki adet 18650 pil yeterlidir. Köşeleri görmek için iki IR engel sensörü, telefondan sürmek için de bir HC-05 Bluetooth modülü ekleyebilirsin.

Engelden kaçan robot servo olmadan yapılabilir mi?

Evet. Sensör sabit olarak öne bakar; robot engel görünce önce bir yöne döner ve yeniden ölçer, yol hâlâ kapalıysa diğer yöne döner. Bu yöntem daha ucuzdur ama robot her denemede gövdesini çevirdiği için daha yavaş karar verir; servo ise dönmeden önce en açık yönü bulmayı sağlar.

HC-SR04 neden bazı engelleri görmüyor?

Ultrasonik sensör yankıya göre ölçer. Perde ve koltuk gibi yumuşak yüzeyler sesi emer, eğik duvarlar sesi başka yöne yansıtır, sandalye ayağı gibi ince nesneler ise çok zayıf yankı verir. Sensör, yaklaşık 15 derecelik konisinin dışında ya da altında kalan engelleri de göremez; bu açıkları IR sensörlerle kapatabilirsin.

Engelden kaçan robot kaç cm'de durmalı?

Başlangıç için 25 cm iyi bir değerdir: iki ölçüm arasında alınan yol, fren mesafesi ve yerinde dönmek için gereken pay toplanınca yaklaşık 20 cm eder, üstüne de küçük bir güvenlik payı eklenir. Hızlı robotlarda ve kaygan zeminde eşiği 30–35 cm'ye çıkar; 12 cm'den yakın bir engelde ise önce geri çekilmek gerekir.

Robot için L298N mi, L293D shield mi daha iyi?

L298N kanal başına 2 A verebildiği için daha dayanıklıdır ve pinleri istediğin gibi seçmeni sağlar. L293D shield Uno'nun üstüne takıldığı için kablolamayı azaltır ve dört motor sürebilir, ama kanal başına sürekli akımı 0,6 A'dir ve birçok pini kullanır. TT motorlu küçük bir 2WD robot için ikisi de iş görür.

Engelden kaçan robot telefondan kontrol edilebilir mi?

Evet. Robota bir HC-05 Bluetooth modülü ekleyip Android telefondaki bir kumanda uygulamasından harf göndererek otomatik ve manuel mod arasında geçebilirsin. Manuel modda ultrasonik sensörü çarpışma koruması olarak kullanmak robotun duvara sürülmesini engeller. ESP32 kullanırsan dahili Bluetooth sayesinde ayrı bir modüle gerek kalmaz.

Tüm projeleri gör
HC-SR04 Mesafe Ölçme (Serial Ekran)
#27Başlangıç

HC-SR04 Mesafe Ölçme (Serial Ekran)

Yarasalar karanlıkta yollarını nasıl buluyorsa HC-SR04 sensörü de öyle çalışır: duyamadığın bir ses gönderir, yankısını dinler ve önündeki cismin ne kadar uzakta olduğunu ölçer. Arduino bu mesafeyi sa

4.6(45)
68
Engelden Kaçan Robot
#81İleri
Premium

Engelden Kaçan Robot

Kendi yolunu bulan akıllı bir araç yapmaya ne dersin? Engelden Kaçan Robot, önündeki ultrasonik sensör sayesinde çevresini sürekli tarar. Bir engelle karşılaştığında durur, servo motoruyla sağına ve s

5.0(38)
49
L298N ile Bluetooth Kontrollü Araba
#80İleri
Premium

L298N ile Bluetooth Kontrollü Araba

Telefonunu kumandaya çevirip kendi robot arabanı sür. Arduino, Bluetooth modülü üzerinden telefondaki bir kumanda uygulamasından gelen komutları alır ve motor sürücüyle tekerlekleri döndürür; araba il

5.0(38)
65
Servo Motor Kullanım
#35Başlangıç
Premium

Servo Motor Kullanım

Küçük bir servo motorun kolu sırayla üç konuma döner: en başa, tam ortaya ve en sona, her durakta iki saniye bekleyerek. Arduino motora hangi açıda durması gerektiğini söyler, motor da oraya gidip ora

4.7(48)
67
HC-SR04 ile Park Sensörü
#28Orta
Premium

HC-SR04 ile Park Sensörü

Arabalardaki park sensörünün küçük bir benzeri: bir şey yaklaştıkça bip sesleri hızlanır. Mesafe sensörü önündeki nesneyi ölçer; 20 santimetreden uzaktaysa yeşil LED yanar, daha yakına gelince kırmızı

4.8(64)
84
TCRT5000 ile Yansıma (Yakınlık) Okuma
#77Orta
Premium

TCRT5000 ile Yansıma (Yakınlık) Okuma

Görünmez ışık yollayıp yansımasını ölçen küçük bir sensörle önündeki yüzeyi algılarsın. Sensör bir cisme ya da açık renkli bir yüzeye yaklaştıkça daha çok ışık geri döner; Arduino bu değeri her yarım

4.6(33)
43
TCRT5000 Siyah Beyaz Zemin Tespiti
#78Orta
Premium

TCRT5000 Siyah Beyaz Zemin Tespiti

Çizgi izleyen robotların gözü olan küçük bir sensörle zeminin siyah mı beyaz mı olduğunu anlarsın. TCRT5000 sensörü zemine gözle görülmeyen bir ışık gönderip ne kadarının geri döndüğüne bakar; beyaz z

4.7(40)
49
ESP32 HC-SR04 Ultrasonik Mesafe Ölçer
#116Orta

ESP32 HC-SR04 Ultrasonik Mesafe Ölçer

HC-SR04 ultrasonik sensör, gönderdiği ses dalgasının nesneye çarpıp geri dönme süresini (Time of Flight) ölçerek mesafe hesaplar. Bu projede ESP32 ile TRIG pinine 10 mikrosaniyelik darbe gönderip, ECHO pininden gelen yankı süresini `pulseIn()` ile okuyacağız. Hesaplanan mesafeyi cm cinsinden 16x2 I2C LCD'de göstereceğiz. HC-SR04 5V çalıştığı için ECHO pinine **gerilim bölücü** zorunludur — aksi halde ESP32'nin 3.3V GPIO'su zarar görür.

4.9(28)
44