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.
| 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.
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 dasVM. 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.
- Stelle das Messgerät auf DC-Volt und lege es über die Batterieklemmen, nicht über den Arduino.
- Notiere die Ruhespannung.
- Starte die Motoren, idealerweise unter echter Last mit Roboter auf dem Boden.
- 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
- Keine Motoren mehr durchs Board speisen. Gratis, und fixt einen großen Anteil der Fälle sofort.
- Den Pack wechseln. Alkali oder 9 V → NiMH oder 18650. Meist der ganze Fix.
- Die Massen verbinden. Gratis, und sein Fehlen verursacht seltsamere Fehler als ein Brown-out.
- Den Bulk-Kondensator hinzufügen. Für Rauschen und Sensor-Glitches, nicht für Resets.
- 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.
- 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.
- 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.
- ProjektEinen Mecanum-Roboter bauen, der seitwärts fährt
- ProjektEinen Raumabdeckungsroboter bauen, der Böden kehrt
- ProjektEinen Stiftplotter bauen: Zwei Schrittmotorachsen, die ankommen
- ProjektGPS-Wegpunkt-Rover bauen: Fährt seine Route selbst
- ProjektMini-Sumo-Roboter bauen: Grip, Keil, Kantensensorik
- ProjektPick-and-Place-Roboter: Der Fehlgriffe erkennt
- LernpfadElektronik-Grundlagen für Roboter: Ein Lernpfad
- LernpfadHolonomer Antrieb: Seitwärts fahren ohne Drehen
- LernpfadPräzisionsbewegung mit Schrittmotoren: Ein Lernpfad
Weiterführende Literatur