Robô que desvia de obstáculos com Arduino: guia com HC-SR04 e servo
Monte um robô que desvia de obstáculos com Arduino: varredura com HC-SR04 sobre um servo, ligação do L298N ou L293D, lógica de parar-recuar-virar, sensores IR e modo manual por Bluetooth.
Um robô que desvia de obstáculos é um carrinho robô autônomo que percebe uma parede, uma cadeira ou uma caixa à frente sem bater nela e encontra um novo caminho. A montagem mais comum usa um Arduino Uno, um sensor de distância ultrassônico HC-SR04, um servo motor SG90 que gira o sensor para a esquerda e para a direita, dois motores CC com redução e um driver de motor L298N (ou L293D). O robô anda em linha reta, para quando chega perto de um obstáculo, recua um pouco, vira a “cabeça” para os dois lados para medir e depois segue na direção mais livre.
Neste guia transformamos o carrinho 2WD do artigo carrinho robô com Arduino em um robô autônomo, mantendo a mesma pinagem. Passo a passo, vemos as tabelas de ligação, como os limites de distância são calculados, um código que cresce em três etapas, as superfícies que o sensor não enxerga e como cobrir essas falhas com sensores infravermelhos e, por fim, a troca para o modo manual por Bluetooth pelo celular. O código para controlar o mesmo robô com um ESP32 ou um Raspberry Pi Pico está no fim do artigo.

Como funciona um robô que desvia de obstáculos?
Todo robô autônomo repete dezenas de vezes por segundo o mesmo ciclo de três passos: sentir, decidir, agir. O HC-SR04 cuida de sentir: ele emite um som curto de 40 kHz que seu ouvido não escuta e mede quanto tempo o eco leva para voltar; divida esse tempo em microssegundos por 58 e você tem a distância em centímetros. O Arduino decide, comparando a distância medida com os limites que você definiu. O L298N e os motores cuidam de agir. Como o sensor fica sobre um servo, o robô consegue “olhar” para a esquerda e para a direita sem girar o corpo, o que permite decisões de curva muito melhores do que as de um robô com sensor fixo. Os detalhes de como o sensor funciona estão no artigo sensor de distância HC-SR04.
| Peça | Função | Observação |
|---|---|---|
| Arduino Uno (ou Nano) | Cérebro: mede, decide e comanda os motores | Um ESP32 ou Pico também serve |
| HC-SR04 | Olho: mede a distância até o obstáculo à frente | Funciona com 5 V, 2–400 cm |
| Servo SG90 | Pescoço: gira o sensor para a esquerda e para a direita | O sensor é preso ao braço do servo com um suporte plástico |
| L298N ou shield L293D | Músculo: dá direção e velocidade aos motores | Um pino do Arduino não fornece a corrente de um motor |
| 2 motores TT com redução e uma roda boba | Tração diferencial | As curvas são feitas girando as duas rodas em sentidos opostos |
| 2 células 18650 (7,4 V) | Alimentação | Uma bateria de 9 V não basta |
| 2 sensores de obstáculo IR (opcional) | Detectam obstáculos nos cantos e nas laterais | Módulos com LM393, como o FC-51 |
| HC-05 (opcional) | Controle manual pelo celular | Funciona só com Android |




Alimentação: não deixe o servo reiniciar o robô
Como o robô que desvia de obstáculos acrescenta um servo aos motores, a distribuição da alimentação importa mais do que em um carrinho robô comum. Um SG90 consome cerca de 10 mA parado; no instante em que começa a se mover, sobe para 100–250 mA e, quando forçado, chega perto de meio ampère. Se esse pico coincidir com a corrente de partida dos motores, o barramento de 5 V do Arduino cai por um instante e a placa reinicia. A solução é simples: os motores funcionam com a bateria via L298N, o Arduino funciona com a bateria via VIN, e o servo, o HC-SR04, os sensores IR e o HC-05 dividem o pino 5V do Arduino, com um capacitor de 470 µF colocado junto aos fios de alimentação do servo. Mesmo com o servo em movimento, a carga total no barramento de 5 V fica abaixo de meio ampère e, como é breve, o regulador do Uno dá conta. Para calcular a autonomia da bateria com os seus números, use a ferramenta Autonomia da bateria.
| Seção | Alimentado por | Corrente típica |
|---|---|---|
| 2 motores TT | Terminal 12V do L298N ← 2 × 18650 (7,4 V) | 0,3–0,6 A andando, até 2 A na partida |
| Arduino Uno | VIN ← positivo da bateria (depois da chave) | Cerca de 50 mA |
| Servo SG90 | 5V do Arduino + capacitor de 470 µF | ~10 mA parado, 100–250 mA em movimento |
| HC-SR04 | 5V do Arduino | Cerca de 15 mA |
| 2 sensores IR | 5V do Arduino | 20–40 mA cada |
| HC-05 | 5V do Arduino | 30–40 mA durante o pareamento, cerca de 10 mA conectado |
Esquema de ligação: Arduino Uno, ESP32 e Pico
Os pinos dos motores são os mesmos do guia do carrinho robô, então você pode partir para este projeto só acrescentando a cabeça com o sensor, sem desmontar o carrinho. Colocar os pinos de velocidade no 5 e no 6 é proposital: no Uno, a biblioteca Servo usa o Timer1 e por isso desativa o PWM nos pinos 9 e 10; se as velocidades dos motores estivessem nesses pinos, o controle de velocidade quebraria assim que o servo fosse conectado. O HC-SR04 e os sensores IR vão em A0–A3. Os pinos analógicos do Uno também podem funcionar como os pinos digitais 14–19 quando necessário, e essa escolha facilita usar o mesmo código com um shield L293D, porque o shield não usa esses pinos. Você pode ver a disposição dos pinos da sua placa na ferramenta Pinagem.
| Ligação | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| ENA do L298N (velocidade do motor esquerdo) | D5 | GPIO 25 | GP15 |
| IN1, IN2 do L298N | D7, D8 | GPIO 27, 33 | GP14, GP13 |
| ENB do L298N (velocidade do motor direito) | D6 | GPIO 26 | GP10 |
| IN3, IN4 do L298N | D9, D4 | GPIO 32, 4 | GP12, GP11 |
| Sinal do servo (laranja) | D3 | GPIO 13 | GP16 |
| Servo + (vermelho), − (marrom) | 5V, GND | 5V do L298N, GND | 5V do L298N, GND |
| TRIG do HC-SR04 | A0 | GPIO 18 | GP17 |
| ECHO do HC-SR04 | A1 (direto) | GPIO 19, por um divisor de 1 kΩ / 2 kΩ | GP18, por um divisor de 1 kΩ / 2 kΩ |
| VCC do HC-SR04 | 5V | 5V do L298N | 5V do L298N |
| OUT do IR esquerdo, OUT do IR direito | A2, A3 | GPIO 23, 22 | GP19, GP20 |
| VCC dos sensores IR | 5V | 3V3 | 3V3 |
| TXD, RXD do HC-05 | D10, D11 (RXD por divisor) | Não precisa (Bluetooth embutido) | GP1, GP0 (UART0) |
| Alimentação da placa | VIN ← bateria (depois da chave) | 5V (VIN) ← 5V do L298N | VSYS ← 5V do L298N |
Monte e teste a cabeça com o sensor

- Ligue o servo sem o braço e carregue o sketch de teste abaixo. No fim de cada varredura, o código leva o servo a 90 graus e espera 2 segundos; desconecte o cabo USB durante essa pausa e o servo fica onde está. Encaixe o braço no eixo de modo que o sensor aponte bem para a frente; assim, 90 graus significam “em frente”.
- Prenda o HC-SR04 ao braço com um suporte plástico ou cola quente. Os fios do sensor precisam ser longos o bastante para não esticar quando o servo gira; um cabo enroscando no chassi durante a varredura de 140 graus é um defeito comum.
- Monte o sensor a 8–12 cm do chão e perto do ponto mais à frente do corpo. Baixo demais, ele capta ecos do chão e da textura do tapete; alto demais, não enxerga obstáculos baixos.
- Incline o sensor 3–5 graus para cima. Um sensor perfeitamente nivelado e baixo pode confundir um eco do chão com um obstáculo, principalmente em pisos ásperos.
- Abra o Monitor Serial em 9600 baud. Se você vir valores pequenos nas linhas de 125 e 160 graus quando põe a mão à esquerda do sensor, o lado esquerdo corresponde aos ângulos grandes; se for o contrário, defina SERVO_DIR como -1 no sketch avançado e troque os valores de LEFT_ANGLE e RIGHT_ANGLE no primeiro 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 linha mostra um ângulo e a distância naquela direção. Se não houver nada a cerca de 4 metros do sensor, você vai ler 0 cm: o pulseIn() retorna 0 quando nenhum eco chega em 25 ms. No código principal vamos tratar 0 como “caminho livre”, mas um sensor com fio solto ou com TRIG e ECHO trocados também costuma ler 0. Por isso, antes de colocar o robô no chão, ponha a mão a 10, 30 e 60 cm e confira se os valores realmente mudam; um robô colocado para andar sem esse teste vai direto na parede sem diminuir a velocidade.
O algoritmo de decisão: parar, recuar, virar para a direção mais livre
Dividir o comportamento do robô em zonas por distância deixa o código simples e confiável. Sem obstáculo à frente, ele anda em velocidade máxima; vai reduzindo conforme o obstáculo se aproxima e, ao chegar ao limite de parada, para e faz a varredura. Durante a varredura, o sensor olha 70 e 35 graus para a esquerda e 35 e 70 graus para a direita; são feitas três leituras em cada direção e usa-se a mediana delas. O robô vira para a direção com a maior distância, naquele ângulo. Se nenhuma direção estiver livre, por exemplo quando ele entra num canto, ele recua um pouco e gira 180 graus.
| Distância medida | Zona | O que o robô faz |
|---|---|---|
| Acima de 60 cm (ou sem eco) | Livre | Em frente em velocidade máxima (PWM 200) |
| 25–60 cm | Aproximação | Reduz a velocidade conforme a distância (PWM de 200 até 130) |
| 12–25 cm | Obstáculo | Para, confirma a leitura, faz a varredura dos dois lados, vira para a direção mais livre |
| Abaixo de 12 cm | Perto demais | Primeiro recua por 350 ms, depois faz a varredura |
| Todas as direções abaixo de 35 cm | Beco sem saída | Recua e gira 180 graus |
Esses limites não foram escolhidos ao acaso. A distância de parada é a soma de três margens: a margem de reação (distância percorrida entre duas leituras), a margem de frenagem (quanto o robô desliza depois que os motores são cortados) e a margem de giro (para o nariz não encostar no obstáculo enquanto ele gira no lugar). Um carrinho com motores TT em 6 V anda a cerca de 0,5–0,7 m/s e percorre 3–4 cm em um intervalo de medição de 60 ms. A margem de frenagem é de uns 5 cm e, para um corpo de 15 cm de largura que gira no lugar, a margem de giro fica em torno de 10 cm. Isso soma cerca de 18–20 cm; com alguns centímetros de folga de segurança, STOP_CM = 25 é um bom ponto de partida. Se o seu robô for mais rápido ou o piso for escorregadio, aumente o limite para 30–35 cm. Em corredores estreitos, por outro lado, um limite grande demais faz o robô parar e varrer o tempo todo, então faça o ajuste final no seu próprio percurso.
Código passo a passo 1: seu primeiro robô que desvia de obstáculos
A primeira versão é o esqueleto do algoritmo: seguir em frente, parar a 25 cm, recuar, olhar para a esquerda e para a direita, virar para o lado mais livre. A função de motor drive(left, right) é a mesma do guia do carrinho robô e aceita valores de -255 a 255; positivo é para a frente, negativo é para trás. Depois de carregar o código, primeiro coloque o carrinho sobre uma caixa, com as rodas fora do chão, e observe como ele para e vira quando você aproxima a mão do sensor; se tudo parecer certo, coloque o robô no chão.
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 cumpre a tarefa, mas tem alguns pontos fracos: uma única leitura ruim faz o robô parar à toa, ele sempre se aproxima dos obstáculos na mesma velocidade, só olha em duas direções e o tempo de giro não depende do ângulo. E como ele espera com delay(), não consegue reagir a nada durante a varredura. Os detalhes da lógica de espera estão no artigo millis() e delay(); a próxima versão corrige a maioria desses problemas.
Código passo a passo 2: filtragem, redução gradual, IR e Bluetooth
A versão avançada funciona com a mesma ligação, e não tem problema se você ainda não instalou os sensores IR e o HC-05. Como as entradas IR são configuradas com INPUT_PULLUP, um sensor que não está ligado lê “sem obstáculo” (HIGH); sem um HC-05, o robô simplesmente começa no modo automático. A tabela abaixo explica o que cada parte do código faz.
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}| Função ou configuração | O que faz |
|---|---|
| pingCm() | Faz uma leitura; retorna 400 cm (espaço livre) se nenhum eco chegar em 25 ms |
| distanceCm() | Faz 3 leituras com 60 ms de intervalo e retorna a mediana; descarta um valor fora da curva |
| autoStep() | Mede a cada 60 ms; conforme a distância e os sensores IR, segue em frente, reduz, desvia ou chama avoid() |
| avoid() | Para, confirma a leitura, recua se necessário, olha em 4 direções e vira para a mais livre |
| turnBy(degrees) | Gira o robô no lugar pelo ângulo dado; o tempo é calculado com MS_PER_DEG |
| manualStep() | Aplica a direção enviada pelo celular; para a 15 cm quando anda para a frente e para se nenhum comando chegar em 600 ms |
| SERVO_CENTER, SERVO_DIR | O ângulo em que o sensor aponta para a frente e qual lado é a esquerda |
| STOP_CM, SLOW_CM, BACK_CM | Os limites de parada, de redução e de recuo |
Para um robô que desvia de obstáculos, o filtro de mediana é melhor que a média: para três leituras como 24, 26 e 400 cm, a média dá 150 cm e engana o robô, enquanto a mediana escolhe 26 cm. Se quiser se aprofundar, veja o artigo filtragem de dados de sensores. A divisão entre modo automático e manual também é, na verdade, uma pequena máquina de estados; explicamos essa abordagem em detalhes no guia máquina de estados no Arduino. Depois de carregar o código, ajuste o robô ao seu cômodo nesta ordem:
- Centro do servo: com o robô de frente e alinhado a uma parede lisa, se o sensor não apontar bem para ela, mude SERVO_CENTER em passos pequenos, como 85 ou 95.
- Tempo de giro: acrescente temporariamente uma linha turnBy(360); no fim do setup() e observe se o robô dá uma volta completa. Se ele girar 300 graus em vez de 360, defina MS_PER_DEG como 4.0 × 360 / 300 = 4.8. Como esse valor muda com a bateria e com o piso, ajuste-o com a bateria cheia no piso em que o robô vai andar.
- Limite de parada: mande o robô em direção a uma parede e meça a que distância o nariz dele fica da parede onde ele para. Se for menos de 10 cm, aumente STOP_CM em 5 cm.
- Velocidade lenta: se SLOW estiver abaixo do limite de partida dos motores, o robô empaca na zona de aproximação; nesse caso, aumente SLOW para 150.
Pontos cegos do HC-SR04: superfícies macias, paredes inclinadas e obstáculos finos
Um sensor ultrassônico enxerga superfícies que refletem bem o som, mas nem todo obstáculo devolve o som ao sensor. O fabricante recomenda um alvo de pelo menos 0,5 m², o mais plano possível, para bons resultados. Numa sala de verdade, porém, o robô encontra cortinas, sofás, pés de cadeira e cantos de parede. O som ricocheteia no mesmo ângulo em que chega, como uma bola de bilhar: um sensor de frente para uma parede recebe um eco forte, mas se a linha de visão dele se desviar mais de uns 30–40 graus da perpendicular, o som resvala para outra direção e nunca volta. Nesse caso, o pulseIn() retorna 0 e o código acha que o caminho está livre. Esse é o motivo mais comum de robôs raspando nas paredes.
| Obstáculo ou superfície | Por que não é visto | Solução |
|---|---|---|
| Cortinas, sofás, almofadas, roupas, pelo de animais | Superfícies macias e porosas absorvem o som e o eco volta muito fraco | Adicione um sensor IR para curta distância e faça o robô andar devagar |
| Aproximação de uma parede em ângulo | O som é refletido para longe e o eco não chega ao sensor | Adicione sensores IR inclinados para fora nos cantos dianteiros e varra em ângulos mais próximos |
| Pés de cadeira e mesa, postes finos | A superfície refletora é minúscula e o eco é fraco | Reduza a velocidade, use o filtro de mediana, adicione sensores IR nas laterais |
| Obstáculos baixos, abaixo do sensor (cabos, soleiras, chinelos) | Ficam abaixo do cone de som | Adicione um sensor IR voltado para baixo ou uma microchave no para-choque dianteiro |
| Bordas de mesa, vãos de escada | O sensor ultrassônico olha para a frente e não vê um vão no chão | Detecção de queda com um TCRT5000 voltado para o chão |
| Objetos a menos de 2 cm | Zona morta do sensor: o eco volta antes de o pulso de som terminar | Configure o robô para recuar a 12 cm |
| Cantos e corredores estreitos | O som rebate entre duas paredes e volta atrasado, então a distância parece maior | Filtro de mediana e leituras em vários ângulos |
Adicionando sensores IR
Os sensores ultrassônicos e os infravermelhos (IR) compensam as fraquezas uns dos outros. Sensores de obstáculo IR como o FC-51 têm um LED infravermelho e um receptor; quando a luz refletida é forte o bastante, o comparador LM393 coloca a saída em LOW e o LED da placa acende. O alcance é ajustável com um trimpot de cerca de 2 a 30 cm, mas ele não mede distância, só diz “obstáculo” ou “sem obstáculo”. Se você colocar dois sensores IR nos cantos dianteiros do robô, inclinados 30–45 graus para fora, eles detectam pés de cadeira e paredes inclinadas que ficam fora do cone estreito do sensor ultrassônico. No código avançado, o robô desvia rapidamente para a direita quando o IR esquerdo dispara e para a esquerda quando o IR direito dispara; se os dois dispararem ao mesmo tempo, ele para e faz a varredura como se houvesse um obstáculo à frente.
| Característica | HC-SR04 (ultrassônico) | FC-51 (obstáculo IR) | TCRT5000 (IR reflexivo) |
|---|---|---|---|
| Saída | Distância (cm) | Sim / não (digital) | Valor analógico e saída digital |
| Alcance | 2–400 cm | Cerca de 2–30 cm (ajustável) | Cerca de 1–15 mm |
| Ângulo de detecção | Cerca de 15° | Cerca de 35° | Muito estreito |
| Ponto fraco | Superfícies macias, inclinadas e finas | Superfícies pretas e foscas, luz do sol | Mudanças de altura, luz do sol |
| Enxerga bem | Vidro, objetos pretos, superfícies grandes e planas | Tecido, cantos, obstáculos próximos | Cor do chão e bordas de mesa |
| Posição no robô | Olho principal, girado pelo servo | Cantos dianteiros | Sob o para-choque dianteiro, voltado para o chão |

Para robôs que andam sobre uma mesa, o obstáculo mais perigoso é, na verdade, uma queda. Se você instalar um módulo TCRT5000 voltado para o chão sob o para-choque dianteiro, vai saber no instante em que o chão some embaixo dele. Ligue a saída digital do módulo no D2, que ainda está livre no Uno; primeiro confira no Monitor Serial para qual nível a saída vai quando não há chão, depois leia esse pino logo no início de autoStep() e, se o chão sumir, pare os motores na hora e faça o robô recuar. Essa verificação precisa vir antes da medição de distância; cair sempre custa mais caro que bater.
Passando para o modo manual por Bluetooth
Às vezes você vai querer dirigir o robô autônomo com as próprias mãos: para levá-lo de um cômodo a outro, colocá-lo diante de um obstáculo e testar a reação dele ou só para brincar. A conexão com o HC-05 no código avançado torna isso possível. Quando o celular envia a letra A, o robô passa para o modo automático; quando envia M, passa para o modo manual. No modo manual são usadas as letras de controle do guia do carrinho robô (F, B, L, R, S), com uma diferença: se o robô vir um obstáculo a menos de 15 cm enquanto anda para a frente, ele para, apesar do seu comando. Essa proteção contra colisão permite aproveitar os sensores do robô autônomo também na direção manual. Os passos de pareamento do HC-05 e os comandos AT estão no artigo módulo Bluetooth HC-05.
| Letra | Ação | Em qual modo? |
|---|---|---|
| A | Passar para o modo automático | Nos dois modos |
| M | Passar para o modo manual, parar o robô | Nos dois modos |
| F / B | Para a frente / para trás | Manual |
| L / R | Girar no lugar para a esquerda / direita | Manual |
| S | Parar | Manual |
| 0–9 | Nível de velocidade (PWM 100–240) | Manual |
- Ligue o pino TXD do HC-05 no D10, o pino RXD no D11 por um divisor de 1 kΩ / 2 kΩ, o VCC no 5V e o GND no GND.
- Pareie o HC-05 nas configurações de Bluetooth do seu celular Android com o PIN 1234 (ou 0000, se não funcionar).
- Instale pela Play Store um app de controle remoto de carrinho Bluetooth e conecte-o ao HC-05. Se você deixar o Arduino na USB e abrir o Monitor Serial em 9600 baud, o código mostra cada letra que chega do celular.
- A maioria dos apps de controle tem botões extras, como luzes ou buzina, e cada um envia sua própria letra. Descubra essas letras no Monitor Serial e coloque-as no lugar de 'A' e 'M' no código; se preferir definir os botões você mesmo, pode criar seu próprio controle com o guia App Inventor com Arduino e Bluetooth.
- Desconecte o cabo USB e ligue a chave; depois de 3 segundos, o robô parte no modo automático. Passe para o modo manual e dirija-o pelo celular.
Montando com um shield de motor L293D

Se você não gosta de um emaranhado de fios, pode usar no lugar do L298N um shield de motor L293D (o Adafruit Motor Shield V1 e seus clones), que encaixa direto no Arduino Uno. O shield aciona quatro motores CC, tem duas barras de pinos para servos e alimenta os servos pelo barramento de 5 V do Arduino. Os motores não são ligados diretamente aos pinos do Arduino, mas pelo registrador de deslocamento 74HC595 da placa, então você os controla com a biblioteca Adafruit Motor Shield (a da V1, não a da V2), ou seja, a AFMotor, instalada pelo Gerenciador de Bibliotecas. O shield usa D3, D4, D5, D6, D7, D8, D11 e D12 para os motores e D9 e D10 para os servos; A0–A5, D2 e D13 ficam livres. Como o HC-SR04 e os sensores IR já estão em A0–A3, você só precisa mudar o código dos motores, o pino do servo e os pinos do HC-05.
| Configuração | Com o L298N | Com o shield L293D |
|---|---|---|
| Ligação dos motores | Terminais OUT1–OUT4 | Terminais M1 (esquerdo) e M2 (direito) |
| Código dos motores | digitalWrite e analogWrite dentro de drive() | AFMotor dentro de drive() |
| SERVO_PIN | 3 | 9 ou 10, conforme a barra de pinos em que você ligar o servo |
| HC-05 | D10, D11 | A4 (RX) e A5 (TX): SoftwareSerial bt(A4, A5) |
| Corrente por canal | 2 A contínuos | 0,6 A contínuos, 1,2 A de pico |
| Alimentação dos motores | Terminal 12V | Terminal EXT_PWR; com o jumper PWR colocado, o Arduino funciona com a mesma fonte |
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}O limite de 0,6 A por canal do L293D basta para a direção normal de um robô 2WD com motores TT, mas quando uma roda empurra um obstáculo e trava, o motor pode puxar mais de 1 A e o chip esquenta rápido. Por isso, um algoritmo de varredura que para o robô antes de ele forçar contra um obstáculo também aumenta a vida útil do shield. Os ajustes de jumper do shield e um sketch de carrinho com quatro motores estão no artigo shield de motor L293D, e o funcionamento interno do L298N no guia controle de motor CC com L298N.
Robô que desvia de obstáculos com ESP32
Há duas diferenças importantes no ESP32. Primeiro, a biblioteca Servo do Arduino não funciona no ESP32, então geramos o sinal do servo diretamente com o LEDC, a unidade de PWM da placa: em um sinal de 50 Hz, um pulso de 0,544–2,4 ms corresponde a 0–180 graus. Como os motores funcionam a 1 kHz e o servo a 50 Hz, os canais dos motores são configurados primeiro e o canal do servo depois, para que o servo tenha seu próprio timer. Segundo, o ESP32 tem Bluetooth clássico embutido, então não é preciso um HC-05; o robô aparece no celular como “BlueGrays-Robot” e as letras de direção e de modo são as mesmas da versão do Uno. O código é para a versão 3.x do core Arduino do ESP32 (ledcAttach e ledcWrite trabalham com números de pino) e, como precisa de Bluetooth clássico, não compila para o ESP32-S3 nem para o 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}Robô que desvia de obstáculos com Raspberry Pi Pico (MicroPython)
No Raspberry Pi Pico, a função time_pulse_us() do MicroPython funciona como o pulseIn() do Arduino; ela retorna um valor negativo quando nenhum eco chega, então tratamos isso como 400 cm, ou seja, espaço livre. Para o servo usamos PWM de 50 Hz e duty_ns(), que recebe a largura do pulso em nanossegundos. O servo no GP16 e os pinos de velocidade dos motores no GP15 e no GP10 ficam em fatias de PWM diferentes, então um não atrapalha a frequência do outro. Desta vez, as velocidades são dadas em porcentagem. O Pico não tem Bluetooth, então, se você quiser o modo manual, pode ligar um HC-05 na UART0 (GP0, GP1), como no guia do carrinho robô.
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 readingsIdeias de projetos com um robô que desvia de obstáculos
- Tela de radar: envie os ângulos e as distâncias da varredura pela porta serial e desenhe uma tela de radar verde no computador; detalhes no artigo projeto de radar com Arduino.
- Linha mais obstáculo: faça o robô contornar uma caixa no caminho enquanto segue uma pista, como um robô seguidor de linha, e depois voltar à linha; é uma tarefa comum em competições.
- Robô que segue a parede: mantenha o sensor virado 60 graus para a direita e ande a uma distância constante de 20 cm da parede; é o primeiro passo para resolver labirintos.
- Robô com display: mostre a distância e o modo em um pequeno display OLED de 0,96 polegada; a depuração fica muito mais fácil.
- Protótipo de robô aspirador: esboce um robô de limpeza que anda pela casa com escolhas aleatórias de direção, chaves de impacto e um sensor de queda.
- Bengala inteligente: transforme a mesma lógica de medição de distância em um aviso por vibração para pessoas com deficiência visual; veja bengala inteligente com Arduino.
- Projeto escolar ou de competição: apresente-o com uma pista de obstáculos, cronometragem e uma demonstração dos modos automático e manual; para ideias, dê uma olhada no artigo ideias de projetos Arduino para feira de ciências.
Erros comuns e como resolvê-los
| Problema | Causa provável | Solução |
|---|---|---|
| O robô bate na parede sem parar | O sensor lê 0 (fio rompido, TRIG e ECHO trocados) e o código trata isso como espaço livre | Rode o sketch de teste, teste 10–60 cm com a mão, confira os pinos TRIG e ECHO |
| Ele raspa na parede ao se aproximar em ângulo | O som é refletido pela superfície inclinada em outra direção | Adicione sensores IR nos cantos dianteiros e reduza a velocidade |
| O Arduino reinicia quando para para fazer a varredura | A corrente do servo e dos motores derruba o barramento de 5 V | Capacitor de 470 µF no servo, células 18650 totalmente carregadas; se precisar, alimente o servo por uma fonte de 5 V separada |
| O robô para e faz a varredura sem motivo | Uma única leitura ruim ou um eco do chão | Suba o sensor para 8–12 cm e incline-o levemente para cima; use a confirmação por mediana |
| O robô vira para o lado bloqueado | O servo está montado ao contrário, esquerda e direita estão trocadas | Defina SERVO_DIR como -1 |
| As curvas ficam curtas ou longas demais | MS_PER_DEG não está calibrado ou a bateria está fraca | Meça com turnBy(360) e reajuste com a bateria cheia |
| Ele fica virando para a esquerda e para a direita em um canto | Os dois lados estão perto do limite e a decisão fica alternando | Aumente o limite de beco sem saída (STOP_CM + 10) ou faça-o preferir a última direção de curva |
| A cabeça com o sensor treme | Alimentação fraca do servo ou interrupções da SoftwareSerial | Encurte o cabo do servo, adicione um capacitor, evite enviar comandos Bluetooth com mais frequência que o necessário |
Por reunir leitura de sensores, filtragem, tomada de decisão e controle de motores em um único projeto, o robô que desvia de obstáculos é a melhor porta de entrada para a robótica. Primeiro observe o comportamento básico com o primeiro sketch, depois ajuste os limites ao seu cômodo com a versão avançada e, por fim, reforce seu robô com sensores IR e Bluetooth. No app BlueGrays você encontra, passo a passo e com esquemas do circuito e código, os projetos de robô que desvia de obstáculos, carrinho controlado por Bluetooth com L298N, medição de distância com HC-SR04 e sensor de estacionamento.
Perguntas frequentes
Do que você precisa para montar um robô que desvia de obstáculos?
Um robô básico que desvia de obstáculos precisa de um Arduino Uno, um sensor ultrassônico HC-SR04, um servo SG90, um driver de motor L298N ou L293D, um kit de chassi 2WD com dois motores CC com redução e duas células 18650. Você pode adicionar dois sensores de obstáculo IR para enxergar os cantos e um módulo Bluetooth HC-05 para dirigi-lo pelo celular.
Dá para montar um robô que desvia de obstáculos sem servo?
Sim. O sensor aponta para a frente; quando o robô vê um obstáculo, primeiro vira para um lado e mede de novo e, se o caminho continuar bloqueado, vira para o outro lado. Isso sai mais barato, mas o robô decide mais devagar, porque precisa girar o corpo inteiro a cada tentativa; com um servo, ele encontra a direção mais livre antes de virar.
Por que o HC-SR04 não vê alguns obstáculos?
Um sensor ultrassônico mede pelo eco. Superfícies macias como cortinas e sofás absorvem o som, paredes inclinadas o refletem em outra direção e objetos finos como pés de cadeira dão um eco muito fraco. O sensor também não enxerga obstáculos fora ou abaixo do seu cone de cerca de 15 graus; você pode cobrir essas falhas com sensores IR.
A que distância um robô que desvia de obstáculos deve parar?
25 cm é um bom valor inicial: a distância percorrida entre duas leituras, a distância de frenagem e a margem necessária para girar no lugar somam cerca de 20 cm, mais uma pequena folga de segurança. Para robôs mais rápidos e pisos escorregadios, aumente para 30–35 cm; com um obstáculo a menos de 12 cm, o robô deve recuar primeiro.
Qual é melhor para um robô, o L298N ou um shield L293D?
O L298N fornece 2 A por canal, então é mais robusto e permite escolher os pinos livremente. O shield L293D encaixa no Uno, por isso reduz a fiação e aciona quatro motores, mas sua corrente contínua é de 0,6 A por canal e ele ocupa muitos pinos. Para um robô 2WD pequeno com motores TT, qualquer um dos dois serve.
Dá para controlar um robô que desvia de obstáculos pelo celular?
Sim. Adicione um módulo Bluetooth HC-05 e envie letras de um app de controle remoto em um celular Android para alternar entre os modos automático e manual. Usar o sensor ultrassônico como proteção contra colisão no modo manual impede que você jogue o robô contra uma parede. Com um ESP32, você não precisa de um módulo separado, graças ao Bluetooth embutido.
Projetos para montar depois desta leitura
Ver todos os projetos
Medição de distância com HC-SR04 (Monitor Serial)
O sensor HC-SR04 funciona como um morcego que se orienta no escuro: emite um som que você não consegue ouvir, escuta o eco e mede a distância até o objeto à sua frente. O Arduino escreve essa distânci

Robô que desvia de obstáculos
Que tal construir um veículo inteligente que encontra seu próprio caminho? O robô que desvia de obstáculos monitora o ambiente constantemente com o sensor ultrassônico na parte da frente. Ao encontrar

Carrinho controlado por Bluetooth com L298N
Transforme seu celular em um controle remoto e dirija seu próprio carrinho robô. O Arduino recebe os comandos de um aplicativo de controle remoto no celular por meio de um módulo Bluetooth e aciona as

Uso de servo motor
O braço de um pequeno servo motor gira para três posições em sequência: o início, o meio e o fim do percurso, esperando dois segundos em cada parada. O Arduino informa ao motor em qual ângulo ficar, e

Sensor de estacionamento com HC-SR04
Uma versão em miniatura do sensor de estacionamento de um carro: quanto mais perto um objeto chega, mais rápidos ficam os bipes. O sensor de distância mede a distância até o objeto à sua frente; se el

Leitura de reflexão (proximidade) com TCRT5000
Com um pequeno sensor que emite luz invisível e mede quanto dela é refletido, você detecta a superfície à frente dele. Quanto mais o sensor se aproxima de um objeto ou de uma superfície clara, mais lu

Detecção de superfícies pretas e brancas com TCRT5000
Com o pequeno sensor que funciona como o olho dos robôs seguidores de linha, você identifica se o piso é preto ou branco. O TCRT5000 emite luz invisível em direção ao piso e verifica quanto dela é ref

Medidor ultrassônico de distância HC-SR04 com ESP32
O sensor ultrassônico HC-SR04 calcula a distância medindo o tempo que a onda sonora emitida leva para atingir um objeto e retornar (tempo de voo). Neste projeto, enviaremos um pulso de 10 microssegundos do ESP32 ao pino TRIG do sensor e leremos a duração do eco no pino ECHO com pulseIn(). Exibiremos a distância calculada em centímetros em um LCD I2C 16x2. Como o HC-SR04 opera a 5V, é preciso usar um divisor de tensão no pino ECHO; caso contrário, o GPIO de 3,3V do ESP32 será danificado.
Mais artigos
App BlueGrays
Monte este circuito passo a passo pelo celular
Esquema do circuito, código e lista de componentes em uma só tela. Mais de 200 projetos com Arduino, ESP32 e Pico prontos para montar.
Escaneie com seu celular
Aponte a câmera para o código para abrir a loja de aplicativos do seu celular.
ou abrir a loja

