Robot evasor de obstáculos con Arduino: HC-SR04 y servo
Robot evasor de obstáculos con Arduino: HC-SR04 sobre un servo, conexión del L298N o L293D, lógica parar-retroceder-girar, sensores IR y modo manual Bluetooth.
Un robot evasor de obstáculos es un coche robot autónomo que detecta una pared, una silla o una caja delante de él sin chocar y busca un camino nuevo. El montaje más habitual usa un Arduino Uno, un sensor ultrasónico HC-SR04, un servomotor SG90 que gira el sensor a izquierda y derecha, dos motorreductores de corriente continua y un driver de motores L298N (o L293D). El robot avanza en línea recta, se detiene al acercarse a un obstáculo, retrocede un poco, gira la cabeza hacia ambos lados para medir y sigue su camino hacia la dirección más despejada.
En esta guía convertimos el coche 2WD de nuestro artículo coche robot con Arduino en un robot autónomo manteniendo el mismo patillaje. Paso a paso veremos las tablas de conexión, cómo se calculan los umbrales de distancia, un código que crece en tres etapas, las superficies que el sensor no ve y cómo cubrir esos huecos con sensores infrarrojos y, por último, cómo pasar a modo manual por Bluetooth desde el móvil. Al final del artículo tienes también el código para manejar el mismo robot con un ESP32 o una Raspberry Pi Pico.

¿Cómo funciona un robot evasor de obstáculos?
Todo robot autónomo repite el mismo ciclo de tres pasos decenas de veces por segundo: percibir, decidir, actuar. La percepción la hace el HC-SR04: emite un sonido corto de 40 kHz que tu oído no puede oír y mide cuánto tarda en volver el eco; si divides ese tiempo en microsegundos entre 58, obtienes la distancia en centímetros. La decisión la toma el Arduino comparando la distancia medida con los umbrales que tú definas. De la acción se encargan el L298N y los motores. Como el sensor va montado sobre un servo, el robot puede "mirar" a izquierda y derecha sin girar el cuerpo, y eso le permite decidir los giros mucho mejor que un robot con el sensor fijo. Los detalles de cómo funciona el sensor están en el artículo Sensor de distancia HC-SR04.
| Pieza | Función | Nota |
|---|---|---|
| Arduino Uno (o Nano) | Cerebro: mide, decide y da órdenes a los motores | También sirven un ESP32 o una Pico |
| HC-SR04 | Ojo: mide la distancia al obstáculo de delante | Funciona a 5 V, 2–400 cm |
| Servo SG90 | Cuello: gira el sensor a izquierda y derecha | El sensor se fija al brazo del servo con un soporte de plástico |
| L298N o shield L293D | Músculo: da dirección y velocidad a los motores | Un pin de Arduino no puede dar la corriente del motor |
| 2 motorreductores TT y rueda loca | Tracción diferencial | Gira haciendo que las dos ruedas giren en sentidos opuestos |
| 2 baterías 18650 (7,4 V) | Energía | Una pila de 9 V no basta |
| 2 sensores IR de obstáculos (opcional) | Detectan obstáculos en las esquinas y los lados | Módulos con LM393 como el FC-51 |
| HC-05 (opcional) | Control manual desde el móvil | Solo funciona con Android |




Alimentación: que el servo no reinicie el robot
Como el robot evasor de obstáculos suma un servo a los motores, el reparto de energía importa más que en un coche robot normal. Un SG90 consume unos 10 mA en reposo; en cuanto empieza a moverse sube a 100–250 mA y, si se fuerza, se acerca a medio amperio. Si ese pico coincide con la corriente de arranque de los motores, la línea de 5 V del Arduino cae por un instante y la placa se reinicia. La solución es sencilla: los motores se alimentan de la batería a través del L298N, el Arduino de la batería a través de VIN, y el servo, el HC-SR04, los sensores IR y el HC-05 comparten el pin 5V del Arduino, con un condensador de 470 µF junto a los cables de alimentación del servo. Incluso con el servo en movimiento, la carga total de la línea de 5 V se queda por debajo de medio amperio y, como dura poco, el regulador del Uno la aguanta. Para calcular la autonomía con tus propios valores, usa la herramienta Duración de la batería.
| Parte | Se alimenta de | Corriente típica |
|---|---|---|
| 2 motores TT | Borne 12V del L298N ← 2 × 18650 (7,4 V) | 0,3–0,6 A en marcha, hasta 2 A al arrancar |
| Arduino Uno | VIN ← positivo de la batería (después del interruptor) | Unos 50 mA |
| Servo SG90 | 5V del Arduino + condensador de 470 µF | ~10 mA en reposo, 100–250 mA en movimiento |
| HC-SR04 | 5V del Arduino | Unos 15 mA |
| 2 sensores IR | 5V del Arduino | 20–40 mA cada uno |
| HC-05 | 5V del Arduino | 30–40 mA buscando emparejamiento, unos 10 mA conectado |
Esquema de conexión: Arduino Uno, ESP32 y Pico
Los pines de los motores son los mismos que en la guía del coche robot, así que puedes pasar a este proyecto añadiendo solo la cabeza del sensor, sin desmontar el coche. Poner los pines de velocidad en el 5 y el 6 es intencionado: en el Uno la librería Servo usa el Timer1 y por eso desactiva el PWM de los pines 9 y 10; si las velocidades de los motores estuvieran ahí, el control de velocidad dejaría de funcionar en cuanto conectaras el servo. El HC-SR04 y los sensores IR van en A0–A3. Los pines analógicos del Uno también funcionan como pines digitales 14–19 cuando hace falta, y esta elección facilita usar el mismo código con un shield L293D, porque el shield no toca esos pines. Puedes ver la distribución de pines de tu placa en la herramienta Pinout.
| Conexión | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| L298N ENA (velocidad motor izquierdo) | D5 | GPIO 25 | GP15 |
| L298N IN1, IN2 | D7, D8 | GPIO 27, 33 | GP14, GP13 |
| L298N ENB (velocidad motor derecho) | D6 | GPIO 26 | GP10 |
| L298N IN3, IN4 | D9, D4 | GPIO 32, 4 | GP12, GP11 |
| Señal del servo (naranja) | D3 | GPIO 13 | GP16 |
| Servo + (rojo), − (marrón) | 5V, GND | 5V del L298N, GND | 5V del L298N, GND |
| HC-SR04 TRIG | A0 | GPIO 18 | GP17 |
| HC-SR04 ECHO | A1 (directo) | GPIO 19, con divisor de 1 kΩ / 2 kΩ | GP18, con divisor de 1 kΩ / 2 kΩ |
| HC-SR04 VCC | 5V | 5V del L298N | 5V del L298N |
| OUT del IR izquierdo, OUT del IR derecho | A2, A3 | GPIO 23, 22 | GP19, GP20 |
| VCC de los sensores IR | 5V | 3V3 | 3V3 |
| HC-05 TXD, RXD | D10, D11 (RXD con divisor) | No hace falta (Bluetooth integrado) | GP1, GP0 (UART0) |
| Alimentación de la placa | VIN ← batería (después del interruptor) | 5V (VIN) ← 5V del L298N | VSYS ← 5V del L298N |
Monta y prueba la cabeza del sensor

- Conecta el servo sin el brazo y sube el sketch de prueba de abajo. Al final de cada barrido el código lleva el servo a 90 grados y espera 2 segundos; desconecta el cable USB durante esa pausa y el servo se queda donde está. Coloca el brazo en el eje de modo que el sensor mire justo al frente; así 90 grados significa "de frente".
- Fija el HC-SR04 al brazo con un soporte de plástico o con silicona caliente. Los cables del sensor deben ser lo bastante largos para no tensarse cuando gira el servo; que el cable se enganche en el chasis durante el barrido de 140 grados es una avería frecuente.
- Coloca el sensor a 8–12 cm del suelo y cerca del punto más adelantado del chasis. Si va muy bajo recibe ecos del suelo y de la textura de la alfombra; si va muy alto no ve los obstáculos bajos.
- Inclina el sensor 3–5 grados hacia arriba. Un sensor totalmente horizontal y bajo puede confundir un eco del suelo con un obstáculo, sobre todo en suelos rugosos.
- Abre el Monitor Serie a 9600 baudios. Si al poner la mano a la izquierda del sensor ves valores pequeños en las líneas de 125 y 160 grados, la izquierda corresponde a los ángulos grandes; si ocurre al revés, pon SERVO_DIR a -1 en el sketch avanzado e intercambia los valores de LEFT_ANGLE y RIGHT_ANGLE en el primer sketch.
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}Cada línea muestra un ángulo y la distancia en esa dirección. Si no hay nada a unos 4 metros delante del sensor, leerás 0 cm: pulseIn() devuelve 0 si no llega ningún eco en 25 ms. En el código principal trataremos el 0 como "camino libre", pero un sensor con un cable suelto o con TRIG y ECHO invertidos también suele leer 0. Por eso, antes de poner el robot en el suelo, coloca la mano a 10, 30 y 60 cm y comprueba que los valores cambian de verdad; un robot que se pone en marcha sin esta prueba se estrella contra la pared sin frenar.
El algoritmo de decisión: parar, retroceder, girar hacia la dirección más despejada
Dividir el comportamiento del robot en zonas según la distancia hace que el código sea sencillo y fiable. Sin obstáculos delante avanza a toda velocidad, frena a medida que se acerca un obstáculo y, al llegar al umbral de parada, se detiene y hace un barrido. Durante el barrido el sensor mira 70 y 35 grados a la izquierda y 35 y 70 grados a la derecha; en cada dirección se toman tres medidas y se usa la mediana. El robot gira hacia la dirección con la mayor distancia, exactamente ese ángulo. Si ninguna dirección está libre, por ejemplo porque se ha metido en una esquina, retrocede un poco y gira 180 grados.
| Distancia medida | Zona | Qué hace el robot |
|---|---|---|
| Más de 60 cm (o sin eco) | Libre | Avanza a toda velocidad (PWM 200) |
| 25–60 cm | Aproximación | Frena en proporción a la distancia (de PWM 200 a 130) |
| 12–25 cm | Obstáculo | Se detiene, confirma la medida, barre a ambos lados y gira hacia la dirección más despejada |
| Menos de 12 cm | Demasiado cerca | Primero retrocede 350 ms y después barre |
| Todas las direcciones a menos de 35 cm | Callejón sin salida | Retrocede y gira 180 grados |
Estos umbrales no son aleatorios. La distancia de parada es la suma de tres márgenes: el margen de reacción (lo que avanza entre dos medidas), el margen de frenado (lo que se desliza el robot después de cortar los motores) y el margen de giro (para que el morro no toque el obstáculo al girar sobre sí mismo). Un coche con motores TT a 6 V va a unos 0,5–0,7 m/s y recorre 3–4 cm en un intervalo de medida de 60 ms. El margen de frenado es de unos 5 cm y, para un chasis de 15 cm de ancho que gira sobre sí mismo, el margen de giro ronda los 10 cm. En total salen unos 18–20 cm; con unos centímetros más de margen de seguridad, STOP_CM = 25 es un buen punto de partida. Si tu robot es más rápido o el suelo resbala, sube el umbral a 30–35 cm. En pasillos estrechos, en cambio, un umbral demasiado grande hace que el robot se pare y barra todo el rato; haz el ajuste final en tu propio recorrido.
Código paso a paso 1: tu primer robot evasor de obstáculos
La primera versión es el esqueleto del algoritmo: avanzar, parar a 25 cm, retroceder, mirar a izquierda y derecha y girar hacia el lado más despejado. La función de los motores drive(izquierdo, derecho) es la misma que en la guía del coche robot y admite valores de -255 a 255; positivo es adelante y negativo, atrás. Después de subir el código, pon primero el coche sobre una caja con las ruedas en el aire y observa cómo se para y gira al acercar la mano al sensor; si todo va bien, deja el robot en el suelo.
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}Este código cumple, pero tiene algunos puntos débiles: una sola medida errónea detiene el robot sin motivo, siempre se acerca a los obstáculos a la misma velocidad, solo mira en dos direcciones y sus tiempos de giro no dependen del ángulo. Además, como espera con delay(), no puede reaccionar a nada durante el barrido. Los detalles de la lógica de espera están en el artículo millis() y delay(); la siguiente versión resuelve la mayoría de estos problemas.
Código paso a paso 2: filtro, frenado progresivo, IR y Bluetooth
La versión avanzada funciona con las mismas conexiones y no pasa nada si todavía no has puesto los sensores IR ni el HC-05. Como las entradas IR se configuran con INPUT_PULLUP, un sensor que no está conectado lee "sin obstáculo" (HIGH); sin HC-05 el robot simplemente arranca en modo automático. En la tabla de abajo tienes qué hace cada parte del código.
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}| Función o ajuste | Qué hace |
|---|---|
| pingCm() | Hace una medida; devuelve 400 cm (espacio libre) si no llega eco en 25 ms |
| distanceCm() | Toma 3 medidas separadas 60 ms y devuelve la mediana; descarta un valor aislado que se dispara |
| autoStep() | Mide cada 60 ms; según la distancia y los IR avanza, frena, esquiva o llama a avoid() |
| avoid() | Se detiene, confirma la medida, retrocede si hace falta, mira en 4 direcciones y gira hacia la más despejada |
| turnBy(grados) | Gira el robot sobre sí mismo el ángulo indicado; el tiempo se calcula con MS_PER_DEG |
| manualStep() | Aplica la dirección enviada desde el móvil; al avanzar se para a 15 cm y se detiene si no llega ninguna orden en 600 ms |
| SERVO_CENTER, SERVO_DIR | El ángulo en el que el sensor mira al frente y el sentido de la izquierda |
| STOP_CM, SLOW_CM, BACK_CM | Umbrales de parada, frenado y retroceso |
Para un robot evasor de obstáculos el filtro de mediana es mejor que la media: con tres medidas como 24, 26 y 400 cm la media sale 150 cm y engaña al robot, mientras que la mediana elige 26 cm. Si quieres profundizar, consulta el artículo filtrar datos de sensores. La separación entre modo automático y manual también es, en realidad, una pequeña máquina de estados; explicamos este enfoque en detalle en la guía máquina de estados en Arduino. Después de subir el código, ajusta el robot a tu habitación en este orden:
- Centro del servo: con el robot perpendicular a una pared lisa, si el sensor no apunta justo hacia ella, cambia SERVO_CENTER en pasos pequeños, por ejemplo a 85 o 95.
- Tiempo de giro: añade temporalmente la línea turnBy(360); al final de setup() y observa si el robot da una vuelta completa. Si gira 300 grados en lugar de 360, pon MS_PER_DEG en 4,0 × 360 / 300 = 4,8. Como este valor cambia con la batería y el suelo, ajústalo con la batería llena y sobre el suelo por el que va a circular.
- Umbral de parada: envía el robot hacia una pared y mide a qué distancia de ella queda el morro cuando se para. Si es menos de 10 cm, sube STOP_CM 5 cm.
- Velocidad lenta: si SLOW queda por debajo del umbral de arranque de los motores, el robot se queda parado en la zona de aproximación; en ese caso sube SLOW a 150.
Puntos ciegos del HC-SR04: superficies blandas, paredes inclinadas y obstáculos finos
Un sensor ultrasónico ve bien las superficies que devuelven el sonido, pero no todos los obstáculos lo devuelven hacia el sensor. El fabricante recomienda un objetivo de al menos 0,5 m² y lo más liso posible para obtener buenos resultados. En una habitación real, sin embargo, el robot se encuentra con cortinas, sofás, patas de sillas y esquinas. El sonido rebota con el mismo ángulo con el que llega, como una bola de billar: un sensor que mira de frente a una pared recibe un eco fuerte, pero si su dirección se desvía más de unos 30–40 grados de la perpendicular, el sonido sale rebotado hacia otro lado y nunca vuelve. Entonces pulseIn() devuelve 0 y el código cree que el camino está libre. Es la causa más común de que los robots se rocen contra las paredes.
| Obstáculo o superficie | Por qué no se ve | Solución |
|---|---|---|
| Cortinas, sofás, cojines, ropa, pelo de mascota | Las superficies blandas y porosas absorben el sonido y el eco vuelve muy débil | Añade un sensor IR para corta distancia y conduce el robot despacio |
| Acercarse a una pared en diagonal | El sonido se refleja hacia otro lado y el eco no llega al sensor | Pon sensores IR inclinados hacia fuera en las esquinas delanteras y barre con ángulos más juntos |
| Patas de sillas y mesas, postes finos | La superficie que refleja es muy pequeña y el eco es débil | Baja la velocidad, usa el filtro de mediana y añade IR en los lados |
| Obstáculos bajos bajo el sensor (cables, umbrales de puertas, zapatillas) | Quedan por debajo del cono de sonido | Añade al parachoques delantero un sensor IR mirando hacia abajo o un microinterruptor |
| Bordes de mesa, huecos de escalera | El sensor ultrasónico mira hacia delante y no ve un hueco en el suelo | Detección de vacío con un TCRT5000 mirando al suelo |
| Objetos a menos de 2 cm | Zona muerta del sensor: el eco vuelve antes de que termine la emisión del sonido | Ajusta el robot para que retroceda a 12 cm |
| Esquinas y pasillos estrechos | El sonido rebota entre dos paredes y vuelve tarde, así que la distancia parece mayor | Filtro de mediana y medidas desde varios ángulos |
Refuerzo con sensores IR
Los sensores ultrasónicos e infrarrojos (IR) compensan los puntos débiles del otro. Los sensores IR de obstáculos como el FC-51 constan de un LED infrarrojo y un receptor; cuando la luz reflejada es lo bastante fuerte, el comparador LM393 pone la salida en LOW y se enciende el LED de la placa. Su alcance se ajusta con un trimmer entre unos 2 y 30 cm, pero no mide distancias, solo dice "hay obstáculo" o "no hay". Si colocas dos sensores IR en las esquinas delanteras del robot, inclinados 30–45 grados hacia fuera, detectarás las patas de sillas y las paredes inclinadas que quedan fuera del cono estrecho del ultrasónico. En el código avanzado, el robot esquiva brevemente a la derecha cuando salta el IR izquierdo y a la izquierda cuando salta el derecho; si saltan los dos a la vez, se para y barre como si tuviera un obstáculo delante.
| Característica | HC-SR04 (ultrasónico) | FC-51 (IR de obstáculos) | TCRT5000 (IR reflectivo) |
|---|---|---|---|
| Salida | Distancia (cm) | Sí / no (digital) | Valor analógico y salida digital |
| Alcance | 2–400 cm | Unos 2–30 cm (ajustable) | Unos 1–15 mm |
| Ángulo de detección | Unos 15° | Unos 35° | Muy estrecho |
| Punto débil | Superficies blandas, inclinadas y finas | Superficies negras y mates, luz solar | Cambios de altura, luz solar |
| Ve bien | Vidrio, objetos negros, superficies grandes y lisas | Tela, esquinas, obstáculos cercanos | Color del suelo y bordes de mesa |
| Lugar en el robot | Ojo principal, movido por el servo | Esquinas delanteras | Bajo el parachoques delantero, mirando al suelo |

Para los robots que se mueven sobre una mesa, el obstáculo más peligroso es en realidad el vacío. Si colocas un módulo TCRT5000 mirando al suelo bajo el parachoques delantero, sabrás al instante cuándo desaparece el suelo. Conecta la salida digital del módulo a D2, que sigue libre en el Uno; comprueba primero en el Monitor Serie qué nivel toma la salida cuando no hay suelo y después lee ese pin al principio de autoStep() y, si el suelo ha desaparecido, detén los motores de inmediato y haz retroceder al robot. Esta comprobación debe ir antes de la medida de distancia; caerse siempre sale más caro que chocar.
Pasar a modo manual por Bluetooth
A veces querrás conducir el robot autónomo a mano: para llevarlo de una habitación a otra, ponerlo delante de un obstáculo y probar su reacción o simplemente para jugar. La conexión con el HC-05 del código avanzado lo permite. Cuando el móvil envía la letra A, el robot pasa a modo automático; cuando envía M, pasa a modo manual. En modo manual se usan las letras de control de la guía del coche robot (F, B, L, R, S), con una diferencia: si el robot ve un obstáculo a menos de 15 cm mientras avanza, se detiene aunque le hayas dado la orden. Esta protección anticolisión te permite aprovechar los sensores del robot autónomo también al conducir a mano. Los pasos para emparejar el HC-05 y los comandos AT están en el artículo Módulo Bluetooth HC-05.
| Letra | Acción | ¿En qué modo? |
|---|---|---|
| A | Pasar a modo automático | En ambos modos |
| M | Pasar a modo manual y detener el robot | En ambos modos |
| F / B | Adelante / atrás | Manual |
| L / R | Girar sobre sí mismo a la izquierda / derecha | Manual |
| S | Parar | Manual |
| 0–9 | Nivel de velocidad (PWM 100–240) | Manual |
- Conecta el pin TXD del HC-05 a D10, el pin RXD a D11 a través de un divisor de 1 kΩ / 2 kΩ, VCC a 5V y GND a GND.
- Empareja el HC-05 en los ajustes Bluetooth de tu móvil Android con el PIN 1234 (o 0000 si no funciona).
- Instala desde Play Store una app de mando para coches Bluetooth y conéctala al HC-05. Si mantienes el Arduino conectado por USB y abres el Monitor Serie a 9600 baudios, el código muestra cada letra que llega del móvil.
- La mayoría de las apps de mando tienen botones extra, como luces o claxon, y cada uno envía su propia letra. Mira esas letras en el Monitor Serie y ponlas en el código en lugar de 'A' y 'M'; si prefieres definir tú los botones, puedes hacer tu propio mando con la guía App Inventor con Arduino por Bluetooth.
- Desconecta el cable USB y acciona el interruptor; a los 3 segundos el robot arranca en modo automático. Pasa a modo manual y condúcelo desde el móvil.
Montaje con un shield de motores L293D

Si no te gusta el lío de cables, en lugar del L298N puedes usar un shield de motores L293D (el Adafruit Motor Shield V1 y sus clones) que se coloca directamente encima del Arduino Uno. El shield puede mover cuatro motores de corriente continua, tiene dos conectores para servos y los alimenta desde la línea de 5 V del Arduino. Los motores no van conectados directamente a los pines del Arduino, sino a través del registro de desplazamiento 74HC595 de la placa, así que se controlan con la Adafruit Motor Shield library (la de la V1, no la V2), es decir, AFMotor, que instalas desde el Gestor de librerías. El shield usa D3, D4, D5, D6, D7, D8, D11 y D12 para los motores y D9 y D10 para los servos; A0–A5, D2 y D13 quedan libres. Como el HC-SR04 y los sensores IR ya están en A0–A3, solo tienes que cambiar el código de los motores, el pin del servo y los pines del HC-05.
| Ajuste | Con el L298N | Con el shield L293D |
|---|---|---|
| Conexión de los motores | Bornes OUT1–OUT4 | Bornes M1 (izquierdo) y M2 (derecho) |
| Código de los motores | digitalWrite y analogWrite dentro de drive() | AFMotor dentro de drive() |
| SERVO_PIN | 3 | 9 o 10, según el conector donde pongas el servo |
| HC-05 | D10, D11 | A4 (RX) y A5 (TX): SoftwareSerial bt(A4, A5) |
| Corriente por canal | 2 A continuos | 0,6 A continuos, 1,2 A de pico |
| Alimentación de los motores | Borne 12V | Borne EXT_PWR; con el jumper PWR puesto, el Arduino también se alimenta de la misma fuente |
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}El límite de 0,6 A por canal del L293D basta para la marcha normal de un robot 2WD con motores TT, pero cuando una rueda empuja contra un obstáculo y se bloquea, el motor puede consumir más de 1 A y el chip se calienta rápido. Por eso un algoritmo de barrido que detiene el robot antes de empujar contra un obstáculo también alarga la vida del shield. Los ajustes de jumpers del shield y un sketch para un coche de cuatro motores están en el artículo Shield de motores L293D, y el funcionamiento interno del L298N en la guía Control de motores DC con el L298N.
Robot evasor de obstáculos con ESP32
En el ESP32 hay dos diferencias importantes. La primera es que la librería Servo de Arduino no funciona en el ESP32, así que generamos la señal del servo directamente con LEDC, la unidad PWM de la placa: en una señal de 50 Hz, un pulso de 0,544–2,4 ms corresponde a 0–180 grados. Como los motores trabajan a 1 kHz y el servo a 50 Hz, primero se configuran los canales de los motores y después el del servo; así el servo usa su propio temporizador. La segunda es que el ESP32 lleva Bluetooth clásico integrado, de modo que no hace falta un HC-05; el robot aparece en el móvil como "BlueGrays-Robot" y las letras de dirección y de modo son las mismas que en la versión para Uno. El código es para la versión 3.x del core de Arduino para ESP32 (ledcAttach y ledcWrite trabajan con números de pin) y, como necesita Bluetooth clásico, no compila en el ESP32-S3 ni en el ESP32-C3.
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}Robot evasor de obstáculos con Raspberry Pi Pico (MicroPython)
En la Raspberry Pi Pico, la función time_pulse_us() de MicroPython funciona como pulseIn() en Arduino; si no llega eco devuelve un valor negativo, que tratamos como 400 cm, es decir, espacio libre. Para el servo se usan PWM a 50 Hz y duty_ns(), que recibe el ancho de pulso en nanosegundos. El servo en GP16 y los pines de velocidad de los motores en GP15 y GP10 están en slices de PWM distintos, así que no se alteran la frecuencia entre sí. Esta vez las velocidades se dan en porcentaje. La Pico no tiene Bluetooth; si quieres el modo manual, conecta un HC-05 a la UART0 (GP0, GP1) como en la guía del coche robot.
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 readingsIdeas de proyectos con un robot evasor de obstáculos
- Pantalla de radar: envía por el puerto serie los ángulos y distancias del barrido y dibuja una pantalla de radar verde en el ordenador; tienes los detalles en el artículo Proyecto de radar con Arduino.
- Línea y obstáculo: haz que el robot, mientras recorre una pista como un robot seguidor de línea, rodee una caja que se le cruce y vuelva a la línea; es una prueba habitual en competiciones.
- Robot que sigue paredes: deja el sensor girado 60 grados a la derecha y avanza a 20 cm constantes de la pared; es el primer paso para resolver laberintos.
- Robot con pantalla: muestra la distancia y el modo en una pequeña pantalla OLED; depurar resulta mucho más fácil.
- Prototipo de robot aspirador: diseña un robot de limpieza que recorra la casa con elección aleatoria de dirección, interruptores de choque y sensor de vacío.
- Bastón inteligente: convierte la misma lógica de medir distancias en un aviso por vibración para personas con discapacidad visual; consulta Bastón inteligente con Arduino.
- Proyecto escolar o de competición: preséntalo con un circuito de obstáculos, cronómetro y una demostración de los modos automático y manual; para más ideas, echa un vistazo al artículo ideas de proyectos de Arduino para ferias de ciencias.
Errores frecuentes y cómo solucionarlos
| Problema | Causa probable | Solución |
|---|---|---|
| El robot choca contra la pared sin pararse | El sensor lee 0 (cable roto, TRIG y ECHO invertidos) y el código lo toma como espacio libre | Ejecuta el sketch de prueba, prueba con la mano entre 10 y 60 cm y revisa los pines TRIG y ECHO |
| Roza la pared al acercarse en diagonal | El sonido se refleja en la superficie inclinada hacia otro lado | Añade sensores IR en las esquinas delanteras y baja la velocidad |
| El Arduino se reinicia al pararse a barrer | La corriente del servo y los motores hunde la línea de 5 V | Condensador de 470 µF en el servo y baterías 18650 cargadas; si hace falta, alimenta el servo con una fuente de 5 V aparte |
| El robot se para y barre sin motivo | Una medida errónea aislada o un eco del suelo | Sube el sensor a 8–12 cm e inclínalo un poco hacia arriba; usa la confirmación por mediana |
| El robot gira hacia el lado bloqueado | El servo está montado al revés y se han cruzado izquierda y derecha | Pon SERVO_DIR a -1 |
| Los giros se quedan cortos o se pasan | MS_PER_DEG no está calibrado o la batería está baja | Mide con turnBy(360) y vuelve a ajustarlo con la batería llena |
| En una esquina no deja de girar a un lado y a otro | Los dos lados están cerca del umbral y la decisión cambia sin parar | Aumenta el umbral de callejón sin salida (STOP_CM + 10) o haz que prefiera el último sentido de giro |
| La cabeza del sensor da tirones | Alimentación del servo floja o interrupciones de SoftwareSerial | Acorta el cable del servo, añade un condensador y no envíes órdenes Bluetooth más a menudo de lo necesario |
Como reúne en un solo proyecto la lectura de sensores, el filtrado, la toma de decisiones y el control de motores, el robot evasor de obstáculos es la mejor puerta de entrada a la robótica. Observa primero el comportamiento básico con el primer sketch, ajusta después los umbrales a tu habitación con la versión avanzada y, por último, refuerza tu robot con sensores IR y Bluetooth. En la app BlueGrays encontrarás paso a paso, con esquemas y código, los proyectos del robot evasor de obstáculos, el coche controlado por Bluetooth con L298N, la medición de distancia con HC-SR04 y el sensor de aparcamiento.
Preguntas frecuentes
¿Qué se necesita para hacer un robot evasor de obstáculos?
Para un robot evasor de obstáculos básico basta con un Arduino Uno, un sensor ultrasónico HC-SR04, un servo SG90, un driver de motores L298N o L293D, un kit de chasis 2WD con dos motorreductores y dos baterías 18650. Puedes añadir dos sensores IR de obstáculos para ver las esquinas y un módulo Bluetooth HC-05 para manejarlo desde el móvil.
¿Se puede hacer un robot evasor de obstáculos sin servo?
Sí. El sensor mira fijo hacia delante; cuando el robot ve un obstáculo, primero gira hacia un lado y vuelve a medir, y si el camino sigue bloqueado gira hacia el otro. Es más barato, pero el robot decide más despacio porque tiene que girar todo el cuerpo en cada intento; con un servo encuentra la dirección más despejada antes de girar.
¿Por qué el HC-SR04 no detecta algunos obstáculos?
Un sensor ultrasónico mide por el eco. Las superficies blandas como cortinas y sofás absorben el sonido, las paredes inclinadas lo desvían y los objetos finos, como las patas de una silla, dan un eco muy débil. Tampoco ve los obstáculos que quedan fuera o por debajo de su cono de unos 15 grados; esos huecos se cubren con sensores IR.
¿A qué distancia debe pararse un robot evasor de obstáculos?
25 cm es un buen valor inicial: lo que avanza entre dos medidas, la distancia de frenado y el margen para girar sobre sí mismo suman unos 20 cm, más un pequeño margen de seguridad. En robots más rápidos y suelos resbaladizos súbelo a 30–35 cm; con un obstáculo a menos de 12 cm conviene retroceder primero.
¿Qué es mejor para un robot, el L298N o un shield L293D?
El L298N da 2 A por canal, así que es más robusto y te deja elegir los pines libremente. El shield L293D se coloca sobre el Uno, reduce el cableado y puede mover cuatro motores, pero su corriente continua es de 0,6 A por canal y ocupa muchos pines. Para un robot 2WD pequeño con motores TT, cualquiera de los dos sirve.
¿Se puede controlar un robot evasor de obstáculos desde el móvil?
Sí. Añade un módulo Bluetooth HC-05 y envía letras desde una app de mando en un móvil Android para cambiar entre modo automático y manual. Usar el sensor ultrasónico como protección anticolisión en modo manual evita que estrelles el robot contra la pared. Con un ESP32 no necesitas un módulo aparte gracias a su Bluetooth integrado.
Proyectos para hacer después de leer esto
Ver todos los proyectos
HC-SR04 Medición de distancia (pantalla en serie)
El sensor HC-SR04 funciona igual que un murciélago que se orienta en la oscuridad: emite un sonido que no puedes oír, escucha el eco y mide a qué distancia está el objeto que tiene delante. El Arduino

Robot para evitar obstáculos
¿Qué le parece construir un vehículo inteligente que encuentre su propio camino? El Robot Evitador de Obstáculos explora constantemente su entorno gracias al sensor ultrasónico que lleva delante. Cuan

Coche controlado por Bluetooth con L298N
Convierte tu móvil en un mando y conduce tu propio coche robot. El Arduino recibe por un módulo Bluetooth las órdenes de una app de control remoto en el móvil y mueve las ruedas con un controlador de

Uso del servomotor
El brazo de un pequeño servomotor gira por turnos a tres posiciones: al principio del todo, justo al centro y al final del todo, esperando dos segundos en cada parada. El Arduino le dice al motor en q

Sensor de aparcamiento con HC-SR04
Una versión en miniatura del sensor de aparcamiento de los coches: cuanto más se acerca algo, más rápidos son los pitidos. El sensor de distancia mide el objeto que tiene delante; si está a más de 20

Lectura de reflexión (proximidad) con TCRT5000
Con un pequeño sensor que emite luz invisible y mide cuánta rebota, detectas la superficie que tiene delante. Cuanto más se acerca un objeto o una superficie clara, más luz vuelve, y el Arduino escrib

TCRT5000 Detección de superficie blanca y negra
Con el pequeño sensor que hace de ojo en los robots sigue líneas, sabrás si el suelo es negro o blanco. El TCRT5000 lanza una luz invisible hacia el suelo y mira cuánta vuelve; sobre blanco se enciend

ESP32 HC-SR04 Medidor de distancia por ultrasonidos
El sensor ultrasónico HC-SR04 calcula la distancia midiendo el tiempo de vuelo (Time of Flight) de la onda sonora que emite al chocar contra un objeto y rebotar. En este proyecto, enviaremos un pulso de 10 microsegundos desde el ESP32 al pin TRIG del sensor y leeremos el tiempo de eco procedente del pin ECHO mediante `pulseIn()`. Mostraremos la distancia calculada en centímetros en una pantalla LCD I2C de 16x2. Dado que el HC-SR04 funciona a 5 V, es obligatorio utilizar un **divisor de tensión**
Más artículos
App BlueGrays
Monta este circuito paso a paso en tu teléfono
Esquema, código y lista de piezas en una sola pantalla. Más de 160 proyectos de Arduino, ESP32 y Pico listos para montar.
Escanea con tu teléfono
Apunta la cámara al código y se abrirá la tienda de apps de tu teléfono.
o ábrela en la tienda

