Tutorial · Beginner · 30 min
Waarom je robot reset zodra de motoren starten
De reset die op een softwarebug lijkt, is een voedingsprobleem. Inwendige batterijweerstand, een echt stroombudget en het gat dat de brown-outdetector mist.
Inleiding
De robot werkt op de werkbank. Je zet hem op de vloer, de motoren lopen op, en alles stopt — de seriële monitor start opnieuw vanaf het begin, de LED-reeks begint opnieuw, en wat de robot ook deed is vergeten.
Vrijwel iedereens eerste reflex is om in de code te duiken. In de code staat niets.
Wat er is gebeurd, is dat het starten van de motoren meer stroom trok dan de batterij kon leveren zonder dat de spanning inzakte, de voeding onder het niveau kwam dat de microcontroller nodig heeft, en het bord opnieuw opstartte. Dit is een brown-out, en het is de meest voorkomende storing bij een eerste robot — deze site zegt dit op het pad voor motorbesturing, op vrijwel elke componentpagina en in de probleemoplossingslijst van de helft van zijn projecten. Dit is de pagina die het uitleegt.
Bepaal eerst of het een reset is en geen crash
Deze hebben verschillende oplossingen, en van een afstand zien ze er identiek uit.
Zet een herkenbare regel helemaal bovenaan in setup():
void setup() {
Serial.begin(115200);
Serial.println(F("---- BOOT ----"));
}
Laat de robot nu draaien tot hij zich misgedraagt en kijk naar de monitor:
---- BOOT ----verschijnt opnieuw → het bord is gereset. Het is de voeding. Lees verder.- De uitvoer stopt, of herhaalt één regel voor altijd → het bord zit vast in een lus of is gecrasht. Dat is een codeprobleem, en dit is de verkeerde pagina.
- De uitvoer verandert in onleesbare tekens → de voeding zakt in maar niet ver genoeg om te resetten. Ook voeding, en dezelfde oplossingen gelden.
Op AVR-borden kun je een stap verder gaan en de chip zelf vragen waarom hij is gereset. MCUSR bevat de reden, maar main() draait lang nadat die gelezen moet worden, dus vang hem op in een .init3-functie die als eerste draait:
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"));
}
Eén eerlijke kanttekening: een Uno met Optiboot laat zijn bootloader MCUSR uitlezen en wissen voordat jouw code het ziet, en bewaart de waarde in register r2. Als elke vlag op je bord nul leest, is dat de reden — val terug op de ---- BOOT -----test, die niets nodig heeft en nooit liegt.
Waarom de spanning instort
Een batterij is geen spanningsbron. Het is een spanningsbron in serie met een weerstand, en die weerstand bepaalt of je robot werkt:
V_terminal = V_open_circuit − I × R_internal
R_internal is een eigenschap van de chemie en de celgrootte, en het verschilt enorm tussen de pakketten die in dezelfde la liggen.
| Pakket | Nullast | R_internal | Bij een piek van 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 alkaline | 6,0 V | ≈ 0,80 Ω | 4,40 V |
| 9 V PP3-block | 9,0 V | ≈ 2,0 Ω | 5,00 V, en nog dalend |
Drie dingen volgen daaruit die meer waard zijn dan de getallen:
Het 9-voltsblok is de slechtste batterij die je in een robot kunt doen. Er staat de hoogste spanning op gedrukt en ongeveer 500 mAh capaciteit achter twee ohm inwendige weerstand. Het is een batterij voor een rookmelder.
Alkalinecellen zijn niet stijf. Hun inwendige weerstand is ruwweg zes keer die van een NiMH-cel, en die stijgt naarmate ze ontladen — dus de robot werkt twintig minuten en begint daarna te resetten, wat op een storing met tussenpozen lijkt en eigenlijk gewoon een lege batterij is die zich precies hoort te gedragen.
Oplaadbare cellen winnen op het punt dat ertoe doet. NiMH en Li-ion zijn de standaardchemie voor roboten niet omdat ze oplaadbaar zijn, maar omdat ze hun spanning houden onder belasting.
Budget de stroom voordat je het pakket kiest
Tel op wat de robot trekt, twee keer — één keer voor normaal gebruik en één keer voor het slechtste moment.
| Onderdeel | Normaal | Slechtste geval |
|---|---|---|
| Arduino Uno-bord | 45 mA | 45 mA |
| TT-reductiemotor ×2 | 400 mA | 2000 mA (beide vastgelopen) |
| IR-reflectiearray | 100 mA | 100 mA |
| HC-SR04 | 15 mA | 15 mA |
| SG90-servo | 150 mA | 700 mA (vastgelopen) |
| HC-05-bluetooth | 30 mA | 40 mA |
| ESP32 (wifi) | 80 mA | 250 mA (zendpiek) |
Een gewone lijnvolger — Uno, twee motoren, een IR-array — is ongeveer 545 mA in bedrijf en 2,15 A op het slechtste moment. Dat slechtste moment is normaal kort; de uitzondering is een robot die op zijn tractiegrens duwt en net zo lang op vastloopstroom zit als de duw duurt. Dimensioneer het pakket op het eerste getal en de spanningsval op het tweede, want ze beantwoorden verschillende vragen:
runtime ≈ capacity / running current 2000 mAh / 545 mA ≈ 3.7 h ideal
sag = worst-case current × R_internal
Verwacht in werkelijkheid ongeveer twee derde van dat getal. Je ontlaadt een pakket nooit tot nul, de pieken kosten meer dan hun gemiddelde, en capaciteitsopgaven worden gemeten bij zachte ontlaadraten.
Het gat dat de brown-outdetector mist
Elke AVR heeft een brown-outdetector die de chip in reset houdt onder een ingestelde spanning. Op een Arduino Uno staan die zekeringen standaard op 2,7 V.
Een ATmega328P op 16 MHz is niet gespecificeerd om op 2,7 V te draaien. De snelheidsklasse in de datasheet loopt van 4 MHz bij 1,8 V tot 20 MHz bij 4,5 V, en interpoleren naar 16 MHz geeft een minimum van ongeveer 3,8 V.
Dit verklaart het symptoom dat mensen het lastigst vinden geloven: een robot die niet netjes resettet maar zich gewoon vreemd gedraagt zodra de motoren lopen. Beschadigde variabelen, een servo die springt, een I²C-sensor die nonsens teruggeeft, een toestandsmachine in een toestand waar hij geen overgang in heeft. In die band voert de chip instructies uit buiten zijn gegarandeerde bedrijfsvoorwaarden, en “gegarandeerd” is precies het woord dat het werk doet.
Behandel “hij resette niet” dus niet als bewijs dat de voeding in orde is.
Eén pakket, twee rails
De oplossing is niet twee batterijen. Het is één pakket dat twee paden voedt.
- Motorstroom gaat van het pakket rechtstreeks naar de motorvoedingsaansluiting van de driver — de
+12Vvan een L298N, deVMvan een TB6612FNG. Die loopt nooit door de microcontroller. - Logica-stroom komt uit een geregelde 5 V — de spanningsregelaar op de driver, een aparte buck-module, of USB terwijl je ontwikkelt.
- De massa’s moeten aan elkaar vastzitten. Elk signaal tussen het bord en de driver is een spanning gemeten ten opzichte van massa; zonder gedeelde referentie betekenen de richtingspinnen niets. Dit is de op één na meest voorkomende bedradingsfout, na de eerste.
Laat nooit motoren lopen via de 5 V-pin of de barrel-jackregelaar van een Arduino. De regelaar van de Uno is een klein lineair onderdeel dat het verschil tussen in- en uitgang als warmte verbrandt: bij 7,4 V in, 5 V uit en 500 mA is dat (7.4 − 5) × 0.5 = 1.2 W in een behuizing zonder koellichaam. Hij wordt warm, dan schakelt hij thermisch uit, en de robot reset om een reden die niets met de batterij te maken heeft.
Wat een condensator werkelijk verhelpt
Het standaardadvies is om een grote elektrolytcondensator over de motorvoeding te zetten. Dat advies klopt, de reden die er meestal voor wordt gegeven, is verkeerd, en de rekensom beslist het:
ΔV = I × Δt / C
Een condensator van 1000 µF die een startpiek van 2 A gedurende 10 ms moet dragen, zakt 2 × 0.010 / 0.001 = 20 V. Die taak kan hij niet doen. Een condensator draagt de startpiek van de motor niet — alleen een stuggere batterij doet dat.
Wat hij wel draagt, zijn de snelle rimpels. Bij een PWM-schakelfrequentie van 8 kHz is de periode 125 µs, dus dezelfde condensator ziet 2 × 0.000125 / 0.001 = 0.25 V rimpels — een taak die hij met gemak doet. Hij vangt ook de commutatiepieken op die borstelmotoren de voeding terugsturen.
Plaats er dus een — 470 tot 1000 µF over de motorvoeding, plus een 0,1 µF keramisch dicht bij de driver — en verwacht dat hij ruis en ondeugdelijke sensoren opknapt. Verwacht niet dat hij een reset verhelpt. Als een condensator jouw reset heeft genezen, stond het pakket op de grens en heb je jezelf heel weinig marge gekocht.
Meet in plaats van te gokken
Tien minuten met een multimeter verslaat een middag theorieën.
- Zet de meter op gelijkspanning en plaats hem over de batterijklemmen, niet over de Arduino.
- Noteer de rustspanning.
- Start de motoren, liefst onder echte belasting met de robot op de vloer.
- Kijk naar het getal.
Een daling van meer dan een paar tiende volt is je antwoord. Om R_internal daaruit te halen, deel je: een pakket van 6,0 V dat naar 4,4 V zakt terwijl het ongeveer 2 A trekt, geeft (6.0 − 4.4) / 2 = 0.8 Ω.
Herhaal daarna de meting op de 5 V-rail terwijl de motoren starten. Dat is het getal dat de microcontroller daadwerkelijk ervaart, en als het richting 3,8 V zakt, heb je de fout gevonden zonder oscilloscoop.
Goedkope meters middelen te traag om de allerlaagste dip van een milliseconde te vangen, dus behandel wat je afleest als optimistisch. De dip is minstens zo erg.
Oplossingen, goedkoopste eerst
- Stop met motoren voeden via het bord. Gratis, en het verhelpt een groot deel van de gevallen in één keer.
- Wissel van pakket. Alkaline of 9 V → NiMH of 18650. Meestal de hele oplossing.
- Bind de massa’s aan elkaar. Gratis, en het ontbreken ervan veroorzaakt vreemdere storingen dan een brown-out.
- Voeg de grote condensator toe. Voor ruis en sensorstoringen, niet voor resets.
- Ramp de motoren op in plaats van ze in één stap aan te zetten. Een bewegingsprofiel spreidt de startstroom over de tijd in plaats van hem in één moment te vragen — dezelfde code die het wielslippen stopt.
- Dimensioneer de motoren goed. Als een motor om meer koppelmoment wordt gevraagd dan hij hoort te leveren, trekt hij meer stroom dan hij hoort te trekken; de rekenkunde voor reductiemotoren staat vóór dit hele probleem.
- Scheid de spanningsregelaar. Een kleine buck-converter voor de logica-rail ontkoppelt die volledig van de spanningsval op de motorvoeding.
Zodra de rail klopt, is die in software meten de volgende stap — en eerlijk een spanning lezen is lastiger dan het lijkt, want de standaardreferentie van de ADC is de rail.
Als het misgaat
| Symptoom | Meestal |
|---|---|
| Seriële uitvoer hervat bovenaan als de motoren starten | Brown-out; controleer het pakket vóór de code |
| Werkt op USB, faalt op batterij | Het pakket kan de piek niet leveren, USB wel |
| Werkt twintig minuten en reset daarna steeds opnieuw | Inwendige weerstand van alkaline die stijgt tijdens het ontladen |
| Vreemd gedrag zonder nette reset | Voeding in de band 2,7–3,8 V; buiten spec, maar onder geen enkele drempel |
| Sensoren geven alleen tijdens het rijden nonsens terug | Voedingsruis; grote condensator en gescheiden rails |
| Richtingspinnen lijken niets te doen | Geen gemeenschappelijke massa tussen bord en driver |
| Regelaar te heet om aan te raken | Logica via een lineaire regelaar met te hoge ingangsspanning |
| Een condensator hielp, toen kwam het terug | Pakket stond op de grens; de condensator kocht milliseconden, niet ampères |
Voeding is de laag onder elke andere laag van een robot, en daarom bootst een storing hier een storing elders na. Als er iets onverklaarbaars begint te gebeuren op het moment dat de motoren draaien, meet dan eerst de batterij — dat kost tien minuten en het klopt vaker dan welke andere gok dan ook.
Verken de graaf
Onderdeel van deze bouwprojecten
Projecten en leerroutes waarin deze tutorial voorkomt.
- ProjectBouw een GPS-waypointrover die zelfstandig rijdt
- ProjectBouw een grijp-en-plaatsrobot die weet wanneer hij misgreep
- ProjectBouw een Mecanum-robot die zijwaarts rijdt
- ProjectBouw een mini-sumo robot: grip, wig en randherkenning
- ProjectBouw een penplotter: twee stappenmotorassen die op de plek komen
- ProjectBouw een robot die een gebied afdekt en de vloer veegt
- LeerrouteElektronicafundamenten voor robots: een leertraject
- LeerrouteHolonome aandrijving: zijwaarts bewegen zonder te draaien
- LeerrouteNauwkeurige beweging met stappenmotoren: een leertraject
Verder lezen