Robot evita ostacoli con Arduino: HC-SR04 e servo passo passo
Robot evita ostacoli con Arduino: HC-SR04 su servo, collegamento L298N o L293D, logica stop-retro-svolta, soglie di distanza, sensori IR e modalità Bluetooth.
Un robot evita ostacoli è una macchinina robot autonoma che riconosce un muro, una sedia o una scatola davanti a sé senza urtarli e si trova una nuova strada. La versione più diffusa usa un Arduino Uno, un sensore a ultrasuoni HC-SR04, un servomotore SG90 che ruota il sensore a sinistra e a destra, due motoriduttori in corrente continua e un driver motori L298N (o L293D). Il robot procede dritto, si ferma quando si avvicina a un ostacolo, arretra un po', gira la testa da entrambi i lati per misurare e poi riparte nella direzione più libera.
In questa guida trasformiamo la macchinina 2WD del nostro articolo macchinina robot con Arduino in un robot autonomo, mantenendo la stessa piedinatura. Passo dopo passo vediamo le tabelle dei collegamenti, come si calcolano le soglie di distanza, un codice che cresce in tre fasi, le superfici che il sensore non vede e come coprire questi vuoti con sensori a infrarossi e, infine, il passaggio alla modalità manuale via Bluetooth dallo smartphone. In fondo all'articolo trovi anche il codice per guidare lo stesso robot con un ESP32 o un Raspberry Pi Pico.

Come funziona un robot evita ostacoli?
Ogni robot autonomo ripete lo stesso ciclo in tre passi decine di volte al secondo: percepire, decidere, agire. La percezione è affidata all'HC-SR04: emette un breve suono a 40 kHz che il tuo orecchio non sente e misura quanto impiega l'eco a tornare; dividendo questo tempo in microsecondi per 58 ottieni la distanza in centimetri. La decisione spetta ad Arduino, che confronta la distanza misurata con le soglie che hai impostato. All'azione pensano l'L298N e i motori. Dato che il sensore è montato su un servo, il robot può "guardare" a sinistra e a destra senza girare il telaio, e così sceglie le svolte molto meglio di un robot con sensore fisso. I dettagli sul funzionamento del sensore sono nell'articolo Sensore di distanza HC-SR04.
| Componente | Compito | Nota |
|---|---|---|
| Arduino Uno (o Nano) | Cervello: misura, decide e comanda i motori | Vanno bene anche ESP32 o Pico |
| HC-SR04 | Occhio: misura la distanza dall'ostacolo davanti | Funziona a 5 V, 2–400 cm |
| Servo SG90 | Collo: ruota il sensore a sinistra e a destra | Il sensore si fissa alla squadretta del servo con un supporto in plastica |
| L298N o shield L293D | Muscoli: danno direzione e velocità ai motori | Un pin di Arduino non può fornire la corrente del motore |
| 2 motoriduttori TT e ruota pivotante | Trazione differenziale | Sterza facendo girare le due ruote in versi opposti |
| 2 batterie 18650 (7,4 V) | Energia | Una pila da 9 V non basta |
| 2 sensori IR di ostacolo (opzionali) | Rilevano ostacoli agli angoli e ai lati | Moduli con LM393 come l'FC-51 |
| HC-05 (opzionale) | Controllo manuale dallo smartphone | Funziona solo con Android |




Alimentazione: evitare che il servo resetti il robot
Dato che il robot evita ostacoli aggiunge un servo ai motori, la distribuzione dell'alimentazione conta più che su una normale macchinina robot. Un SG90 assorbe circa 10 mA da fermo; appena inizia a muoversi sale a 100–250 mA e, sotto sforzo, si avvicina a mezzo ampere. Se questo picco coincide con la corrente di spunto dei motori, la linea a 5 V di Arduino crolla per un istante e la scheda si riavvia. La soluzione è semplice: i motori si alimentano dalla batteria tramite l'L298N, Arduino dalla batteria tramite VIN, mentre servo, HC-SR04, sensori IR e HC-05 condividono il pin 5V di Arduino, con un condensatore da 470 µF vicino ai fili di alimentazione del servo. Anche con il servo in movimento, il carico totale sulla linea a 5 V resta sotto il mezzo ampere e, dato che dura poco, il regolatore dell'Uno lo regge. Per calcolare l'autonomia con i tuoi valori usa lo strumento Durata della batteria.
| Parte | Alimentata da | Corrente tipica |
|---|---|---|
| 2 motori TT | Morsetto 12V dell'L298N ← 2 × 18650 (7,4 V) | 0,3–0,6 A in marcia, fino a 2 A alla partenza |
| Arduino Uno | VIN ← positivo della batteria (dopo l'interruttore) | Circa 50 mA |
| Servo SG90 | 5V di Arduino + condensatore da 470 µF | ~10 mA da fermo, 100–250 mA in movimento |
| HC-SR04 | 5V di Arduino | Circa 15 mA |
| 2 sensori IR | 5V di Arduino | 20–40 mA ciascuno |
| HC-05 | 5V di Arduino | 30–40 mA in accoppiamento, circa 10 mA connesso |
Schema dei collegamenti: Arduino Uno, ESP32 e Pico
I pin dei motori sono gli stessi della guida alla macchinina robot, quindi puoi passare a questo progetto aggiungendo solo la testa del sensore, senza smontare la macchinina. Mettere i pin di velocità su 5 e 6 è una scelta voluta: sull'Uno la libreria Servo usa il Timer1 e quindi disattiva il PWM sui pin 9 e 10; se le velocità dei motori fossero lì, il controllo di velocità smetterebbe di funzionare appena collegato il servo. HC-SR04 e sensori IR vanno su A0–A3. I pin analogici dell'Uno funzionano all'occorrenza anche come pin digitali 14–19, e questa scelta rende facile usare lo stesso codice con uno shield L293D, perché lo shield non tocca questi pin. Puoi vedere la piedinatura della tua scheda nello strumento Pinout.
| Collegamento | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| L298N ENA (velocità motore sinistro) | D5 | GPIO 25 | GP15 |
| L298N IN1, IN2 | D7, D8 | GPIO 27, 33 | GP14, GP13 |
| L298N ENB (velocità motore destro) | D6 | GPIO 26 | GP10 |
| L298N IN3, IN4 | D9, D4 | GPIO 32, 4 | GP12, GP11 |
| Segnale del servo (arancione) | D3 | GPIO 13 | GP16 |
| Servo + (rosso), − (marrone) | 5V, GND | 5V dell'L298N, GND | 5V dell'L298N, GND |
| HC-SR04 TRIG | A0 | GPIO 18 | GP17 |
| HC-SR04 ECHO | A1 (diretto) | GPIO 19, tramite partitore 1 kΩ / 2 kΩ | GP18, tramite partitore 1 kΩ / 2 kΩ |
| HC-SR04 VCC | 5V | 5V dell'L298N | 5V dell'L298N |
| OUT IR sinistro, OUT IR destro | A2, A3 | GPIO 23, 22 | GP19, GP20 |
| VCC dei sensori IR | 5V | 3V3 | 3V3 |
| HC-05 TXD, RXD | D10, D11 (RXD tramite partitore) | Non serve (Bluetooth integrato) | GP1, GP0 (UART0) |
| Alimentazione della scheda | VIN ← batteria (dopo l'interruttore) | 5V (VIN) ← 5V dell'L298N | VSYS ← 5V dell'L298N |
Montare e provare la testa del sensore

- Collega il servo senza squadretta e carica lo sketch di prova qui sotto. Alla fine di ogni scansione il codice porta il servo a 90 gradi e aspetta 2 secondi; stacca il cavo USB durante questa pausa e il servo resta fermo dov'è. Monta la squadretta sull'albero in modo che il sensore guardi dritto davanti; così 90 gradi significa "dritto".
- Fissa l'HC-SR04 alla squadretta con un supporto in plastica o con colla a caldo. I cavi del sensore devono essere abbastanza lunghi da non tendersi quando il servo ruota; un cavo che si impiglia nel telaio durante la scansione di 140 gradi è un guasto frequente.
- Monta il sensore a 8–12 cm da terra, vicino al punto più avanzato del telaio. Troppo in basso riceve echi dal pavimento e dalla trama del tappeto; troppo in alto non vede gli ostacoli bassi.
- Inclina il sensore di 3–5 gradi verso l'alto. Un sensore perfettamente orizzontale e montato in basso può scambiare un'eco del pavimento per un ostacolo, soprattutto sui pavimenti ruvidi.
- Apri il Monitor seriale a 9600 baud. Se mettendo la mano a sinistra del sensore vedi valori piccoli nelle righe a 125 e 160 gradi, la sinistra corrisponde agli angoli grandi; se succede il contrario, imposta SERVO_DIR a -1 nello sketch avanzato e scambia i valori di LEFT_ANGLE e RIGHT_ANGLE nel primo 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}Ogni riga mostra un angolo e la distanza in quella direzione. Se davanti al sensore non c'è niente entro circa 4 metri, leggerai 0 cm: pulseIn() restituisce 0 se non arriva nessuna eco entro 25 ms. Nel codice principale tratteremo lo 0 come "strada libera", ma anche un sensore con un cavo staccato o con TRIG ed ECHO invertiti di solito legge 0. Perciò, prima di mettere il robot a terra, tieni la mano a 10, 30 e 60 cm e controlla che i valori cambino davvero; un robot avviato senza questa prova va a sbattere contro il muro senza rallentare.
L'algoritmo di decisione: fermarsi, arretrare, girare verso la direzione più libera
Suddividere il comportamento del robot in zone in base alla distanza rende il codice semplice e affidabile. Senza ostacoli davanti avanza alla massima velocità, rallenta man mano che un ostacolo si avvicina e, quando raggiunge la soglia di stop, si ferma e fa una scansione. Durante la scansione il sensore guarda a 70 e 35 gradi a sinistra e a 35 e 70 gradi a destra; in ogni direzione si fanno tre misure e si usa la mediana. Il robot gira verso la direzione con la distanza maggiore, esattamente di quell'angolo. Se nessuna direzione è libera, per esempio perché è finito in un angolo, arretra un po' e gira di 180 gradi.
| Distanza misurata | Zona | Cosa fa il robot |
|---|---|---|
| Oltre 60 cm (o nessuna eco) | Libera | Avanti alla massima velocità (PWM 200) |
| 25–60 cm | Avvicinamento | Rallenta in proporzione alla distanza (da PWM 200 a 130) |
| 12–25 cm | Ostacolo | Si ferma, conferma la misura, scansiona entrambi i lati e gira verso la direzione più libera |
| Meno di 12 cm | Troppo vicino | Prima arretra per 350 ms, poi scansiona |
| Tutte le direzioni sotto i 35 cm | Vicolo cieco | Arretra e gira di 180 gradi |
Queste soglie non sono scelte a caso. La distanza di arresto è la somma di tre margini: il margine di reazione (la strada percorsa tra due misure), il margine di frenata (quanto scivola il robot dopo che i motori si spengono) e il margine di rotazione (perché il muso non tocchi l'ostacolo mentre gira sul posto). Una macchinina con motori TT a 6 V va a circa 0,5–0,7 m/s e percorre 3–4 cm in un intervallo di misura di 60 ms. Il margine di frenata è di circa 5 cm e, per un telaio largo 15 cm che gira sul posto, il margine di rotazione è intorno ai 10 cm. In totale fanno circa 18–20 cm; aggiungendo qualche centimetro di margine di sicurezza, STOP_CM = 25 è un buon punto di partenza. Se il tuo robot è più veloce o il pavimento è scivoloso, alza la soglia a 30–35 cm. Nei corridoi stretti, invece, una soglia troppo grande fa sì che il robot si fermi e scansioni di continuo; fai la regolazione finale sul tuo percorso.
Codice passo passo 1: il tuo primo robot evita ostacoli
La prima versione è lo scheletro dell'algoritmo: avanzare, fermarsi a 25 cm, arretrare, guardare a sinistra e a destra e girare verso il lato più libero. La funzione dei motori drive(sinistro, destro) è la stessa della guida alla macchinina robot e accetta valori da -255 a 255; positivo significa avanti, negativo indietro. Dopo aver caricato il codice, appoggia prima la macchinina su una scatola con le ruote sollevate e osserva come si ferma e gira quando avvicini la mano al sensore; se va tutto bene, metti il robot a terra.
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}Questo codice fa il suo lavoro, ma ha qualche punto debole: una sola misura sbagliata ferma il robot senza motivo, si avvicina agli ostacoli sempre alla stessa velocità, guarda solo in due direzioni e i tempi di rotazione non dipendono dall'angolo. Inoltre, dato che aspetta con delay(), durante la scansione non può reagire a nulla. I dettagli sulla logica di attesa sono nell'articolo millis() e delay(); la versione successiva risolve la maggior parte di questi problemi.
Codice passo passo 2: filtro, rallentamento graduale, IR e Bluetooth
La versione avanzata funziona con gli stessi collegamenti, e non è un problema se non hai ancora montato i sensori IR e l'HC-05. Dato che gli ingressi IR sono impostati con INPUT_PULLUP, un sensore non collegato legge "nessun ostacolo" (HIGH); senza HC-05 il robot parte semplicemente in modalità automatica. Nella tabella qui sotto trovi cosa fa ogni parte del codice.
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}| Funzione o impostazione | Cosa fa |
|---|---|
| pingCm() | Esegue una misura; restituisce 400 cm (spazio libero) se non arriva eco entro 25 ms |
| distanceCm() | Fa 3 misure a 60 ms di distanza e restituisce la mediana; scarta un singolo valore anomalo |
| autoStep() | Misura ogni 60 ms; in base alla distanza e agli IR avanza, rallenta, schiva o chiama avoid() |
| avoid() | Si ferma, conferma la misura, arretra se serve, guarda in 4 direzioni e gira verso la più libera |
| turnBy(gradi) | Fa ruotare il robot sul posto dell'angolo indicato; il tempo si calcola con MS_PER_DEG |
| manualStep() | Applica la direzione inviata dallo smartphone; in avanti si ferma a 15 cm e si arresta se per 600 ms non arrivano comandi |
| SERVO_CENTER, SERVO_DIR | L'angolo a cui il sensore guarda dritto e il verso corrispondente alla sinistra |
| STOP_CM, SLOW_CM, BACK_CM | Soglie di arresto, rallentamento e retromarcia |
Per un robot evita ostacoli il filtro mediano è meglio della media: con tre misure come 24, 26 e 400 cm la media risulta 150 cm e inganna il robot, mentre la mediana sceglie 26 cm. Se vuoi approfondire, leggi l'articolo filtrare i dati dei sensori. Anche la separazione tra modalità automatica e manuale è in realtà una piccola macchina a stati; spieghiamo questo approccio nel dettaglio nella guida macchina a stati con Arduino. Dopo aver caricato il codice, regola il robot sulla tua stanza in quest'ordine:
- Centro del servo: con il robot perpendicolare a un muro liscio, se il sensore non punta dritto verso di esso, cambia SERVO_CENTER a piccoli passi, per esempio 85 o 95.
- Tempo di rotazione: aggiungi temporaneamente la riga turnBy(360); alla fine di setup() e guarda se il robot compie un giro completo. Se gira di 300 gradi invece di 360, imposta MS_PER_DEG a 4,0 × 360 / 300 = 4,8. Dato che questo valore cambia con la batteria e il pavimento, regolalo con la batteria carica sul pavimento dove il robot dovrà muoversi.
- Soglia di stop: manda il robot verso un muro e misura a che distanza dal muro si trova il muso quando si ferma. Se è meno di 10 cm, aumenta STOP_CM di 5 cm.
- Velocità lenta: se SLOW è sotto la soglia di avvio dei motori, il robot resta fermo nella zona di avvicinamento; in quel caso alza SLOW a 150.
Punti ciechi dell'HC-SR04: superfici morbide, pareti inclinate e ostacoli sottili
Un sensore a ultrasuoni vede bene le superfici che riflettono il suono, ma non tutti gli ostacoli lo rimandano verso il sensore. Il produttore consiglia un bersaglio di almeno 0,5 m² e il più liscio possibile per ottenere buoni risultati. In una stanza reale, però, il robot incontra tende, divani, gambe di sedie e angoli. Il suono rimbalza con lo stesso angolo con cui arriva, come una palla da biliardo: un sensore rivolto frontalmente a un muro riceve un'eco forte, ma se la sua direzione si discosta di più di circa 30–40 gradi dalla perpendicolare, il suono viene deviato altrove e non torna mai. In quel caso pulseIn() restituisce 0 e il codice crede che la strada sia libera. È il motivo più comune per cui i robot strisciano contro i muri.
| Ostacolo o superficie | Perché non viene visto | Soluzione |
|---|---|---|
| Tende, divani, cuscini, vestiti, pelo di animali | Le superfici morbide e porose assorbono il suono e l'eco torna molto debole | Aggiungi un sensore IR per la breve distanza e guida il robot piano |
| Avvicinarsi a un muro in diagonale | Il suono viene riflesso altrove e l'eco non raggiunge il sensore | Aggiungi agli angoli anteriori sensori IR inclinati verso l'esterno e infittisci gli angoli di scansione |
| Gambe di sedie e tavoli, pali sottili | La superficie riflettente è minuscola e l'eco è debole | Riduci la velocità, usa il filtro mediano, aggiungi IR ai lati |
| Ostacoli bassi sotto il sensore (cavi, soglie, pantofole) | Restano sotto il cono del suono | Aggiungi al paraurti anteriore un sensore IR rivolto in basso o un microinterruttore |
| Bordi del tavolo, trombe delle scale | Il sensore a ultrasuoni guarda avanti e non vede un vuoto nel pavimento | Rilevamento del vuoto con un TCRT5000 rivolto al pavimento |
| Oggetti a meno di 2 cm | Zona morta del sensore: l'eco torna prima che l'emissione del suono sia finita | Imposta il robot in modo che arretri a 12 cm |
| Angoli e corridoi stretti | Il suono rimbalza tra due pareti e torna in ritardo, quindi la distanza sembra maggiore | Filtro mediano e misure da più angoli |
Rinforzo con sensori IR
I sensori a ultrasuoni e a infrarossi (IR) compensano a vicenda i propri limiti. I sensori IR di ostacolo come l'FC-51 sono formati da un LED a infrarossi e da un ricevitore; quando la luce riflessa è abbastanza forte, il comparatore LM393 porta l'uscita a LOW e il LED sulla scheda si accende. La portata si regola con un trimmer tra circa 2 e 30 cm, ma il sensore non misura la distanza: dice solo "ostacolo" o "nessun ostacolo". Se metti due sensori IR agli angoli anteriori del robot, inclinati di 30–45 gradi verso l'esterno, intercetti le gambe delle sedie e le pareti inclinate che restano fuori dal cono stretto degli ultrasuoni. Nel codice avanzato il robot schiva brevemente a destra quando scatta l'IR sinistro e a sinistra quando scatta quello destro; se scattano entrambi insieme, si ferma e scansiona come se avesse un ostacolo davanti.
| Caratteristica | HC-SR04 (ultrasuoni) | FC-51 (IR di ostacolo) | TCRT5000 (IR a riflessione) |
|---|---|---|---|
| Uscita | Distanza (cm) | Sì / no (digitale) | Valore analogico e uscita digitale |
| Portata | 2–400 cm | Circa 2–30 cm (regolabile) | Circa 1–15 mm |
| Angolo di rilevamento | Circa 15° | Circa 35° | Molto stretto |
| Punto debole | Superfici morbide, inclinate e sottili | Superfici nere e opache, luce del sole | Variazioni di altezza, luce del sole |
| Vede bene | Vetro, oggetti neri, grandi superfici lisce | Tessuti, angoli, ostacoli vicini | Colore del pavimento e bordi del tavolo |
| Posizione sul robot | Occhio principale, mosso dal servo | Angoli anteriori | Sotto il paraurti anteriore, rivolto al pavimento |

Per i robot che girano su un tavolo, l'ostacolo più pericoloso è in realtà il vuoto. Se monti sotto il paraurti anteriore un modulo TCRT5000 rivolto verso il pavimento, saprai subito quando il pavimento sparisce. Collega l'uscita digitale del modulo a D2, ancora libero sull'Uno; controlla prima nel Monitor seriale quale livello assume l'uscita quando manca il pavimento, poi leggi questo pin all'inizio di autoStep() e, se il pavimento è sparito, ferma subito i motori e fai arretrare il robot. Questo controllo deve venire prima della misura di distanza; cadere costa sempre più che urtare.
Passare alla modalità manuale via Bluetooth
A volte vorrai guidare a mano il robot autonomo: per portarlo da una stanza all'altra, metterlo davanti a un ostacolo e provarne la reazione o semplicemente per giocare. Il collegamento con l'HC-05 del codice avanzato lo rende possibile. Quando lo smartphone invia la lettera A, il robot passa alla modalità automatica; quando invia M, passa a quella manuale. In modalità manuale si usano le lettere di comando della guida alla macchinina robot (F, B, L, R, S), con una differenza: se il robot vede un ostacolo a meno di 15 cm mentre va avanti, si ferma nonostante il tuo comando. Questa protezione anticollisione ti permette di sfruttare i sensori del robot autonomo anche nella guida manuale. I passaggi per accoppiare l'HC-05 e i comandi AT sono nell'articolo Modulo Bluetooth HC-05.
| Lettera | Azione | In quale modalità? |
|---|---|---|
| A | Passa alla modalità automatica | In entrambe |
| M | Passa alla modalità manuale, ferma il robot | In entrambe |
| F / B | Avanti / indietro | Manuale |
| L / R | Ruota sul posto a sinistra / destra | Manuale |
| S | Stop | Manuale |
| 0–9 | Livello di velocità (PWM 100–240) | Manuale |
- Collega il pin TXD dell'HC-05 a D10, il pin RXD a D11 tramite un partitore 1 kΩ / 2 kΩ, VCC al 5V e GND al GND.
- Accoppia l'HC-05 nelle impostazioni Bluetooth del tuo smartphone Android con il PIN 1234 (o 0000 se non funziona).
- Installa dal Play Store un'app di telecomando per auto Bluetooth e collegala all'HC-05. Se tieni Arduino collegato via USB e apri il Monitor seriale a 9600 baud, il codice mostra ogni lettera che arriva dallo smartphone.
- La maggior parte delle app di telecomando ha tasti extra, come luci o clacson, e ognuno invia una propria lettera. Leggi queste lettere nel Monitor seriale e mettile nel codice al posto di 'A' e 'M'; se preferisci definire tu i tasti, puoi crearti un telecomando con la guida App Inventor con Arduino via Bluetooth.
- Stacca il cavo USB e accendi l'interruttore; dopo 3 secondi il robot parte in modalità automatica. Passa alla modalità manuale e guidalo dallo smartphone.
Montaggio con uno shield motori L293D

Se non ti piace il groviglio di cavi, al posto dell'L298N puoi usare uno shield motori L293D (l'Adafruit Motor Shield V1 e i suoi cloni) che si innesta direttamente sopra l'Arduino Uno. Lo shield può pilotare quattro motori in corrente continua, ha due connettori per servo e alimenta i servo dalla linea a 5 V di Arduino. I motori non sono collegati direttamente ai pin di Arduino ma passano per il registro a scorrimento 74HC595 della scheda, quindi si pilotano con la Adafruit Motor Shield library (quella per la V1, non la V2), cioè AFMotor, che installi dal Gestore librerie. Lo shield usa D3, D4, D5, D6, D7, D8, D11 e D12 per i motori e D9 e D10 per i servo; A0–A5, D2 e D13 restano liberi. Dato che HC-SR04 e sensori IR sono già su A0–A3, devi cambiare solo il codice dei motori, il pin del servo e i pin dell'HC-05.
| Impostazione | Con l'L298N | Con lo shield L293D |
|---|---|---|
| Collegamento dei motori | Morsetti OUT1–OUT4 | Morsetti M1 (sinistro) e M2 (destro) |
| Codice dei motori | digitalWrite e analogWrite dentro drive() | AFMotor dentro drive() |
| SERVO_PIN | 3 | 9 o 10, a seconda del connettore in cui inserisci il servo |
| HC-05 | D10, D11 | A4 (RX) e A5 (TX): SoftwareSerial bt(A4, A5) |
| Corrente per canale | 2 A continui | 0,6 A continui, 1,2 A di picco |
| Alimentazione dei motori | Morsetto 12V | Morsetto EXT_PWR; con il jumper PWR inserito anche Arduino si alimenta dalla stessa sorgente |
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}Il limite di 0,6 A per canale dell'L293D basta per la marcia normale di un robot 2WD con motori TT, ma quando una ruota spinge contro un ostacolo e si blocca, il motore può assorbire più di 1 A e il chip si scalda in fretta. Per questo un algoritmo di scansione che ferma il robot prima che spinga contro un ostacolo allunga anche la vita dello shield. Le impostazioni dei jumper dello shield e uno sketch per una macchinina a quattro motori sono nell'articolo Shield motori L293D, mentre il funzionamento interno dell'L298N è nella guida Controllo di motori DC con l'L298N.
Robot evita ostacoli con ESP32
Sull'ESP32 ci sono due differenze importanti. La prima è che la libreria Servo di Arduino non funziona sull'ESP32, quindi generiamo il segnale del servo direttamente con LEDC, l'unità PWM della scheda: in un segnale a 50 Hz un impulso di 0,544–2,4 ms corrisponde a 0–180 gradi. Dato che i motori lavorano a 1 kHz e il servo a 50 Hz, si configurano prima i canali dei motori e poi quello del servo; così il servo usa un proprio timer. La seconda è che l'ESP32 ha il Bluetooth classico integrato, quindi l'HC-05 non serve; il robot compare sullo smartphone come "BlueGrays-Robot" e le lettere di direzione e di modalità sono le stesse della versione per Uno. Il codice è per la versione 3.x del core Arduino per ESP32 (ledcAttach e ledcWrite lavorano con i numeri dei pin) e, poiché richiede il Bluetooth classico, non si compila per ESP32-S3 ed 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 evita ostacoli con Raspberry Pi Pico (MicroPython)
Sul Raspberry Pi Pico la funzione time_pulse_us() di MicroPython funziona come pulseIn() su Arduino; se non arriva nessuna eco restituisce un valore negativo, che trattiamo come 400 cm, cioè spazio libero. Per il servo si usano il PWM a 50 Hz e duty_ns(), che riceve la larghezza dell'impulso in nanosecondi. Il servo su GP16 e i pin di velocità dei motori su GP15 e GP10 si trovano su slice PWM diversi, quindi non si disturbano la frequenza a vicenda. Questa volta le velocità sono espresse in percentuale. Il Pico non ha Bluetooth; se vuoi la modalità manuale, collega un HC-05 alla UART0 (GP0, GP1) come nella guida alla macchinina 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 readingsIdee di progetto con un robot evita ostacoli
- Schermo radar: invia sulla porta seriale gli angoli e le distanze della scansione e disegna uno schermo radar verde sul computer; i dettagli sono nell'articolo Progetto radar con Arduino.
- Linea e ostacolo: fai in modo che il robot, mentre segue un percorso come un robot segui linea, aggiri una scatola che trova sulla strada e torni sulla linea; è una prova comune nelle gare.
- Robot che segue i muri: tieni il sensore girato di 60 gradi a destra e procedi a 20 cm costanti dal muro; è il primo passo per risolvere i labirinti.
- Robot con display: mostra la distanza e la modalità su un piccolo display OLED; il debug diventa molto più semplice.
- Prototipo di robot aspirapolvere: progetta un robot per le pulizie che gira per casa con scelta casuale della direzione, interruttori d'urto e sensore di vuoto.
- Bastone intelligente: trasforma la stessa logica di misura della distanza in un avviso a vibrazione per persone ipovedenti; vedi Bastone intelligente con Arduino.
- Progetto scolastico o da gara: presentalo con un percorso a ostacoli, il cronometro e una dimostrazione delle modalità automatica e manuale; per altre idee dai un'occhiata all'articolo idee di progetti Arduino per concorsi scientifici.
Errori comuni e soluzioni
| Problema | Causa probabile | Soluzione |
|---|---|---|
| Il robot va a sbattere contro il muro senza fermarsi | Il sensore legge 0 (cavo interrotto, TRIG ed ECHO invertiti) e il codice lo considera spazio libero | Esegui lo sketch di prova, prova con la mano tra 10 e 60 cm, controlla i pin TRIG ed ECHO |
| Striscia contro il muro quando ci arriva in diagonale | Il suono viene riflesso altrove dalla superficie inclinata | Aggiungi sensori IR agli angoli anteriori e riduci la velocità |
| Arduino si riavvia quando si ferma a scansionare | La corrente del servo e dei motori fa crollare la linea a 5 V | Condensatore da 470 µF sul servo, batterie 18650 cariche; se serve, alimenta il servo da una sorgente a 5 V separata |
| Il robot si ferma e scansiona senza motivo | Una singola misura sbagliata o un'eco dal pavimento | Porta il sensore a 8–12 cm e inclinalo leggermente verso l'alto; usa la conferma con la mediana |
| Il robot gira verso il lato bloccato | Servo montato al contrario, sinistra e destra scambiate | Imposta SERVO_DIR a -1 |
| Le rotazioni sono troppo corte o troppo lunghe | MS_PER_DEG non è calibrato o la batteria è scarica | Misura con turnBy(360) e regola di nuovo con la batteria carica |
| In un angolo continua a girare a destra e a sinistra | Entrambi i lati sono vicini alla soglia e la decisione cambia di continuo | Aumenta la soglia di vicolo cieco (STOP_CM + 10) o fagli preferire l'ultimo verso di rotazione |
| La testa del sensore dà scatti | Alimentazione del servo debole o interrupt di SoftwareSerial | Accorcia il cavo del servo, aggiungi un condensatore, non inviare comandi Bluetooth più spesso del necessario |
Dato che riunisce in un solo progetto lettura dei sensori, filtraggio, decisioni e controllo dei motori, il robot evita ostacoli è il modo migliore per entrare nella robotica. Osserva prima il comportamento di base con il primo sketch, poi regola le soglie sulla tua stanza con la versione avanzata e infine potenzia il tuo robot con sensori IR e Bluetooth. Nell'app BlueGrays trovi passo passo, con schemi elettrici e codice, i progetti del robot evita ostacoli, della macchinina comandata via Bluetooth con L298N, della misura di distanza con l'HC-SR04 e del sensore di parcheggio.
Domande frequenti
Cosa serve per costruire un robot evita ostacoli?
Per un robot evita ostacoli di base bastano un Arduino Uno, un sensore a ultrasuoni HC-SR04, un servo SG90, un driver motori L298N o L293D, un kit telaio 2WD con due motoriduttori e due batterie 18650. Puoi aggiungere due sensori IR di ostacolo per vedere gli angoli e un modulo Bluetooth HC-05 per guidarlo dallo smartphone.
Si può fare un robot evita ostacoli senza servo?
Sì. Il sensore guarda fisso in avanti; quando il robot vede un ostacolo, prima gira da una parte e misura di nuovo, e se la strada è ancora bloccata gira dall'altra. Costa meno, ma il robot decide più lentamente perché a ogni tentativo deve girare tutto il telaio; con un servo trova la direzione più libera prima di girare.
Perché l'HC-SR04 non rileva alcuni ostacoli?
Un sensore a ultrasuoni misura tramite l'eco. Le superfici morbide come tende e divani assorbono il suono, le pareti inclinate lo deviano altrove e gli oggetti sottili come le gambe delle sedie danno un'eco molto debole. Il sensore non vede nemmeno gli ostacoli che restano fuori o sotto il suo cono di circa 15 gradi; puoi coprire questi vuoti con sensori IR.
A che distanza deve fermarsi un robot evita ostacoli?
25 cm sono un buon valore di partenza: la strada percorsa tra due misure, lo spazio di frenata e il margine per girare sul posto sommati fanno circa 20 cm, più un piccolo margine di sicurezza. Per robot più veloci e pavimenti scivolosi sali a 30–35 cm; con un ostacolo a meno di 12 cm conviene prima arretrare.
Per un robot è meglio l'L298N o uno shield L293D?
L'L298N fornisce 2 A per canale, quindi è più robusto e ti lascia scegliere i pin liberamente. Lo shield L293D si innesta sull'Uno, riduce i cablaggi e può pilotare quattro motori, ma la sua corrente continua è di 0,6 A per canale e occupa molti pin. Per un piccolo robot 2WD con motori TT vanno bene entrambi.
Si può comandare un robot evita ostacoli dallo smartphone?
Sì. Aggiungi un modulo Bluetooth HC-05 e invia lettere da un'app di telecomando su uno smartphone Android per passare dalla modalità automatica a quella manuale. Usare il sensore a ultrasuoni come protezione anticollisione in modalità manuale ti impedisce di mandare il robot contro il muro. Con un ESP32 non serve un modulo separato grazie al Bluetooth integrato.
Progetti da realizzare dopo questo articolo
Vedi tutti i progetti
HC-SR04 Misura della distanza (display seriale)
Il sensore HC-SR04 funziona come un pipistrello che trova la strada al buio: manda un suono che non puoi sentire, ne ascolta l'eco e misura quanto è lontano l'oggetto davanti a sé. L'Arduino scrive qu

Robot che evita gli ostacoli
Che ne dite di costruire un veicolo intelligente che trova la propria strada? L'Obstacle Avoiding Robot scruta costantemente l'ambiente circostante grazie al sensore a ultrasuoni posto davanti a lui.

Auto controllata via Bluetooth con L298N
Trasforma il telefono in un telecomando e guida la tua auto robot. L'Arduino riceve tramite un modulo Bluetooth i comandi di un'app di controllo remoto sul telefono e fa girare le ruote con un driver

Utilizzo del servomotore
Il braccio di un piccolo servomotore ruota a turno in tre posizioni: tutto all'inizio, esattamente a metà e tutto alla fine, aspettando due secondi a ogni fermata. L'Arduino dice al motore a quale ang

Sensore di parcheggio con HC-SR04
Una versione in miniatura del sensore di parcheggio delle auto: più qualcosa si avvicina, più i bip accelerano. Il sensore di distanza misura l'oggetto davanti a sé; se è a più di 20 centimetri resta

Lettura della riflessione (prossimità) con TCRT5000
Con un piccolo sensore che manda luce invisibile e misura quanta ne torna indietro, rilevi la superficie che ha davanti. Più un oggetto o una superficie chiara si avvicina, più luce ritorna, e l'Ardui

TCRT5000 Rilevamento di superfici bianche e nere
Con il piccolo sensore che fa da occhio ai robot seguilinea capisci se il pavimento è nero o bianco. Il TCRT5000 manda una luce invisibile verso il pavimento e controlla quanta ne torna indietro; sul

ESP32 HC-SR04 Misuratore di distanza a ultrasuoni
Il sensore a ultrasuoni HC-SR04 calcola la distanza misurando il tempo di volo (Time of Flight) dell'onda sonora che emette dopo aver colpito un oggetto e essere rimbalzata indietro. In questo progetto invieremo un impulso di 10 microsecondi dall'ESP32 al pin TRIG del sensore e leggeremo il tempo di eco proveniente dal pin ECHO con `pulseIn()`. Visualizziamo la distanza calcolata in cm su un display LCD I2C 16x2. Poiché l'HC-SR04 funziona a 5V, è necessario un **divisore di tensione** sul pin EC
Altri articoli
App BlueGrays
Costruisci questo circuito passo passo sul telefono
Schema, codice ed elenco componenti in un’unica schermata. Oltre 160 progetti Arduino, ESP32 e Pico da realizzare.
Inquadra con il telefono
Punta la fotocamera sul codice: si apre lo store del tuo telefono.
oppure apri lo store

