Guides · 20 min de lecture

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.

Capteur de distance à ultrasons HC-SR04
HC-SR04 : le transducteur marqué T émet un son à 40 kHz et celui marqué R écoute l'écho. C'est l'œil du robot ; il mesure de 2 à 400 cm dans un angle étroit d'environ 15 degrés.

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èceRôleRemarque
Arduino Uno (ou Nano)Cerveau : mesure, décide et commande les moteursUn ESP32 ou une Pico conviennent aussi
HC-SR04Œil : mesure la distance de l'obstacle devantFonctionne en 5 V, 2 à 400 cm
Servo SG90Cou : oriente le capteur à gauche et à droiteLe capteur se fixe sur le palonnier du servo avec un support en plastique
L298N ou shield L293DMuscle : donne sens et vitesse aux moteursUne broche Arduino ne peut pas fournir le courant d'un moteur
2 motoréducteurs TT et roue follePropulsion différentielleLe robot tourne en faisant tourner les deux roues en sens opposés
2 accus 18650 (7,4 V)ÉnergieUne pile 9 V ne suffit pas
2 capteurs IR d'obstacle (facultatif)Repèrent les obstacles aux coins et sur les côtésModules à LM393 comme le FC-51
HC-05 (facultatif)Pilotage manuel depuis le téléphoneFonctionne uniquement avec Android
Arduino Uno
Arduino Uno
L298N
L298N
TT motor
TT motor
18650 Li-ion
18650 Li-ion

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émentAlimenté parCourant typique
2 moteurs TTBornier 12V du L298N ← 2 × 18650 (7,4 V)0,3–0,6 A en roulant, jusqu'à 2 A au démarrage
Arduino UnoVIN ← plus de l'accu (après l'interrupteur)Environ 50 mA
Servo SG905V de l'Arduino + condensateur de 470 µF~10 mA au repos, 100–250 mA en mouvement
HC-SR045V de l'ArduinoEnviron 15 mA
2 capteurs IR5V de l'Arduino20–40 mA chacun
HC-055V de l'Arduino30–40 mA en appairage, environ 10 mA connecté
Relie toutes les masses entre elles (moins de l'accu, L298N, Arduino, servo, capteurs) ; sans masse commune, le capteur lit des distances absurdes et le servo tremble. N'alimente pas le servo par le même fil Dupont fin que les moteurs. Si tu utilises un servo plus puissant comme le MG90S ou le MG996R, ne l'alimente pas depuis l'Arduino mais depuis un module abaisseur séparé réglé sur 5 V (par exemple un LM2596).

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.

ConnexionArduino UnoESP32Raspberry Pi Pico
L298N ENA (vitesse moteur gauche)D5GPIO 25GP15
L298N IN1, IN2D7, D8GPIO 27, 33GP14, GP13
L298N ENB (vitesse moteur droit)D6GPIO 26GP10
L298N IN3, IN4D9, D4GPIO 32, 4GP12, GP11
Signal du servo (orange)D3GPIO 13GP16
Servo + (rouge), − (marron)5V, GND5V du L298N, GND5V du L298N, GND
HC-SR04 TRIGA0GPIO 18GP17
HC-SR04 ECHOA1 (direct)GPIO 19, via un pont diviseur 1 kΩ / 2 kΩGP18, via un pont diviseur 1 kΩ / 2 kΩ
HC-SR04 VCC5V5V du L298N5V du L298N
OUT de l'IR gauche, OUT de l'IR droitA2, A3GPIO 23, 22GP19, GP20
VCC des capteurs IR5V3V33V3
HC-05 TXD, RXDD10, D11 (RXD via un pont diviseur)Inutile (Bluetooth intégré)GP1, GP0 (UART0)
Alimentation de la carteVIN ← accu (après l'interrupteur)5V (VIN) ← 5V du L298NVSYS ← 5V du L298N
La broche ECHO du HC-SR04 fournit un signal de 5 V, alors que les broches de l'ESP32 et de la Pico fonctionnent en 3,3 V et ne tolèrent pas le 5 V. Monte un pont diviseur de tension ainsi : ECHO → 1 kΩ → point relié à la carte → 2 kΩ → GND, soit 5 V × 2 / 3 ≈ 3,3 V. TRIG n'a pas besoin de pont diviseur ; un signal de 3,3 V suffit à déclencher le capteur. Sur l'ESP32 et la Pico, la carte, le servo et le HC-SR04 se partagent la même sortie 5V du L298N, si bien que tu peux approcher la limite d'environ 0,5 A de ce petit régulateur ; si tu constates des redémarrages, alimente le servo avec une source 5 V séparée.

Monter et tester la tête du capteur

Micro servomoteur SG90
SG90 : un micro servo de 9 grammes qui tourne de 60 degrés en environ 0,1 seconde sous 4,8 V. Le support en plastique qui porte le HC-SR04 se visse sur le palonnier du servo.
  1. 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 ».
  2. 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.
  3. 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.
  4. 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.
  5. 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.
Arduino (C++)
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éeZoneCe que fait le robot
Plus de 60 cm (ou pas d'écho)LibreAvance à pleine vitesse (PWM 200)
25–60 cmApprocheRalentit proportionnellement à la distance (PWM de 200 à 130)
12–25 cmObstacleS'arrête, confirme la mesure, balaie des deux côtés et tourne vers la direction la plus dégagée
Moins de 12 cmTrop prèsRecule d'abord pendant 350 ms, puis balaie
Toutes les directions à moins de 35 cmCul-de-sacRecule 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.

Arduino (C++)
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.

Arduino (C++)
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églageCe 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_DIRL'angle auquel le capteur regarde droit devant et le sens correspondant à la gauche
STOP_CM, SLOW_CM, BACK_CMSeuils 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 :

  1. 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.
  2. 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.
  3. 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.
  4. 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 surfacePourquoi il n'est pas vuSolution
Rideaux, canapés, coussins, vêtements, poils d'animauxLes surfaces molles et poreuses absorbent le son, l'écho revient très faibleAjoute un capteur IR pour la courte distance et fais rouler le robot lentement
Approche d'un mur en biaisLe son est renvoyé ailleurs et l'écho n'atteint pas le capteurAjoute 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 finsLa surface réfléchissante est minuscule et l'écho faibleRé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 sonoreAjoute sur le pare-chocs avant un capteur IR orienté vers le bas ou un micro-interrupteur
Bords de table, cages d'escalierLe capteur à ultrasons regarde devant lui et ne voit pas un vide dans le solDétection de vide avec un TCRT5000 tourné vers le sol
Objets à moins de 2 cmZone morte du capteur : l'écho revient avant la fin de l'émission sonoreRègle le robot pour qu'il recule à 12 cm
Coins et couloirs étroitsLe son rebondit entre deux murs et revient tard, la distance paraît plus grandeFiltre médian et mesures sous plusieurs angles
Couvrir ce qu'un capteur ne voit pas avec un autre capteur est toujours plus efficace que de chercher à rendre un seul capteur parfait. Mais commence par ralentir : un robot à 0,3 m/s ne heurte pas aussi fort qu'un robot à 0,7 m/s après la même mesure erronée, et il lui reste le temps d'une seconde mesure. Nous avons rassemblé les forces et faiblesses de différents capteurs de distance dans l'article comparatif des capteurs de distance Arduino.

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éristiqueHC-SR04 (ultrasons)FC-51 (IR d'obstacle)TCRT5000 (IR réfléchissant)
SortieDistance (cm)Oui / non (numérique)Valeur analogique et sortie numérique
Portée2–400 cmEnviron 2–30 cm (réglable)Environ 1–15 mm
Angle de détectionEnviron 15°Environ 35°Très étroit
Point faibleSurfaces molles, en biais et finesSurfaces noires et mates, soleilVariations de hauteur, soleil
Voit bienVerre, objets noirs, grandes surfaces planesTissu, coins, obstacles prochesCouleur du sol et bords de table
Place sur le robotŒil principal, orienté par le servoCoins avantSous le pare-chocs avant, tourné vers le sol
Capteur infrarouge réfléchissant TCRT5000
TCRT5000 : monté vers le bas à 5–10 mm du sol, il repère les bords de table et les cages d'escalier ; dès que la réflexion disparaît, le robot peut s'arrêter et reculer.

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.

LettreActionDans quel mode ?
APasser en mode automatiqueDans les deux modes
MPasser en mode manuel, arrêter le robotDans les deux modes
F / BAvant / arrièreManuel
L / RPivoter sur place à gauche / à droiteManuel
SStopManuel
0–9Niveau de vitesse (PWM 100–240)Manuel
  1. 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.
  2. 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).
  3. 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.
  4. 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.
  5. 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.
Sur l'Uno, SoftwareSerial retarde les autres interruptions pendant environ 1 ms lorsqu'il reçoit un octet ; pendant ce temps, les impulsions de la bibliothèque Servo peuvent s'allonger et la tête du capteur peut tressauter légèrement. Cela ne perturbe pas le comportement du robot, mais si ça te gêne, tu peux passer au Bluetooth intégré de l'ESP32. Les HC-05 et HC-06 ne fonctionnent pas avec un iPhone ; pour un iPhone, il faut un module BLE ou un ESP32 programmé en BLE.

Montage avec un shield moteur L293D

Shield de commande moteur L293D
Shield moteur L293D : il s'enfiche directement sur une Arduino Uno ; quatre moteurs à courant continu se branchent sur les borniers M1 à M4 et deux servos sur les connecteurs du coin.

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églageAvec le L298NAvec le shield L293D
Branchement des moteursBorniers OUT1–OUT4Borniers M1 (gauche) et M2 (droite)
Code moteurdigitalWrite et analogWrite dans drive()AFMotor dans drive()
SERVO_PIN39 ou 10, selon le connecteur où tu branches le servo
HC-05D10, D11A4 (RX) et A5 (TX) : SoftwareSerial bt(A4, A5)
Courant par canal2 A en continu0,6 A en continu, 1,2 A en pointe
Alimentation des moteursBornier 12VBornier EXT_PWR ; avec le cavalier PWR en place, l'Arduino est alimentée par la même source
Arduino (C++)
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.

Arduino (C++)
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.

MicroPython
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 readings

Idé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èmeCause probableSolution
Le robot fonce dans le mur sans s'arrêterLe capteur lit 0 (fil coupé, TRIG et ECHO inversés) et le code le prend pour un espace libreLance 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 biaisLe son est renvoyé ailleurs par la surface inclinéeAjoute des capteurs IR aux coins avant et réduis la vitesse
L'Arduino redémarre quand il s'arrête pour balayerLe courant du servo et des moteurs fait chuter la ligne 5 VCondensateur 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 raisonUne mesure erronée isolée ou un écho du solMonte 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éesMets SERVO_DIR à -1
Les virages sont bien trop courts ou trop longsMS_PER_DEG n'est pas calibré ou l'accu est faibleMesure avec turnBy(360) et refais le réglage avec un accu plein
Dans un coin, il n'arrête pas de tourner à gauche puis à droiteLes deux côtés sont proches du seuil et la décision bascule sans cesseAugmente le seuil de cul-de-sac (STOP_CM + 10) ou fais-lui privilégier le dernier sens de rotation
La tête du capteur tressauteAlimentation du servo trop faible ou interruptions de SoftwareSerialRaccourcis le câble du servo, ajoute un condensateur, n'envoie pas de commandes Bluetooth plus souvent que nécessaire
Le moyen le plus rapide de trouver l'origine d'un problème est de laisser le robot branché en USB, de le faire tourner roues dans le vide et d'afficher les valeurs de pingCm() dans le moniteur série. Si les valeurs sont cohérentes, le problème est mécanique ou vient de l'alimentation. Pour les autres raisons pour lesquelles une Arduino redémarre sans cesse, consulte l'article Arduino qui redémarre tout seul.

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é.

Voir tous les projets
HC-SR04 Mesure de la distance (affichage en série)
#27Débutant

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

4.6(45)
68
Robot évitant les obstacles
#81Avancé
Premium

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

5.0(38)
49
Voiture contrôlée par Bluetooth avec L298N
#80Avancé
Premium

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

5.0(38)
65
Utilisation du servomoteur
#35Débutant
Premium

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 à

4.7(48)
67
Capteur de stationnement avec HC-SR04
#28Intermédiaire
Premium

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

4.8(64)
84
Lecture de réflexion (proximité) avec TCRT5000
#77Intermédiaire
Premium

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

4.6(33)
43
TCRT5000 Détection de surface noire et blanche
#78Intermédiaire
Premium

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

4.7(40)
49
ESP32 HC-SR04 Télémètre à ultrasons
#116Intermédiaire

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

4.9(28)
44