CURSUL 11

Studiu de Caz: Robotul GT

Durată: 122 min de predare Nivel: licență, anul II - recomandat după Cursul 10 Disciplină: Microcontrollere și Microprocesoare Laborator asociat: Laboratorul 07 PDF: descarcă suportul EN English version

Cursul precedent a descris o metodă de proiectare pe un exemplu mic, cu un singur senzor și o singură ieșire de putere. Un exemplu mic are avantajul clarității, dar ascunde tocmai ceea ce face dificilă proiectarea reală: concurența dintre subsisteme pentru aceleași resurse ale microcontrolerului. Cursul de față reia aceeași metodă pe un proiect care are aceste dificultăți din plin - un robot mobil autonom construit pentru un concurs studențesc, urmărit de la regulament până la lecțiile de pe teren.

1Obiectul și structura cursului6 min

Robotul are un șasiu pe patru roți, dintre care două motoare cu tracțiune diferențială, patru senzori de linie, doi senzori cu ultrasunete, două encodere optice, un buton de urgență și o legătură serială. Aproape toate modulele studiate în curs intervin simultan, iar un ATmega328P cu douăzeci de pini utilizabili oferă mai puține resurse decât cere suma subsistemelor - proiectarea constă, în bună măsură, în alegerea compromisurilor.

Recapitulare din Cursul 10
  • Metoda: descriere, caiet de sarcini, partiționare cu interfețe fixate dinainte, mașină cu stări finite, cod pe straturi testat separat, plan de testare legat de cerințe (Cursul 10)
  • Rezerva de resurse (minimum 20%) e regula practică - vom vedea ce se întâmplă când nu mai există (Cursul 10)
  • Să întocmiți un buget de resurse (pini, timere, canale ADC, întreruperi) și să explicați compromisurile impuse de saturarea lui
  • Să explicați de ce numărarea impulsurilor de encoder se face în întrerupere, iar calculul vitezei în bucla principală
  • Să descrieți cele trei fenomene fizice care trebuie tratate la comanda unui motor cu punte H: zona moartă, saltul de comandă, oprirea de urgență
  • Să calculați eroarea de poziție a unui robot urmăritor de linie dintr-un regulator proporțional și să explicați limitele coeficientului Kp
  • Să recunoașteți de ce nivelul de testare pe teren nu poate fi înlocuit de laborator

2Contextul și tema de proiect6 min

Contextul competițional impune exact ceea ce lipsește de obicei din proiectele didactice: un caiet de sarcini scris de altcineva, pe care nu îl poți negocia, un termen fix și o probă publică în care sistemul fie funcționează, fie nu. Organizatorii publică regulamentul, dimensiunile terenului, limitele de gabarit și componentele obligatorii de siguranță - butonul de oprire de urgență accesibil din exterior, oprirea automată la pierderea legăturii, alimentarea de la acumulatori proprii. Toate acestea intră direct în caietul de sarcini, fără discuție.

Robotul trebuie să parcurgă autonom un traseu marcat cu bandă, să ocolească obstacolele, să raporteze permanent starea proprie și să poată fi oprit oricând de la o telecomandă. Ceea ce face greu proiectul nu este partea de algoritmică - modestă aici, un regulator proporțional și o mașină cu cinci stări -, ci încadrarea în resurse și respectarea timpilor.

3Caietul de sarcini al robotului9 min

Regulamentul concursului e descrierea sistemului, în limbaj obișnuit, scrisă de cineva care nu proiectează electronică: „robotul trebuie să urmărească traseul marcat” și „trebuie să evite coliziunile” - formulări neverificabile. Primul act tehnic al echipei e transformarea lor într-un caiet de sarcini cu cerințe măsurabile, prin întrebări puse organizatorului: cât de lată e banda? Care e raza minimă de curbură? Traseul are intersecții? Fiecare răspuns devine o cerință.

CodCerințăCriteriu de acceptare
CF1Urmărește banda de 19mm, curbură min. 250mm10 tururi fără pierderea traseului
CF3Obstacol la <250mm: oprire în ≤300msmăsurarea distanței de oprire, 20 repetări
CF5Oprire de urgență: motoarele la zero în ≤50msosciloscop, 20 de repetări
CF7Odometrie: eroare sub 3% pe 2m drept10 încercări, comparație cu ruleta
CN4Niciun defect de senzor nu produce comandă maximădeconectarea pe rând a fiecărui senzor

CF4, CN3 și CN4 nu apar nicăieri în regulament - rezultă din lista de întrebări despre situațiile anormale. CN4 exprimă un principiu ne-evident: un senzor deconectat produce, la intrarea ADC, o valoare nedefinită, iar dacă algoritmul o folosește ca validă, rezultatul poate fi o comandă maximă pe motor - exact comportamentul periculos.

Butonul de urgență nu trebuie să depindă numai de software

Corect proiectat, întrerupe fizic alimentarea etajului de putere, printr-un contact în serie cu puntea H, și abia în al doilea rând anunță microcontrolerul printr-o intrare digitală. Un buton care doar aprinde un bit citit de program nu protejează exact în cazul care contează cel mai mult: acela în care programul s-a blocat.

4Partiționarea sistemului și bugetul de resurse13 min

Cu cerințele fixate, urmează partiționarea: senzori de linie → poziție; encodere → viteze; ultrasunete → ecou; interfața cu operatorul → buton/START/LED; ieșire → PWM și sens către puntea H, cadre către USART. Fiecare subsistem livrează o singură mărime și ascunde complet implementarea din spate - dacă se schimbă tipul senzorului de linie sau modelul punții, se rescrie un singur subsistem.

Absentă din tabelul subsistemelor: orice modul care folosește magistralele sincrone din Cursul 09. Echipa a dorit inițial un afișaj și un senzor inerțial pe I²C, dar ambele au fost eliminate - motivul devine clar imediat: nu au mai rămas pini.

Cum se ajunge de la regulament la harta pinilor: puneti pasii in ordine

Bugetul de resurse

Bugetul de resurse al robotului: cerut fata de disponibil

Placa Arduino Uno oferă douăzeci de pini de intrare-ieșire. Bugetul se face pe patru capitole: pini, timere, canale analogice, surse de întrerupere.

PinDirecțieSemnal
PD0, PD1USARTComenzi telecomandă / jurnal
PD2, PD3IntrareINT0, INT1 - encodere stâng/drept
PD4, PD7IeșireSensul motoarelor stâng/drept
PB1, PB2IeșireOC1A, OC1B - PWM motoare
PB0, PB3IntrarePCINT0/3 - ecoul ultrasunetelor
PC0...PC3AnalogicăCei patru senzori de linie
PC4, PC5Analog. / IntrareTensiune acumulator / buton urgență
Potriviti subsistemul robotului cu modulul de microcontroler pe care se sprijina

Nu a rămas niciun pin liber. Consecințe, toate decizii conștiente: (1) patru senzori de linie, nu șase - poziția se cunoaște mai grosier, robotul taie curbele la viteze mari. (2) I²C dispare - PC4/PC5 au fost date senzorului de tensiune și butonului de urgență. (3) nu mai există pin de test liber - soluția de compromis a fost reutilizarea pinului LED-ului de stare, cu prețul unui LED care clipește dezordonat în timpul măsurătorilor.

Un al doilea conflict: două funcții, un singur USART Cerințele cer o consolă de depanare (primește jurnalul) și o telecomandă (trimite comenzi), dar ATmega328P are un singur USART. Un al doilea USART în software ar cere dezactivarea întreruperilor pe durata fiecărui caracter, strică odometria. Soluția aleasă: cele două funcții împart aceleași linii - telecomanda pe RXD, jurnalul pe TXD, ascultat doar în laborator. Nu se pierde nimic, pentru că nimeni nu citește consola în timpul probei.

5Timerele, canalele analogice și întreruperile10 min

Timer1 generează cele două semnale PWM pentru motoare, în modul PWM rapid pe opt biți, prescalare 8: frecvența rezultată e 16.000.000/(8·256) = 7812,5 Hz, peste domeniul audibil, ceea ce elimină fluieratul motoarelor. Timer2 produce ticul de 10 ms al buclei, în mod CTC, prescalare 1024, comparator la 155: perioada e 1024·156/16.000.000 = 9,98 ms. Timer0 rămâne bazei de timp în microsecunde, pentru măsurarea ecourilor.

De ce nu s-a folosit modulul de captură al lui Timer1 pentru ecouri

Manualul recomandă captura pe ICP1 pentru măsurarea precisă a lățimii unui impuls - dar aici Timer1 e deja ocupat de PWM pe opt biți, numărătorul lui se rotește la fiecare 128 µs, în timp ce un ecou poate dura până la 17,5 ms. Compromisul acceptat: citirea bazei de timp în microsecunde la cele două fronturi, rezoluție 4 µs. Sunetul parcurge dus-întors ~1cm în 58 µs, deci 4 µs înseamnă sub un milimetru - mult sub eroarea proprie a senzorului. Compromisul se sprijină pe un calcul, nu pe o impresie.

Convertorul analog-numeric are șase canale accesibile - patru pentru senzorii de linie, unul pentru tensiunea acumulatorului. La prescalarea 128, ceasul ADC e 125 kHz, o conversie durează 13 perioade (104 µs); cele cinci conversii dintr-un ciclu consumă ~520 µs, peste 5% din perioada de 10 ms - nu mult, dar nici neglijabil.

Sursele de întrerupere folosite: INT0/INT1 (encodere), vectorul de schimbare de pin al portului B (ecouri), comparația lui Timer2 (ticul de sistem), recepția USART (comenzi). Vectorul de schimbare de pin e comun unui întreg port - rutina trebuie să deducă ce pin s-a schimbat, comparând valoarea curentă cu cea memorată, și se declanșează pe ambele fronturi, ceea ce e util aici, pentru că avem nevoie de amândouă.

Exercițiu rezolvat - încărcarea produsă de numărarea encoderelor

Roțile au diametrul 80mm, discurile encoderelor 60 de fante, viteza maximă proiectată 0,6 m/s. ISR-ul durează măsurat ~4 µs, inclusiv salvarea contextului.

Vezi rezolvarea

Circumferința roții: π·80 = 251,3 mm; cu 60 de fante, un impuls la fiecare 251,3/60 = 4,19 mm. La 600 mm/s, o roată produce 600/4,19 = 143 impulsuri/s. Cu două roți și numărare pe un singur front: 286 întreruperi/s, deci 286·4 µs = 1,14 ms de procesor pe secundă, adică 0,11%. Chiar dublând rezoluția prin numărarea ambelor fronturi, încărcarea rămâne sub un sfert de procent. Numărarea în întrerupere e practic gratuită - justifică alegerea ei în locul scrutării.

6Arhitectura programului și mașina cu stări13 min

Arhitectura e cea hibridă din Cursul 10: întreruperile captează evenimentele rapide, o buclă principală ritmată de un tic de 10 ms ia deciziile. Regula de aur: nimic lent nu are voie să intre în buclă. Dacă bucla ar aștepta sincron ecoul unui senzor, ar sta blocată până la 17,5 ms - mai mult decât toată perioada ticului; robotul ar merge orb și ar ieși de pe traseu în curbă. De aceea ecoul se măsoară prin întreruperi, iar jurnalul se depune într-un tampon circular. Măsurată cu osciloscopul, lățimea impulsului de prelucrare a fost de ~1,1 ms - 11% din perioadă, mult sub limita de 50% cerută de CN1, rezervă care a permis mai târziu adăugarea odometriei.

Mașina cu stări

StareEvenimentStare următoare
AȘTEPTARESTARTURMĂRIRE LINIE
URMĂRIRE LINIEobstacol detectatOCOLIRE
URMĂRIRE LINIElinie sub pragLINIE PIERDUTĂ
OCOLIREocolire încheiatăURMĂRIRE LINIE
LINIE PIERDUTĂ2s fără linieAȘTEPTARE
orice starebuton urgență / STOPURGENȚĂ
Trei decizii ascunse în diagramă Nicio tranziție directă OCOLIRE→LINIE PIERDUTĂ: pe durata ocolirii, absența liniei e normală. Nicio tranziție directă LINIE PIERDUTĂ→OCOLIRE: fără să știe unde e traseul, n-are sens o manevră relativă la el. Ieșirea din URGENȚĂ nu e automată la eliberarea butonului, ci cere o acțiune explicită, ca un contact imperfect să nu repornească motoarele. Fiecare e o cerință de siguranță tradusă în absența unei tranziții.
volatile uint8_t bTic = 0;   /* ridicat de ISR-ul lui Timer2 */

int main(void) {
    motoareOprireImediata();      /* stare sigura INAINTE de configurare */
    porturiInit(); pwmInit(); ticInit(); encodereInit();
    ultrasuneteInit(); adcInit(); usartInit(38400);
    sei();

    for (;;) {
        if (!bTic) continue;      /* asteptam ticul; nicio intarziere activa */
        bTic = 0;

        odometrieActualizeaza(); senzoriLinieCiteste();
        ultrasunetePlanifica();  alimentareVerifica();

        if (butonUrgenta() || comandaStop()) {
            motoareOprireImediata(); stare = ST_URGENTA;
        }

        switch (stare) {
        case ST_URMARIRE:
            if (distantaFata() < D_OBSTACOL_MM) { ocolireIncepe(); stare = ST_OCOLIRE; }
            else if (!linieVizibila()) { cronometruPornire(&tCautare, 2000); stare = ST_LINIE_PIERDUTA; }
            else regulatorLinie();
            break;
        /* ... celelalte cazuri, cate unul per stare ... */
        default:                    /* stare imposibila: oprire preventiva */
            motoareOprireImediata(); stare = ST_URGENTA; break;
        }
        jurnalUmpleTampon();        /* depune octeti, nu asteapta transmisia */
    }
}

Verificarea condițiilor de urgență se face înaintea instrucțiunii de selecție, deci se aplică din orice stare, iar ramura implicită duce sistemul în starea sigură - dacă variabila de stare ajunge, dintr-un motiv oarecare, la o valoare imposibilă, robotul se oprește.

7Comanda motoarelor: zonă moartă, rampă, urgență10 min

Motoarele de curent continuu se comandă în PWM, nu în tensiune continuă variabilă - inductanța înfășurării netezește curentul, motorul se comportă ca și cum ar primi tensiunea medie, iar elementul de comutație disipă puțin. Sensul se obține inversând polaritatea printr-o punte H. Regula absolută: cele două brațe ale aceleiași ramuri nu conduc niciodată simultan, altfel e scurtcircuit direct pe acumulator.

Trei fenomene fizice trebuie tratate explicit. Zona moartă: sub un anumit factor de umplere, cuplul nu învinge frecarea statică din reductor, motorul rămâne nemișcat dar consumă curent. Necompensată, regulatorul produce, pentru erori mici, comenzi care nu se materializează în nicio mișcare. Compensarea comprimă domeniul util al comenzii peste intervalul dintre pragul măsurat și valoarea maximă. Saltul de comandă: o trecere bruscă de la zero la maxim cere un curent limitat doar de rezistența înfășurării - cade tensiunea pe acumulator (poate reseta microcontrolerul), patinează roțile (strică odometria). Soluția: o rampă, limitarea variației comenzii pe ciclu; cu pas de 8 unități pe 10 ms, trecerea 0→255 durează 319 ms, imperceptibilă dar suficientă să elimine vârful de curent.

Oprirea de urgență ocolește deliberat rampa

CF5 cere zero în ≤50 ms; rampa ar avea nevoie de peste 300 ms. Funcția de oprire imediată scrie direct zero în registrele de comparare, fără limitarea de variație - o excepție justificată de siguranță, care trebuie scrisă în comentariu, altfel un cititor ulterior o „corectează”.

#define PWM_MIN 40      /* masurat: sub acest prag motorul nu se roteste */
#define RAMPA_PAS 8      /* variatie maxima a comenzii intr-un tic de 10 ms */

/* Comprima domeniul util 1..255 peste PWM_MIN..PWM_MAX */
static uint8_t pwmDinComanda(int16_t modul) {
    if (modul <= 0) return 0;
    if (modul > 255) modul = 255;
    return (uint8_t)(PWM_MIN + ((uint32_t)modul * (PWM_MAX - PWM_MIN)) / 255);
}

static int16_t aplicaRampa(int16_t curent, int16_t dorit) {
    if (dorit > curent + RAMPA_PAS) return curent + RAMPA_PAS;
    if (dorit < curent - RAMPA_PAS) return curent - RAMPA_PAS;
    return dorit;
}

/* Oprire de urgenta: ocoleste DELIBERAT rampa, pt. cerinta de 50 ms. */
void motoareOprireImediata(void) {
    OCR1A = 0; OCR1B = 0;
    cmdStangaAplicata = 0; cmdDreaptaAplicata = 0;
}

8Odometria din encodere10 min

Fiecare roată motoare poartă un disc cu fante, citit de o pereche LED-fototranzistor. Cu un disc de 60 de fante și roată de 80 mm, un front corespunde la 4,19 mm de drum.

Numărarea se face în întrerupere, calculul în bucla principală Prin scrutare, programul ar trebui să citească pinul mai des decât se poate produce un impuls - la 0,6 m/s, impulsurile vin la ~7 ms unele de altele, o buclă de 10 ms le-ar pierde pe majoritatea. Simetric, calculul (conversie în mm, împărțirea la timp, viteza liniară și unghiulară) nu are ce căuta în întrerupere - o împărțire în virgulă mobilă acolo ar dura sute de microsecunde, întârziind toate celelalte întreruperi, inclusiv pe cea a celeilalte roți.

Un encoder cu un singur canal nu poate distinge sensul de rotație. Soluția riguroasă - encoder în cuadratură - ar cere alte două intrări de întrerupere, pe care nu le avem. Soluția de compromis: rutina adună contorul cu semnul comenzii date motorului, presupunând că roata se învârte încotro a fost trimisă. Presupunerea e falsă la frânare (inerția duce roata mai departe) și când o roată e blocată și împinsă înapoi - ambele introduc erori de odometrie, acceptate ca preț al economiei de pini.

volatile int32_t nImpulsStanga = 0, nImpulsDreapta = 0;
volatile int8_t sensStanga = +1, sensDreapta = +1;   /* scris de motoareComanda(), citit de ISR */

ISR(INT0_vect) { nImpulsStanga += sensStanga; }    /* front crescator, encoder stang */
ISR(INT1_vect) { nImpulsDreapta += sensDreapta; }

/* Contorul are 4 octeti; citirea se protejeaza, dar starea anterioara a bitului I
   se reface, ca sa nu validam intreruperile intr-o sectiune unde erau deja oprite. */
int32_t encoderCiteste(volatile int32_t *pContor) {
    int32_t v; uint8_t sreg = SREG;
    cli(); v = *pContor; SREG = sreg;
    return v;
}
Odometrie: rezolutie, viteza si eroarea de cuantizare
Exercițiu rezolvat - viteza din impulsuri și fereastra de măsurare

Într-un ciclu de 10 ms, contoarele cresc cu 12 impulsuri (stânga) și 10 (dreapta). Ecartamentul robotului e 150 mm. Se cer vitezele și eroarea de cuantizare.

Vezi rezolvarea

Direct pe fereastra de 10 ms: 12·4,19/0,01 = 5030 mm/s - absurd pentru un robot mic. Motivul: la 0,5 m/s, în 10 ms sosește doar ~1 impuls/roată, rezultatul e dominat de cuantizare. Refăcut pe o fereastră de 100 ms, cu aceleași 12 și 10 impulsuri: vs=503 mm/s, vd=419 mm/s, deci v=(503+419)/2=461 mm/s și ω=(419−503)/150=−0,56 rad/s (rotire spre dreapta). Cuantizarea introduce o incertitudine de un impuls, adică 4,19/0,1=41,9 mm/s, ~8% din viteză. Concluzie practică: fereastra de calcul trebuie mai lungă decât ciclul de reglare - contoarele se citesc la 10 ms, dar viteza se calculează pe ultimele zece citiri, cu o întârziere de 100 ms acceptabilă, pentru că viteza servește la raportare, nu la urmărirea liniei.

9Urmărirea liniei: regulatorul proporțional11 min

Cei patru senzori de linie sunt perechi LED-fototranzistor spre podea. Prima operație obligatorie e normalizarea: pentru fiecare senzor se memorează, la o calibrare pe pistă înainte de probă, valoarea pe fond deschis și pe bandă, iar valoarea citită se aduce prin interpolare liniară la o scară comună 0-100. Fără ea, diferențele dintre exemplarele de fototranzistoare ar face senzorii necomparabili, iar poziția calculată ar fi deplasată sistematic.

Poziția liniei e o medie ponderată: fiecărui senzor i se atribuie o poziție convențională (−3, −1, +1, +3), iar eroarea e centrul de greutate al răspunsurilor normalizate. Suma răspunsurilor servește dublu - dacă e sub un prag, niciun senzor nu vede banda, linia e pierdută (implementarea concretă a cerinței CN4).

#define KP_LINIE 18          /* unitati de comanda pe unitate de eroare */
#define PRAG_LINIE 40         /* suma normalizata sub care linia e pierduta */
static const int8_t pozitie[4] = { -3, -1, +1, +3 };

int16_t linieEroare(const uint8_t n[4], int16_t *pSuma) {
    int16_t suma = 0; int32_t moment = 0;
    for (uint8_t i = 0; i < 4; i++) { suma += n[i]; moment += (int32_t)pozitie[i] * n[i]; }
    *pSuma = suma;
    if (suma < PRAG_LINIE) return 0;             /* eroare nedefinita: linia e pierduta */
    return (int16_t)(moment * 100 / suma);
}

void regulatorLinie(void) {
    int16_t suma, e = linieEroare(nSenzor, &suma);
    int16_t u = (int16_t)(((int32_t)KP_LINIE * e) / 100);
    motoareComanda(VITEZA_BAZA + u, VITEZA_BAZA - u);
}

Corecția e proporțională cu eroarea, adunată la comanda unei roți și scăzută din cealaltă. Cu Kp prea mic, robotul reacționează leneș, taie curbele pe interior; cu Kp prea mare, oscilează cu amplitudine crescândă în jurul liniei. Limita superioară a lui Kp e dată de întârzierea totală din buclă (perioada ciclului, constanta de timp a motoarelor, inerția robotului).

Exercițiu rezolvat - eroarea de poziție și comenzile rezultate

Senzorii, normalizați, indică 5, 20, 90, 35 (stânga→dreapta). Viteza de bază 120, Kp=18.

Vezi rezolvarea

Suma = 5+20+90+35 = 150 (peste pragul 40, linia e vizibilă). Momentul = (−3·5)+(−1·20)+(1·90)+ (3·35) = −15−20+90+105 = 160. Eroarea (sutimi) = 160·100/150 = 106, adică 1,06 unități spre dreapta. Corecția u = 18·106/100 = 19. Comenzi: stânga 120+19=139, dreapta 120−19=101 - robotul virează spre dreapta, către bandă. Eroarea maximă posibilă e ±3 unități (±300 sutimi), deci u ∈ [−54, 54], iar comenzile rămân în [66, 174] - nu se saturează niciodată la 255, proprietate bună: dacă s-ar satura la eroare maximă, robotul ar pierde capacitatea de a mai corecta, exact în situația cea mai grea. Toate calculele s-au făcut cu numere întregi, factor de scară 100 explicit - pe un microcontroler fără virgulă mobilă, nu e o preferință de stil, ci diferența dintre câteva microsecunde și câteva sute.

10Planul de testare, cu nivelul de teren8 min

Planul urmează cele patru niveluri din Cursul 10, cu o particularitate: ultimul nivel nu se poate face în laborator. Un robot impecabil pe masa de lucru poate eșua pe pistă din motive care n-au nimic de-a face cu programul - iluminarea sălii, aderența pardoselii.

Nivelul de modul: funcțiile pure (eroarea de poziție, compresia peste zona moartă, rampa) compilate pentru calculatorul gazdă, încercate la limite. Nivelul de subsistem: roți ridicate de pe sol, comandă crescătoare pentru a găsi pragul zonei moarte; rotire manuală de zece ture pentru a verifica 600 de impulsuri la encoder. Nivelul de integrare: motoarele pornite mai numără corect encoderele, sau perturbațiile punții produc fronturi false? Nivelul de teren: scenarii complete pe pistă.

TestCerințăMod de efectuareCriteriu
T2-roți ridicate, comandă crescătoareprag zonei moarte identic ±5 la ambele roți
T6-motoare la comandă maximă, roți blocatecontoarele nu cresc cu fronturi parazite
T8CF5buton urgență la viteză maximă, 20 repetăricomanda punții la zero în <50ms
T11-caractere aleatoare pe interfața serialăcadre invalide ignorate, fără blocaj
Testele fără cerință asociată provin din testarea atipică T2, T6, T11, T12 nu au un cod de cerință. Dacă un asemenea test descoperă un defect, corectura se face nu doar în cod, ci și în caietul de sarcini. T6 în special: dacă eșuează, remediul nu e în software, ci în cablaj - separarea traseelor de forță de cele de semnal, torsadarea firelor de motor, condensatoare de decuplare la borne.

11Ce a mers și ce nu8 min

Un studiu de caz care raportează numai succese nu e de niciun folos - greșelile se repetă cu regularitate la fiecare generație de echipe.

A mers ceea ce a fost decis pe hârtie înainte de a fi construit: partiționarea cu interfețe scrise a permis ca modulul de tracțiune să fie înlocuit la jumătatea proiectului fără nicio modificare în restul programului; mașina cu stări a dat un comportament definit situațiilor neprevăzute; regula „nimic lent în buclă” n-a trebuit niciodată reconsiderată.

Nu a mers ceea ce a fost decis din mers. Prima greșeală: calibrarea senzorilor cu valori compilate în program - la concurs, iluminarea și pardoseala erau altele, robotul n-a mai văzut banda. Remediul: o calibrare pornită prin butonul de start, cu robotul plimbat manual peste bandă. A doua: alimentarea comună a electronicii și motoarelor - la accelerare bruscă, tensiunea cădea suficient încât microcontrolerul se reseta în mijlocul pistei. Remedierile: un condensator mare la intrarea stabilizatorului, rampa de accelerare, și un circuit de supraveghere a tensiunii. A treia: încrederea în odometrie ca sursă de poziție absolută - erorile se acumulează, iar după câțiva metri poziția estimată se depărtează vizibil de cea reală; utilă pentru manevre scurte, nu pentru navigație pe termen lung fără corecție periodică.

Toate cele trei greșeli au un element comun

Au fost descoperite la trecerea de la laborator la mediul real - exact nivelul de testare pe care echipele îl programează ultimul și îl scurtează primul când timpul se termină. Concluzia practică: se merge pe pista reală cât mai devreme, cu un robot incomplet, nu târziu cu unul terminat.

Nimic din ce a făcut acest proiect posibil n-a fost o cunoștință nouă despre ATmega328P - registrele timerelor, întreruperile, lanțul ADC erau toate cunoscute din capitolele anterioare. Ceea ce a decis rezultatul a fost ordinea deciziilor, disciplina de a scrie interfețele înaintea implementărilor și obiceiul de a verifica prin măsurare, nu prin presupunere - diferența dintre a ști să programezi un microcontroler și a ști să proiectezi un sistem cu microcontroler.

12Erori frecvente4 min

  • „Calibrăm senzorii o dată, în laborator, și punem pragurile în cod." Iluminarea și culoarea pardoselii de la concurs diferă de cele din laborator - robotul nu mai vede banda deloc. Calibrarea trebuie făcută la fața locului, printr-o procedură pornită la fața locului (buton dedicat), niciodată presupusă din valori compilate.
  • „Electronica și motoarele se alimentează de la același acumulator, ca să fie mai simplu." Vârful de curent la accelerare bruscă face tensiunea să cadă suficient încât microcontrolerul se resetează. Condensator mare la intrarea stabilizatorului, rampă de accelerare și circuit de supraveghere a tensiunii, aplicate împreună.
  • „Odometria ne dă poziția robotului, deci o putem folosi pentru navigație pe distanțe lungi." Cuantizarea, patinarea și sensul presupus (nu măsurat) al roților se acumulează - eroarea crește nemărginit cu distanța. Odometria e utilă doar pentru manevre scurte, unde eroarea n-are timp să se adune; pentru distanțe lungi e nevoie de o corecție periodică independentă.

13Rezumat și glosar5 min

Un proiect complet arată că metoda din Cursul 10 se comportă la fel sub presiunea resurselor insuficiente ca și pe exemplul mic: interfețele scrise dinainte permit dezvoltare în paralel și înlocuirea unui subsistem fără a strica restul; bugetul de resurse (pini, timere, canale ADC, întreruperi) transformă „microcontrolerul ăsta ar trebui să fie suficient” într-o listă verificabilă, iar saturarea lui obligă la compromisuri explicite, documentate, nu întâmplătoare; mașina cu stări face imposibile prin construcție combinațiile periculoase; regula „nimic lent în buclă” se verifică prin măsurătoare cu osciloscopul, nu prin presupunere; comanda motoarelor cere tratarea explicită a zonei moarte, a saltului de comandă și a excepției de oprire de urgență; odometria numără în întrerupere și calculează în buclă, pe o fereastră suficient de lungă pentru a domina zgomotul de cuantizare; urmărirea liniei e un regulator proporțional simplu, dar cu normalizare obligatorie și cu Kp limitat de întârzierea totală a buclei; iar planul de testare adaugă, față de proiectele de laborator, un nivel de teren care nu poate fi înlocuit. Lecția finală nu ține de registre, ci de disciplină: interfețele înaintea implementării, măsurătoarea înaintea presupunerii.

Buget de resurse
listă a resurselor cerute de subsisteme, comparată explicit cu ce oferă microcontrolerul ales.
Zona moartă
intervalul de comandă sub care motorul consumă curent dar nu se rotește, din cauza frecării statice.
Rampă de accelerație
limitarea variației comenzii pe ciclu, pentru a elimina vârful de curent la salturi bruște.
Normalizare
aducerea citirilor unor senzori diferiți la o scară comună, prin calibrare pe cele două extreme.
Nivel de testare pe teren
ultimul nivel al planului de testare, care exercită condițiile reale, ireproductibile în laborator.

14Întrebări de verificare7 min

  1. Transformați cerința „robotul trebuie să evite coliziunile” în două-trei cerințe verificabile, cu valori numerice și metoda de măsurare.
  2. Explicați de ce butonul de oprire de urgență nu trebuie să acționeze numai prin software.
  3. De ce nu a putut fi folosit modulul de captură al lui Timer1 pentru măsurarea ecoului?
  4. Un coleg mută calculul vitezelor din bucla principală în ISR-ul encoderului, „ca să fie mai actual”. Ce se strică?
  5. Ce este zona moartă a unui motor cu reductor și ce comportament produce, necompensată, la un robot urmăritor de linie?
  6. Descrieți procedura de calibrare a senzorilor de linie pe pistă și explicați de ce valorile compilate în program nu o pot înlocui.

15Încheierea disciplinei2 min

Acesta este ultimul curs al disciplinei Microcontrollere și Microprocesoare. Drumul complet - de la biții și registrele unui microcontroler izolat, până la un sistem autonom care trebuie să funcționeze corect din prima încercare, pe o pistă necunoscută - a folosit, la fiecare pas, aceleași principii: măsoară, nu presupune; scrie interfața înainte de implementare; testează pe straturi.

Aplicarea integrată a tuturor acestor noțiuni - achiziție, decizie, comandă, fără delay() - se exersează în Laboratorul 07, ultima lucrare de laborator a disciplinei.