Ratgeber · 15 Min. Lesezeit

Linienfolger-Roboter mit Arduino: TCRT5000-Sensoren und PID

Linienfolger mit Arduino bauen: TCRT5000-Sensorleiste, Sensoranordnung und Kalibrierung, if-else- und PID-Code, Kp/Kd einstellen, Motortreiber, Akku und Regeln.

Ein Linienfolger-Roboter ist ein autonomes Fahrzeug, das eine schwarze (oder weiße) Linie auf dem Boden mit Infrarotsensoren erkennt und die Geschwindigkeit seiner beiden Räder ständig so anpasst, dass er auf dieser Linie bleibt. Er gehört zu den ersten echten Roboterprojekten mit Arduino, weil er Sensoren auslesen, Entscheidungen treffen und Motoren steuern in einem einzigen Projekt vereint. Fahrerlose Transportfahrzeuge (FTS), die in Fabriken Material transportieren, arbeiten nach demselben Prinzip, und der Linienfolger ist eine der beliebtesten Kategorien bei Schul-Roboterwettbewerben.

In dieser Anleitung lernst du, wie der TCRT5000-Sensor funktioniert, worin sich Sensorleisten mit 2, 3 und 5 Sensoren unterscheiden, in welcher Höhe und mit welchem Abstand du die Sensoren montierst und wie du den Schwellwert kalibrierst. Zuerst bringst du den Roboter mit einem einfachen if-else-Sketch zum Fahren, danach wechselst du zu einer Version mit PID-Regelung, die der Linie folgt, ohne Zickzack zu fahren oder in Kurven von der Strecke abzukommen. Neben funktionierendem Code für Arduino Uno, ESP32 und Raspberry Pi Pico findest du hier auch die Wahl von Motortreiber und Akku, typische Wettbewerbsregeln und Lösungen für die häufigsten Probleme.

TCRT5000 Infrarot-Reflexionssensor
TCRT5000: Eine blau getönte Infrarot-LED und ein schwarzer Fototransistor sitzen nebeneinander in einem Gehäuse; Liniensensor-Module sind um dieses Bauteil herum aufgebaut.

Wie funktioniert ein Linienfolger-Roboter?

Eine weiße Fläche wirft den Großteil des Infrarotlichts zurück, eine matte schwarze Fläche schluckt das meiste davon. Die Sensoren unter dem Roboter messen diesen Unterschied und erkennen so, wo die Linie relativ zum Roboter liegt. Der Roboter hat keine Lenkung, er dreht per Differentialantrieb: Dreht das linke Rad schneller als das rechte, fährt der Roboter eine Rechtskurve, dreht es langsamer, eine Linkskurve. Das Programm durchläuft hunderte Male pro Sekunde dieselbe Schleife: Sensoren lesen, berechnen, wie weit die Linie verrutscht ist, und die Geschwindigkeit der beiden Motoren entsprechend korrigieren. Je schneller und stabiler dieser Regelkreis läuft, desto ruhiger fährt der Roboter.

BauteilEmpfehlungHinweis
Mikrocontroller-BoardArduino Uno oder NanoDer Nano passt leichter auf ein kleines Chassis; ESP32 oder Pico gehen auch
LiniensensorTCRT5000-Leiste mit 3 oder 5 Sensoren, QTR-8AEine Leiste mit Analogausgängen liefert mit PID bessere Ergebnisse
MotortreiberL298N oder TB6612FNGDer TB6612FNG ist kleiner und effizienter
Motoren2 Getriebemotoren (TT 1:48 oder N20)Beide Motoren sollten dasselbe Modell mit gleicher Drehzahl sein
Räder2 Antriebsräder und 1 Stützrad (Kugelrolle)Räder mit 65 mm Durchmesser sind am verbreitetsten
Stromversorgung2 × 18650-Li-Ionen-Akkus (7,4 V) und ein SchalterEin 9-V-Block ist für Motoren zu schwach
SonstigesChassis, Taster, JumperkabelEin Taster erleichtert Kalibrierung und Start
Arduino Nano
Arduino Nano
L298N
L298N
N20
N20
18650 Li-ion
18650 Li-ion

Der TCRT5000-Sensor: Leisten mit 2, 3 und 5 Sensoren

Der TCRT5000 besteht aus einer Infrarot-LED (950 nm) und einem Fototransistor, die nebeneinander im selben Gehäuse sitzen. Je mehr Licht der LED vom Boden reflektiert wird und den Fototransistor erreicht, desto stärker leitet der Transistor. Am besten arbeitet der Sensor in etwa 2,5 mm Abstand; über 1 cm nimmt der Unterschied zwischen Schwarz und Weiß schnell ab. Fertige Module enthalten einen LM393-Komparator: Der DO-Pin liefert bei den meisten Modulen je nach dem am Potentiometer eingestellten Schwellwert über Schwarz HIGH und über Weiß LOW, und die LED auf der Platine leuchtet über weißem Untergrund. Der AO-Pin liefert den rohen Analogwert: hoch über Schwarz, niedrig über Weiß. Alle Details zum Sensor findest du im Beitrag TCRT5000 Infrarotsensor.

SensorenAnordnungAlgorithmusWann sinnvoll?
2Links und rechts der Linie; beide sehen normalerweise Weißif-elseErster Versuch, breite Strecke mit sanften Kurven
3Mittlerer über der Linie, äußere an ihren Rändernif-else oder einfacher PIDSchulprojekte, Wettbewerbe für Einsteiger
5Reihe mit gleichen Abständen, mittlerer über der LiniePID (gewichteter Mittelwert)Schnelles, ruhiges Linienfolgen
8Breite Reihe (wie QTR-8A)PID plus KreuzungserkennungWettbewerbsstrecken mit engen Kurven und Kreuzungen

Sensoranordnung: Höhe, Abstand und Vorbau

Egal wie gut dein Code ist: Sitzen die Sensoren an der falschen Stelle, fährt der Roboter nicht sauber. Montiere die Sensorleiste vor der Radachse, parallel zum Boden und symmetrisch zur Mittellinie des Roboters. Diese Maße sind ein guter Ausgangspunkt:

  • Höhe: Lass 3–5 mm zwischen der Unterseite des Sensors und dem Boden. Pololu nennt für seine QTR-8A-Leiste 3 mm als optimalen und 6 mm als maximal empfohlenen Abstand. Sitzt der Sensor höher, schrumpft der Unterschied zwischen Schwarz und Weiß und der Roboter verliert die Linie.
  • Abstand: Bei analogen Leisten sollte der Abstand zwischen zwei Sensoren zwischen der Hälfte und zwei Dritteln der Linienbreite liegen; bei einer 20-mm-Linie funktionieren 10–13 mm gut. So liegt die Linie meist unter zwei Sensoren und die Position wird weich berechnet.
  • Digitale Anordnung mit 3 Sensoren: Der mittlere Sensor sitzt genau über der Linie, die äußeren knapp außerhalb ihrer Ränder; bei einer 20-mm-Linie etwa 18–22 mm von Mitte zu Mitte.
  • Vorbau: Setze die Sensoren 4–8 cm vor die Radachse. Sitzen sie zu nah, erkennt der Roboter Kurven zu spät; sitzen sie zu weit vorn, schwenkt schon eine kleine Drehung die Sensoren stark aus und der Roboter zittert.
  • Umgebungslicht: Sonnenlicht und Halogenlampen enthalten ebenfalls Infrarotlicht. Eine schwarze Schürze oder Klebeband rund um die Sensoren macht die Messwerte deutlich stabiler.
Ein häufiger Fehler ist, die Sensoren schräg oder zu hoch zu montieren. Verliert der Roboter in der ersten Kurve die Linie, prüfe zuerst die Sensorhöhe und erst dann den Code: Schon wenige Millimeter können die Messwerte halbieren. Glänzendes Isolierband reflektiert außerdem Infrarot, sodass eine schwarze Linie manchmal weiß erscheint; nimm nach Möglichkeit mattes Band oder eine gedruckte Linie.

Verdrahtung: Arduino Uno, ESP32 und Pico

Die folgende Tabelle stimmt genau mit dem Sensortest und den PID-Sketches überein; die Verdrahtung des if-else-Beispiels mit 3 Sensoren steht in dessen eigenem Abschnitt. Nummeriere die Sensoren von links nach rechts, von hinten auf den Roboter gesehen: Der Sensor ganz links ist Nummer 1. Als Motortreiber verwenden wir einen L298N; was die einzelnen Pins tun und wie die H-Brücke arbeitet, erklärt der Beitrag Gleichstrommotor mit dem L298N steuern ausführlich. Der Pico hat nur drei Analogeingänge, deshalb nutzen wir dort die Sensoren 1, 3 und 5.

AnschlussArduino UnoESP32Raspberry Pi Pico
Sensor 1 (ganz links) AOA0GPIO 36GP26
Sensor 2 AOA1GPIO 39-
Sensor 3 (Mitte) AOA2GPIO 34GP27
Sensor 4 AOA3GPIO 35-
Sensor 5 (ganz rechts) AOA4GPIO 32GP28
Sensor VCC5V3V33V3
L298N ENA (Geschwindigkeit linker Motor)D9GPIO 25GP15
L298N IN1, IN2D8, D7GPIO 26, 27GP14, GP13
L298N ENB (Geschwindigkeit rechter Motor)D10GPIO 33GP10
L298N IN3, IN4D5, D4GPIO 18, 19GP11, GP12
Starttaster (anderes Bein an GND)D2GPIO 23GP16
GNDGNDGNDGND (gemeinsam mit Akku und Treiber)
Minuspol des Akkus, GND des Motortreibers und GND des Boards müssen unbedingt verbunden sein; ohne gemeinsame Masse verhalten sich die Motoren unberechenbar. Versorge die Motoren niemals über den 5V-Pin des Boards. Am ESP32 und Pico versorgst du die Sensoren mit 3V3: Der AO-Ausgang eines mit 5 V betriebenen Moduls kann bis auf 5 V steigen und die 3,3-V-Analogeingänge beschädigen.

Motortreiber und Akku auswählen

Der L298N ist günstig und robust, doch seine Bipolartransistoren schlucken 1,5–3 V: Von einem 7,4-V-Akkupack kommen an den Motoren nur etwa 5–5,5 V an. Bei kleinen, schnellen Robotern funktioniert ein TB6612FNG oder DRV8833 mit MOSFETs besser. Beim TB6612FNG bleibt der Code gleich: Du verbindest die Leitungen von ENA, IN1 und IN2 mit PWMA, AIN1 und AIN2 (bei Kanal B mit PWMB, BIN1 und BIN2) und legst den STBY-Pin an VCC. Der DRV8833 hat keinen eigenen Geschwindigkeits-Pin (Enable); das PWM-Signal geht direkt auf die IN-Pins, deshalb musst du die Funktion drive() anpassen. Am praktischsten ist die Versorgung mit zwei 18650-Li-Ionen-Akkus in Reihe: voll geladen 8,4 V, nominal 7,4 V, und hohe Ströme liefern sie problemlos. 9-V-Blockbatterien schaffen den Motorstrom nicht und sind schnell leer. Wie lange dein Akku hält, berechnest du mit dem Werkzeug Akkulaufzeit.

TreiberMotorspannungStrom pro KanalSpannungsabfallFazit
L298N5–35 V2 A1,5–3 VRobust, aber groß und ineffizient
TB6612FNG4,5–13,5 V1,2 A (Spitze 3,2 A)Sehr geringBestes Preis-Leistungs-Verhältnis für Linienfolger
DRV88332,7–10,8 V1,5 A (Spitze 2 A)Sehr geringKleine Roboter mit N20-Motoren
L9110S2,5–12 V0,8 AGeringNur leichte, langsame Roboter
Gelber TT-Getriebemotor mit Rad
Gelber TT-Getriebemotor mit 65-mm-Rad: Das Getriebe senkt die Drehzahl und erhöht das Drehmoment; er ist der häufigste Motor in Linienfolger- und Roboterauto-Projekten.

Ein Rechenbeispiel zur Geschwindigkeit: Gelbe TT-Motoren (1:48) drehen bei 6 V ohne Last etwa 200 U/min. Ein 65-mm-Rad hat einen Umfang von π × 65 ≈ 204 mm, also schafft der Roboter höchstens 200 × 0,204 ≈ 41 m pro Minute, rund 0,7 m pro Sekunde. Für die meisten Schulstrecken reicht das locker; die eigentliche Herausforderung ist, bei diesem Tempo die Linie nicht zu verlieren. Schnellere Roboter nutzen N20-Mikrogetriebemotoren und ein leichtes Chassis. Sinkt die Akkuspannung, werden die Motoren langsamer und deine eingestellten PID-Werte passen nicht mehr; die Akkuspannung mit einem Spannungsteiler zu messen und die Geschwindigkeit daran anzupassen, ist eine Verbesserung für Fortgeschrittene.

Schritt 1: Sensoren testen und Schwellwert finden

Bevor du die Motoren anschließt, solltest du wissen, was die Sensoren sehen. Der folgende Sketch gibt die rohen Analogwerte aller fünf Sensoren durch Tabs getrennt aus; öffnest du in der Arduino IDE Werkzeuge > Serieller Plotter mit 115200 Baud, erscheint jeder Sensor als eigene Linie. Schiebe den Roboter langsam von Hand über die Linie und beobachte, wie sich die Werte ändern.

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}

Ein typisches TCRT5000-Modul liefert bei 5 V auf weißem Untergrund 30–200 und auf der schwarzen Linie 600–1000, doch diese Zahlen hängen von Sensorhöhe, Bodenmaterial und Umgebungslicht ab. Der einfachste Schwellwert ist: Schwellwert = (Weißwert + Schwarzwert) / 2. Liest du zum Beispiel auf Weiß 80 und auf Schwarz 820, liegt der Schwellwert bei 450. Da jeder Sensor etwas anders misst, erledigt der PID-Sketch das automatisch: Während der Kalibrierung speichert er für jeden Sensor den kleinsten und größten Wert und skaliert danach jede Messung mit (Messwert − Min) × 1000 / (Max − Min) auf 0 (Weiß) bis 1000 (Schwarz). So liegt der Schwellwert für jeden Sensor bei 500.

Bei Modulen mit digitalem Ausgang (DO) stellst du den Schwellwert am Potentiometer auf der Platine ein. Dreh den Trimmer so, dass die LED des Moduls über der schwarzen Linie aus und über dem weißen Untergrund an ist; die Stellung genau in der Mitte zwischen diesen beiden Punkten ist am stabilsten. Stell das Ganze auf der Wettbewerbsstrecke und unter derselben Beleuchtung ein.

Schritt 2: Einfacher if-else-Algorithmus (3 Sensoren)

Für einen ersten funktionierenden Roboter ist es am verständlichsten, je nach Zustand der drei Sensoren aus ein paar festen Bewegungen zu wählen. Verbinde die DO-Ausgänge eines 3-Kanal-Moduls mit A0 (links), A1 (Mitte) und A2 (rechts); die Analogpins des Uno funktionieren auch als Digitaleingänge. Die Logik zeigt diese Tabelle (1 bedeutet, der Sensor sieht die Linie):

LinksMitteRechtsBedeutungBewegung
010Linie in der MitteGeradeaus
110Linie leicht linksLinkes Rad langsamer, sanft nach links
100Linie weit linksLinkes Rad rückwärts, scharf nach links
011Linie leicht rechtsRechtes Rad langsamer, sanft nach rechts
001Linie weit rechtsRechtes Rad rückwärts, scharf nach rechts
111Kreuzung oder ZiellinieGeradeaus drüber
000Linie verlorenZur zuletzt gesehenen Seite drehen
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}

Die Variable lastSide merkt sich, auf welcher Seite der Roboter die Linie zuletzt gesehen hat; verlieren in einer scharfen Kurve alle drei Sensoren die Linie, dreht der Roboter zu dieser Seite, um sie wiederzufinden, statt ziellos geradeaus zu fahren. Mit zwei Sensoren ist dieselbe Logik noch einfacher: Die Sensoren sitzen links und rechts der Linie, solange beide Weiß sehen, fährt der Roboter geradeaus, sieht der linke Schwarz, dreht er nach links, sieht der rechte Schwarz, nach rechts. Die Schwäche dieser Methode sind die stufigen Bewegungen: Der Roboter wechselt nur zwischen wenigen festen Geschwindigkeiten, fährt deshalb selbst auf Geraden leichte Schlangenlinien und fliegt bei höherem Tempo in den Kurven von der Strecke.

Schritt 3: Ruhiges Linienfolgen mit PID

PID korrigiert proportional dazu, wie weit der Roboter von der Linie abgewichen ist. Zuerst wird aus den fünf Sensoren die Position der Linie als gewichteter Mittelwert berechnet: Der Wert jedes Sensors (0–1000) wird mit seiner eigenen Position (0, 1000, 2000, 3000, 4000) multipliziert, alles wird addiert und durch die Summe der Werte geteilt. Das Ergebnis liegt zwischen 0 und 4000, bei genau mittiger Linie bei 2000. Lesen die Sensoren zum Beispiel 0, 0, 300, 900, 0, ergibt sich die Position (300 × 2000 + 900 × 3000) / 1200 = 2750. Der Fehler = Position − 2000: Ein positiver Wert heißt, die Linie liegt rechts, ein negativer, sie liegt links.

Die Korrektur ist die Summe aus drei Termen: Korrektur = Kp × Fehler + Ki × Summe der Fehler + Kd × (Fehler − vorheriger Fehler). Der P-Anteil lenkt umso stärker, je größer die Abweichung ist. Der D-Anteil schaut, wie schnell sich der Fehler ändert, bremst, wenn sich der Roboter der Linie nähert, und dämpft so das Schwingen. Der I-Anteil gleicht kleine Fehler aus, die lange auf einer Seite bleiben, wird beim Linienfolger aber meist auf null gelassen. Danach läuft der linke Motor mit Grundgeschwindigkeit + Korrektur und der rechte mit Grundgeschwindigkeit − Korrektur. Bei einem Fehler von 800 und Kp = 0,06 beträgt die Korrektur zum Beispiel 48; bei Grundgeschwindigkeit 140 läuft der linke Motor mit 188, der rechte mit 92, und der Roboter biegt sanft nach rechts ab.

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}

Drück nach dem Hochladen einmal den Taster: Die LED auf dem Board leuchtet 5 Sekunden lang. Schieb den Roboter in dieser Zeit ein paarmal von Hand nach links und rechts über die Linie, damit jeder Sensor sowohl Schwarz als auch Weiß sieht. Geht die LED aus, setz den Roboter auf die Linie und drück den Taster erneut. Die Schleife läuft dank millis() alle 5 ms; die feste Periode sorgt dafür, dass der D-Anteil immer gleich skaliert ist. Verschwindet die Linie ganz, gibt readPosition() je nach zuletzt gesehener Seite 0 oder 4000 zurück, und der Roboter sucht sie mit der schärfsten Drehung. Wie du den Taster anschließt, steht in Taster am Arduino anschließen, Timing ohne delay erklärt Arduino millis() und delay().

Kp und Kd einstellen

  1. Halte die Grundgeschwindigkeit niedrig (zum Beispiel 100) und setze Ki und Kd auf null.
  2. Beginne mit einem kleinen Kp (0,02) und erhöhe ihn schrittweise, bis der Roboter die Kurven schafft. Fängt der Roboter an, schnell und gleichmäßig um die Linie zu pendeln, hast du die Obergrenze für Kp erreicht; bleib etwas darunter.
  3. Starte Kd bei etwa dem Zehnfachen von Kp und erhöhe ihn, bis das Pendeln verschwindet. Wird Kd zu groß, zittert der Roboter und die Motoren surren.
  4. Fährt der Roboter auf Geraden schnurgerade und in Kurven flüssig, erhöhe die Grundgeschwindigkeit um 10–20 und wiederhole die Schritte 2 und 3.
  5. Probiere Ki nur dann mit einem sehr kleinen Wert (etwa 0,0001), wenn der Roboter auf langen Geraden ständig zu einer Seite driftet.
  6. Notiere die gefundenen Werte und die aktuelle Akkuspannung; mit sinkender Spannung verhalten sich dieselben Werte anders.
Im Maßstab dieses Beitrags (Position 0–4000, PWM 0–255, 5-ms-Schleife) ist ein guter Startbereich 0,04–0,08 für Kp und 0,4–1,2 für Kd. Setz beim Einstellen kein Serial.print in die Schleife: Jede Ausgabe bremst die Schleife und bringt den D-Anteil durcheinander. Musst du Werte sehen, gib sie höchstens alle 200 ms aus.

Linienfolger mit dem ESP32: PID per Handy einstellen

Der große Vorteil des ESP32: Du kannst die PID-Werte per Handy ändern, ohne jedes Mal neuen Code hochzuladen. Die Logik entspricht der Uno-Version, die Unterschiede sind: Der Analogeingang des ESP32 hat 12 Bit (0–4095), die Sensoren gehören an ADC1-Pins, also GPIO 32–39 (ADC2-Pins lassen sich bei aktivem WLAN nicht lesen), und die Sensoren werden mit 3,3 V versorgt. analogWrite() funktioniert in den Versionen 2.x und 3.x des ESP32-Arduino-Cores. Installiere auf deinem Android-Handy eine App wie Serial Bluetooth Terminal, verbinde dich mit dem Gerät "CizgiRobot" und sende Befehle wie p0.07, d0.9 oder s160; der Roboter übernimmt den neuen Wert sofort und meldet ihn zurück. Klassisches Bluetooth gibt es nur beim originalen ESP32 (nicht beim S3 oder C3), und iPhones unterstützen diese Art serieller Verbindung nicht. Wenn der ESP32 neu für dich ist, lies zuerst Was ist der ESP32?.

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}
Manche Wettbewerbsregeln verbieten WLAN-, Bluetooth- oder Funkmodule am Roboter oder sogar Boards mit WLAN überhaupt. Am sichersten ist es, den ESP32 zum Einstellen und Üben zu nutzen und im Wettbewerb mit einem Arduino Uno, Nano oder einem Pico ohne WLAN anzutreten; frag im Zweifel die Veranstalter.

Linienfolger mit Raspberry Pi Pico und MicroPython

Der Pico hat nur drei frei nutzbare Analogeingänge (GP26, GP27, GP28), deshalb arbeitet der folgende Code mit drei analogen Sensoren und berechnet die Position zwischen 0 und 2000. Willst du fünf Sensoren nutzen, kannst du ein 5-Kanal-Modul mit Digitalausgängen an beliebige fünf GPIOs hängen und jeden Sensor als 0 oder 1000 zählen; der gewichtete Mittelwert wird gröber, funktioniert aber trotzdem. read_u16() liefert Werte von 0 bis 65535, die Motorgeschwindigkeit wird in Prozent angegeben (-100 bis 100). Installation und erste Schritte findest du in Was ist der Raspberry Pi Pico?.

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

Wettbewerbsregeln und Strecke

Die Regeln für Linienfolger-Wettbewerbe ändern sich je nach Veranstalter und Jahr, die Grundidee bleibt aber gleich: Der Roboter muss die Strecke in möglichst kurzer Zeit absolvieren, ohne die Linie zu verlassen. Im Einsteiger-Regelwerk 2024 für Linienfolger beim türkischen MEB-Roboterwettbewerb ist die Strecke zum Beispiel eine 20 ± 2 mm breite weiße Linie auf schwarzem Untergrund. Der Roboter darf samt Rädern höchstens 280 × 180 mm messen und mit Akku höchstens 1 kg wiegen; die Betriebsspannung darf 12 V nicht überschreiten, vorgeschrieben sind zwei 6–12-V-Getriebemotoren in L-Bauform mit Kunststoffgetriebe, Räder mit höchstens 65 mm Durchmesser und eine Sensorleiste mit höchstens 8 Sensoren. Auf der Strecke gibt es eine Brücke mit 13 Grad Steigung und einen quadratischen Abschnitt, bei dem das Abkürzen durch die Mitte 20 Strafsekunden kostet; ein Roboter, dessen Gehäuse vollständig auf dem schwarzen Untergrund steht, gilt als von der Strecke abgekommen, und wer länger als 30 Sekunden reglos auf der Linie steht, wird disqualifiziert. Andere Schul- und Regionalwettbewerbe nutzen das Gegenteil, eine 20 ± 2 mm breite schwarze Linie auf weißem Grund, und verlangen kleinere Roboter mit höchstens 500 Gramm. Kurz gesagt: Lies vor dem Bau unbedingt das Regelwerk des jeweiligen Jahres.

  • Invertierte Strecke: Für eine weiße Linie auf schwarzem Grund setzt du in den Sketches die Variable WHITE_LINE auf true. Wechseln die Farben innerhalb der Strecke, kannst du eine Prüfung ergänzen, die umschaltet, sobald die äußeren Sensoren Schwarz und der mittlere Weiß sehen.
  • 90-Grad-Kurven: Senke vor der Kurve die Grundgeschwindigkeit oder lass das innere Rad rückwärts drehen; in den Sketches dieses Beitrags dreht das innere Rad ohnehin rückwärts, sobald die Korrektur größer als die Grundgeschwindigkeit ist.
  • Kreuzungen: Sehen alle Sensoren gleichzeitig die Linie, fahr geradeaus drüber; musst du eine Richtung wählen, baue einen Zustandsautomaten, der die Kreuzungen zählt.
  • Unterbrochene Linien: Verschwindet die Linie kurz, fahr 100–200 ms geradeaus weiter, statt sofort zu drehen.
  • Rampe und Brücke: In der Steigung ändert sich der Abstand zwischen Sensor und Boden; halte die Sensorleiste niedrig und nah an den Rädern und fahr die Rampe etwas langsamer.
  • Übungsstrecke: Klebe 19 mm breites mattes schwarzes Isolierband auf weißen matten Karton oder eine Schaumstoffplatte; mach die Kurven bei den ersten Versuchen nicht enger als 15 cm Radius.

Häufige Probleme und Lösungen

ProblemMögliche UrsacheLösung
Roboter fährt auf Geraden ZickzackKp zu groß, Kd zu klein oder zu schnellKp um etwa 20 % senken, Kd erhöhen; bei Bedarf Grundgeschwindigkeit senken
Fliegt in Kurven von der StreckeZu schnell, Sensorleiste zu schmal oder falsche Reaktion bei LinienverlustGrundgeschwindigkeit senken, inneres Rad rückwärts drehen lassen, bei Verlust zur zuletzt gesehenen Seite drehen
Roboter zieht immer zu einer SeiteMotoren drehen unterschiedlich schnell oder Sensorleiste nicht mittigSensoren zentrieren, schnelleren Motor mit kleinem Faktor (zum Beispiel 0,95) drosseln
Sensoren sehen überall Schwarz oder WeißKeine Kalibrierung, Sensor zu hoch oder Trimmer verstelltHöhe auf 3–5 mm senken, Kalibrierung auf der Strecke wiederholen
Arduino startet beim Anfahren neuAkkuspannung bricht kurz ein oder gemeinsame Masse schlechtBoard getrennt versorgen oder 470-µF-Kondensator an die Versorgung, Kabel kürzen
Roboter verhält sich bei schwachem Akku andersMotordrehzahl sinkt mit der SpannungAkkuspannung messen und Geschwindigkeit skalieren oder vor jedem Lauf vollen Akku einsetzen
Zu Hause klappt es, in der Halle nichtUmgebungslicht oder glänzender Boden stört das InfrarotSensoren mit schwarzer Schürze abschirmen, unter Wettbewerbslicht kalibrieren
Am schnellsten grenzt du ein Problem ein, wenn du die Räder anhebst und den Roboter von Hand über die Linie bewegst: Beschleunigen die Räder in die richtige Richtung, stimmen Sensoren und Code, und das Problem liegt in der Mechanik oder der Stromversorgung. Eine ausführliche Diagnose von Neustarts findest du in Arduino startet ständig neu.

Nächste Schritte und Projektideen

  • Linienfolger mit Hinderniserkennung: Ein HC-SR04 vorne lässt den Roboter Hindernissen auf der Strecke ausweichen; die Entscheidungslogik steht in Hindernisvermeidender Roboter.
  • Bluetooth-Modus: Per Taster vom Linienfolgen auf Handysteuerung umschalten; mit dem HC-05 Bluetooth-Modul schnell nachgerüstet.
  • Rundenzeitmesser: Ein Roboter, der die Startlinie erkennt und die Rundenzeit auf einem OLED-Display anzeigt.
  • Labyrinthlöser: Ein Roboter, der Kreuzungen zählt, sie sich merkt und im zweiten Durchgang den kürzesten Weg fährt.
  • Lagerroboter: Ein kleines FTS-Modell, das an Stationen entlang der Linie hält und mit einem Servo eine Last abwirft.
  • Eine solide Basis: Hast du Chassis, Motoren und Akku noch nicht montiert, beginne mit der Anleitung Arduino-Roboterauto bauen.

Ein Linienfolger-Roboter vermittelt Sensoren, Algorithmen, Motoren und Stromversorgung auf einmal und wird mit jeder Einstellrunde ein bisschen schneller. Bring den Roboter zuerst mit der einfachen if-else-Version zum Fahren, wechsle dann zu PID und stimme die Werte geduldig ab. Wenn du wissen willst, wie PWM die Motordrehzahl einstellt, ist Was ist PWM? die passende Fortsetzung. In der BlueGrays-App findest du die Projekte zur Schwarz-Weiß-Erkennung mit dem TCRT5000, zum Bluetooth-gesteuerten Auto mit L298N und zum hindernisvermeidenden Roboter Schritt für Schritt mit Schaltplänen und Code.

Häufige Fragen

Wie viele Sensoren braucht ein Linienfolger?

Für einen funktionierenden Roboter reichen 2 Sensoren, mit 3 Sensoren und einfachem if-else-Code fährt er aber deutlich stabiler. Für flüssiges, schnelles Linienfolgen empfehlen sich 5 oder 8 analoge Sensoren mit PID. Viele Wettbewerbe erlauben höchstens 8 Sensoren.

Warum fährt mein Linienfolger Zickzack?

Meist ist Kp zu groß und Kd zu klein: Der Roboter korrigiert jede Abweichung zu stark und schießt auf die andere Seite der Linie. Kp etwas senken, Kd erhöhen, die Grundgeschwindigkeit reduzieren und die Sensoren 4–8 cm vor die Räder setzen dämpft das Pendeln. Bei einfachem if-else-Code ist leichtes Zickzack normal, weil der Roboter nur zwischen wenigen festen Geschwindigkeiten wechselt.

Geht ein Linienfolger auch ohne PID?

Ja. Mit einfachem if-else-Code und 2 oder 3 Sensoren folgt der Roboter der Linie, und für Schulprojekte reicht das oft aus. Mit steigendem Tempo verliert er aber in Kurven die Linie und pendelt auf Geraden. Willst du im Wettbewerb vorne landen, kommst du um PID nicht herum.

Welcher Akku eignet sich für einen Linienfolger-Roboter?

Am praktischsten sind zwei 18650-Li-Ionen-Akkus in Reihe: Sie liefern nominal 7,4 V und problemlos den hohen Strom der Motoren. Leichte Roboter nutzen auch einen 2S-LiPo-Akku. Ein 9-V-Block schafft den Motorstrom nicht und ist schnell leer.

Was ändert sich im Code bei weißer Linie auf schwarzem Grund?

Du musst nur die Sensorwerte umkehren: Setzt du in den Sketches dieses Beitrags WHITE_LINE auf true, wird der normierte Wert zu 1000 − Wert und der Roboter folgt der weißen Linie. Bei digitalen Modulen kehrt dieselbe Variable den DO-Wert um. Kalibrieren solltest du trotzdem auf der Wettbewerbsstrecke.

Wie hoch sollte der TCRT5000 über dem Boden sitzen?

Am besten arbeitet der TCRT5000 bei etwa 2,5 mm; in der Praxis sind 3–5 mm zwischen Sensor und Boden ein guter Wert. Über 1 cm wird der Unterschied zwischen Schwarz und Weiß sehr klein und der Roboter verliert die Linie. Auf Strecken mit Rampen hilft es, die Sensorleiste nah an den Rädern zu montieren.

Alle Projekte ansehen
TCRT5000 Schwarz-Weiß-Untergrunderkennung
#78Mittel
Premium

TCRT5000 Schwarz-Weiß-Untergrunderkennung

Projekt, das die Farbe des Bodens mit dem Sensor TCRT5000 erkennt und eine rote LED auf schwarzem Boden und eine grüne LED auf weißem Boden aufleuchten lässt. Es lehrt die Logik zur Erkennung von Farb

4.7(40)
49
TCRT5000 Abstandsmessung
#77Mittel
Premium

TCRT5000 Abstandsmessung

Projekt, das die von einer Oberfläche reflektierte Lichtmenge mit dem TCRT5000-Infrarotsensor misst und als Analogwert auf dem I2C-LCD-Bildschirm anzeigt. Lehrt die Grundlagen der optischen Abstands-/

4.6(33)
43
Bluetooth-gesteuertes Auto mit L298N
#80Fortgeschritten
Premium

Bluetooth-gesteuertes Auto mit L298N

4WD-Roboterauto-Projekt mit vier Motoren und Allradantrieb, das vom Telefon aus mit Arduino und HC-05 Bluetooth-Modul gesteuert werden kann. Insgesamt vier Räder werden gleichzeitig angetrieben, indem

5.0(38)
64
Roboter zur Hindernisvermeidung
#81Fortgeschritten
Premium

Roboter zur Hindernisvermeidung

Wie wäre es, ein intelligentes Fahrzeug zu bauen, das seinen Weg selbst findet? Der Obstacle Avoiding Robot scannt dank des Ultraschallsensors vor ihm ständig seine Umgebung. Wenn er auf ein Hindernis

5.0(38)
49
HC-SR04 Abstandsmessung (serielle Anzeige)
#27Anfänger

HC-SR04 Abstandsmessung (serielle Anzeige)

HC-SR04 Ultraschallsensor zur Abstandsmessung und Anzeige des Ergebnisses in cm auf dem seriellen Monitor. Er lehrt das Prinzip der Abstandsberechnung mit Schallwellen.

4.6(44)
66
LED mit Taster schalten (Push)
#9Anfänger

LED mit Taster schalten (Push)

Sofortiges (Druck-)Steuerprojekt, bei dem die LED aufleuchtet, wenn die Taste gedrückt wird, und erlischt, wenn sie losgelassen wird. Lehrt die Verwendung von Tastenlesung und bedingtem Ausdruck mit d

4.9(112)
190
ESP32 LED-Überblendung mit PWM
#110Anfänger
Premium

ESP32 LED-Überblendung mit PWM

Ändern Sie die Helligkeit der LED stufenlos von 0 bis max, von max bis 0. Wir steuern PWM mit analogWrite() wie in Arduino - die gleiche Syntax mit ESP32 Arduino Core 3.0+ funktioniert reibungslos. Pin-Definition + pinMode + analogWrite ist genug.

4.8(36)
54
Raspberry Pi Pico PWM LED Fade (Atem-Effekt, GP14)
#160Anfänger
Premium

Raspberry Pi Pico PWM LED Fade (Atem-Effekt, GP14)

Die LED blinkt nicht mehr nur — sie atmet! 🌬️ Mit der **PWM** (Pulsweitenmodulation) des Raspberry Pi Pico steigt die Helligkeit langsam an und fällt wieder ab. Lerne die machine.PWM-Klasse, duty_u16() (16-Bit-Helligkeit 0–65535), PWM-Frequenz, range()-Schleifen und analogähnliche Ausgabe. Grundlage für RGB-Farbmischung, Motorsteuerung, Servos und Buzzer.

0.0(0)
4