Hindernisvermeidender Roboter mit Arduino: HC-SR04 und Servo
Hindernisvermeidender Roboter mit Arduino: HC-SR04 auf einem Servo, L298N oder L293D verdrahten, Stopp-Rückwärts-Drehen-Logik, IR-Sensoren und Bluetooth-Modus.
Ein hindernisvermeidender Roboter ist ein selbstfahrendes Roboterauto, das eine Wand, einen Stuhl oder eine Kiste vor sich erkennt, ohne dagegenzufahren, und sich einen neuen Weg sucht. Der verbreitetste Aufbau besteht aus einem Arduino Uno, einem HC-SR04-Ultraschallsensor, einem SG90-Servomotor, der den Sensor nach links und rechts dreht, zwei Getriebemotoren und einem L298N (oder L293D) als Motortreiber. Der Roboter fährt geradeaus, hält an, wenn er einem Hindernis nahekommt, setzt ein Stück zurück, dreht seinen Kopf zu beiden Seiten, misst und fährt dann in die freieste Richtung weiter.
In dieser Anleitung machen wir aus dem 2WD-Auto aus unserem Artikel Arduino-Roboterauto einen autonomen Roboter und behalten dabei die Pinbelegung bei. Schritt für Schritt geht es um die Anschlusstabellen, die Berechnung der Abstandsschwellen, einen Code, der in drei Stufen wächst, die Oberflächen, die der Sensor nicht sieht, wie du diese Lücken mit Infrarotsensoren schließt, und schließlich um den Wechsel in den manuellen Modus per Bluetooth vom Handy aus. Den Code, um denselben Roboter mit einem ESP32 oder einem Raspberry Pi Pico zu fahren, findest du am Ende des Artikels.

Wie funktioniert ein Roboter, der Hindernissen ausweicht?
Jeder autonome Roboter wiederholt dieselbe Schleife aus drei Schritten dutzende Male pro Sekunde: wahrnehmen, entscheiden, handeln. Das Wahrnehmen übernimmt der HC-SR04: Er sendet einen kurzen 40-kHz-Ton, den dein Ohr nicht hört, und misst, wie lange das Echo zurück braucht; teilst du diese Zeit in Mikrosekunden durch 58, erhältst du den Abstand in Zentimetern. Das Entscheiden übernimmt der Arduino, indem er den gemessenen Abstand mit den von dir festgelegten Schwellen vergleicht. Für das Handeln sorgen der L298N und die Motoren. Weil der Sensor auf einem Servo sitzt, kann der Roboter nach links und rechts "schauen", ohne seinen Körper zu drehen, und trifft dadurch viel bessere Lenkentscheidungen als ein Roboter mit festem Sensor. Wie der Sensor im Detail funktioniert, erfährst du im Artikel HC-SR04 Abstandssensor.
| Bauteil | Aufgabe | Hinweis |
|---|---|---|
| Arduino Uno (oder Nano) | Gehirn: misst, entscheidet, steuert die Motoren | ESP32 oder Pico gehen auch |
| HC-SR04 | Auge: misst den Abstand zum Hindernis davor | Läuft mit 5 V, 2–400 cm |
| SG90-Servo | Hals: dreht den Sensor nach links und rechts | Der Sensor wird mit einem Kunststoffhalter am Servohorn befestigt |
| L298N oder L293D-Shield | Muskel: gibt den Motoren Richtung und Tempo | Ein Arduino-Pin liefert keinen Motorstrom |
| 2 TT-Getriebemotoren und Stützrad | Differentialantrieb | Gelenkt wird, indem sich beide Räder gegenläufig drehen |
| 2 × 18650-Akku (7,4 V) | Energie | Ein 9-V-Block reicht nicht |
| 2 IR-Hindernissensoren (optional) | Erkennen Hindernisse an Ecken und Seiten | Module mit LM393 wie der FC-51 |
| HC-05 (optional) | Manuelle Steuerung per Handy | Funktioniert nur mit Android |




Stromversorgung: Damit der Servo keinen Reset auslöst
Weil beim hindernisvermeidenden Roboter zu den Motoren noch ein Servo kommt, ist die Stromverteilung wichtiger als bei einem gewöhnlichen Roboterauto. Ein SG90 zieht im Stillstand etwa 10 mA; sobald er sich bewegt, steigt der Strom auf 100–250 mA, unter Last nähert er sich einem halben Ampere. Fällt diese Spitze mit dem Anlaufstrom der Motoren zusammen, bricht die 5-V-Schiene des Arduino kurz ein und das Board startet neu. Die Lösung ist einfach: Die Motoren laufen über den L298N am Akku, der Arduino über VIN am Akku, und Servo, HC-SR04, IR-Sensoren und HC-05 teilen sich den 5V-Pin des Arduino; direkt an die Versorgungsleitungen des Servos kommt ein 470-µF-Kondensator. Selbst wenn sich der Servo bewegt, bleibt die Gesamtlast auf der 5-V-Schiene unter einem halben Ampere, und weil sie nur kurz anliegt, schafft das der Spannungsregler des Uno. Um die Akkulaufzeit mit deinen eigenen Werten zu berechnen, nutze das Werkzeug Akkulaufzeit.
| Teil | Versorgt über | Typischer Strom |
|---|---|---|
| 2 TT-Motoren | L298N-Klemme 12V ← 2 × 18650 (7,4 V) | 0,3–0,6 A beim Fahren, bis zu 2 A beim Anfahren |
| Arduino Uno | VIN ← Pluspol des Akkus (nach dem Schalter) | Etwa 50 mA |
| SG90-Servo | Arduino 5V + 470-µF-Kondensator | ~10 mA im Stillstand, 100–250 mA in Bewegung |
| HC-SR04 | Arduino 5V | Etwa 15 mA |
| 2 IR-Sensoren | Arduino 5V | Je 20–40 mA |
| HC-05 | Arduino 5V | 30–40 mA beim Koppeln, etwa 10 mA verbunden |
Schaltplan: Arduino Uno, ESP32 und Pico
Die Motorpins sind dieselben wie in der Anleitung zum Roboterauto; so kannst du zu diesem Projekt wechseln, indem du nur den Sensorkopf ergänzt, ohne dein Auto zu zerlegen. Dass die Geschwindigkeitspins auf 5 und 6 liegen, ist Absicht: Auf dem Uno nutzt die Servo-Bibliothek Timer1 und schaltet dadurch PWM an den Pins 9 und 10 ab; lägen die Motorgeschwindigkeiten dort, wäre die Drehzahlregelung kaputt, sobald der Servo angeschlossen ist. HC-SR04 und IR-Sensoren kommen an A0–A3. Die Analogpins des Uno arbeiten bei Bedarf auch als Digitalpins 14–19, und diese Wahl erleichtert es, denselben Code mit einem L293D-Shield zu verwenden, denn das Shield nutzt diese Pins nicht. Die Pinbelegung deines Boards siehst du im Werkzeug Pinout.
| Verbindung | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| L298N ENA (Tempo linker Motor) | D5 | GPIO 25 | GP15 |
| L298N IN1, IN2 | D7, D8 | GPIO 27, 33 | GP14, GP13 |
| L298N ENB (Tempo rechter Motor) | D6 | GPIO 26 | GP10 |
| L298N IN3, IN4 | D9, D4 | GPIO 32, 4 | GP12, GP11 |
| Servo-Signal (orange) | D3 | GPIO 13 | GP16 |
| Servo + (rot), − (braun) | 5V, GND | L298N 5V, GND | L298N 5V, GND |
| HC-SR04 TRIG | A0 | GPIO 18 | GP17 |
| HC-SR04 ECHO | A1 (direkt) | GPIO 19, über 1-kΩ/2-kΩ-Teiler | GP18, über 1-kΩ/2-kΩ-Teiler |
| HC-SR04 VCC | 5V | L298N 5V | L298N 5V |
| IR links OUT, IR rechts OUT | A2, A3 | GPIO 23, 22 | GP19, GP20 |
| IR-Sensor VCC | 5V | 3V3 | 3V3 |
| HC-05 TXD, RXD | D10, D11 (RXD über Teiler) | Nicht nötig (Bluetooth eingebaut) | GP1, GP0 (UART0) |
| Versorgung des Boards | VIN ← Akku (nach dem Schalter) | 5V (VIN) ← L298N 5V | VSYS ← L298N 5V |
Sensorkopf montieren und testen

- Schließe den Servo ohne Horn an und lade den Test-Sketch unten hoch. Am Ende jedes Schwenks fährt der Code den Servo auf 90 Grad und wartet 2 Sekunden; zieh in dieser Pause das USB-Kabel ab, der Servo bleibt stehen. Steck das Horn so auf die Welle, dass der Sensor genau nach vorn schaut; so bedeutet 90 Grad "geradeaus".
- Befestige den HC-SR04 mit einem Kunststoffhalter oder Heißkleber am Horn. Die Sensorkabel müssen lang genug sein, um sich beim Drehen des Servos nicht zu spannen; ein Kabel, das beim 140-Grad-Schwenk am Chassis hängen bleibt, ist ein häufiger Fehler.
- Montiere den Sensor 8–12 cm über dem Boden und nahe am vordersten Punkt des Gehäuses. Zu tief empfängt er Echos vom Boden und von der Teppichstruktur, zu hoch sieht er niedrige Hindernisse nicht.
- Neige den Sensor um 3–5 Grad nach oben. Ein ganz waagerechter, tief sitzender Sensor kann besonders auf rauen Böden ein Echo vom Boden für ein Hindernis halten.
- Öffne den Seriellen Monitor mit 9600 Baud. Siehst du kleine Werte in den Zeilen für 125 und 160 Grad, wenn du die Hand links neben den Sensor hältst, liegt links bei den großen Winkeln; ist es umgekehrt, setze im erweiterten Sketch SERVO_DIR auf -1 und vertausche im ersten Sketch die Werte von LEFT_ANGLE und RIGHT_ANGLE.
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}Jede Zeile zeigt einen Winkel und den Abstand in diese Richtung. Ist im Umkreis von etwa 4 Metern vor dem Sensor nichts, liest du 0 cm: pulseIn() liefert 0, wenn innerhalb von 25 ms kein Echo kommt. Im Hauptcode werten wir 0 als "Weg frei", aber auch ein Sensor mit losem Kabel oder vertauschten TRIG- und ECHO-Pins liest meist 0. Halte deshalb deine Hand auf 10, 30 und 60 cm, bevor du den Roboter auf den Boden stellst, und prüfe, ob sich die Werte wirklich ändern; ein ungetesteter Roboter fährt ungebremst gegen die Wand.
Der Entscheidungsalgorithmus: anhalten, zurücksetzen, in die freieste Richtung drehen
Das Verhalten des Roboters nach Abstand in Zonen aufzuteilen, macht den Code einfach und zuverlässig. Ohne Hindernis fährt er mit voller Geschwindigkeit, kommt ein Hindernis näher, wird er langsamer, und an der Stoppschwelle hält er an und scannt. Beim Scannen schaut der Sensor 70 und 35 Grad nach links und 35 und 70 Grad nach rechts; in jede Richtung werden drei Messungen gemacht und deren Median verwendet. Der Roboter dreht sich um genau diesen Winkel in die Richtung mit dem größten Abstand. Ist keine Richtung frei, etwa weil er in eine Ecke gefahren ist, setzt er ein Stück zurück und dreht um 180 Grad.
| Gemessener Abstand | Zone | Was der Roboter tut |
|---|---|---|
| Über 60 cm (oder kein Echo) | Frei | Volle Fahrt voraus (PWM 200) |
| 25–60 cm | Annäherung | Wird abstandsabhängig langsamer (PWM 200 bis 130) |
| 12–25 cm | Hindernis | Hält an, bestätigt die Messung, scannt beide Seiten, dreht in die freieste Richtung |
| Unter 12 cm | Zu nah | Setzt erst 350 ms zurück, dann scannt er |
| Alle Richtungen unter 35 cm | Sackgasse | Setzt zurück und dreht um 180 Grad |
Diese Schwellen sind nicht zufällig gewählt. Der Anhalteabstand ist die Summe aus drei Reserven: der Reaktionsreserve (Weg zwischen zwei Messungen), der Bremsreserve (wie weit der Roboter nach dem Abschalten der Motoren rollt) und der Drehreserve (damit seine Nase beim Drehen auf der Stelle das Hindernis nicht berührt). Ein Auto mit TT-Motoren an 6 V fährt etwa 0,5–0,7 m/s und legt in einem Messintervall von 60 ms 3–4 cm zurück. Die Bremsreserve beträgt etwa 5 cm, bei einem 15 cm breiten Gehäuse, das auf der Stelle dreht, liegt die Drehreserve um 10 cm. Zusammen ergibt das etwa 18–20 cm; mit ein paar Zentimetern Sicherheitsreserve obendrauf ist STOP_CM = 25 ein guter Startwert. Ist dein Roboter schneller oder der Boden rutschig, erhöhe die Schwelle auf 30–35 cm. In engen Fluren führt eine zu große Schwelle dagegen dazu, dass der Roboter ständig anhält und scannt; die Feinabstimmung machst du am besten auf deiner eigenen Strecke.
Code Schritt für Schritt 1: dein erster hindernisvermeidender Roboter
Die erste Version ist das Gerüst des Algorithmus: vorwärts fahren, bei 25 cm anhalten, zurücksetzen, nach links und rechts schauen, zur freieren Seite drehen. Die Motorfunktion drive(links, rechts) ist dieselbe wie in der Anleitung zum Roboterauto und nimmt Werte von -255 bis 255 an; positiv heißt vorwärts, negativ rückwärts. Stell das Auto nach dem Hochladen zuerst so auf eine Kiste, dass die Räder frei drehen, und beobachte, wie es anhält und dreht, wenn du die Hand dem Sensor näherst; passt alles, stell den Roboter auf den Boden.
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}Dieser Code erfüllt seinen Zweck, hat aber einige Schwächen: Eine einzige Fehlmessung lässt den Roboter grundlos anhalten, er nähert sich Hindernissen immer mit demselben Tempo, schaut nur in zwei Richtungen, und seine Drehzeiten hängen nicht vom Winkel ab. Und weil er mit delay() wartet, kann er während des Scannens auf nichts reagieren. Die Einzelheiten zum Warten findest du im Artikel millis() und delay(); die nächste Version behebt die meisten dieser Probleme.
Code Schritt für Schritt 2: Filter, sanftes Abbremsen, IR und Bluetooth
Die erweiterte Version funktioniert mit derselben Verdrahtung, und es macht nichts, wenn du IR-Sensoren und HC-05 noch nicht angeschlossen hast. Weil die IR-Eingänge mit INPUT_PULLUP eingerichtet sind, liest ein nicht angeschlossener Sensor "kein Hindernis" (HIGH); ohne HC-05 startet der Roboter einfach im Automatikmodus. Was die einzelnen Teile des Codes tun, steht in der Tabelle unten.
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}| Funktion oder Einstellung | Was sie macht |
|---|---|
| pingCm() | Macht eine Messung; liefert 400 cm (freier Raum), wenn innerhalb von 25 ms kein Echo kommt |
| distanceCm() | Macht 3 Messungen im Abstand von 60 ms und liefert den Median; ein einzelner Ausreißer fällt heraus |
| autoStep() | Misst alle 60 ms; fährt je nach Abstand und IR-Sensoren weiter, bremst, weicht aus oder ruft avoid() auf |
| avoid() | Hält an, bestätigt die Messung, setzt bei Bedarf zurück, schaut in 4 Richtungen und dreht in die freieste |
| turnBy(Grad) | Dreht den Roboter um den angegebenen Winkel auf der Stelle; die Zeit wird mit MS_PER_DEG berechnet |
| manualStep() | Setzt die Richtung vom Handy um; hält bei Vorwärtsfahrt bei 15 cm an und stoppt, wenn 600 ms kein Befehl kommt |
| SERVO_CENTER, SERVO_DIR | Der Winkel, bei dem der Sensor genau nach vorn schaut, und die Richtung für links |
| STOP_CM, SLOW_CM, BACK_CM | Schwellen zum Anhalten, Abbremsen und Zurücksetzen |
Für einen hindernisvermeidenden Roboter ist ein Medianfilter besser als ein Mittelwert: Bei drei Messungen wie 24, 26 und 400 cm ergibt der Mittelwert 150 cm und führt den Roboter in die Irre, der Median wählt dagegen 26 cm. Wenn du tiefer einsteigen willst, lies den Artikel Sensordaten filtern. Die Trennung zwischen Automatik- und manuellem Modus ist eigentlich auch ein kleiner Zustandsautomat; diesen Ansatz erklären wir ausführlich in der Anleitung Arduino State Machine. Stimme den Roboter nach dem Hochladen in dieser Reihenfolge auf deinen Raum ab:
- Servomitte: Steht der Roboter rechtwinklig vor einer glatten Wand und der Sensor zeigt nicht genau darauf, ändere SERVO_CENTER in kleinen Schritten, etwa auf 85 oder 95.
- Drehzeit: Füge am Ende von setup() vorübergehend die Zeile turnBy(360); ein und beobachte, ob der Roboter eine volle Umdrehung macht. Dreht er 300 statt 360 Grad, setze MS_PER_DEG auf 4,0 × 360 / 300 = 4,8. Weil sich dieser Wert mit Akku und Boden ändert, stimme ihn mit vollem Akku auf dem Boden ab, auf dem der Roboter fahren soll.
- Stoppschwelle: Lass den Roboter auf eine Wand zufahren und miss, wie weit seine Nase dort, wo er anhält, von der Wand entfernt ist. Sind es weniger als 10 cm, erhöhe STOP_CM um 5 cm.
- Langsames Tempo: Liegt SLOW unter der Anlaufschwelle der Motoren, bleibt der Roboter in der Annäherungszone stehen; erhöhe SLOW dann auf 150.
Tote Winkel des HC-SR04: weiche Oberflächen, schräge Wände und dünne Hindernisse
Ein Ultraschallsensor sieht Oberflächen gut, die Schall zurückwerfen, aber nicht jedes Hindernis schickt den Schall zum Sensor zurück. Der Hersteller empfiehlt für gute Ergebnisse ein Ziel von mindestens 0,5 m², das möglichst glatt ist. In einem echten Raum trifft der Roboter dagegen auf Vorhänge, Sofas, Stuhlbeine und Zimmerecken. Schall prallt wie eine Billardkugel im selben Winkel ab, in dem er ankommt: Ein Sensor, der senkrecht auf eine Wand schaut, bekommt ein starkes Echo, weicht seine Blickrichtung aber um mehr als etwa 30–40 Grad von der Senkrechten ab, wird der Schall weggelenkt und kommt nie zurück. Dann liefert pulseIn() 0 und der Code hält den Weg für frei. Das ist der häufigste Grund, warum Roboter an Wänden entlangschrammen.
| Hindernis oder Oberfläche | Warum es nicht erkannt wird | Lösung |
|---|---|---|
| Vorhänge, Sofas, Kissen, Kleidung, Tierfell | Weiche, poröse Oberflächen schlucken den Schall, das Echo kommt sehr schwach zurück | IR-Sensor für kurze Distanz ergänzen, Roboter langsam fahren |
| Schräge Annäherung an eine Wand | Der Schall wird weggelenkt, das Echo erreicht den Sensor nicht | Nach außen geneigte IR-Sensoren an den vorderen Ecken, engere Scanwinkel |
| Stuhl- und Tischbeine, dünne Stangen | Die reflektierende Fläche ist winzig, das Echo schwach | Tempo senken, Medianfilter nutzen, seitlich IR-Sensoren ergänzen |
| Niedrige Hindernisse unter dem Sensor (Kabel, Türschwellen, Hausschuhe) | Sie liegen unter dem Schallkegel | Nach unten gerichteten IR-Sensor oder Mikroschalter an der Frontstoßstange |
| Tischkanten, Treppenabsätze | Der Ultraschallsensor schaut nach vorn und sieht keine Lücke im Boden | Absturzerkennung mit einem zum Boden gerichteten TCRT5000 |
| Gegenstände näher als 2 cm | Totzone des Sensors: Das Echo kommt zurück, bevor der Schallimpuls beendet ist | Roboter so einstellen, dass er bei 12 cm zurücksetzt |
| Ecken und enge Flure | Der Schall prallt zwischen zwei Wänden hin und her und kommt spät zurück, der Abstand wirkt größer | Medianfilter und Messungen aus mehreren Winkeln |
Verstärkung mit IR-Sensoren
Ultraschall- und Infrarotsensoren (IR) gleichen gegenseitig ihre Schwächen aus. IR-Hindernissensoren wie der FC-51 bestehen aus einer Infrarot-LED und einem Empfänger; ist das reflektierte Licht stark genug, zieht der Komparator LM393 den Ausgang auf LOW und die LED auf der Platine leuchtet. Die Reichweite lässt sich per Trimmpoti von etwa 2 bis 30 cm einstellen, aber der Sensor misst keinen Abstand, er meldet nur "Hindernis" oder "kein Hindernis". Setzt du zwei IR-Sensoren an die vorderen Ecken des Roboters, um 30–45 Grad nach außen geneigt, erfassen sie Stuhlbeine und schräge Wände außerhalb des schmalen Ultraschallkegels. Im erweiterten Code weicht der Roboter kurz nach rechts aus, wenn der linke IR-Sensor auslöst, und nach links, wenn der rechte auslöst; lösen beide gleichzeitig aus, hält er an und scannt, als stünde ein Hindernis vor ihm.
| Eigenschaft | HC-SR04 (Ultraschall) | FC-51 (IR-Hindernis) | TCRT5000 (IR-Reflexion) |
|---|---|---|---|
| Ausgabe | Abstand (cm) | Ja / nein (digital) | Analogwert und Digitalausgang |
| Reichweite | 2–400 cm | Etwa 2–30 cm (einstellbar) | Etwa 1–15 mm |
| Erfassungswinkel | Etwa 15° | Etwa 35° | Sehr schmal |
| Schwachstelle | Weiche, schräge und dünne Oberflächen | Schwarze, matte Flächen, Sonnenlicht | Höhenänderungen, Sonnenlicht |
| Sieht gut | Glas, schwarze Gegenstände, große glatte Flächen | Stoff, Ecken, nahe Hindernisse | Bodenfarbe und Tischkanten |
| Platz am Roboter | Hauptauge, vom Servo geschwenkt | Vordere Ecken | Unter der Frontstoßstange, zum Boden gerichtet |

Für Roboter, die auf einem Tisch unterwegs sind, ist das gefährlichste Hindernis eigentlich ein Abgrund. Baust du ein zum Boden gerichtetes TCRT5000-Modul unter die Frontstoßstange, merkst du sofort, wenn der Boden darunter verschwindet. Verbinde den Digitalausgang des Moduls mit D2, der am Uno noch frei ist; prüfe zuerst im Seriellen Monitor, welchen Pegel der Ausgang ohne Boden annimmt, lies dann diesen Pin ganz am Anfang von autoStep() und stoppe bei fehlendem Boden sofort die Motoren und setze zurück. Diese Prüfung muss vor der Abstandsmessung kommen; ein Absturz ist immer teurer als ein Zusammenstoß.
Per Bluetooth in den manuellen Modus wechseln
Manchmal willst du den autonomen Roboter von Hand fahren: um ihn von einem Zimmer ins andere zu bringen, ihn vor ein Hindernis zu stellen und seine Reaktion zu testen oder einfach zum Spielen. Die HC-05-Verbindung im erweiterten Code macht das möglich. Sendet das Handy den Buchstaben A, wechselt der Roboter in den Automatikmodus, bei M in den manuellen Modus. Im manuellen Modus gelten die Steuerbuchstaben aus der Anleitung zum Roboterauto (F, B, L, R, S), mit einem Unterschied: Sieht der Roboter bei Vorwärtsfahrt ein Hindernis näher als 15 cm, hält er trotz deines Befehls an. Dieser Kollisionsschutz lässt dich die Sensoren des autonomen Roboters auch beim manuellen Fahren nutzen. Kopplung und AT-Befehle des HC-05 findest du im Artikel HC-05 Bluetooth-Modul.
| Buchstabe | Funktion | In welchem Modus? |
|---|---|---|
| A | In den Automatikmodus wechseln | In beiden Modi |
| M | In den manuellen Modus wechseln, Roboter anhalten | In beiden Modi |
| F / B | Vorwärts / rückwärts | Manuell |
| L / R | Auf der Stelle links / rechts drehen | Manuell |
| S | Anhalten | Manuell |
| 0–9 | Geschwindigkeitsstufe (PWM 100–240) | Manuell |
- Verbinde den TXD-Pin des HC-05 mit D10, den RXD-Pin über einen 1-kΩ/2-kΩ-Teiler mit D11, VCC mit 5V und GND mit GND.
- Kopple den HC-05 in den Bluetooth-Einstellungen deines Android-Handys mit der PIN 1234 (oder 0000, falls das nicht klappt).
- Installiere aus dem Play Store eine Bluetooth-Auto-Fernsteuerungs-App und verbinde sie mit dem HC-05. Lässt du den Arduino am USB und öffnest den Seriellen Monitor mit 9600 Baud, gibt der Code jeden Buchstaben aus, der vom Handy kommt.
- Die meisten Fernsteuerungs-Apps haben Zusatztasten wie Licht oder Hupe, und jede sendet einen eigenen Buchstaben. Lies diese Buchstaben im Seriellen Monitor ab und setze sie im Code anstelle von 'A' und 'M' ein; willst du die Tasten selbst festlegen, kannst du mit der Anleitung App Inventor Arduino Bluetooth deine eigene Fernsteuerung bauen.
- Zieh das USB-Kabel ab und leg den Schalter um; nach 3 Sekunden fährt der Roboter im Automatikmodus los. Wechsle in den manuellen Modus und steuere ihn per Handy.
Aufbau mit einem L293D-Motor-Shield

Wenn du keinen Kabelsalat magst, kannst du statt des L298N ein L293D-Motor-Shield (Adafruit Motor Shield V1 und seine Klone) verwenden, das direkt auf den Arduino Uno gesteckt wird. Das Shield kann vier Gleichstrommotoren treiben, hat zwei Servo-Anschlüsse und versorgt die Servos aus der 5-V-Schiene des Arduino. Die Motoren hängen nicht direkt an Arduino-Pins, sondern am Schieberegister 74HC595 auf der Platine; deshalb steuerst du sie mit der Adafruit Motor Shield library (der für V1, nicht V2), also AFMotor, die du über den Bibliotheksverwalter installierst. Das Shield belegt D3, D4, D5, D6, D7, D8, D11 und D12 für die Motoren und D9 und D10 für die Servos; A0–A5, D2 und D13 bleiben frei. Da HC-SR04 und IR-Sensoren schon an A0–A3 hängen, musst du nur den Motorcode, den Servo-Pin und die HC-05-Pins ändern.
| Einstellung | Mit L298N | Mit L293D-Shield |
|---|---|---|
| Motoranschluss | Klemmen OUT1–OUT4 | Klemmen M1 (links) und M2 (rechts) |
| Motorcode | digitalWrite und analogWrite in drive() | AFMotor in drive() |
| SERVO_PIN | 3 | 9 oder 10, je nach Anschluss, an dem der Servo steckt |
| HC-05 | D10, D11 | A4 (RX) und A5 (TX): SoftwareSerial bt(A4, A5) |
| Strom pro Kanal | 2 A dauerhaft | 0,6 A dauerhaft, 1,2 A Spitze |
| Motorversorgung | Klemme 12V | Klemme EXT_PWR; mit gestecktem PWR-Jumper läuft auch der Arduino an derselben Quelle |
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}Die Grenze von 0,6 A pro Kanal reicht beim L293D für normales Fahren eines 2WD-Roboters mit TT-Motoren, aber wenn ein Rad gegen ein Hindernis drückt und blockiert, kann der Motor über 1 A ziehen und der Chip wird schnell heiß. Ein Scan-Algorithmus, der den Roboter anhält, bevor er gegen ein Hindernis drückt, verlängert deshalb auch die Lebensdauer des Shields. Die Jumper-Einstellungen des Shields und einen Sketch für ein Auto mit vier Motoren findest du im Artikel L293D Motor Shield, den inneren Aufbau des L298N in der Anleitung Gleichstrommotor mit dem L298N steuern.
Hindernisvermeidender Roboter mit dem ESP32
Beim ESP32 gibt es zwei wichtige Unterschiede. Erstens funktioniert die Servo-Bibliothek des Arduino auf dem ESP32 nicht; wir erzeugen das Servosignal deshalb direkt mit LEDC, der PWM-Einheit des Boards: Bei einem 50-Hz-Signal entspricht ein Impuls von 0,544–2,4 ms 0–180 Grad. Weil die Motoren mit 1 kHz und der Servo mit 50 Hz laufen, werden zuerst die Motorkanäle und danach der Servokanal eingerichtet; so bekommt der Servo seinen eigenen Timer. Zweitens hat der ESP32 klassisches Bluetooth eingebaut, ein HC-05 ist also überflüssig; der Roboter erscheint auf dem Handy als "BlueGrays-Robot", und die Richtungs- und Modusbuchstaben sind dieselben wie in der Uno-Version. Der Code ist für Version 3.x des ESP32-Arduino-Cores geschrieben (ledcAttach und ledcWrite arbeiten mit Pinnummern) und lässt sich, weil er klassisches Bluetooth braucht, für ESP32-S3 und ESP32-C3 nicht kompilieren.
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}Hindernisvermeidender Roboter mit dem Raspberry Pi Pico (MicroPython)
Auf dem Raspberry Pi Pico funktioniert die MicroPython-Funktion time_pulse_us() wie pulseIn() beim Arduino; kommt kein Echo, liefert sie einen negativen Wert, den wir als 400 cm, also freien Raum, werten. Für den Servo nutzen wir 50-Hz-PWM und duty_ns(), das die Impulsbreite in Nanosekunden erwartet. Der Servo an GP16 und die Motor-Geschwindigkeitspins an GP15 und GP10 liegen auf verschiedenen PWM-Slices und stören sich deshalb nicht in der Frequenz. Die Geschwindigkeiten werden diesmal in Prozent angegeben. Der Pico hat kein Bluetooth; willst du den manuellen Modus, schließe einen HC-05 wie in der Anleitung zum Roboterauto an UART0 (GP0, GP1) an.
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 readingsProjektideen mit dem hindernisvermeidenden Roboter
- Radaranzeige: Schick die Winkel und Abstände aus dem Scan über die serielle Schnittstelle und zeichne am Computer einen grünen Radarbildschirm; Details im Artikel Arduino-Radar-Projekt.
- Linie und Hindernis: Lass den Roboter wie einen Linienfolger-Roboter einer Strecke folgen, um eine Kiste auf dem Weg herumfahren und zur Linie zurückkehren; eine typische Wettbewerbsaufgabe.
- Wandfolger: Dreh den Sensor dauerhaft um 60 Grad nach rechts und fahr in konstant 20 cm Abstand an der Wand entlang; der erste Schritt zum Lösen von Labyrinthen.
- Roboter mit Display: Zeig Abstand und Modus auf einem kleinen OLED-Display an; die Fehlersuche wird viel einfacher.
- Saugroboter-Prototyp: Entwirf einen Putzroboter, der mit zufälliger Richtungswahl, Stoßschaltern und Absturzsensor durch die Wohnung fährt.
- Intelligenter Blindenstock: Mach aus derselben Abstandsmessung eine Vibrationswarnung für sehbehinderte Menschen; siehe Arduino-Blindenstock.
- Schul- oder Wettbewerbsprojekt: Präsentiere ihn mit Hindernisparcours, Zeitmessung und einer Vorführung von Automatik- und manuellem Modus; Ideen findest du im Artikel Arduino-Projektideen für Wettbewerbe.
Häufige Fehler und Lösungen
| Problem | Wahrscheinliche Ursache | Lösung |
|---|---|---|
| Der Roboter fährt ungebremst gegen die Wand | Der Sensor liest 0 (Kabelbruch, TRIG und ECHO vertauscht) und der Code wertet das als freien Raum | Test-Sketch ausführen, mit der Hand 10–60 cm prüfen, TRIG- und ECHO-Pins kontrollieren |
| Er schrammt an der Wand, wenn er schräg ankommt | Der Schall wird von der schrägen Fläche weggelenkt | IR-Sensoren an den vorderen Ecken ergänzen, Tempo senken |
| Der Arduino startet neu, wenn er zum Scannen anhält | Servo- und Motorstrom lassen die 5-V-Schiene einbrechen | 470-µF-Kondensator am Servo, volle 18650-Akkus; notfalls Servo aus separater 5-V-Quelle versorgen |
| Der Roboter hält grundlos an und scannt | Eine einzelne Fehlmessung oder ein Echo vom Boden | Sensor auf 8–12 cm Höhe setzen und leicht nach oben neigen; Median-Bestätigung nutzen |
| Der Roboter dreht zur blockierten Seite | Servo andersherum montiert, links und rechts vertauscht | SERVO_DIR auf -1 setzen |
| Drehungen sind viel zu kurz oder zu lang | MS_PER_DEG nicht kalibriert oder Akku schwach | Mit turnBy(360) messen und mit vollem Akku neu einstellen |
| Er dreht in einer Ecke ständig hin und her | Beide Seiten liegen nahe an der Schwelle, die Entscheidung kippt ständig | Sackgassen-Schwelle (STOP_CM + 10) erhöhen oder die zuletzt gewählte Drehrichtung bevorzugen |
| Der Sensorkopf zuckt | Schwache Servoversorgung oder SoftwareSerial-Interrupts | Servokabel kürzen, Kondensator ergänzen, nicht unnötig oft Bluetooth-Befehle senden |
Weil er Sensoren auslesen, Filtern, Entscheiden und Motorsteuerung in einem Projekt vereint, ist der hindernisvermeidende Roboter der beste Einstieg in die Robotik. Beobachte zuerst mit dem ersten Sketch das Grundverhalten, stimme dann mit der erweiterten Version die Schwellen auf deinen Raum ab und rüste deinen Roboter zum Schluss mit IR-Sensoren und Bluetooth auf. In der BlueGrays-App findest du den hindernisvermeidenden Roboter, das per Bluetooth gesteuerte Auto mit L298N, Abstandsmessung mit dem HC-SR04 und Einparkhilfe als Projekte Schritt für Schritt mit Schaltplänen und Code.
Häufige Fragen
Was braucht man für einen Roboter, der Hindernissen ausweicht?
Für einen einfachen hindernisvermeidenden Roboter brauchst du einen Arduino Uno, einen HC-SR04-Ultraschallsensor, einen SG90-Servo, einen L298N- oder L293D-Motortreiber, einen 2WD-Chassis-Bausatz mit zwei Getriebemotoren und zwei 18650-Akkus. Zwei IR-Hindernissensoren für die Ecken und ein HC-05-Bluetooth-Modul für die Steuerung per Handy kannst du ergänzen.
Geht ein hindernisvermeidender Roboter auch ohne Servo?
Ja. Der Sensor schaut fest nach vorn; sieht der Roboter ein Hindernis, dreht er sich zuerst in eine Richtung und misst erneut, ist der Weg weiter versperrt, dreht er in die andere. Das ist günstiger, aber der Roboter entscheidet langsamer, weil er für jeden Versuch seinen ganzen Körper drehen muss; mit einem Servo findet er die freieste Richtung schon vor dem Drehen.
Warum erkennt der HC-SR04 manche Hindernisse nicht?
Ein Ultraschallsensor misst über das Echo. Weiche Oberflächen wie Vorhänge und Sofas schlucken den Schall, schräge Wände lenken ihn in eine andere Richtung, und dünne Gegenstände wie Stuhlbeine liefern nur ein sehr schwaches Echo. Hindernisse außerhalb oder unterhalb seines etwa 15 Grad breiten Kegels sieht der Sensor ebenfalls nicht; diese Lücken schließt du mit IR-Sensoren.
Bei welchem Abstand sollte der Roboter anhalten?
25 cm sind ein guter Startwert: Weg zwischen zwei Messungen, Bremsweg und die Reserve zum Drehen auf der Stelle ergeben zusammen etwa 20 cm, dazu kommt eine kleine Sicherheitsreserve. Bei schnelleren Robotern und rutschigem Boden erhöhst du auf 30–35 cm; ist ein Hindernis näher als 12 cm, sollte der Roboter zuerst zurücksetzen.
Was ist besser für einen Roboter, L298N oder L293D-Shield?
Der L298N liefert 2 A pro Kanal, ist dadurch robuster und lässt dich die Pins frei wählen. Das L293D-Shield wird auf den Uno gesteckt, spart Verdrahtung und kann vier Motoren treiben, liefert aber dauerhaft nur 0,6 A pro Kanal und belegt viele Pins. Für einen kleinen 2WD-Roboter mit TT-Motoren reichen beide.
Kann man einen hindernisvermeidenden Roboter per Handy steuern?
Ja. Ergänze ein HC-05-Bluetooth-Modul und sende Buchstaben aus einer Fernsteuerungs-App auf einem Android-Handy, um zwischen Automatik- und manuellem Modus zu wechseln. Nutzt du den Ultraschallsensor im manuellen Modus als Kollisionsschutz, kannst du den Roboter nicht gegen die Wand fahren. Mit einem ESP32 brauchst du dank eingebautem Bluetooth kein separates Modul.
Projekte, die du danach bauen kannst
Alle Projekte ansehen
HC-SR04 Abstandsmessung (serielle Anzeige)
Der Sensor HC-SR04 funktioniert wie eine Fledermaus, die im Dunkeln ihren Weg findet: Er sendet einen Ton, den du nicht hörst, lauscht auf das Echo und misst, wie weit der Gegenstand vor ihm entfernt

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

Bluetooth-gesteuertes Auto mit L298N
Mach dein Handy zur Fernbedienung und steuere dein eigenes Roboterauto. Der Arduino empfängt über ein Bluetooth-Modul die Befehle einer Fernsteuer-App auf dem Handy und treibt die Räder über einen Mot

Verwendung von Servomotoren
Der Arm eines kleinen Servomotors dreht sich nacheinander in drei Stellungen: ganz an den Anfang, genau in die Mitte und ganz ans Ende, mit jeweils zwei Sekunden Pause. Der Arduino sagt dem Motor, bei

Parksensor mit HC-SR04
Eine Mini-Ausgabe der Einparkhilfe im Auto: Je näher etwas kommt, desto schneller piept es. Der Abstandssensor misst das Hindernis vor ihm; ist es weiter als 20 Zentimeter entfernt, leuchtet die grüne

Reflexion (Nähe) mit TCRT5000 messen
Mit einem kleinen Sensor, der unsichtbares Licht aussendet und misst, wie viel davon zurückkommt, erkennst du die Fläche vor ihm. Je näher ein Gegenstand oder eine helle Fläche kommt, desto mehr Licht

TCRT5000 Schwarz-Weiß-Untergrunderkennung
Mit dem kleinen Sensor, der bei Linienfolger-Robotern als Auge dient, erkennst du, ob der Boden schwarz oder weiß ist. Der TCRT5000 schickt unsichtbares Licht auf den Boden und prüft, wie viel davon z

ESP32 HC-SR04 Ultraschall-Entfernungsmesser
Der Ultraschallsensor HC-SR04 berechnet die Entfernung, indem er die Zeit misst, die die von ihm ausgesendete Schallwelle benötigt, um auf ein Objekt zu treffen und zurückzukehren (Time of Flight). In diesem Projekt senden wir mit dem ESP32 einen 10-Mikrosekunden-Impuls an den TRIG-Pin und lesen die vom ECHO-Pin kommende Echozeit mit `pulseIn()`. Die berechnete Entfernung geben wir in cm auf einem 16x2-I2C-LCD aus. Da der HC-SR04 mit 5 V betrieben wird, ist ein **Spannungsteiler** am ECHO-Pin zw
Weitere Artikel
BlueGrays-App
Diese Schaltung Schritt für Schritt auf dem Handy nachbauen
Schaltplan, Code und Teileliste auf einem Bildschirm. Über 160 Arduino-, ESP32- und Pico-Projekte zum Nachbauen.
Mit dem Handy scannen
Richte die Kamera auf den Code, dann öffnet sich der App-Store deines Handys.
oder im Store öffnen

