Robot éviteur d'obstacles Arduino : HC-SR04 et servo pas à pas
Robot éviteur d'obstacles Arduino : HC-SR04 sur un servo, câblage L298N ou L293D, logique arrêt-recul-virage, seuils de distance, capteurs IR et mode Bluetooth.
Un robot éviteur d'obstacles est une voiture robot autonome qui repère un mur, une chaise ou une boîte devant elle sans la percuter et se trouve un nouveau chemin. Le montage le plus courant utilise une Arduino Uno, un capteur à ultrasons HC-SR04, un servomoteur SG90 qui oriente le capteur à gauche et à droite, deux motoréducteurs à courant continu et un driver moteur L298N (ou L293D). Le robot avance tout droit, s'arrête à l'approche d'un obstacle, recule un peu, tourne la tête des deux côtés pour mesurer puis repart dans la direction la plus dégagée.
Dans ce guide, nous transformons la voiture 2WD de notre article voiture robot Arduino en robot autonome en gardant le même brochage. Pas à pas, nous voyons les tableaux de câblage, le calcul des seuils de distance, un code qui grandit en trois étapes, les surfaces que le capteur ne voit pas et comment combler ces lacunes avec des capteurs infrarouges, et enfin le passage en mode manuel par Bluetooth depuis ton téléphone. Le code pour piloter le même robot avec un ESP32 ou une Raspberry Pi Pico se trouve à la fin de l'article.

Comment fonctionne un robot éviteur d'obstacles ?
Tout robot autonome répète la même boucle en trois étapes des dizaines de fois par seconde : percevoir, décider, agir. La perception, c'est le HC-SR04 : il émet un court son à 40 kHz que ton oreille n'entend pas et mesure le temps de retour de l'écho ; en divisant ce temps en microsecondes par 58, tu obtiens la distance en centimètres. La décision revient à l'Arduino, qui compare la distance mesurée aux seuils que tu as fixés. L'action est assurée par le L298N et les moteurs. Comme le capteur est monté sur un servo, le robot peut « regarder » à gauche et à droite sans tourner son châssis, ce qui lui permet de choisir ses virages bien mieux qu'un robot à capteur fixe. Le fonctionnement détaillé du capteur est expliqué dans l'article Capteur de distance HC-SR04.
| Pièce | Rôle | Remarque |
|---|---|---|
| Arduino Uno (ou Nano) | Cerveau : mesure, décide et commande les moteurs | Un ESP32 ou une Pico conviennent aussi |
| HC-SR04 | Œil : mesure la distance de l'obstacle devant | Fonctionne en 5 V, 2 à 400 cm |
| Servo SG90 | Cou : oriente le capteur à gauche et à droite | Le capteur se fixe sur le palonnier du servo avec un support en plastique |
| L298N ou shield L293D | Muscle : donne sens et vitesse aux moteurs | Une broche Arduino ne peut pas fournir le courant d'un moteur |
| 2 motoréducteurs TT et roue folle | Propulsion différentielle | Le robot tourne en faisant tourner les deux roues en sens opposés |
| 2 accus 18650 (7,4 V) | Énergie | Une pile 9 V ne suffit pas |
| 2 capteurs IR d'obstacle (facultatif) | Repèrent les obstacles aux coins et sur les côtés | Modules à LM393 comme le FC-51 |
| HC-05 (facultatif) | Pilotage manuel depuis le téléphone | Fonctionne uniquement avec Android |




Alimentation : éviter que le servo fasse redémarrer le robot
Comme le robot éviteur d'obstacles ajoute un servo aux moteurs, la répartition de l'alimentation compte davantage que sur une voiture robot ordinaire. Un SG90 consomme environ 10 mA au repos ; dès qu'il bouge, il monte à 100–250 mA, et sous effort il approche le demi-ampère. Si ce pic tombe en même temps que le courant de démarrage des moteurs, la ligne 5 V de l'Arduino s'effondre un instant et la carte redémarre. La solution est simple : les moteurs sont alimentés par l'accu via le L298N, l'Arduino par l'accu via VIN, et le servo, le HC-SR04, les capteurs IR et le HC-05 se partagent la broche 5V de l'Arduino, avec un condensateur de 470 µF placé près des fils d'alimentation du servo. Même quand le servo bouge, la charge totale sur la ligne 5 V reste sous le demi-ampère et, comme elle est brève, le régulateur de l'Uno la supporte. Pour calculer l'autonomie avec tes propres valeurs, utilise l'outil Autonomie de la batterie.
| Élément | Alimenté par | Courant typique |
|---|---|---|
| 2 moteurs TT | Bornier 12V du L298N ← 2 × 18650 (7,4 V) | 0,3–0,6 A en roulant, jusqu'à 2 A au démarrage |
| Arduino Uno | VIN ← plus de l'accu (après l'interrupteur) | Environ 50 mA |
| Servo SG90 | 5V de l'Arduino + condensateur de 470 µF | ~10 mA au repos, 100–250 mA en mouvement |
| HC-SR04 | 5V de l'Arduino | Environ 15 mA |
| 2 capteurs IR | 5V de l'Arduino | 20–40 mA chacun |
| HC-05 | 5V de l'Arduino | 30–40 mA en appairage, environ 10 mA connecté |
Schéma de câblage : Arduino Uno, ESP32 et Pico
Les broches des moteurs sont les mêmes que dans le guide de la voiture robot ; tu peux donc passer à ce projet en ajoutant simplement la tête de capteur, sans démonter ta voiture. Placer les broches de vitesse sur 5 et 6 est voulu : sur l'Uno, la bibliothèque Servo utilise le Timer1 et désactive donc le PWM des broches 9 et 10 ; si les vitesses des moteurs s'y trouvaient, le réglage de vitesse ne marcherait plus dès que le servo serait branché. Le HC-SR04 et les capteurs IR vont sur A0–A3. Les broches analogiques de l'Uno peuvent aussi servir de broches numériques 14 à 19, et ce choix facilite l'utilisation du même code avec un shield L293D, car le shield ne touche pas à ces broches. Tu peux voir le brochage de ta carte dans l'outil Pinout.
| Connexion | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| L298N ENA (vitesse moteur gauche) | D5 | GPIO 25 | GP15 |
| L298N IN1, IN2 | D7, D8 | GPIO 27, 33 | GP14, GP13 |
| L298N ENB (vitesse moteur droit) | D6 | GPIO 26 | GP10 |
| L298N IN3, IN4 | D9, D4 | GPIO 32, 4 | GP12, GP11 |
| Signal du servo (orange) | D3 | GPIO 13 | GP16 |
| Servo + (rouge), − (marron) | 5V, GND | 5V du L298N, GND | 5V du L298N, GND |
| HC-SR04 TRIG | A0 | GPIO 18 | GP17 |
| HC-SR04 ECHO | A1 (direct) | GPIO 19, via un pont diviseur 1 kΩ / 2 kΩ | GP18, via un pont diviseur 1 kΩ / 2 kΩ |
| HC-SR04 VCC | 5V | 5V du L298N | 5V du L298N |
| OUT de l'IR gauche, OUT de l'IR droit | A2, A3 | GPIO 23, 22 | GP19, GP20 |
| VCC des capteurs IR | 5V | 3V3 | 3V3 |
| HC-05 TXD, RXD | D10, D11 (RXD via un pont diviseur) | Inutile (Bluetooth intégré) | GP1, GP0 (UART0) |
| Alimentation de la carte | VIN ← accu (après l'interrupteur) | 5V (VIN) ← 5V du L298N | VSYS ← 5V du L298N |
Monter et tester la tête du capteur

- Branche le servo sans son palonnier et téléverse le croquis de test ci-dessous. À la fin de chaque balayage, le code ramène le servo à 90 degrés et attend 2 secondes ; débranche le câble USB pendant cette pause, le servo reste en place. Monte le palonnier sur l'axe de façon que le capteur regarde droit devant ; ainsi, 90 degrés signifie « tout droit ».
- Fixe le HC-SR04 sur le palonnier avec un support en plastique ou de la colle chaude. Les fils du capteur doivent être assez longs pour ne pas se tendre quand le servo tourne ; un câble qui accroche le châssis pendant le balayage de 140 degrés est une panne fréquente.
- Place le capteur à 8–12 cm du sol, près du point le plus avancé du châssis. Trop bas, il capte des échos du sol et de la texture du tapis ; trop haut, il ne voit pas les obstacles bas.
- Incline le capteur de 3 à 5 degrés vers le haut. Un capteur parfaitement horizontal et placé bas peut prendre un écho du sol pour un obstacle, surtout sur un sol rugueux.
- Ouvre le moniteur série à 9600 bauds. Si tu vois de petites valeurs sur les lignes 125 et 160 degrés quand tu places ta main à gauche du capteur, la gauche correspond aux grands angles ; dans le cas contraire, mets SERVO_DIR à -1 dans le croquis avancé et échange les valeurs de LEFT_ANGLE et RIGHT_ANGLE dans le premier croquis.
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}Chaque ligne affiche un angle et la distance dans cette direction. S'il n'y a rien à moins de 4 mètres environ devant le capteur, tu liras 0 cm : pulseIn() renvoie 0 si aucun écho n'arrive en 25 ms. Dans le code principal, nous considérerons 0 comme « voie libre », mais un capteur avec un fil débranché ou avec TRIG et ECHO inversés lit souvent 0 lui aussi. Avant de poser le robot au sol, place donc ta main à 10, 30 et 60 cm et vérifie que les valeurs changent vraiment ; un robot lancé sans ce test fonce dans le mur sans ralentir.
L'algorithme de décision : s'arrêter, reculer, tourner vers la direction la plus dégagée
Découper le comportement du robot en zones selon la distance rend le code à la fois simple et fiable. Sans obstacle devant, il avance à pleine vitesse, il ralentit à mesure qu'un obstacle se rapproche et, une fois au seuil d'arrêt, il s'arrête et balaie. Pendant le balayage, le capteur regarde à 70 et 35 degrés à gauche puis à 35 et 70 degrés à droite ; trois mesures sont prises dans chaque direction et la médiane est retenue. Le robot tourne vers la direction qui donne la plus grande distance, exactement de cet angle. Si aucune direction n'est libre, par exemple s'il s'est engagé dans un coin, il recule un peu et fait demi-tour.
| Distance mesurée | Zone | Ce que fait le robot |
|---|---|---|
| Plus de 60 cm (ou pas d'écho) | Libre | Avance à pleine vitesse (PWM 200) |
| 25–60 cm | Approche | Ralentit proportionnellement à la distance (PWM de 200 à 130) |
| 12–25 cm | Obstacle | S'arrête, confirme la mesure, balaie des deux côtés et tourne vers la direction la plus dégagée |
| Moins de 12 cm | Trop près | Recule d'abord pendant 350 ms, puis balaie |
| Toutes les directions à moins de 35 cm | Cul-de-sac | Recule et fait demi-tour |
Ces seuils ne sont pas choisis au hasard. La distance d'arrêt est la somme de trois marges : la marge de réaction (le trajet parcouru entre deux mesures), la marge de freinage (la distance sur laquelle le robot glisse après la coupure des moteurs) et la marge de rotation (pour que son nez ne touche pas l'obstacle quand il pivote sur place). Une voiture à moteurs TT alimentés en 6 V roule à environ 0,5–0,7 m/s et parcourt 3 à 4 cm pendant un intervalle de mesure de 60 ms. La marge de freinage est d'environ 5 cm et, pour un châssis de 15 cm de large qui pivote sur place, la marge de rotation tourne autour de 10 cm. Au total, cela fait environ 18 à 20 cm ; avec quelques centimètres de marge de sécurité en plus, STOP_CM = 25 est un bon point de départ. Si ton robot est plus rapide ou si le sol est glissant, monte le seuil à 30–35 cm. Dans les couloirs étroits, en revanche, un seuil trop grand fait que le robot s'arrête et balaie sans cesse ; fais le réglage final sur ton propre parcours.
Code pas à pas 1 : ton premier robot éviteur d'obstacles
La première version est le squelette de l'algorithme : avancer, s'arrêter à 25 cm, reculer, regarder à gauche et à droite, tourner du côté le plus dégagé. La fonction moteur drive(gauche, droite) est la même que dans le guide de la voiture robot et accepte des valeurs de -255 à 255 ; positif signifie en avant, négatif en arrière. Après le téléversement, pose d'abord la voiture sur une boîte, roues dans le vide, et observe comment elle s'arrête et tourne quand tu approches ta main du capteur ; si tout va bien, pose le robot au sol.
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}Ce code fait le travail, mais il a quelques faiblesses : une seule mesure erronée arrête le robot pour rien, il s'approche toujours des obstacles à la même vitesse, il ne regarde que dans deux directions et ses durées de rotation ne dépendent pas de l'angle. En plus, comme il attend avec delay(), il ne peut réagir à rien pendant le balayage. Les détails de cette logique d'attente sont dans l'article millis() et delay() ; la version suivante règle la plupart de ces problèmes.
Code pas à pas 2 : filtre, ralentissement progressif, IR et Bluetooth
La version avancée fonctionne avec le même câblage, et ce n'est pas grave si tu n'as pas encore monté les capteurs IR ni le HC-05. Comme les entrées IR sont configurées en INPUT_PULLUP, un capteur non branché lit « pas d'obstacle » (HIGH) ; sans HC-05, le robot démarre simplement en mode automatique. Le tableau ci-dessous explique le rôle de chaque partie du code.
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}| Fonction ou réglage | Ce qu'elle fait |
|---|---|
| pingCm() | Fait une mesure ; renvoie 400 cm (espace libre) si aucun écho n'arrive en 25 ms |
| distanceCm() | Prend 3 mesures espacées de 60 ms et renvoie la médiane ; élimine une valeur aberrante isolée |
| autoStep() | Mesure toutes les 60 ms ; selon la distance et les IR, avance, ralentit, esquive ou appelle avoid() |
| avoid() | S'arrête, confirme la mesure, recule si besoin, regarde dans 4 directions et tourne vers la plus dégagée |
| turnBy(degrés) | Fait pivoter le robot sur place de l'angle donné ; la durée est calculée avec MS_PER_DEG |
| manualStep() | Applique la direction envoyée par le téléphone ; s'arrête à 15 cm en marche avant et s'arrête si aucune commande n'arrive pendant 600 ms |
| SERVO_CENTER, SERVO_DIR | L'angle auquel le capteur regarde droit devant et le sens correspondant à la gauche |
| STOP_CM, SLOW_CM, BACK_CM | Seuils d'arrêt, de ralentissement et de recul |
Pour un robot éviteur d'obstacles, un filtre médian vaut mieux qu'une moyenne : avec trois mesures comme 24, 26 et 400 cm, la moyenne donne 150 cm et trompe le robot, alors que la médiane retient 26 cm. Pour aller plus loin, consulte l'article filtrer les données d'un capteur. La séparation entre mode automatique et mode manuel est en réalité une petite machine à états ; nous détaillons cette approche dans le guide machine à états Arduino. Après le téléversement, règle le robot sur ta pièce dans cet ordre :
- Centre du servo : robot face à un mur plat et bien perpendiculaire, si le capteur ne vise pas droit vers lui, modifie SERVO_CENTER par petits pas, par exemple 85 ou 95.
- Durée de rotation : ajoute temporairement la ligne turnBy(360); à la fin de setup() et regarde si le robot fait un tour complet. S'il tourne de 300 degrés au lieu de 360, règle MS_PER_DEG sur 4,0 × 360 / 300 = 4,8. Comme cette valeur dépend de l'accu et du sol, fais le réglage avec un accu plein, sur le sol où le robot va rouler.
- Seuil d'arrêt : envoie le robot vers un mur et mesure la distance entre son nez et le mur là où il s'arrête. Si elle est inférieure à 10 cm, augmente STOP_CM de 5 cm.
- Vitesse lente : si SLOW est sous le seuil de démarrage des moteurs, le robot reste bloqué dans la zone d'approche ; dans ce cas, monte SLOW à 150.
Angles morts du HC-SR04 : surfaces molles, murs en biais et obstacles fins
Un capteur à ultrasons voit bien les surfaces qui renvoient le son, mais tous les obstacles ne le renvoient pas vers le capteur. Le fabricant recommande une cible d'au moins 0,5 m², aussi plane que possible, pour de bons résultats. Dans une vraie pièce, pourtant, le robot rencontre des rideaux, des canapés, des pieds de chaise et des coins de mur. Le son rebondit avec le même angle que celui d'arrivée, comme une boule de billard : un capteur face à un mur reçoit un écho fort, mais si son axe s'écarte de plus de 30 à 40 degrés environ de la perpendiculaire, le son ricoche ailleurs et ne revient jamais. pulseIn() renvoie alors 0 et le code croit la voie libre. C'est la cause la plus fréquente des robots qui frottent contre les murs.
| Obstacle ou surface | Pourquoi il n'est pas vu | Solution |
|---|---|---|
| Rideaux, canapés, coussins, vêtements, poils d'animaux | Les surfaces molles et poreuses absorbent le son, l'écho revient très faible | Ajoute un capteur IR pour la courte distance et fais rouler le robot lentement |
| Approche d'un mur en biais | Le son est renvoyé ailleurs et l'écho n'atteint pas le capteur | Ajoute aux coins avant des capteurs IR orientés vers l'extérieur et resserre les angles de balayage |
| Pieds de chaise et de table, poteaux fins | La surface réfléchissante est minuscule et l'écho faible | Réduis la vitesse, utilise le filtre médian, ajoute des IR sur les côtés |
| Obstacles bas sous le capteur (câbles, seuils de porte, chaussons) | Ils restent sous le cône sonore | Ajoute sur le pare-chocs avant un capteur IR orienté vers le bas ou un micro-interrupteur |
| Bords de table, cages d'escalier | Le capteur à ultrasons regarde devant lui et ne voit pas un vide dans le sol | Détection de vide avec un TCRT5000 tourné vers le sol |
| Objets à moins de 2 cm | Zone morte du capteur : l'écho revient avant la fin de l'émission sonore | Règle le robot pour qu'il recule à 12 cm |
| Coins et couloirs étroits | Le son rebondit entre deux murs et revient tard, la distance paraît plus grande | Filtre médian et mesures sous plusieurs angles |
Renfort avec des capteurs IR
Les capteurs à ultrasons et infrarouges (IR) compensent mutuellement leurs faiblesses. Les capteurs IR d'obstacle comme le FC-51 se composent d'une LED infrarouge et d'un récepteur ; quand la lumière réfléchie est assez forte, le comparateur LM393 met la sortie à LOW et la LED de la carte s'allume. La portée se règle au potentiomètre ajustable entre environ 2 et 30 cm, mais le capteur ne mesure pas de distance : il dit seulement « obstacle » ou « pas d'obstacle ». En plaçant deux capteurs IR aux coins avant du robot, orientés de 30 à 45 degrés vers l'extérieur, tu repères les pieds de chaise et les murs en biais qui sortent du cône étroit du capteur à ultrasons. Dans le code avancé, le robot esquive brièvement à droite quand l'IR gauche se déclenche, et à gauche quand c'est l'IR droit ; si les deux se déclenchent ensemble, il s'arrête et balaie comme si un obstacle se trouvait devant lui.
| Caractéristique | HC-SR04 (ultrasons) | FC-51 (IR d'obstacle) | TCRT5000 (IR réfléchissant) |
|---|---|---|---|
| Sortie | Distance (cm) | Oui / non (numérique) | Valeur analogique et sortie numérique |
| Portée | 2–400 cm | Environ 2–30 cm (réglable) | Environ 1–15 mm |
| Angle de détection | Environ 15° | Environ 35° | Très étroit |
| Point faible | Surfaces molles, en biais et fines | Surfaces noires et mates, soleil | Variations de hauteur, soleil |
| Voit bien | Verre, objets noirs, grandes surfaces planes | Tissu, coins, obstacles proches | Couleur du sol et bords de table |
| Place sur le robot | Œil principal, orienté par le servo | Coins avant | Sous le pare-chocs avant, tourné vers le sol |

Pour les robots qui évoluent sur une table, l'obstacle le plus dangereux est en fait le vide. Si tu fixes sous le pare-chocs avant un module TCRT5000 tourné vers le sol, tu sauras immédiatement quand le sol disparaît. Relie la sortie numérique du module à D2, encore libre sur l'Uno ; vérifie d'abord dans le moniteur série le niveau que prend la sortie quand il n'y a plus de sol, puis lis cette broche tout au début de autoStep() et, si le sol a disparu, arrête aussitôt les moteurs et fais reculer le robot. Ce contrôle doit passer avant la mesure de distance : une chute coûte toujours plus cher qu'un choc.
Passer en mode manuel par Bluetooth
Tu voudras parfois piloter le robot autonome à la main : pour l'emmener d'une pièce à l'autre, le placer devant un obstacle pour tester sa réaction ou simplement pour jouer. La liaison HC-05 du code avancé le permet. Quand le téléphone envoie la lettre A, le robot passe en mode automatique ; quand il envoie M, il passe en mode manuel. En mode manuel, on utilise les lettres de commande du guide de la voiture robot (F, B, L, R, S), avec une différence : si le robot voit un obstacle à moins de 15 cm en marche avant, il s'arrête malgré ta commande. Cette protection anticollision te permet de profiter des capteurs du robot autonome même en pilotage manuel. Les étapes d'appairage du HC-05 et les commandes AT se trouvent dans l'article Module Bluetooth HC-05.
| Lettre | Action | Dans quel mode ? |
|---|---|---|
| A | Passer en mode automatique | Dans les deux modes |
| M | Passer en mode manuel, arrêter le robot | Dans les deux modes |
| F / B | Avant / arrière | Manuel |
| L / R | Pivoter sur place à gauche / à droite | Manuel |
| S | Stop | Manuel |
| 0–9 | Niveau de vitesse (PWM 100–240) | Manuel |
- Relie la broche TXD du HC-05 à D10, la broche RXD à D11 via un pont diviseur 1 kΩ / 2 kΩ, VCC au 5V et GND au GND.
- Appaire le HC-05 dans les réglages Bluetooth de ton téléphone Android avec le code 1234 (ou 0000 si ça ne marche pas).
- Installe depuis le Play Store une application de télécommande de voiture Bluetooth et connecte-la au HC-05. Si tu laisses l'Arduino branché en USB et ouvres le moniteur série à 9600 bauds, le code affiche chaque lettre reçue du téléphone.
- La plupart des applications de télécommande ont des boutons supplémentaires, comme les phares ou le klaxon, et chacun envoie sa propre lettre. Repère ces lettres dans le moniteur série et mets-les dans le code à la place de 'A' et 'M' ; si tu préfères définir les boutons toi-même, tu peux créer ta propre télécommande avec le guide App Inventor et Arduino en Bluetooth.
- Débranche le câble USB et bascule l'interrupteur ; au bout de 3 secondes, le robot démarre en mode automatique. Passe en mode manuel et pilote-le depuis ton téléphone.
Montage avec un shield moteur L293D

Si tu n'aimes pas les nœuds de câbles, tu peux remplacer le L298N par un shield moteur L293D (l'Adafruit Motor Shield V1 et ses clones) qui s'enfiche directement sur l'Arduino Uno. Le shield peut piloter quatre moteurs à courant continu, possède deux connecteurs de servo et alimente les servos depuis la ligne 5 V de l'Arduino. Les moteurs ne sont pas reliés directement aux broches de l'Arduino mais passent par le registre à décalage 74HC595 de la carte ; tu les pilotes donc avec la Adafruit Motor Shield library (celle de la V1, pas de la V2), c'est-à-dire AFMotor, installée depuis le gestionnaire de bibliothèques. Le shield utilise D3, D4, D5, D6, D7, D8, D11 et D12 pour les moteurs et D9 et D10 pour les servos ; A0–A5, D2 et D13 restent libres. Comme le HC-SR04 et les capteurs IR sont déjà sur A0–A3, il te suffit de modifier le code moteur, la broche du servo et les broches du HC-05.
| Réglage | Avec le L298N | Avec le shield L293D |
|---|---|---|
| Branchement des moteurs | Borniers OUT1–OUT4 | Borniers M1 (gauche) et M2 (droite) |
| Code moteur | digitalWrite et analogWrite dans drive() | AFMotor dans drive() |
| SERVO_PIN | 3 | 9 ou 10, selon le connecteur où tu branches le servo |
| HC-05 | D10, D11 | A4 (RX) et A5 (TX) : SoftwareSerial bt(A4, A5) |
| Courant par canal | 2 A en continu | 0,6 A en continu, 1,2 A en pointe |
| Alimentation des moteurs | Bornier 12V | Bornier EXT_PWR ; avec le cavalier PWR en place, l'Arduino est alimentée par la même source |
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}La limite de 0,6 A par canal du L293D suffit pour la conduite normale d'un robot 2WD à moteurs TT, mais quand une roue pousse contre un obstacle et se bloque, le moteur peut tirer plus de 1 A et la puce chauffe vite. Un algorithme de balayage qui arrête le robot avant qu'il ne pousse contre un obstacle prolonge donc aussi la vie du shield. Les réglages des cavaliers du shield et un croquis de voiture à quatre moteurs sont dans l'article Shield moteur L293D, et le fonctionnement interne du L298N dans le guide Commander un moteur à courant continu avec le L298N.
Robot éviteur d'obstacles avec un ESP32
Sur l'ESP32, il y a deux différences importantes. D'abord, la bibliothèque Servo d'Arduino ne fonctionne pas sur l'ESP32 ; nous générons donc le signal du servo directement avec LEDC, l'unité PWM de la carte : dans un signal à 50 Hz, une impulsion de 0,544 à 2,4 ms correspond à 0–180 degrés. Comme les moteurs tournent à 1 kHz et le servo à 50 Hz, on configure d'abord les canaux des moteurs puis celui du servo ; ainsi, le servo dispose de son propre timer. Ensuite, l'ESP32 intègre le Bluetooth classique, donc plus besoin de HC-05 ; le robot apparaît sur le téléphone sous le nom « BlueGrays-Robot » et les lettres de direction et de mode sont les mêmes que dans la version Uno. Le code est écrit pour la version 3.x du core Arduino de l'ESP32 (ledcAttach et ledcWrite fonctionnent avec des numéros de broche) et, comme il a besoin du Bluetooth classique, il ne compile pas pour l'ESP32-S3 ni l'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 éviteur d'obstacles avec une Raspberry Pi Pico (MicroPython)
Sur la Raspberry Pi Pico, la fonction time_pulse_us() de MicroPython fonctionne comme pulseIn() sur Arduino ; si aucun écho n'arrive, elle renvoie une valeur négative, que nous traitons comme 400 cm, c'est-à-dire un espace libre. Pour le servo, on utilise un PWM à 50 Hz et duty_ns(), qui prend la largeur d'impulsion en nanosecondes. Le servo sur GP16 et les broches de vitesse des moteurs sur GP15 et GP10 se trouvent sur des tranches PWM différentes, si bien qu'ils ne perturbent pas leurs fréquences respectives. Cette fois, les vitesses sont données en pourcentage. La Pico n'a pas de Bluetooth ; si tu veux le mode manuel, branche un HC-05 sur l'UART0 (GP0, GP1) comme dans le guide de la voiture 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 readingsIdées de projets avec un robot éviteur d'obstacles
- Écran radar : envoie les angles et distances du balayage par le port série et dessine un écran radar vert sur ton ordinateur ; tous les détails dans l'article Projet radar Arduino.
- Ligne et obstacle : fais en sorte que le robot, tout en suivant une piste comme un robot suiveur de ligne, contourne une boîte posée sur son chemin puis revienne sur la ligne ; c'est une épreuve classique des concours.
- Robot qui longe les murs : garde le capteur tourné de 60 degrés vers la droite et avance à 20 cm constants du mur ; c'est la première étape pour résoudre un labyrinthe.
- Robot avec écran : affiche la distance et le mode sur un petit écran OLED ; le débogage devient bien plus simple.
- Prototype de robot aspirateur : conçois un robot de ménage qui parcourt la maison avec un choix de direction aléatoire, des contacts de choc et un capteur de vide.
- Canne intelligente : transforme la même logique de mesure de distance en alerte vibrante pour les personnes malvoyantes ; voir Canne intelligente Arduino.
- Projet scolaire ou de concours : présente-le avec un parcours d'obstacles, un chronomètre et une démonstration des modes automatique et manuel ; pour d'autres idées, jette un œil à l'article idées de projets Arduino pour concours scientifiques.
Erreurs fréquentes et solutions
| Problème | Cause probable | Solution |
|---|---|---|
| Le robot fonce dans le mur sans s'arrêter | Le capteur lit 0 (fil coupé, TRIG et ECHO inversés) et le code le prend pour un espace libre | Lance le croquis de test, essaie entre 10 et 60 cm avec ta main, vérifie les broches TRIG et ECHO |
| Il frotte le mur quand il arrive en biais | Le son est renvoyé ailleurs par la surface inclinée | Ajoute des capteurs IR aux coins avant et réduis la vitesse |
| L'Arduino redémarre quand il s'arrête pour balayer | Le courant du servo et des moteurs fait chuter la ligne 5 V | Condensateur de 470 µF sur le servo, accus 18650 chargés ; si besoin, alimente le servo par une source 5 V séparée |
| Le robot s'arrête et balaie sans raison | Une mesure erronée isolée ou un écho du sol | Monte le capteur à 8–12 cm et incline-le légèrement vers le haut ; utilise la confirmation par médiane |
| Le robot tourne vers le côté bloqué | Servo monté à l'envers, gauche et droite inversées | Mets SERVO_DIR à -1 |
| Les virages sont bien trop courts ou trop longs | MS_PER_DEG n'est pas calibré ou l'accu est faible | Mesure avec turnBy(360) et refais le réglage avec un accu plein |
| Dans un coin, il n'arrête pas de tourner à gauche puis à droite | Les deux côtés sont proches du seuil et la décision bascule sans cesse | Augmente le seuil de cul-de-sac (STOP_CM + 10) ou fais-lui privilégier le dernier sens de rotation |
| La tête du capteur tressaute | Alimentation du servo trop faible ou interruptions de SoftwareSerial | Raccourcis le câble du servo, ajoute un condensateur, n'envoie pas de commandes Bluetooth plus souvent que nécessaire |
Parce qu'il réunit dans un seul projet la lecture de capteurs, le filtrage, la prise de décision et la commande des moteurs, le robot éviteur d'obstacles est la meilleure porte d'entrée vers la robotique. Observe d'abord le comportement de base avec le premier croquis, règle ensuite les seuils sur ta pièce avec la version avancée, puis renforce ton robot avec des capteurs IR et le Bluetooth. Dans l'application BlueGrays, tu trouveras pas à pas, avec schémas et code, les projets du robot éviteur d'obstacles, de la voiture pilotée en Bluetooth avec un L298N, de la mesure de distance avec le HC-SR04 et du capteur de recul.
Questions fréquentes
De quoi a-t-on besoin pour fabriquer un robot éviteur d'obstacles ?
Pour un robot éviteur d'obstacles de base, il suffit d'une Arduino Uno, d'un capteur à ultrasons HC-SR04, d'un servo SG90, d'un driver moteur L298N ou L293D, d'un kit de châssis 2WD avec deux motoréducteurs et de deux accus 18650. Tu peux ajouter deux capteurs IR d'obstacle pour voir les coins et un module Bluetooth HC-05 pour le piloter depuis ton téléphone.
Peut-on faire un robot éviteur d'obstacles sans servo ?
Oui. Le capteur regarde fixement devant ; quand le robot voit un obstacle, il tourne d'abord d'un côté et mesure à nouveau, et si la voie est encore bloquée, il tourne de l'autre côté. C'est moins cher, mais le robot décide plus lentement parce qu'il doit faire pivoter tout son châssis à chaque essai ; avec un servo, il trouve la direction la plus dégagée avant de tourner.
Pourquoi le HC-SR04 ne voit-il pas certains obstacles ?
Un capteur à ultrasons mesure grâce à l'écho. Les surfaces molles comme les rideaux et les canapés absorbent le son, les murs en biais le renvoient ailleurs et les objets fins comme les pieds de chaise donnent un écho très faible. Le capteur ne voit pas non plus les obstacles situés hors de son cône d'environ 15 degrés ou en dessous ; tu peux combler ces lacunes avec des capteurs IR.
À quelle distance un robot éviteur d'obstacles doit-il s'arrêter ?
25 cm est une bonne valeur de départ : le trajet parcouru entre deux mesures, la distance de freinage et la marge nécessaire pour pivoter sur place totalisent environ 20 cm, auxquels s'ajoute une petite marge de sécurité. Pour les robots plus rapides et les sols glissants, monte à 30–35 cm ; face à un obstacle à moins de 12 cm, il vaut mieux reculer d'abord.
Pour un robot, vaut-il mieux un L298N ou un shield L293D ?
Le L298N fournit 2 A par canal : il est plus robuste et te laisse choisir les broches librement. Le shield L293D s'enfiche sur l'Uno, réduit le câblage et peut piloter quatre moteurs, mais son courant continu est de 0,6 A par canal et il occupe beaucoup de broches. Pour un petit robot 2WD à moteurs TT, les deux font l'affaire.
Peut-on piloter un robot éviteur d'obstacles depuis un téléphone ?
Oui. Ajoute un module Bluetooth HC-05 et envoie des lettres depuis une application de télécommande sur un téléphone Android pour passer du mode automatique au mode manuel. Utiliser le capteur à ultrasons comme protection anticollision en mode manuel t'empêche d'envoyer le robot dans le mur. Avec un ESP32, pas besoin de module séparé grâce à son Bluetooth intégré.
Projets à réaliser après cet article
Voir tous les projets
HC-SR04 Mesure de la distance (affichage en série)
Le capteur HC-SR04 fonctionne comme une chauve-souris qui trouve son chemin dans le noir : il envoie un son que tu ne peux pas entendre, écoute l'écho et mesure à quelle distance se trouve l'objet dev

Robot évitant les obstacles
Que diriez-vous de construire un véhicule intelligent qui trouve son propre chemin ? Le Robot Évitant les Obstacles scrute en permanence son environnement grâce au capteur ultrasonique situé devant lu

Voiture contrôlée par Bluetooth avec L298N
Transforme ton téléphone en télécommande et pilote ta propre voiture robot. L'Arduino reçoit, via un module Bluetooth, les ordres d'une appli de télécommande sur ton téléphone et fait tourner les roue

Utilisation du servomoteur
Le bras d'un petit servomoteur tourne tour à tour vers trois positions : tout au début, pile au milieu et tout au bout, en attendant deux secondes à chaque arrêt. L'Arduino indique au moteur l'angle à

Capteur de stationnement avec HC-SR04
Une version miniature du radar de recul des voitures : plus un objet s'approche, plus les bips s'accélèrent. Le capteur de distance mesure l'obstacle devant lui ; au-delà de 20 centimètres, la LED ver

Lecture de réflexion (proximité) avec TCRT5000
Avec un petit capteur qui envoie une lumière invisible et mesure ce qui revient, tu détectes la surface placée devant lui. Plus un objet ou une surface claire s'approche, plus la lumière renvoyée est

TCRT5000 Détection de surface noire et blanche
Avec le petit capteur qui sert d'œil aux robots suiveurs de ligne, tu sais si le sol est noir ou blanc. Le TCRT5000 envoie une lumière invisible vers le sol et regarde combien revient ; sur du blanc l

ESP32 HC-SR04 Télémètre à ultrasons
Le capteur à ultrasons HC-SR04 calcule la distance en mesurant le temps de vol (Time of Flight) de l'onde sonore qu'il émet lorsqu'elle rebondit sur un objet. Dans ce projet, nous enverrons une impulsion de 10 microsecondes depuis l'ESP32 vers la broche TRIG du capteur et nous lirons la durée de l'écho provenant de la broche ECHO à l'aide de la fonction `pulseIn()`. Nous afficherons la distance calculée en cm sur un écran LCD I2C 16x2. Le capteur HC-SR04 fonctionnant en 5 V, un **diviseur de ten
Autres articles
Application BlueGrays
Réalise ce montage pas à pas sur ton téléphone
Schéma, code et liste des composants sur un seul écran. Plus de 160 projets Arduino, ESP32 et Pico à réaliser.
Scanne avec ton téléphone
Pointe l’appareil photo vers le code : la boutique d’applications de ton téléphone s’ouvre.
ou ouvre la boutique

