Tutoriel · Débutant · 30 min de lecture

Pourquoi votre robot redémarre quand les moteurs démarrent

Le redémarrage qui ressemble à un bogue logiciel vient de l'alimentation : résistance interne, budget de courant, et angle mort du détecteur de microcoupure.

Introduction

Le robot marche sur l’établi. Vous le posez au sol, les moteurs démarrent, et tout s’arrête — le moniteur série redémarre du début, la séquence de LED recommence, et ce que le robot faisait est oublié.

Le premier instinct de presque tous est d’aller chercher dans le code. Il n’y a rien dans le code.

Ce qui est arrivé est que le démarrage des moteurs a tiré plus de courant que la batterie ne pouvait fournir sans que sa tension chute, l’alimentation est passée sous ce qu’exige le microcontrôleur, et la carte a redémarré. C’est une microcoupure, et c’est la panne la plus courante d’un premier robot — ce site le dit sur le parcours de commande moteur, sur presque chaque page de composant, et dans la liste de dépannage de la moitié de ses projets. Ceci est la page qui l’explique.

D’abord, établissez que c’est un redémarrage et pas un plantage

Ils exigent des correctifs différents, et ils se ressemblent vus de l’autre côté de la pièce.

Mettez une ligne distinctive tout en haut de setup() :

void setup() {
  Serial.begin(115200);
  Serial.println(F("---- BOOT ----"));
}

Faites tourner le robot jusqu’à la panne et regardez le moniteur :

  • ---- BOOT ---- réapparaît → la carte a redémarré. C’est l’alimentation. Continuez de lire.
  • La sortie s’arrête, ou répète une ligne indéfiniment → la carte est coincée dans une boucle ou a planté. C’est un problème de code, et ceci est la mauvaise page.
  • La sortie devient des caractères poubelle → l’alimentation s’affaisse mais pas assez pour redémarrer. Aussi l’alimentation, et les mêmes correctifs s’appliquent.

Sur les cartes AVR vous pouvez aller plus loin et demander à la puce pourquoi elle a redémarré. MCUSR tient la raison, mais main() tourne longtemps après le moment où il faut la lire, donc capturez-la dans une fonction .init3 qui tourne d’abord :

uint8_t resetFlags __attribute__((section(".noinit")));
void captureReset(void) __attribute__((naked, used, section(".init3")));
void captureReset(void) {
  resetFlags = MCUSR;
  MCUSR = 0;
}

void setup() {
  Serial.begin(115200);
  if (resetFlags & _BV(BORF))  Serial.println(F("cause: BROWN-OUT"));
  if (resetFlags & _BV(EXTRF)) Serial.println(F("cause: reset pin"));
  if (resetFlags & _BV(PORF))  Serial.println(F("cause: power-on"));
  if (resetFlags & _BV(WDRF))  Serial.println(F("cause: watchdog"));
}

Une réserve honnête : un Uno sous Optiboot a son chargeur qui lit MCUSR et l’efface avant que votre code le voie, cachant la valeur dans le registre r2. Si chaque drapeau lit zéro sur votre carte, voilà pourquoi — rabattez-vous sur le test ---- BOOT ----, qui n’a besoin de rien et ne ment jamais.

Pourquoi la tension s’effondre

Une batterie n’est pas une source de tension. C’est une source de tension en série avec une résistance, et c’est cette résistance qui décide si votre robot marche :

V_terminal = V_open_circuit − I × R_internal

R_internal est une propriété de la chimie et de la taille des éléments, et elle varie énormément entre les packs du même tiroir.

Un graphe de la tension aux bornes en fonction du courant tiré pour quatre packs de batteries. Un bloc 9 volts PP3 part le plus haut à 9 volts mais chute raide, croisant 5 volts à 2 ampères. Quatre piles AA alcalines partent de 6 volts et tombent à 4,4 volts. Deux éléments 18650 au lithium partent de 7,4 volts et restent presque plats à 7,2 volts. Quatre AA NiMH partent le plus bas à 4,8 volts et restent presque plates à 4,56 volts. Une ligne verticale en pointillés marque l'appel de 2 ampères de deux moteurs qui démarrent.
Deux motoréducteurs TT démarrant ensemble tirent environ 2 A. Le bloc 9 V a la plus haute tension à vide en rayon et donne le pire résultat en charge — il perd 4 V en y arrivant. Le pack NiMH part 4,2 V plus bas et arrive 0,6 V plus haut. Télécharger le SVG
Pack À vide R_internal Sur un appel de 2 A
2×18650 Li-ion 7.4 V ≈ 0.10 Ω 7.20 V
4×AA NiMH 4.8 V ≈ 0.12 Ω 4.56 V
4×AA alcalines 6.0 V ≈ 0.80 Ω 4.40 V
Bloc 9 V PP3 9.0 V ≈ 2.0 Ω 5.00 V, et chutant

Trois choses suivent qui valent plus que les nombres :

Le bloc neuf volts est la pire batterie à mettre dans un robot. Il a la plus haute tension imprimée dessus et environ 500 mAh de capacité derrière deux ohms de résistance interne. C’est une batterie pour un détecteur de fumée.

Les piles alcalines ne sont pas raides. Leur résistance interne vaut environ six fois celle d’un élément NiMH, et elle monte en se déchargeant — donc le robot marche vingt minutes puis recommence à redémarrer, ce qui se lit comme une panne intermittente et n’est qu’une batterie plate se comportant exactement comme elle doit.

Les rechargeables gagnent sur ce qui compte. NiMH et Li-ion sont les chimies standard des robots non parce que rechargeables mais parce qu’elles tiennent leur tension en charge.

Budgétez le courant avant de choisir le pack

Additionnez ce que le robot tire, deux fois — une fois pour l’usage normal, et une fois pour l’instant pire.

Partie En marche Pire cas
Carte Arduino Uno 45 mA 45 mA
Motoréducteur TT ×2 400 mA 2000 mA (les deux bloqués)
Barrette IR de réflectance 100 mA 100 mA
HC-SR04 15 mA 15 mA
Servo SG90 150 mA 700 mA (bloqué)
Bluetooth HC-05 30 mA 40 mA
ESP32 (Wi-Fi) 80 mA 250 mA (rafale d’émission)

Un simple suiveur de ligne — Uno, deux moteurs, une barrette IR — fait environ 545 mA en marche et 2,15 A à l’instant pire. L’instant pire est normalement bref ; l’exception est un robot poussant à sa limite de traction, qui reste au courant de blocage tant que dure la poussée. Dimensionnez le pack sur le premier nombre et l’affaissement sur le second, parce qu’ils répondent à des questions différentes :

runtime  ≈ capacity / running current      2000 mAh / 545 mA ≈ 3.7 h ideal
sag      = worst-case current × R_internal

Attendez une autonomie réelle aux deux tiers environ de ce chiffre. Vous ne viderez pas un pack à zéro, les appels coûtent plus que leur moyenne, et les capacités nominales se mesurent à des débits de décharge doux.

L’angle mort du détecteur de microcoupure

Chaque AVR a un détecteur de microcoupure qui tient la puce en reset sous une tension réglée. Sur un Arduino Uno ce fusible part à 2,7 V.

Un ATmega328P à 16 MHz n’est pas calibré pour tourner à 2,7 V. Son grade de vitesse en fiche technique va de 4 MHz à 1,8 V jusqu’à 20 MHz à 4,5 V, et interpoler à 16 MHz donne un minimum d’environ 3,8 V.

Un graphe de la fréquence d'horloge maximale en fonction de la tension d'alimentation pour l'ATmega328P. Une ligne monte de 4 mégahertz à 1,8 volt jusqu'à 20 mégahertz à 4,5 volts puis s'aplatit, avec la zone dessous ombrée comme région sûre. Une ligne horizontale en pointillés marque les 16 mégahertz où tourne un Arduino Uno, rencontrant la frontière sûre à 3,8 volts. Une bande rouge entre 2,7 volts et 3,8 volts est annotée comme hors spécification à 16 mégahertz mais au-dessus du seuil de microcoupure.
Entre 2,7 V et 3,8 V la puce tourne plus vite que sa tension d'alimentation ne le permet, et le détecteur de microcoupure — réglé à 2,7 V — ne va pas intervenir. Cette bande de 1,1 V est là où vivent les pannes de robots les plus étranges. Télécharger le SVG

Cela explique le symptôme que les gens croient le plus difficilement : un robot qui ne redémarre pas proprement mais se comporte bizarrement quand les moteurs tournent. Variables corrompues, servo qui saute, capteur I²C renvoyant n’importe quoi, machine à états dans un état vers lequel aucune transition ne mène. Dans cette bande, la puce exécute des instructions hors de ses conditions de fonctionnement garanties, et « garanties » est exactement le mot qui travaille.

Donc ne prenez pas « il n’a pas redémarré » comme preuve que l’alimentation va bien.

Un pack, deux rails

Le correctif n’est pas deux batteries. C’est un pack alimentant deux chemins.

  • Le courant moteur va du pack droit dans la borne d’alimentation moteur du driver — le +12V d’un L298N, le VM d’un TB6612FNG. Il ne passe jamais par le microcontrôleur.
  • Le courant logique vient d’un 5 V régulé — le régulateur embarqué du driver, un module abaisseur séparé, ou l’USB pendant le développement.
  • Les masses doivent être reliées. Chaque signal entre la carte et le driver est une tension mesurée contre la masse ; sans référence commune, les broches de sens ne signifient rien. C’est la deuxième panne de câblage la plus courante après la première.

Ne faites jamais passer les moteurs par la broche 5 V d’un Arduino ni son régulateur de jack. Le régulateur de l’Uno est un petit linéaire qui brûle la différence entre entrée et sortie en chaleur : à 7,4 V en entrée, 5 V en sortie et 500 mA, cela fait (7,4 − 5) × 0,5 = 1,2 W dans un boîtier sans dissipateur. Il chauffera, puis coupera thermiquement, et le robot redémarrera pour une raison sans rapport avec la batterie.

Ce qu’un condensateur corrige vraiment

Le conseil standard est de mettre un gros électrolytique sur l’alimentation moteur. Ce conseil est juste, et la raison d’ordinaire donnée est fausse, et l’arithmétique tranche :

ΔV = I × Δt / C

Un condensateur de 1000 µF à qui on demande de porter un appel de démarrage de 2 A durant 10 ms chuterait de 2 × 0,010 / 0,001 = 20 V. Il ne peut pas faire ce travail. Un condensateur ne porte pas l’appel de démarrage moteur — seule une batterie plus raide le fait.

Ce qu’il porte est le rapide. À une fréquence de découpage PWM de 8 kHz la période est de 125 µs, donc le même condensateur voit 2 × 0,000125 / 0,001 = 0,25 V d’ondulation — un travail qu’il fait confortablement. Il absorbe aussi les pics de commutation que les moteurs à balais renvoient dans l’alimentation.

Mettez-en donc un — 470 à 1000 µF sur l’alimentation moteur, plus un céramique 0,1 µF près du driver — et attendez qu’il nettoie le bruit et les capteurs qui se comportent mal. N’attendez pas qu’il corrige un redémarrage. Si un condensateur a guéri votre redémarrage, le pack était limite et vous vous êtes acheté très peu de marge.

Mesurez plutôt que deviner

Dix minutes avec un multimètre battent un après-midi de théories.

  1. Mettez le multimètre en volts continus et posez-le aux bornes de la batterie, pas aux bornes de l’Arduino.
  2. Notez la tension au repos.
  3. Démarrez les moteurs, idéalement en charge réelle robot au sol.
  4. Regardez le nombre.

Une chute de plus de quelques dixièmes de volt est votre réponse. Pour en tirer R_internal, divisez : un pack de 6,0 V tombant à 4,4 V en tirant environ 2 A donne (6,0 − 4,4) / 2 = 0,8 Ω.

Puis répétez la mesure sur le rail 5 V pendant que les moteurs démarrent. C’est le nombre que le microcontrôleur subit vraiment, et s’il plonge vers 3,8 V vous avez trouvé la panne sans oscilloscope.

Les multimètres bon marché moyennent trop lentement pour attraper le fond d’un creux d’une milliseconde, donc prenez ce que vous lisez comme optimiste. Le creux est au moins aussi mauvais.

Correctifs, le moins cher d’abord

  1. Cessez d’alimenter les moteurs via la carte. Gratuit, et cela corrige une grande fraction des cas d’emblée.
  2. Changez le pack. Alcalines ou 9 V → NiMH ou 18650. D’ordinaire tout le correctif.
  3. Reliez les masses. Gratuit, et son absence cause des pannes plus étranges qu’une microcoupure.
  4. Ajoutez le condensateur de découplage. Pour le bruit et les glitchs de capteurs, pas pour les redémarrages.
  5. Rampez les moteurs au lieu de les échelonner. Un profil de mouvement étale le courant de démarrage dans le temps au lieu de tout exiger en un instant — le même code qui empêche les roues de patiner.
  6. Dimensionnez les moteurs proprement. Si on demande à un moteur plus de couple qu’il ne devrait, il tire plus de courant qu’il ne devrait ; l’arithmétique de dimensionnement des motoréducteurs est en amont de tout ce problème.
  7. Séparez le régulateur. Un petit convertisseur abaisseur pour le rail logique le découple entièrement de l’affaissement moteur.

Une fois le rail trié, le mesurer en logiciel est l’étape suivante — et lire une tension honnêtement est plus dur qu’il n’y paraît, parce que la référence par défaut de l’ADC est le rail.

Quand ça tourne mal

Symptôme Généralement
La sortie série redémarre du haut quand les moteurs démarrent Microcoupure ; vérifiez le pack avant le code
Marche en USB, échoue sur batterie Le pack ne peut fournir l’appel, l’USB peut
Marche vingt minutes, puis redémarre en boucle Résistance interne des alcalines montant en se déchargeant
Comportement bizarre mais pas de redémarrage net Alimentation dans la bande 2,7–3,8 V ; hors spécification, sous aucun seuil
Les capteurs renvoient n’importe quoi seulement en roulant Bruit d’alimentation ; condensateur de découplage et rails séparés
Les broches de sens semblent ne rien faire Pas de masse commune entre carte et driver
Régulateur trop chaud pour toucher Logique tirée via un régulateur linéaire avec trop de tension d’entrée
Un condensateur l’a corrigé, puis c’est revenu Pack limite ; le condensateur a acheté des millisecondes, pas des ampères

L’alimentation est la couche sous chaque autre couche d’un robot, et c’est pourquoi une panne ici imite une panne n’importe où ailleurs. Quand quelque chose d’inexplicable commence dès que les moteurs tournent, mesurez la batterie d’abord — cela coûte dix minutes et c’est juste plus souvent que toute autre hypothèse.

Explorer le graphe

Fait partie de ces réalisations

Les projets et parcours d'apprentissage qui incluent ce tutoriel.

Pour aller plus loin

Références