Autorul poate fi contactat la adresa: Login pentru adresa
Imi doream de mai de mult sa fac un robot putin mai iesit din comun, care sa “ocoleasca” legile fizicii. Dupa ce am urmarit diverse forumuri si bloguri unde se discuta despre clone de Segway, unicicluri sau skateboard-uri care se balanseaza singure, m-am gandit de ce sa nu incerc si eu ceva de genul; in fond pe net erau resurse cu duiumul…
Problema pe care trebuia sa o rezolve robotul meu pentru a sta balansat la vertiacala este foarte asemanatoare cu cea a unui pendul inversat (wikipedia). In cazul meu punctul de “pivotare” al robotului este chiar centrul de greutate, care trebuie pozitionat cat mai sus, astfel incat robotul sa isi balanseze partea inferioara (rotile) in jurul acestui punct de sprijin virtual. Pentru a determina pozitia robotului exista o multime de abordati si aici enumar: intrerupatoare, senzori ultrasonici, senzori cu infrarosu si in cele din urma solutia pe care am folosit-o si eu: accelerometru + giroscop.
Din acest punct proiectul meu diverge fata de majoritatea abordarilor (care folosesc senzori dedicati, interpretati in timp real de microcontrollere). Cum un modul cu senzori era relativ scump de cumparat (probabil transportul ar fi fost mai mult decat modulul in sine), m-am gandit sa folosesc chiar senzorii din telefonul meu cu Android (nu mai scrisesem cod pentru Android la momentul respectiv). Prima idee de cum sa fac interfata cu ATmega-ul de pe placuta a fost sa folosesc un cip FTDI, insa nu cred ca ar fi fost o solutie prea buna intrucat USB-ul transfera datele in blocuri.
Urmarisem cu ceva timp inainte un thread de pe forumul XDA Developers in care un posesor de Samsung Galaxy S2 a incercat si a reusit sa obtina o conexiune UART prin pinii de la portul USB… Evrika! Exact ce imi trebuia. Trebuia doar sa pun un rezistor intre pinul de masa si pinul de id din conectorul USB si un cip din telefon comuta iesirea de pe pinii D+/D- de la USB pe RX/TX de la uart. Aveam practic acces la uart-ul din Application Processor si la cel de la Baseban Processor (cei de la samsung au integrat aceasta functionalitate pentru a comunica cu bootloaderul si cu application processorul, nu pentru scrantiti ca mine).
Incredibil, dar imi era lene sa caut/fac un rezistor de valoarea necesara (TODO: completat valoare), astfel incat mi-am propus sa hack-ui modulul de kernel care comunica cu cipul care facea multiplexarea astfel incat sa il fortez sa comute pe UART. Mult timp am urmarit cai verzi pe pereti. Cei de pe XDA banuiau ca i9100 foloseste acelasi cip ca si i9000, insa, spre surprinderea mea, kernelul pe care il aveam instalat nu avea nici urma de asemenea driver. In ideea ca nu a fost inclus pentru ca cipul se descurca singur, fara asistenta prin SPI de la CPU, mi-am propus sa compilez si sa inserez driverul ca modul. Dupa multi timp am si reusit, insa a fost in van. Driverul nu se putea bind-ui la niciun device de pe busul SPI asteptat pentru ca cipul nu era acolo!!!!
Am descarcat scheme si datasheeturi si poze si am mai cautat pe forumuri si am aflat ca i9100 defapt folosea o alta configuratie decat i9100. Aici cipul de power management (PMIC) se ocupa de multiplexarea conexiunii USB, care mai era multiplexata apoi de alt cip pentru conexiunea MHL. Din fericire cipul era putin mai barbar. Cand un dispozitiv era conectat la portul micro USB a telefonului se genera o intrerupere, apoi valoarea rezistorului dintre masa si pinul de ID era masurata folosind ADC-ul. Nu era insa de ajuns un singur rezistor, trebuia mai apoi sa fie selectat UART-ul de la AP pentru ca baseband processor-ul era conectat by default. Asta din fericire a fost simplu, a trebuit doar sa echo -n “1” > /sys/class/sec/uart_switch/value.
A mai ramas doar sa configurez interfata tty (ttySAC2, in cazul meu). Intr-un tarziu am reusit sa comunit prin UART folosind bus-pirate-ul meu. Era vorba de ceva rudimentar, “cat” si “echo”. Din fericire bus pirate-ul avea uart la 3.3v.
Cum majoritatea persoanelor care au facut un asemenea robot au modelat sistemul ca fiind liniar (desi nu prea e… mai ales cand robotul se inclina mult), am zis sa fac si eu asa. Era necesar doar un controller PID pentru bucla re reactie. Adevarata problema era sa obtin masuratori bune de la senzori. Accelerometrul imi putea indica o valoare statica a unghiului destul de buna, dar atunci cand lucrurile incepeau sa se miste raspunsul sau vine intarziat si are un overshoot deranjant. Pe de cealalta parte giroscopul (care masoara viteza unghiulara) ofera un raspuns foarte prompt si dinamic, dar viteza unghiulara trebuie integrata pentru a obtine unghiul. Eroarea acumulata in urma masuratorilor insa ar fi facut unghiul sa drift-uie foarte mult intr-un timp relativ scurt. Soltiile posibile pentru a obtine date folosibile erau 2 la numar. Prima ar fi fost un filtru complementar, cu o complexitate algoritmica mai mica si mai usor de inteles intuitiv. Rezultate mai bune se obtin insa folosind un filtru Kalman (TODO: insert link).
Varianta cea mai simpla pentru a obtine datele de la senzori ar fi fost sa scriu o aplicatie in Java pentru Android. Din nefericire intervalul la care vin masuratorile senzorilor in Java nu este deloc constant, fapt ce ar fi inrautatit performantele robotului. Soltia imediat urmatoare ar fi fost sa mut tot codul care masoara senzorii intr-o biblioteca nativa arm folosind NDK-ul de Android. Mi s-a parut ca as fi pierdut destul de mult timp sa fac asta avand in vere ca nu mai scrisesem deloc cod pentru Andoid. Imediat m-am gandit… de ce sa nu scap cu totul de Dalvik si sa am doar un binar nativ. Am clonat tot codul sursa de la Android si mi-am bagat nasul prin el. A durat un secol pana mi-am facut o idee despre ce se intampla in spate. Ideea mea de a comunica direct cu driverii din kernel si de a obtine valorile de la ei s-au naruti repede. Pe android exista sensorservice care ruleaza in contextul serverului de sistem si la care trebuie sa te “abonezi” pentru a obtine datele de la senzori. Din fericire am gasit un test pentru sensor service care facea cam ce vroiam eu. Aveam un binar care comunica cu serviciul si imi raporta masuratorile. Ba serviciul era chiar mai dragut; implementa chiar si un set de senzori virtuali (sensor fusion), care faceau exact filtrarea de care aveam eu nevoie.
Motoarele le-am cumparat cu 150lei (impreuna) de la un domn care avea mai multe bormasini electrice desfacute. Sunt mult mai puternice decat orice motor de la pololu in aceeasi bani.
Pentru driveri am vrut sa folosesc niste module de usa de la un camion MAN TGA (don't ask). Modulele au cate 2 integrate bts771 configurate in punte H. Din ce am vazut prin datasheet intuiam ca ar putea sa puna in miscare cele 2 motoare masive de bormasina cu tot cu reductoarele lor care nu se miscau prea liber. M-am inselat amarnic si am descoperit asta tocmai in noaptea de dinainte de prezentare.
Mi-a fost prea lene sa fac o schma pompoasa si fara continut in visio sau mai stiu eu ce, asa ca am luat un pix si am pus pe foaie ce aveam in minte.
Schema bloc de la Galaxy SII
Pe partea de MCU am folosit Eclipse cu pluginul de AVR. Cum VMware Workstation reusea cumva sa faca placa cu atmega invizibila si in host (win7) si guest (ubuntu13) am fost fortat sa programez direct prin spi folosind BusPirate-ul pe care il aveam de ceva timp. Din nefericire mergea foarte incet, insa un tip a facut un patch la avr-dude si la firmware-ul de buspirate care facea lucrurile sa se miste mult mai rapid. Am luat sursele de la avr-dude 6 release candidate, l-am compilat, am pus un bootloader ca sa vad cat de repede merge si am fost uimit. De la 3 minute fara patch acum putea sa arda bootloaderul in cateva secunde.
Problema a fost insa cu Eclipse, mai precis cu partea din plugin-ul Avr Eclipse care facea identificarea cipului. Versiunea rc de avr-dude pe care o aveam afisa lista de microcontrollere suportate in alt format, care nu era compatibil cu o expresie regulata din plugin. Solutia: am luat sursele de la plugin, le-am patchuit, am rezolvat niste dependente ciclice si pana la urma am reusit sa fac un local update site cu ultima versiune de avr-eclipse compatibil cu avr-dude 6rc.
Pe android am avut noroc cu binarul de test pentru sensor service (pe care l-am mentionat in introducere). A trebuit doar sa setez interfata tty de la uart in modul raw si sa ii setez baud-rateul. Am avut probleme cu receptia de date inspre telefon. Atunci cand se inchidea ecranul, dupa o scurta perioada de timp incepeam sa primesc date aiurea (!!!!) si dupa cateva secunde nu mai primeam deloc. Am luat un wakelock, insa asta a facut doar sa nu se opreasca comunicatiile de tot. Am incercat sa analizez kernel logul, insa nu am mai avut timp sa vad mai exact ce se intampla in spate. Imi imaginez ca era oprita vreo sursa externa de ceas folosita de uart sau erau power gated niste parti din SOC.
Sper sa reusesc sa fac robotul in vara, cu niste punti H zdravene home-made. Momentan pot sa spun doar ca am castigat ceva cunostinte legate de modul in care functioneaza android.
Nu te increde in puntile H integrate.
Daca ai impresia ca ceva o sa mearga bine pe ultima suta de metri te inseli amarnic.