Tutorial · Einsteiger · 30 Min. Lesezeit

Warum dein Roboter neustartet, wenn Motoren anlaufen

Der wie ein Softwarebug aussehende Reset ist ein Stromproblem. Batterieinnenwiderstand, echtes Strombudget und die Lücke, die der Brown-out-Detektor verpasst.

Einleitung

Der Roboter läuft auf dem Tisch. Du setzt ihn auf den Boden, die Motoren laufen an, und alles stoppt — der serielle Monitor startet von vorn, die LED-Sequenz beginnt neu, und was der Roboter gerade tat, ist vergessen.

Fast jeder sucht zuerst im Code. Im Code ist nichts.

Was passierte: Das Anlaufen zog mehr Strom, als die Batterie ohne Spannungseinbruch liefern konnte, die Versorgung sackte unter das, was der Mikrocontroller braucht, und das Board startete neu. Das ist ein Brown-out, und es ist der häufigste Fehler am ersten Roboter — diese Seite sagt es auf dem Motorsteuerungs-Pfad, auf fast jeder Bauteilseite und in der Fehlersuchliste halber Projekte. Dies ist die Seite, die ihn erklärt.

Erst klären: Reset oder Absturz

Die brauchen verschiedene Fixes und sehen aus der Ferne identisch aus.

Setze eine markante Zeile ganz oben in setup():

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

Lasse nun den Roboter laufen, bis er spinnt, und beobachte den Monitor:

  • ---- BOOT ---- erscheint erneut → das Board startete neu. Es ist Strom. Weiterlesen.
  • Ausgabe stoppt oder wiederholt eine Zeile ewig → das Board klemmt in einer Schleife oder ist abgestürzt. Das ist ein Codeproblem, und das ist die falsche Seite.
  • Ausgabe wird zu Müllzeichen → die Versorgung sackt, aber nicht weit genug zum Reset. Auch Strom, und dieselben Fixes gelten.

Auf AVR-Boards kannst du einen Schritt weitergehen und den Chip fragen, warum er neustartete. MCUSR hält den Grund, aber main() läuft lange, nachdem er gelesen werden müsste — fange ihn also in einer .init3-Funktion ab, die zuerst läuft:

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"));
}

Ein ehrlicher Vorbehalt: Ein Uno mit Optiboot lässt seinen Bootloader MCUSR lesen und löschen, bevor dein Code es sieht, und legt den Wert in Register r2 ab. Lesen sich alle Flags auf deinem Board null, ist das der Grund — falle auf den ---- BOOT -----Test zurück, der nichts braucht und nie lügt.

Warum die Spannung kollabiert

Eine Batterie ist keine Spannungsquelle. Sie ist eine Spannungsquelle in Reihe mit einem Widerstand, und dieser Widerstand entscheidet, ob dein Roboter läuft:

V_terminal = V_open_circuit − I × R_internal

R_internal ist Eigenschaft von Chemie und Zellgröße und variiert enorm zwischen den Packs in derselben Schublade.

Diagramm von Klemmenspannung über entnommenem Strom für vier Batteriepacks. Ein 9-Volt-Block startet am höchsten bei 9 Volt, fällt aber steil und kreuzt 5 Volt bei 2 Ampere. Vier AA-Alkalizellen starten bei 6 Volt und fallen auf 4,4 Volt. Zwei 18650-Lithiumzellen starten bei 7,4 Volt und bleiben fast flach bei 7,2 Volt. Vier AA-NiMH-Zellen starten am niedrigsten bei 4,8 Volt und bleiben fast flach bei 4,56 Volt. Eine gepunktete Vertikallinie markiert den 2-Ampere-Anlaufstoß zweier Motoren.
Zwei TT-Getriebemotoren ziehen beim gemeinsamen Anlauf etwa 2 A. Der 9-V-Block hat die höchste Leerlaufspannung im Regal und liefert das schlechteste Ergebnis unter Last — er verliert 4 V auf dem Weg. Der NiMH-Pack startet 4,2 V tiefer und kommt 0,6 V höher an. SVG herunterladen
Pack Leerlauf R_innen Bei 2-A-Stoß
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 Alkali 6,0 V ≈ 0,80 Ω 4,40 V
9-V-Block 9,0 V ≈ 2,0 Ω 5,00 V, und fallend

Daraus folgen drei Dinge, die mehr wert sind als die Zahlen:

Der Neunvoltblock ist die schlechteste Batterie, die du in einen Roboter stecken kannst. Er hat die höchste aufgedruckte Spannung und etwa 500 mAh Kapazität hinter zwei Ohm Innenwiderstand. Es ist eine Batterie für einen Rauchmelder.

Alkalizellen sind nicht steif. Ihr Innenwiderstand ist grob sechsmal so hoch wie bei NiMH — und er steigt beim Entladen: Der Roboter läuft zwanzig Minuten und startet dann neu zu resetten, was sich wie ein Wackelkontakt liest und einfach eine leere Batterie ist, die sich exakt benimmt, wie sie soll.

Akkus gewinnen bei dem, was zählt. NiMH und Li-Ion sind nicht Standard-Robotertchemie, weil sie wiederaufladbar sind, sondern weil sie ihre Spannung unter Last halten.

Budgetiere den Strom, bevor du den Pack wählst

Summiere, was der Roboter zieht — zweimal: einmal normal, einmal im schlimmsten Moment.

Teil Laufend Schlimmstenfalls
Arduino-Uno-Board 45 mA 45 mA
TT-Getriebemotor ×2 400 mA 2000 mA (beide blockiert)
IR-Reflexionsarray 100 mA 100 mA
HC-SR04 15 mA 15 mA
SG90-Servo 150 mA 700 mA (blockiert)
HC-05-Bluetooth 30 mA 40 mA
ESP32 (WLAN) 80 mA 250 mA (Sendeburst)

Ein schlichter Linienfolger — Uno, zwei Motoren, IR-Leiste — liegt bei etwa 545 mA laufend und 2,15 A im schlimmsten Moment. Der schlimmste Moment ist meist kurz; Ausnahme ist ein Roboter, der an seiner Haftgrenze schiebt — der sitzt am Blockierstrom, solange der Schub dauert. Bemale den Pack nach der ersten Zahl und den Einbruch nach der zweiten, denn sie beantworten verschiedene Fragen:

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

Erwarte reale Laufzeit um zwei Drittel davon. Du entlädst einen Pack nicht auf null, die Stöße kosten mehr als ihr Mittelwert, und Kapazitätsangaben misst man bei sanften Entladeraten.

Die Lücke, die der Brown-out-Detektor verpasst

Jeder AVR hat einen Brown-out-Detektor, der den Chip unter einer gesetzten Spannung im Reset hält. Auf einem Arduino Uno steht diese Fuse ab Werk auf 2,7 V.

Ein ATmega328P mit 16 MHz darf bei 2,7 V nicht laufen. Seine Datenblatt-Taktstufe reicht von 4 MHz bei 1,8 V bis 20 MHz bei 4,5 V, und interpoliert auf 16 MHz gibt das mindestens etwa 3,8 V.

Diagramm von maximaler Taktfrequenz über Versorgungsspannung für den ATmega328P. Eine Linie steigt von 4 Megahertz bei 1,8 Volt auf 20 Megahertz bei 4,5 Volt und flacht dann ab, die Fläche darunter schattiert als sicherer Bereich. Eine gestrichelte Horizontale markiert die 16 Megahertz eines Arduino Uno und trifft die sichere Grenze bei 3,8 Volt. Ein rotes Band zwischen 2,7 Volt und 3,8 Volt ist annotiert als außerhalb Spec bei 16 Megahertz, aber über der Brown-out-Schwelle.
Zwischen 2,7 V und 3,8 V läuft der Chip schneller, als seine Versorgungsspannung erlaubt, und der Brown-out-Detektor — gesetzt auf 2,7 V — wird nicht eingreifen. In diesem 1,1-V-Band leben die seltsamsten Roboterfehler. SVG herunterladen

Das erklärt das Symptom, das Leute am schwersten glauben: ein Roboter, der nicht sauber neustartet, sondern sich schlicht komisch benimmt, wenn die Motoren laufen. Korrupte Variablen, ein springendes Servo, ein I²C-Sensor mit Unsinn, eine Zustandsmaschine in einem Zustand, in den keine Transition führt. In diesem Band führt der Chip Befehle außerhalb seiner garantierten Betriebsbedingungen aus, und „garantiert“ ist exakt das Wort, das hier arbeitet.

Behandle also „er startete nicht neu“ nicht als Beweis, der Strom sei in Ordnung.

Ein Pack, zwei Schienen

Der Fix sind nicht zwei Batterien. Es ist ein Pack mit zwei Wegen.

  • Motorstrom geht vom Pack direkt in den Motorspeiseanschluss des Treibers — bei einem L298N das +12V, bei einem TB6612FNG das VM. Er läuft nie durch den Mikrocontroller.
  • Logikstrom kommt aus geregelten 5 V — dem Bordregler des Treibers, einem separaten Buck-Modul oder USB während der Entwicklung.
  • Die Massen müssen verbunden sein. Jedes Signal zwischen Board und Treiber ist eine Spannung gegen Masse gemessen; ohne gemeinsame Referenz bedeuten die Richtungspins nichts. Das ist der zweithäufigste Verdrahtungsfehler nach dem ersten.

Betreibe Motoren nie über den 5-V-Pin eines Arduino oder seinen Hohlstecker-Regler. Der Uno-Regler ist ein kleiner Linearregler, der die Differenz zwischen Ein- und Ausgang als Wärme verbrennt: Bei 7,4 V rein, 5 V raus und 500 mA sind das (7,4 − 5) × 0,5 = 1,2 W in einem Gehäuse ohne Kühlkörper. Er wird heiß, dann schaltet er thermisch ab, und der Roboter startet neu aus einem Grund, der nichts mit der Batterie zu tun hat.

Was ein Kondensator wirklich fixt

Der Standardrat ist ein großer Elko über der Motorversorgung. Der Rat stimmt, und die meist genannte Begründung ist falsch, und die Arithmetik entscheidet es:

ΔV = I × Δt / C

Ein 1000-µF-Kondensator, der einen 2-A-Anlaufstoß über 10 ms tragen soll, würde 2 × 0,010 / 0,001 = 20 V einbrechen. Diesen Job kann er nicht. Ein Kondensator trägt nicht den Motoranlaufstoß — nur eine steifere Batterie tut das.

Was er trägt, ist das Schnelle. Bei 8 kHz PWM-Schaltfrequenz ist die Periode 125 µs, also sieht derselbe Kondensator 2 × 0,000125 / 0,001 = 0,25 V Ripple — ein Job, den er bequem macht. Er schluckt auch die Kommutierungsspikes, die Bürstenmotoren die Versorgung hinunterschmeißen.

Verbaue also einen — 470 bis 1000 µF über die Motorversorgung, plus 0,1 µF Keramik nah am Treiber — und erwarte, dass er Rauschen und spinnende Sensoren aufräumt. Erwarte nicht, dass er einen Reset fixt. Wenn ein Kondensator deinen Reset heilte, war der Pack grenzwertig, und du hast dir sehr wenig Reserve gekauft.

Miss es statt zu raten

Zehn Minuten mit Multimeter schlagen einen Nachmittag Theorien.

  1. Stelle das Messgerät auf DC-Volt und lege es über die Batterieklemmen, nicht über den Arduino.
  2. Notiere die Ruhespannung.
  3. Starte die Motoren, idealerweise unter echter Last mit Roboter auf dem Boden.
  4. Beobachte die Zahl.

Ein Einbruch um mehr als ein paar Zehntelvolt ist deine Antwort. Um daraus R_innen zu bekommen, dividiere: Ein 6,0-V-Pack, der bei etwa 2 A auf 4,4 V fällt, gibt (6,0 − 4,4) / 2 = 0,8 Ω.

Wiederhole dann die Messung auf der 5-V-Schiene, während die Motoren anlaufen. Das ist die Zahl, die der Mikrocontroller wirklich erlebt, und sackt sie Richtung 3,8 V, hast du den Fehler ohne Oszi gefunden.

Billige Messgeräte mitteln zu langsam, um das unterste Ende eines Millisekunden-Einbruchs zu fangen — behandle also, was du liest, als optimistisch. Der Einbruch ist mindestens so schlimm.

Fixes, billigster zuerst

  1. Keine Motoren mehr durchs Board speisen. Gratis, und fixt einen großen Anteil der Fälle sofort.
  2. Den Pack wechseln. Alkali oder 9 V → NiMH oder 18650. Meist der ganze Fix.
  3. Die Massen verbinden. Gratis, und sein Fehlen verursacht seltsamere Fehler als ein Brown-out.
  4. Den Bulk-Kondensator hinzufügen. Für Rauschen und Sensor-Glitches, nicht für Resets.
  5. Die Motoren rampen statt springen. Ein Bewegungsprofil verteilt den Anlaufstrom über Zeit statt alles auf einmal zu verlangen — derselbe Code, der das Durchdrehen der Räder stoppt.
  6. Die Motoren sauber bemessen. Wird ein Motor für mehr Moment gefragt, als er sollte, zieht er mehr Strom, als er sollte; die Getriebemotor-Bemessungsarithmetik liegt vor diesem ganzen Problem.
  7. Den Regler trennen. Ein kleiner Buck-Wandler für die Logikschiene entkoppelt sie völlig vom Motoreinbruch.

Sobald die Schiene steht, ist sie in Software zu messen der nächste Schritt — und eine Spannung ehrlich zu lesen ist schwerer, als es aussieht, weil die ADC-Defaultreferenz die Schiene selbst ist.

Wenn es schiefgeht

Symptom Meistens
Serielle Ausgabe startet von vorn, wenn Motoren anlaufen Brown-out; prüfe den Pack vor dem Code
Läuft an USB, versagt an Batterie Der Pack schafft den Stoß nicht, USB schon
Läuft zwanzig Minuten, startet dann dauernd neu Alkali-Innenwiderstand steigt beim Entladen
Komisches Verhalten, aber kein sauberer Reset Versorgung im 2,7–3,8-V-Band; außerhalb Spec, unter keiner Schwelle
Sensoren liefern nur während der Fahrt Unsinn Versorgungsrauschen; Bulk-Kondensator und getrennte Schienen
Richtungspins scheinen nichts zu tun Keine gemeinsame Masse zwischen Board und Treiber
Regler zu heiß zum Anfassen Logik durch einen Linearregler mit zu viel Eingangsspannung gezogen
Ein Kondensator fixte es, dann kam es zurück Pack war grenzwertig; der Kondensator kaufte Millisekunden, keine Ampere

Strom ist die Schicht unter jeder anderen Schicht eines Roboters, weshalb ein Fehler hier einen Fehler überall sonst imitiert. Wenn Unerklärliches beginnt, sobald die Motoren laufen, miss zuerst die Batterie — es kostet zehn Minuten und liegt öfter richtig als jeder andere Tipp.

Den Graphen erkunden

Teil dieser Bauprojekte

Projekte und Lernpfade, die dieses Tutorial enthalten.

Weiterführende Literatur

Quellen