Rehberler · 12 dk okuma

Çizgi İzleyen Robot Yapımı: Arduino, TCRT5000 ve PID Rehberi

Arduino ile çizgi izleyen robot yapımı: TCRT5000 sensörleri, yerleşim ve kalibrasyon, if-else ve PID kodu, Kp/Kd ayarı, motor sürücü, pil ve yarışma kuralları.

Çizgi izleyen robot, zemindeki siyah (ya da beyaz) bir çizgiyi kızılötesi sensörlerle algılayıp iki tekerleğinin hızını sürekli ayarlayarak o çizginin üzerinde kalan otonom bir araçtır. Arduino ile yapılan ilk gerçek robot projelerinin başında gelir, çünkü sensör okumayı, karar vermeyi ve motor kontrolünü tek bir projede birleştirir. Fabrikalarda malzeme taşıyan otonom araçlar (AGV) da aynı fikirle çalışır; okul ve MEB robot yarışmalarının en kalabalık kategorilerinden biri de çizgi izleyendir.

Bu rehberde TCRT5000 sensörünün nasıl çalıştığını, 2, 3 ve 5 sensörlü diziler arasındaki farkı, sensörleri hangi yükseklik ve aralıkla yerleştireceğini ve eşik kalibrasyonunu öğreneceksin. Önce basit bir if-else koduyla robotu yürütecek, sonra PID kontrolüyle zigzag çizmeden ve virajlarda pistten çıkmadan takip eden bir sürüme geçeceksin. Arduino Uno, ESP32 ve Raspberry Pi Pico için çalışan kodların yanında motor sürücü ve pil seçimi, yarışma kuralları ve en sık karşılaşılan sorunların çözümleri de bu yazıda.

TCRT5000 kızılötesi yansıma sensörü
TCRT5000: mavi kılıflı kızılötesi LED ile siyah kılıflı fototransistör aynı gövdede yan yana durur; çizgi sensörü modülleri bu parçanın etrafına kurulur.

Çizgi izleyen robot nasıl çalışır?

Beyaz yüzey kızılötesi ışığın büyük kısmını geri yansıtır, mat siyah yüzey ise çoğunu yutar. Robotun altındaki sensörler bu farkı ölçerek çizginin robota göre nerede olduğunu anlar. Robotun direksiyonu yoktur; diferansiyel sürüş denen yöntemle döner: sol teker sağdakinden hızlı dönerse robot sağa, yavaş dönerse sola kıvrılır. Program saniyede yüzlerce kez aynı döngüyü işletir: sensörleri oku, çizginin ne kadar kaydığını hesapla, iki motorun hızını buna göre düzelt. Bu kapalı döngü ne kadar hızlı ve dengeli çalışırsa robot o kadar akıcı gider.

ParçaÖnerilenNot
Kontrol kartıArduino Uno ya da NanoNano küçük şasiye kolay sığar; ESP32 ve Pico da kullanılabilir
Çizgi sensörü3'lü ya da 5'li TCRT5000 dizisi, QTR-8APID için analog çıkışlı dizi daha iyi sonuç verir
Motor sürücüL298N ya da TB6612FNGTB6612FNG daha küçük ve verimlidir
Motorlar2 adet redüktörlü DC motor (TT 1:48 ya da N20)İki motor aynı model ve aynı devirde olmalı
Tekerlekler2 tahrik tekeri ve 1 sarhoş teker65 mm çaplı tekerlekler yaygındır
Güç2 adet 18650 Li-ion pil (7,4 V) ve anahtar9 V kare pil motorlar için yetersizdir
DiğerŞasi, buton, jumper kablolarButon, kalibrasyonu ve başlatmayı kolaylaştırır
Arduino Nano
Arduino Nano
L298N
L298N
N20
N20
18650 Li-ion
18650 Li-ion

TCRT5000 sensörü: 2, 3 ve 5'li diziler

TCRT5000, aynı gövdede yan yana duran bir kızılötesi LED (950 nm) ve bir fototransistörden oluşur. LED'in ışığı zeminden yansıyıp fototransistöre ne kadar çok ulaşırsa transistör o kadar iletken olur. Sensörün en iyi çalıştığı mesafe yaklaşık 2,5 mm'dir; 1 cm'nin üzerinde siyah ile beyaz arasındaki fark hızla azalır. Hazır modüllerde bir LM393 karşılaştırıcı bulunur: DO pini, potansiyometreyle ayarlanan eşiğe göre çoğu modülde siyahta HIGH, beyazda LOW verir ve kart üzerindeki LED beyaz zeminde yanar. AO pini ise ham analog değeri verir: siyahta yüksek, beyazda düşük okunur. Sensörün tüm ayrıntıları TCRT5000 Kızılötesi Sensör yazısında.

Sensör sayısıYerleşimAlgoritmaNe zaman seçilir?
2Çizginin iki yanında; ikisi de normalde beyazı görürif-elseİlk deneme, geniş ve yumuşak virajlı pist
3Ortadaki çizginin üstünde, yanlardakiler kenarlarındaif-else ya da basit PIDOkul projeleri, temel seviye yarışmalar
5Eşit aralıklı sıra, ortadaki çizginin üstündePID (ağırlıklı ortalama)Hızlı ve akıcı takip
8Geniş sıra (QTR-8A gibi)PID ve kavşak algılamaKeskin virajlı, kavşaklı yarışma pistleri

Sensör yerleşimi: yükseklik, aralık ve öne uzaklık

Kodun ne kadar iyi olursa olsun, sensörler yanlış yerdeyse robot düzgün gidemez. Sensör çubuğunu tekerlek ekseninin önüne, zemine paralel ve robotun orta çizgisine simetrik olacak şekilde monte et. Şu ölçüler iyi bir başlangıç noktasıdır:

  • Yükseklik: Sensörün alt yüzü ile zemin arasında 3–5 mm bırak. Pololu, QTR-8A dizisi için en iyi mesafeyi 3 mm, en fazla mesafeyi 6 mm olarak veriyor. Daha yüksekte siyah ile beyaz arasındaki fark azalır ve robot çizgiyi kaçırır.
  • Aralık: Analog dizilerde iki sensör arasındaki mesafe çizgi genişliğinin yarısı ile üçte ikisi arasında olsun; 20 mm'lik çizgi için 10–13 mm iyi çalışır. Böylece çizgi çoğu zaman iki sensörün altına düşer ve konum yumuşak hesaplanır.
  • 3 sensörlü dijital yerleşim: Orta sensör çizginin tam üstünde, yan sensörler çizgi kenarının hemen dışında dursun; 20 mm'lik çizgi için merkezden merkeze yaklaşık 18–22 mm.
  • Öne uzaklık: Sensörleri tekerlek ekseninin 4–8 cm önüne yerleştir. Çok yakınsa robot virajı geç fark eder; çok uzaksa küçük bir dönüş sensörleri fazla savurur ve robot titrer.
  • Ortam ışığı: Güneş ışığı ve halojen lambalar da kızılötesi ışık içerir. Sensörlerin çevresini siyah bir etek ya da bantla kapatmak ölçümleri kararlı hale getirir.
Sık yapılan bir hata, sensörleri eğik ya da fazla yüksek monte etmektir. Robot ilk virajda çizgiyi kaybediyorsa önce koda değil sensör yüksekliğine bak: birkaç milimetrelik fark bile okunan değerleri yarı yarıya değiştirebilir. Ayrıca parlak elektrik bandı kızılötesini yansıttığı için siyah çizgi bazen beyaz görünür; mümkünse mat bant ya da baskı kullan.

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

Aşağıdaki tablo sensör testi ve PID kodlarıyla birebir aynıdır; 3 sensörlü if-else örneğinin bağlantısı kendi bölümünde anlatılıyor. Sensörleri robotun arkasından bakarak soldan sağa numaralandır: en soldaki sensör 1 numaradır. Motor sürücü olarak L298N kullanıyoruz; pinlerinin görevi ve H-köprüsünün mantığı L298N ile DC Motor Kontrolü yazısında ayrıntılı anlatılıyor. Pico'da yalnızca üç analog giriş olduğu için 1, 3 ve 5 numaralı sensörleri kullanıyoruz.

BağlantıArduino UnoESP32Raspberry Pi Pico
Sensör 1 (en sol) AOA0GPIO 36GP26
Sensör 2 AOA1GPIO 39-
Sensör 3 (orta) AOA2GPIO 34GP27
Sensör 4 AOA3GPIO 35-
Sensör 5 (en sağ) AOA4GPIO 32GP28
Sensör VCC5V3V33V3
L298N ENA (sol motor hızı)D9GPIO 25GP15
L298N IN1, IN2D8, D7GPIO 26, 27GP14, GP13
L298N ENB (sağ motor hızı)D10GPIO 33GP10
L298N IN3, IN4D5, D4GPIO 18, 19GP11, GP12
Başlat butonu (diğer bacağı GND)D2GPIO 23GP16
GNDGNDGNDGND (pil ve sürücüyle ortak)
Pilin eksi ucu, motor sürücünün GND'si ve kartın GND'si mutlaka birleşmelidir; ortak toprak yoksa motorlar rastgele davranır. Motorları asla kartın 5V pininden besleme. ESP32 ve Pico'da sensörleri 3V3'ten besle: 5 V ile beslenen bir modülün AO çıkışı 5 V'a kadar çıkabilir ve 3,3 V'luk analog girişlere zarar verir.

Motor sürücü ve pil seçimi

L298N ucuz ve dayanıklıdır ama içindeki bipolar transistörler 1,5–3 V yutar: 7,4 V'luk bir pil paketinden motorlara ancak 5–5,5 V kalır. Küçük ve hızlı robotlarda MOSFET'li TB6612FNG ya da DRV8833 daha iyi sonuç verir. TB6612FNG'de kod değişmez: ENA, IN1 ve IN2 kablolarını PWMA, AIN1 ve AIN2 pinlerine (B kanalında PWMB, BIN1 ve BIN2'ye) bağlarsın ve STBY pinini VCC'ye bağlarsın. DRV8833'te ise ayrı bir hız (enable) pini yoktur; PWM sinyali doğrudan IN pinlerine verildiği için drive() fonksiyonunu buna göre değiştirmen gerekir. Güç için en pratik seçenek seri bağlı iki adet 18650 Li-ion pildir: dolu iken 8,4 V, nominal 7,4 V verir ve yüksek akımı rahatça sağlar. 9 V kare piller motor akımını kaldıramaz ve çabuk tükenir. Pilinin ne kadar dayanacağını Pil ömrü aracıyla hesaplayabilirsin.

SürücüMotor gerilimiKanal başına akımGerilim kaybıYorum
L298N5–35 V2 A1,5–3 VSağlam ama büyük ve verimsiz
TB6612FNG4,5–13,5 V1,2 A (anlık 3,2 A)Çok azÇizgi izleyen için en iyi fiyat/performans
DRV88332,7–10,8 V1,5 A (anlık 2 A)Çok azN20 motorlu küçük robotlar
L9110S2,5–12 V0,8 AAzYalnızca hafif ve yavaş robotlar
Sarı redüktörlü TT DC motor ve tekerlek
Sarı redüktörlü TT motor ve 65 mm tekerlek: dişli kutusu devri düşürüp torku artırır; çizgi izleyen ve robot araba projelerinin en yaygın motorudur.

Hız için bir örnek: sarı TT motorlar (1:48) 6 V'ta yüksüz yaklaşık 200 dev/dk döner. 65 mm çaplı tekerleğin çevresi π × 65 ≈ 204 mm olduğuna göre robot en fazla 200 × 0,204 ≈ 41 m/dk, yani saniyede yaklaşık 0,7 m gidebilir. Bu, çoğu okul pisti için fazlasıyla yeterlidir; asıl zorluk bu hızda çizgiyi kaybetmemektir. Daha hızlı robotlarda N20 mikro redüktörlü motorlar ve hafif bir şasi kullanılır. Pil gerilimi düştükçe motorlar yavaşlar ve ayarladığın PID değerleri kayar; pil gerilimini bir gerilim bölücü ile ölçüp hızı buna göre ölçeklemek ileri seviye bir iyileştirmedir.

Adım 1: Sensörleri test et ve eşiği bul

Motorları bağlamadan önce sensörlerin ne gördüğünü öğren. Aşağıdaki kod beş sensörün ham analog değerini sekmeyle ayırarak yazdırır; Arduino IDE'de Araçlar > Serial Plotter (Seri Çizici) penceresini 115200 baud ile açarsan her sensörü ayrı bir çizgi olarak görürsün. Robotu elinle çizginin üzerinden yavaşça geçir ve değerlerin nasıl değiştiğine bak.

Arduino (C++)
1// 5 sensörün ham değerlerini okur / reads the raw values of 5 sensors
2const int N = 5;
3const int SENSOR[N] = {A0, A1, A2, A3, A4};   // A0 en solda / A0 is the leftmost
4
5void setup() {
6  Serial.begin(115200);
7}
8
9void loop() {
10  for (int i = 0; i < N; i++) {
11    Serial.print(analogRead(SENSOR[i]));
12    Serial.print(i < N - 1 ? '\t' : '\n');    // Serial Plotter için sekme / tabs for the Serial Plotter
13  }
14  delay(50);
15}

Tipik bir TCRT5000 modülü 5 V'ta beyaz zeminde 30–200, siyah çizgide 600–1000 arası değer verir; ama bu sayılar sensör yüksekliğine, zeminin malzemesine ve ortam ışığına göre değişir. En basit eşik şudur: eşik = (beyaz değeri + siyah değeri) / 2. Örneğin beyazda 80, siyahta 820 okuyorsan eşik 450 olur. Her sensör biraz farklı okuduğu için PID kodunda bu işi otomatik yapacağız: kalibrasyon sırasında her sensörün en küçük ve en büyük değeri kaydedilir, sonra her okuma (okunan − en küçük) × 1000 / (en büyük − en küçük) formülüyle 0 (beyaz) ile 1000 (siyah) arasına ölçeklenir. Böylece eşik her sensör için 500 olur.

Dijital (DO) çıkışlı modüllerde eşiği kart üzerindeki potansiyometreyle ayarlarsın. Trimpotu, sensör siyah çizginin üstündeyken modülün LED'i sönük, beyaz zemindeyken yanık kalacak şekilde çevir; iki durumun tam ortasına denk gelen konum en kararlısıdır. Ayarı yarışacağın pistin üzerinde ve aynı ışık altında yap.

Adım 2: Basit if-else algoritması (3 sensör)

İlk çalışan robot için en anlaşılır yol, üç sensörün durumuna göre hazır hareketler seçmektir. 3'lü modülün DO çıkışlarını A0 (sol), A1 (orta) ve A2 (sağ) pinlerine bağla; Uno'nun analog pinleri dijital giriş olarak da kullanılabilir. Mantık şu tablodaki gibidir (1, sensörün çizgiyi gördüğü anlamına gelir):

SolOrtaSağAnlamıHareket
010Çizgi ortadaDüz git
110Çizgi biraz soldaSol teker yavaşlar, hafif sola
100Çizgi çok soldaSol teker geri döner, sert sola
011Çizgi biraz sağdaSağ teker yavaşlar, hafif sağa
001Çizgi çok sağdaSağ teker geri döner, sert sağa
111Kavşak ya da bitiş çizgisiDüz geç
000Çizgi kaybolduSon görülen tarafa dön
Arduino (C++)
1// 3 sensörlü basit çizgi izleyen / simple 3-sensor line follower
2const int ENA = 9, IN1 = 8, IN2 = 7;      // sol motor / left motor
3const int ENB = 10, IN3 = 5, IN4 = 4;     // sağ motor / right motor
4const int S_LEFT = A0, S_MID = A1, S_RIGHT = A2;   // sensörlerin DO çıkışları / sensor DO outputs
5const bool WHITE_LINE = false;            // siyah zemin + beyaz çizgi ise true / true for a white line on black
6
7const int SPEED = 150;                    // düz gidiş hızı / straight-line speed
8const int SOFT = 80;                      // hafif dönüşte iç teker / inner wheel on a soft turn
9const int BACK = -90;                     // sert dönüşte iç teker geri / inner wheel reverses on a sharp turn
10int lastSide = 0;                         // -1 sol, 1 sağ / -1 left, 1 right
11
12void drive(int left, int right) {         // her biri -255 … 255 / each -255 … 255
13  left = constrain(left, -255, 255);
14  right = constrain(right, -255, 255);
15  digitalWrite(IN1, left >= 0);  digitalWrite(IN2, left < 0);
16  digitalWrite(IN3, right >= 0); digitalWrite(IN4, right < 0);
17  analogWrite(ENA, abs(left));
18  analogWrite(ENB, abs(right));
19}
20
21void setup() {
22  int outputs[] = {ENA, IN1, IN2, ENB, IN3, IN4};
23  for (int p : outputs) pinMode(p, OUTPUT);
24  pinMode(S_LEFT, INPUT);
25  pinMode(S_MID, INPUT);
26  pinMode(S_RIGHT, INPUT);
27}
28
29void loop() {
30  bool l = digitalRead(S_LEFT) != WHITE_LINE;    // true = sensör çizgide / true = sensor on the line
31  bool m = digitalRead(S_MID) != WHITE_LINE;
32  bool r = digitalRead(S_RIGHT) != WHITE_LINE;
33
34  if (m && !l && !r) {                    // tam ortada: düz git / centered: go straight
35    drive(SPEED, SPEED);
36  } else if (l && r) {                    // kavşak ya da bitiş çizgisi: düz geç / junction or finish line: go straight
37    drive(SPEED, SPEED);
38  } else if (l && m) {                    // çizgi biraz solda / line slightly to the left
39    drive(SOFT, SPEED);  lastSide = -1;
40  } else if (r && m) {                    // çizgi biraz sağda / line slightly to the right
41    drive(SPEED, SOFT);  lastSide = 1;
42  } else if (l) {                         // çizgi çok solda / line far to the left
43    drive(BACK, SPEED);  lastSide = -1;
44  } else if (r) {                         // çizgi çok sağda / line far to the right
45    drive(SPEED, BACK);  lastSide = 1;
46  } else {                                // çizgi kayıp: son görülen tarafa dön / line lost: turn to the last seen side
47    if (lastSide < 0)      drive(BACK, SPEED);
48    else if (lastSide > 0) drive(SPEED, BACK);
49    else                   drive(SOFT, SOFT);
50  }
51}

lastSide değişkeni robotun çizgiyi en son hangi tarafta gördüğünü hatırlar; keskin bir virajda üç sensör birden çizgiyi kaçırırsa robot rastgele düz gitmek yerine çizgiyi geri bulmak için o tarafa döner. Çift sensörlü robotlarda aynı mantık daha da basittir: sensörler çizginin iki yanında durur, ikisi de beyaz görüyorsa düz gidilir, soldaki siyah görürse sola, sağdaki siyah görürse sağa dönülür. Bu yöntemin zayıf yanı hareketlerin kademeli olmasıdır: robot yalnızca birkaç sabit hız arasında geçiş yaptığı için düzlükte bile hafif zigzag çizer ve hızı artırınca virajlarda pistten çıkar.

Adım 3: PID ile yumuşak çizgi takibi

PID, robotun çizgiden ne kadar saptığına göre orantılı bir düzeltme yapar. Önce beş sensörden çizginin konumu ağırlıklı ortalamayla hesaplanır: her sensörün 0–1000 arası değeri kendi konumuyla (0, 1000, 2000, 3000, 4000) çarpılıp toplanır ve değerlerin toplamına bölünür. Sonuç 0 ile 4000 arasında bir sayıdır; çizgi tam ortadayken 2000'dir. Örneğin sensörler 0, 0, 300, 900, 0 okuyorsa konum (300 × 2000 + 900 × 3000) / 1200 = 2750 olur. Hata = konum − 2000 olarak tanımlanır: artı değer çizginin sağda, eksi değer solda kaldığını gösterir.

Düzeltme üç terimin toplamıdır: düzeltme = Kp × hata + Ki × hataların toplamı + Kd × (hata − önceki hata). P terimi sapma büyüdükçe daha sert döndürür. D terimi hatanın ne kadar hızlı değiştiğine bakar ve robot çizgiye yaklaşırken frene basarak salınımı söndürür. I terimi uzun süre aynı yönde kalan küçük hataları düzeltir, ama çizgi izleyende çoğu zaman sıfır bırakılır. Sonra sol motor taban hız + düzeltme, sağ motor taban hız − düzeltme ile döner. Örneğin hata 800, Kp 0,06 ise düzeltme 48 olur; taban hız 140 iken sol motor 188, sağ motor 92 ile döner ve robot yumuşakça sağa kıvrılır.

Arduino (C++)
1// PID çizgi izleyen: Arduino Uno + 5 analog sensör + L298N
2// PID line follower: Arduino Uno + 5 analog sensors + L298N
3const int ENA = 9, IN1 = 8, IN2 = 7;      // sol motor / left motor
4const int ENB = 10, IN3 = 5, IN4 = 4;     // sağ motor / right motor
5const int BUTTON = 2;                     // diğer bacağı GND'ye / other leg to GND
6const int N = 5;
7const int SENSOR[N] = {A0, A1, A2, A3, A4};   // A0 en solda / A0 is the leftmost
8const bool WHITE_LINE = false;            // siyah zemin + beyaz çizgi ise true / true for a white line on black
9const int CENTER = (N - 1) * 500;         // orta konum: 2000 / center position: 2000
10
11float Kp = 0.06, Ki = 0.0, Kd = 0.6;      // başlangıç değerleri / starting values
12int baseSpeed = 140;                      // düzlük hızı (0-255) / straight-line speed (0-255)
13const unsigned long PERIOD_MS = 5;        // sabit döngü süresi / fixed loop period
14
15int minVal[N], maxVal[N];
16int lastPosition = CENTER;
17float integral = 0, lastError = 0;
18unsigned long lastTime = 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  analogWrite(ENA, abs(left));
26  analogWrite(ENB, abs(right));
27}
28
29void waitButton() {
30  while (digitalRead(BUTTON) == HIGH) {}  // basılmasını bekle / wait for a press
31  delay(30);
32  while (digitalRead(BUTTON) == LOW) {}   // bırakılmasını bekle / wait for the release
33  delay(30);
34}
35
36void calibrate() {                        // 5 sn: robotu çizginin üstünde sağa sola gezdir / 5 s: sweep the robot across the line
37  for (int i = 0; i < N; i++) { minVal[i] = 1023; maxVal[i] = 0; }
38  digitalWrite(LED_BUILTIN, HIGH);
39  unsigned long start = millis();
40  while (millis() - start < 5000) {
41    for (int i = 0; i < N; i++) {
42      int v = analogRead(SENSOR[i]);
43      if (v < minVal[i]) minVal[i] = v;
44      if (v > maxVal[i]) maxVal[i] = v;
45    }
46  }
47  digitalWrite(LED_BUILTIN, LOW);
48}
49
50int readPosition() {                      // 0 (en sol) … 4000 (en sağ) / 0 (leftmost) … 4000 (rightmost)
51  long sum = 0, weighted = 0;
52  bool onLine = false;
53  for (int i = 0; i < N; i++) {
54    int range = max(maxVal[i] - minVal[i], 1);
55    long v = (long)(analogRead(SENSOR[i]) - minVal[i]) * 1000 / range;
56    v = constrain(v, 0L, 1000L);          // 0 = beyaz, 1000 = siyah / 0 = white, 1000 = black
57    if (WHITE_LINE) v = 1000 - v;
58    if (v > 500) onLine = true;           // eşik: kalibrasyonun ortası / threshold: middle of the calibration
59    if (v > 50) {                         // küçük gürültüyü yok say / ignore small noise
60      weighted += v * (i * 1000L);
61      sum += v;
62    }
63  }
64  if (!onLine) {                          // çizgi kayıp: son görülen tarafa dön / line lost: turn to the last seen side
65    return (lastPosition < CENTER) ? 0 : (N - 1) * 1000;
66  }
67  lastPosition = weighted / sum;
68  return lastPosition;
69}
70
71void setup() {
72  int outputs[] = {ENA, IN1, IN2, ENB, IN3, IN4, LED_BUILTIN};
73  for (int p : outputs) pinMode(p, OUTPUT);
74  pinMode(BUTTON, INPUT_PULLUP);
75  drive(0, 0);
76  waitButton();                           // 1. basış: kalibrasyon / 1st press: calibration
77  calibrate();
78  waitButton();                           // 2. basış: yarış / 2nd press: race
79  lastTime = millis();
80}
81
82void loop() {
83  if (millis() - lastTime < PERIOD_MS) return;
84  lastTime += PERIOD_MS;
85
86  float error = readPosition() - CENTER;  // artı: çizgi sağda / positive: line is to the right
87  integral = constrain(integral + error, -20000.0f, 20000.0f);   // birikmeyi sınırla / anti-windup
88  float derivative = error - lastError;
89  lastError = error;
90
91  int correction = (int)(Kp * error + Ki * integral + Kd * derivative);
92  drive(baseSpeed + correction, baseSpeed - correction);
93}

Kod yüklendikten sonra butona bir kez bas: kart üzerindeki LED 5 saniye yanar. Bu sürede robotu elinle çizginin üzerinde birkaç kez sağa sola kaydır ki her sensör hem siyahı hem beyazı görsün. LED sönünce robotu çizginin üstüne koy ve butona tekrar bas. Döngü millis() ile her 5 ms'de bir çalışır; sabit süre, D teriminin her zaman aynı ölçekte kalmasını sağlar. Çizgi tamamen kaybolursa readPosition() son görülen tarafa göre 0 ya da 4000 döndürür ve robot en sert dönüşle çizgiyi arar. Buton bağlantısı için Arduino'ya Buton Nasıl Bağlanır?, delay kullanmadan zamanlama için Arduino millis() ve delay() yazılarına bakabilirsin.

Kp ve Kd nasıl ayarlanır?

  1. Taban hızı düşük tut (örneğin 100) ve Ki ile Kd'yi sıfırla.
  2. Kp'yi küçük başlat (0,02) ve robot virajları alabilene kadar adım adım artır. Robot çizginin etrafında hızlı ve düzenli salınmaya başladığında Kp'nin üst sınırına gelmişsindir; bu değerin biraz altında kal.
  3. Kd'yi Kp'nin yaklaşık 10 katından başlat ve salınım sönene kadar artır. Kd fazla büyürse robot titrer ve motorlar vızıldar.
  4. Robot düzlükte dümdüz, virajda akıcı gidiyorsa taban hızı 10–20 birim artır ve 2. ile 3. adımları tekrarla.
  5. Ki'yi yalnızca robot uzun düzlüklerde hep bir yana kayıyorsa çok küçük bir değerle (0,0001 gibi) dene.
  6. Bulduğun değerleri ve o anki pil gerilimini not et; pil azaldıkça aynı değerler farklı davranır.
Bu yazıdaki ölçekte (konum 0–4000, PWM 0–255, 5 ms döngü) iyi bir başlangıç aralığı Kp için 0,04–0,08, Kd için 0,4–1,2'dir. Ayar yaparken döngünün içine Serial.print koyma: her yazdırma döngüyü yavaşlatır ve D terimini bozar. Değerleri görmen gerekiyorsa en fazla 200 ms'de bir yazdır.

ESP32 ile çizgi izleyen robot: PID'i telefondan ayarla

ESP32'nin en büyük avantajı, PID değerlerini her seferinde kod yüklemeden telefondan değiştirebilmendir. Kod, Uno sürümüyle aynı mantıktadır; farklar şunlardır: ESP32'nin analog girişi 12 bittir (0–4095), sensörler yalnızca ADC1 pinlerine, yani GPIO 32–39'a bağlanmalıdır (ADC2 pinleri Wi-Fi açıkken okunamaz) ve sensörler 3,3 V ile beslenir. analogWrite() ESP32 Arduino çekirdeğinin 2.x ve 3.x sürümlerinde çalışır. Android telefonuna Serial Bluetooth Terminal gibi bir uygulama kur, "CizgiRobot" cihazına bağlan ve p0.07, d0.9, s160 gibi komutlar gönder; robot yeni değeri hemen kullanır ve geri yazar. Klasik Bluetooth yalnızca orijinal ESP32'de vardır (S3 ve C3'te yoktur) ve iPhone'lar bu tür seri bağlantıyı desteklemez. ESP32'yi yeni tanıyorsan önce ESP32 Nedir? yazısına göz at.

Arduino (C++)
1// ESP32 PID çizgi izleyen + Bluetooth ile canlı ayar
2// ESP32 PID line follower + live tuning over Bluetooth
3#include "BluetoothSerial.h"
4
5BluetoothSerial SerialBT;
6
7const int ENA = 25, IN1 = 26, IN2 = 27;   // sol motor / left motor
8const int ENB = 33, IN3 = 18, IN4 = 19;   // sağ motor / right motor
9const int BUTTON = 23;                    // diğer bacağı GND'ye / other leg to GND
10const int LED_PIN = 2;                    // DevKit üzerindeki LED / onboard LED on the DevKit
11const int N = 5;
12const int SENSOR[N] = {36, 39, 34, 35, 32};   // yalnız ADC1 pinleri / ADC1 pins only
13const bool WHITE_LINE = false;            // siyah zemin + beyaz çizgi ise true / true for a white line on black
14const int CENTER = (N - 1) * 500;
15
16float Kp = 0.06, Ki = 0.0, Kd = 0.6;
17int baseSpeed = 140;
18const unsigned long PERIOD_MS = 5;
19
20int minVal[N], maxVal[N];
21int lastPosition = CENTER;
22float integral = 0, lastError = 0;
23unsigned long lastTime = 0;
24
25void drive(int left, int right) {         // her biri -255 … 255 / each -255 … 255
26  left = constrain(left, -255, 255);
27  right = constrain(right, -255, 255);
28  digitalWrite(IN1, left >= 0);  digitalWrite(IN2, left < 0);
29  digitalWrite(IN3, right >= 0); digitalWrite(IN4, right < 0);
30  analogWrite(ENA, abs(left));
31  analogWrite(ENB, abs(right));
32}
33
34void waitButton() {
35  while (digitalRead(BUTTON) == HIGH) {}
36  delay(30);
37  while (digitalRead(BUTTON) == LOW) {}
38  delay(30);
39}
40
41void calibrate() {                        // ESP32 ADC 12 bit: 0-4095 / ESP32 ADC is 12-bit: 0-4095
42  for (int i = 0; i < N; i++) { minVal[i] = 4095; maxVal[i] = 0; }
43  digitalWrite(LED_PIN, HIGH);
44  unsigned long start = millis();
45  while (millis() - start < 5000) {
46    for (int i = 0; i < N; i++) {
47      int v = analogRead(SENSOR[i]);
48      if (v < minVal[i]) minVal[i] = v;
49      if (v > maxVal[i]) maxVal[i] = v;
50    }
51  }
52  digitalWrite(LED_PIN, LOW);
53}
54
55int readPosition() {
56  long sum = 0, weighted = 0;
57  bool onLine = false;
58  for (int i = 0; i < N; i++) {
59    int range = max(maxVal[i] - minVal[i], 1);
60    long v = (long)(analogRead(SENSOR[i]) - minVal[i]) * 1000 / range;
61    v = constrain(v, 0L, 1000L);
62    if (WHITE_LINE) v = 1000 - v;
63    if (v > 500) onLine = true;
64    if (v > 50) {
65      weighted += v * (i * 1000L);
66      sum += v;
67    }
68  }
69  if (!onLine) {
70    return (lastPosition < CENTER) ? 0 : (N - 1) * 1000;
71  }
72  lastPosition = weighted / sum;
73  return lastPosition;
74}
75
76void readTuning() {                       // telefondan "p0.07", "d0.9", "s160" gibi komutlar / commands like "p0.07", "d0.9", "s160" from the phone
77  if (!SerialBT.available()) return;
78  char cmd = SerialBT.read();
79  if (cmd != 'p' && cmd != 'i' && cmd != 'd' && cmd != 's') return;   // satır sonlarını atla / skip line endings
80  float value = SerialBT.parseFloat();
81  if (cmd == 'p') Kp = value;
82  if (cmd == 'i') { Ki = value; integral = 0; }
83  if (cmd == 'd') Kd = value;
84  if (cmd == 's') baseSpeed = (int)value;
85  SerialBT.printf("Kp=%.3f Ki=%.4f Kd=%.3f speed=%d\n", Kp, Ki, Kd, baseSpeed);
86}
87
88void setup() {
89  int outputs[] = {ENA, IN1, IN2, ENB, IN3, IN4, LED_PIN};
90  for (int p : outputs) pinMode(p, OUTPUT);
91  pinMode(BUTTON, INPUT_PULLUP);
92  drive(0, 0);
93  SerialBT.begin("CizgiRobot");           // telefonda görünecek ad / name shown on the phone
94  SerialBT.setTimeout(20);                // parseFloat robotu bekletmesin / keep parseFloat from blocking
95  waitButton();
96  calibrate();
97  waitButton();
98  lastTime = millis();
99}
100
101void loop() {
102  readTuning();
103  if (millis() - lastTime < PERIOD_MS) return;
104  lastTime += PERIOD_MS;
105
106  float error = readPosition() - CENTER;
107  integral = constrain(integral + error, -20000.0f, 20000.0f);
108  float derivative = error - lastError;
109  lastError = error;
110
111  int correction = (int)(Kp * error + Ki * integral + Kd * derivative);
112  drive(baseSpeed + correction, baseSpeed - correction);
113}
Bazı yarışma şartnameleri robotta Wi-Fi, Bluetooth ya da RF modülü bulunmasını, hatta Wi-Fi'li kartların kullanılmasını tamamen yasaklar. ESP32'yi ayar ve antrenman için kullanıp yarışmaya Arduino Uno, Nano ya da Wi-Fi'siz bir Pico ile katılmak en güvenli yoldur; emin değilsen düzenleyiciye sor.

Raspberry Pi Pico ve MicroPython ile çizgi izleyen

Pico'da kullanıcıya açık yalnızca üç analog giriş vardır (GP26, GP27, GP28), bu yüzden aşağıdaki kod üç analog sensörle çalışır ve konumu 0–2000 arasında hesaplar. Beş sensör kullanmak istersen dijital çıkışlı 5'li bir modülü herhangi beş GPIO'ya bağlayıp her sensörü 0 ya da 1000 sayabilirsin; ağırlıklı ortalama daha kaba olur ama yine çalışır. read_u16() 0–65535 arası değer döndürür, motor hızı ise yüzde olarak (-100 ile 100 arası) verilir. Kurulum ve ilk adımlar için Raspberry Pi Pico Nedir? yazısına bakabilirsin.

MicroPython
1from machine import Pin, PWM, ADC
2import time
3
4ena = PWM(Pin(15)); ena.freq(1000)        # sol motor hızı / left motor speed
5in1 = Pin(14, Pin.OUT); in2 = Pin(13, Pin.OUT)
6enb = PWM(Pin(10)); enb.freq(1000)        # sağ motor hızı / right motor speed
7in3 = Pin(11, Pin.OUT); in4 = Pin(12, Pin.OUT)
8button = Pin(16, Pin.IN, Pin.PULL_UP)     # diğer bacağı GND'ye / other leg to GND
9led = Pin("LED", Pin.OUT)
10sensors = [ADC(26), ADC(27), ADC(28)]     # sol, orta, sağ / left, middle, right
11WHITE_LINE = False                        # siyah zemin + beyaz çizgi ise True / True for a white line on black
12
13N = len(sensors)
14CENTER = (N - 1) * 500                    # orta konum: 1000 / center position: 1000
15Kp, Ki, Kd = 0.05, 0.0, 0.4
16BASE = 45                                 # taban hız (%) / base speed (%)
17min_val = [65535] * N
18max_val = [0] * N
19last_pos = CENTER
20
21def drive(left, right):                   # her biri -100 … 100 / each -100 … 100
22    left = max(-100, min(100, left))
23    right = max(-100, min(100, right))
24    in1.value(left >= 0); in2.value(left < 0)
25    in3.value(right >= 0); in4.value(right < 0)
26    ena.duty_u16(int(abs(left) * 655.35))
27    enb.duty_u16(int(abs(right) * 655.35))
28
29def wait_button():
30    while button.value():                 # basılmasını bekle / wait for a press
31        time.sleep_ms(10)
32    time.sleep_ms(30)
33    while not button.value():             # bırakılmasını bekle / wait for the release
34        time.sleep_ms(10)
35    time.sleep_ms(30)
36
37def calibrate():                          # 5 sn sağa sola gezdir / sweep for 5 s
38    led.on()
39    start = time.ticks_ms()
40    while time.ticks_diff(time.ticks_ms(), start) < 5000:
41        for i, s in enumerate(sensors):
42            v = s.read_u16()
43            min_val[i] = min(min_val[i], v)
44            max_val[i] = max(max_val[i], v)
45    led.off()
46
47def read_position():                      # 0 (sol) … 2000 (sağ) / 0 (left) … 2000 (right)
48    global last_pos
49    total = weighted = 0
50    on_line = False
51    for i, s in enumerate(sensors):
52        rng = max(1, max_val[i] - min_val[i])
53        v = max(0, min(1000, (s.read_u16() - min_val[i]) * 1000 // rng))
54        if WHITE_LINE:
55            v = 1000 - v
56        if v > 500:
57            on_line = True
58        if v > 50:
59            weighted += v * i * 1000
60            total += v
61    if not on_line:                       # çizgi kayıp / line lost
62        return 0 if last_pos < CENTER else (N - 1) * 1000
63    last_pos = weighted // total
64    return last_pos
65
66drive(0, 0)
67wait_button()                             # 1. basış: kalibrasyon / 1st press: calibration
68calibrate()
69wait_button()                             # 2. basış: yarış / 2nd press: race
70integral = 0
71last_error = 0
72while True:
73    t0 = time.ticks_ms()
74    error = read_position() - CENTER
75    integral = max(-10000, min(10000, integral + error))
76    correction = Kp * error + Ki * integral + Kd * (error - last_error)
77    last_error = error
78    drive(BASE + correction, BASE - correction)
79    while time.ticks_diff(time.ticks_ms(), t0) < 5:   # 5 ms sabit döngü / fixed 5 ms loop
80        pass

Yarışma kuralları ve pist

Çizgi izleyen yarışmalarının kuralları düzenleyiciye ve yıla göre değişir, ama temel fikir aynıdır: robot pisti çizgiden çıkmadan en kısa sürede tamamlamalıdır. Örneğin MEB Robot Yarışması'nın 2024 temel seviye çizgi izleyen şartnamesinde pist siyah zemin üzerinde 20 ± 2 mm genişliğinde beyaz çizgidir. Robot tekerlekler dahil en fazla 280 × 180 mm, pil dahil en fazla 1 kg olabilir; çalışma gerilimi 12 V'u geçemez, 6–12 V'luk plastik dişli L tipi redüktörlü iki DC motor, çapı 65 mm'yi geçmeyen tekerlekler ve en fazla 8 sensörlü bir dizi kullanılır. Pistte 13 derece eğimli bir köprü ve ortasından kestirip geçen robota 20 saniye ceza verilen kare bir bölüm vardır; gövdesi tamamen siyah zemine çıkan robot yoldan çıkmış sayılır, çizgi üzerinde 30 saniyeden uzun hareketsiz kalan robot diskalifiye edilir. Bazı il ve okul yarışmaları ise tam tersini, beyaz zemin üzerinde 20 ± 2 mm siyah çizgiyi kullanır ve 500 gramı geçmeyen daha küçük robotlar ister. Kısacası robotunu tasarlamadan önce o yılın şartnamesini mutlaka oku.

  • Ters renkli pist: Siyah zemin ve beyaz çizgi için kodlardaki WHITE_LINE değişkenini true yap. Pist içinde renk değişiyorsa, dış sensörler siyah, ortadaki beyaz görmeye başladığında moda geçen bir kontrol ekleyebilirsin.
  • 90 derecelik dönüşler: Virajdan önce taban hızı düşür ya da iç tekerin geri dönmesine izin ver; bu yazıdaki kodlarda düzeltme taban hızdan büyük olunca iç teker zaten geri döner.
  • Kavşaklar: Tüm sensörler aynı anda çizgiyi gördüğünde düz geç; yön seçmen gerekiyorsa kavşakları sayan bir durum makinesi kur.
  • Kesik çizgiler: Çizgi kısa süre kaybolduğunda hemen dönmek yerine 100–200 ms düz devam etmeyi dene.
  • Rampa ve köprü: Eğimde sensör ile zemin arasındaki mesafe değişir; sensör çubuğunu tekerleklere yakın ve alçak tut, rampada hızı biraz düşür.
  • Antrenman pisti: Beyaz mat karton ya da dekota üzerine 19 mm'lik mat siyah elektrik bandı yapıştır; ilk denemelerde viraj yarıçapını 15 cm'nin altına indirme.

Sık sorunlar ve çözümleri

SorunOlası nedenÇözüm
Robot düzlükte zigzag çiziyorKp çok büyük, Kd çok küçük ya da hız fazlaKp'yi yaklaşık %20 azalt, Kd'yi artır; gerekirse taban hızı düşür
Virajda pistten çıkıyorHız yüksek, sensör dizisi dar ya da çizgi kaybında yanlış tepkiTaban hızı düşür, iç tekerin geri dönmesine izin ver, kayıpta son görülen tarafa dön
Robot hep bir yana çekiyorMotorların devirleri farklı ya da sensör çubuğu ortalı değilSensörleri ortala, hızlı motorun hızını küçük bir çarpanla (örneğin 0,95) düşür
Sensörler her yerde siyah ya da beyaz görüyorKalibrasyon yapılmamış, sensör çok yüksek ya da trimpot ayarsızYüksekliği 3–5 mm'ye indir, kalibrasyonu pistin üzerinde tekrarla
Motor kalkışında Arduino resetleniyorPil gerilimi anlık düşüyor ya da ortak GND zayıfKartı ayrı besle ya da beslemeye 470 µF kondansatör ekle, kabloları kısalt
Pil azaldıkça robot farklı davranıyorGerilim düştükçe motor hızı azalıyorPil gerilimini ölçüp hızı ölçekle ya da her yarıştan önce dolu pil tak
Evde çalışıyor, salonda çalışmıyorOrtam ışığı ya da parlak zemin kızılötesini bozuyorSensörleri siyah etekle kapat, kalibrasyonu yarış ışığında yap
Sorunu ayırmanın en hızlı yolu tekerlekleri havaya kaldırıp robotu elinle çizginin üzerinde gezdirmektir: tekerlekler doğru yöne hızlanıyorsa sensör ve kod tarafı doğrudur, sorun mekanikte ya da güçtedir. Resetlenme sorunlarının ayrıntılı teşhisi için Arduino kendi kendine resetleniyor yazısına bak.

Sonraki adımlar ve proje fikirleri

  • Engelden kaçan çizgi izleyen: Önüne bir HC-SR04 ekleyip pistteki engelin etrafından dolaşan robot; karar mantığı Engelden Kaçan Robot yazısında.
  • Bluetooth modu: Bir düğmeyle çizgi takibinden telefon kumandasına geç; HC-05 Bluetooth Modülü ile kolayca eklenir.
  • Tur zamanlayıcı: Başlangıç çizgisini algılayıp tur süresini OLED ekranda gösteren robot.
  • Labirent çözücü: Kavşakları sayıp ezberleyen ve ikinci turda en kısa yolu izleyen robot.
  • Depo robotu: Çizgi üzerindeki istasyonlarda duran ve bir servoyla yük bırakan küçük bir AGV modeli.
  • Sağlam bir temel: Şasi, motor ve pil montajını henüz yapmadıysan Arduino Robot Araba Yapımı rehberiyle başla.

Çizgi izleyen robot; sensör, algoritma, motor ve güç konularını aynı anda öğreten, her ayar turunda biraz daha hızlanan bir projedir. Önce basit if-else sürümüyle robotu yürüt, sonra PID'e geç ve değerleri sabırla ayarla. Motor hızının PWM ile nasıl ayarlandığını merak ediyorsan PWM Nedir? yazısı iyi bir devam olur. BlueGrays uygulamasında TCRT5000 ile siyah-beyaz zemin tespiti, L298N ile Bluetooth kontrollü araba ve engelden kaçan robot projelerini devre şemaları ve kodlarıyla adım adım bulabilirsin.

Sıkça sorulan sorular

Çizgi izleyen robot için kaç sensör gerekir?

Çalışan bir robot için 2 sensör yeter, ancak 3 sensör basit if-else koduyla çok daha kararlı sonuç verir. Akıcı ve hızlı takip için PID ile birlikte 5 ya da 8 analog sensör önerilir. MEB gibi birçok yarışmada en fazla 8 sensöre izin verilir.

Çizgi izleyen robot neden zigzag yapar?

En sık neden Kp değerinin büyük, Kd değerinin küçük olmasıdır; robot her sapmada fazla sert düzeltip çizginin öbür yanına geçer. Kp'yi biraz azaltıp Kd'yi artırmak, taban hızı düşürmek ve sensörleri tekerleklerin 4–8 cm önüne almak salınımı azaltır. Basit if-else kodlarında robot birkaç sabit hız arasında geçiş yaptığı için hafif zigzag normaldir.

PID olmadan çizgi izleyen robot yapılabilir mi?

Evet, 2 ya da 3 sensörlü basit if-else koduyla robot çizgiyi takip eder ve okul projeleri için çoğu zaman yeterlidir. Ancak hız arttıkça robot virajlarda çizgiyi kaçırır ve düzlükte salınır. Yarışmada iyi derece hedefliyorsan PID'e geçmen gerekir.

Çizgi izleyen robotta hangi pil kullanılmalı?

En pratik seçenek seri bağlı iki adet 18650 Li-ion pildir; nominal 7,4 V verir ve motorların çektiği yüksek akımı rahatça sağlar. Hafif robotlarda 2S LiPo pil de kullanılır. 9 V kare pil motor akımını karşılayamaz ve kısa sürede biter.

Siyah zemin üzerindeki beyaz çizgi için kod nasıl değişir?

Sensör değerlerini tersine çevirmen yeterlidir: bu yazıdaki kodlarda WHITE_LINE değişkenini true yaptığında normalize değer 1000 − değer olur ve robot beyaz çizgiyi takip eder. Dijital modüllerde aynı değişken DO okumasını tersler. Kalibrasyonu yine yarışacağın pistin üzerinde yapmalısın.

TCRT5000 sensörü zeminden ne kadar yüksekte olmalı?

TCRT5000'in en iyi çalıştığı mesafe yaklaşık 2,5 mm'dir; pratikte sensörle zemin arasında 3–5 mm bırakmak iyi sonuç verir. 1 cm'nin üzerinde siyah ile beyaz arasındaki fark çok azalır ve robot çizgiyi kaçırır. Rampalı pistlerde sensör çubuğunu tekerleklere yakın tutmak mesafenin değişmesini azaltır.

Tüm projeleri gör
TCRT5000 Siyah Beyaz Zemin Tespiti
#78Orta
Premium

TCRT5000 Siyah Beyaz Zemin Tespiti

TCRT5000 sensörü ile zemin rengini algılayarak siyah zeminde kırmızı, beyaz zeminde yeşil LED yakan proje. Çizgi izleyen robotların temelini oluşturan renk eşik algılama mantığını öğretir.

4.7(40)
49
TCRT5000 Kullanımı Mesafe Ölçme
#77Orta
Premium

TCRT5000 Kullanımı Mesafe Ölçme

TCRT5000 kızılötesi sensörü ile yüzeyden yansıyan ışık miktarını ölçerek I2C LCD ekranda analog değer olarak gösteren proje. Optik mesafe/yüzey algılama temellerini öğretir.

4.6(33)
43
L298N ile Bluetooth Kontrollü Araba
#80İleri
Premium

L298N ile Bluetooth Kontrollü Araba

Arduino ve HC-05 Bluetooth modülü ile telefondan kontrol edilebilen 4WD (dört motorlu, dört tekerlekten çekişli) robot araba projesi. L298N motor sürücü kartı üzerinden her bir taraftaki iki motor par

5.0(38)
64
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
HC-SR04 Mesafe Ölçme (Serial Ekran)
#27Başlangıç

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

HC-SR04 ultrasonik sensör ile mesafe ölçüp sonucu Serial Monitor'da cm cinsinden gösteren proje. Ses dalgası ile mesafe hesaplama prensibini öğretir.

4.6(44)
66
Buton İle Led Yakma (Push)
#9Başlangıç

Buton İle Led Yakma (Push)

Butona basıldığında LED'i yakan, bırakıldığında söndüren anlık (push) kontrol projesi. digitalRead ile buton okuma ve koşullu ifade kullanımını öğretir.

4.9(112)
190
ESP32 PWM ile LED Soldurma (Fade)
#110Başlangıç
Premium

ESP32 PWM ile LED Soldurma (Fade)

LED'in parlaklığını yumuşak şekilde 0'dan max'a, max'tan 0'a değiştirme. Arduino'daki gibi analogWrite() ile PWM kontrolü yapıyoruz — ESP32 Arduino Core 3.0+ ile aynı syntax sorunsuz çalışır. Pin tanımı + pinMode + analogWrite yeterli.

4.8(36)
54
Raspberry Pi Pico PWM ile LED Fade (Nefes Efekti, GP14)
#160Başlangıç
Premium

Raspberry Pi Pico PWM ile LED Fade (Nefes Efekti, GP14)

LED artık sadece yanıp sönmüyor — nefes alıyor! 🌬️ Raspberry Pi Pico'nun **PWM** (Pulse Width Modulation — darbe genişlik modülasyonu) özelliğiyle LED'in parlaklığı yavaş yavaş artar, sonra yavaş yavaş azalır. Uyku modundaki MacBook ışığı gibi sakin bir "nefes" efekti. Öğrenme: **machine.PWM sınıfı** (pini PWM çıkışına çevirme), **duty_u16()** (0–65535 arası 16-bit parlaklık değeri), **PWM frekansı** (`freq(1000)` — saniyede 1000 kez aç-kapa, göz titremeyi görmez), **range() ile adım adım döngü*

0.0(0)
4