Rete di sensori piuttosto complicata


9

Di recente stavo lavorando a un progetto ed è stato il primo a essere stato coinvolto abbastanza da complicare la rete dei sensori. Alla fine, penso che la comunicazione sia stata il collo di bottiglia in termini di prestazioni complessive e mi chiedo come persone più esperte avrebbero risolto questo problema. Questa è una lettura lunga, ma penso che sia piuttosto interessante, quindi ti preghiamo di attenerci. Il problema era progettare un dirigibile autonomo in grado di navigare su un percorso ad ostacoli e di lanciare palline da ping pong in bersagli di scatole marroni. Ecco qui:

sensori

  • 4D Systems Modulo telecamera uCAM-TTL - Interfaccia UART
  • Bussola digitale HMC6352 - Interfaccia I2C
  • Maxbotix Sonar ez4 - Interfaccia analogica a 1 pin

attuatori

  • 2x driver del motore L293D (collegati a semplici motori hobbistici): sono stati utilizzati per guidare 6 motori in modo bidirezionale. Hanno richiesto input PWM per variare la velocità. Ora 3 dei nostri motori facevano sempre la stessa cosa (quelli che controllavano il movimento su / giù), quindi avevano bisogno solo di 2 uscite PWM dai nostri controller per controllare tutti e 3 i motori. Gli altri 3 motori che controllavano il movimento laterale necessitavano tutti del controllo individuale (per il movimento omnidirezionale), quindi erano necessarie altre 6 uscite PWM dai nostri controller.
  • Servomotore - Interfaccia PWM

Controller

Per ragioni che saranno chiarite in seguito, abbiamo finito con l'uso di 2x ATmega328Ps. Abbiamo usato un Arduino Uno per programmarli (non avevamo accesso a un ISP) ma abbiamo creato un PCB personalizzato, quindi non abbiamo dovuto usare le schede Arduino poiché ciò avrebbe aggiunto peso inutile al nostro dirigibile. Per quanto riguarda il motivo per cui abbiamo scelto l'ATmega328P, conoscevo molto l'ambiente di Arduino e penso che abbia reso lo sviluppo del codice molto più rapido e semplice.

Comunicazione ed elaborazione

  • 2x Xbee Basic
  • 2x ATmega328P
  • Computer desktop che esegue C ++ con openCV

Come puoi vedere dal modulo della fotocamera, la maggior parte del nostro progetto si basava sulla visione artificiale. I dirigibili potevano trasportare solo così tanto peso e non ci sentivamo a nostro agio nell'implementare la visione computerizzata su un microcontrollore. Quindi quello che abbiamo finito è stato usare XBee per inoltrare i dati delle immagini a un computer desktop. Quindi sul lato server abbiamo ricevuto i dati delle immagini e abbiamo usato openCV per elaborare l'immagine e ricavarne elementi. Ora anche il lato server doveva conoscere le informazioni sull'altezza (dal sonar) e le informazioni sulla bussola.

La prima ruga è stata che non siamo riusciti a far controllare la fotocamera da un microcontrollore per un paio di motivi. Il problema principale era che la memoria interna su uP non riusciva a gestire la memorizzazione di un intero frame. Potrebbero esserci stati modi per aggirare questo problema attraverso la codifica intelligente, ma ai fini di questa domanda facciamo finta che fosse impossibile. Quindi, per risolvere questo problema, avevamo il lato server che inviava i comandi della telecamera attraverso il ricetrasmettitore XBee e il ricevitore XBee (a bordo del dirigibile) aveva l'output cablato all'ingresso della telecamera.

La ruga successiva è che non ci sono abbastanza PWM su un singolo ATmega328P per controllare tutti i motori PERCHÉ l'interfaccia I2C utilizza uno dei pin PWM (maledizione ...). Questo è il motivo per cui abbiamo deciso di utilizzare un secondo. In realtà il codice si prestava perfettamente all'elaborazione parallela perché il controllo dell'altezza era completamente indipendente dal controllo del movimento laterale (quindi 2 micros erano probabilmente meglio di uno collegato a un controller PWM). Pertanto, U1 era responsabile di 2 uscite PWM (su / giù) e della lettura del sonar. Gli U2 erano responsabili della lettura della bussola, del controllo di 6 uscite PWM (i motori laterali) e della lettura del sonar. Anche U2 era responsabile della ricezione dei comandi dal server tramite XBee.

Ciò ha portato al nostro primo problema di comunicazione. La linea XBee DOUT era collegata sia al microcontrollore che alla videocamera. Ora, naturalmente, abbiamo progettato un protocollo in modo che i nostri micro comandi ignorassero i comandi della fotocamera e i comandi della fotocamera ignorassero i micro comandi, quindi andava bene. Tuttavia, la videocamera, ignorando i nostri micro comandi, inviava i dati NAK sulla sua linea di output. Dal momento che il comando era destinato al micro, in qualche modo avevamo bisogno di spegnere l'uscita della telecamera sull'XBee. Per risolvere questo problema, abbiamo creato i 2 FET di controllo micro che si trovavano tra la telecamera e l'XBee (questo è il primo FET) e anche tra gli U2 e l'XBee (questo è il secondo FET). Pertanto, quando la telecamera cercava di inviare informazioni al server, il primo FET era "acceso" e il secondo FET era "spento".

Quindi per darti un'idea di come ha funzionato qui ci sono alcuni esempi:

  1. Il server richiede un'immagine: PIC_REQUEST passa attraverso XBee e arriva agli U2 e alla telecamera. Gli U2 lo ignorano e la fotocamera invia indietro i dati dell'immagine.
  2. Il server ha appena finito di elaborare un'immagine e sta inviando i dati del motore per dire al dirigibile di girare a destra - MOTOR_ANGLE (70) si collega a XBee e arriva a U2 e fotocamera. Gli U2 riconoscono come un micro comando e quindi disattivano il FET della fotocamera (ma forse la fotocamera ha già risposto con un NAK ?? chissà ...). U2 quindi risponde al comando modificando le uscite PWM del motore. Quindi riattiva il FET della fotocamera (questa era l'impostazione predefinita poiché i dati dell'immagine erano più importanti).
  3. Il server si rende conto che siamo arrivati ​​a un punto nel percorso ad ostacoli in cui la nostra altezza al passaggio del mouse predefinita ora deve essere di 90 pollici anziché 50 pollici. SET_HEIGHT passa attraverso XBee e succede la stessa cosa dell'esempio 2. U2 riconosce il comando SET_HEIGHT e attiva un interrupt su U1. U1 ora esce dal suo circuito di controllo dell'altezza e attende di ricevere dati seriali da U2. Esatto, più dati seriali. A questo punto il FET degli U2 è attivo (e il FET della videocamera è spento), quindi il server riceve l'altezza che U2 sta inviando anche a U1. Questo era a scopo di verifica. Ora U1 ripristina la sua variabile interna per height2HoverAt. Gli U2 ora disattivano il FET e riaccendono il FET della videocamera.

Ho sicuramente lasciato fuori una buona quantità di informazioni, ma penso che sia abbastanza per capire alcune delle complicazioni. Alla fine, i nostri problemi stavano semplicemente sincronizzando tutto. A volte ci sarebbero dati lasciati nei buffer, ma solo 3 byte (tutti i nostri comandi erano sequenze di 6 byte). A volte perdiamo la connessione con la nostra fotocamera e dobbiamo risincronizzarla.

Quindi la mia domanda è: quali tecniche suggerireste di rendere la comunicazione tra tutti questi componenti più affidabile / robusta / semplice / migliore?

Ad esempio, so che uno sarebbe stato quello di aggiungere un circuito di ritardo tra l'uscita XBee integrata e la videocamera in modo che il micro avesse la possibilità di disattivare il talk line della videocamera prima che rispondesse ai micro comandi con i NAK. Altre idee del genere?

Grazie e sono sicuro che ciò richiederà molte modifiche, quindi rimanete sintonizzati.


Edit1:Non ci è stato possibile collegare i dati UART della videocamera tramite uno dei micro. C'erano due opzioni per i dati della fotocamera, bitmap grezza o JPEG. Per una bitmap non elaborata, la fotocamera ti invia i dati il ​​più velocemente possibile. L'ATmega328P ha solo 128 byte per un buffer seriale (tecnicamente questo è configurabile ma non sono sicuro di come) e non pensavamo che saremmo stati in grado di farlo uscire dal buffer e passare all'XBee abbastanza velocemente. Ciò ha lasciato il metodo JPEG dove invia ogni pacchetto e attende che il controller lo ACK (piccolo prototipo di handshaking). Il più veloce a cui è potuto arrivare è stato di 115200 baud. Ora per qualche motivo, il più veloce che abbiamo potuto trasmettere in modo affidabile grandi quantità di dati sull'XBee è stato di 57600 baud (anche dopo aver effettuato l'accoppiamento nodo / rete per consentire la funzionalità di reinvio automatico). L'aggiunta di un ulteriore punto di arresto nella nostra rete (dalla fotocamera al micro su XBee invece della sola fotocamera su XBee) per il micro ha semplicemente rallentato il tempo impiegato per trasferire un'immagine troppo. Avevamo bisogno di una certa frequenza di aggiornamento delle immagini affinché il nostro algoritmo di controllo del motore funzionasse.


3
Stai facendo un grande sforzo per non espandere le tue abilità di microcontrollore. Non ho nulla contro Arduino, ma non è proprio appropriato. Riesci a farlo funzionare? Probabilmente. Tuttavia, sarebbe molto più utile imparare una piattaforma più capace. Se stai chiedendo come farebbero persone più esperte, direi qualcosa come ARM SBC per openCV e controllo e un FPGA che funge da ponte per tutte le interfacce. Tuttavia, sarebbe un po 'un salto quindi suggerirei solo di provare una nuova cosa importante ... forse un micro a 32 bit con abbastanza periferiche per interfacciarsi a tutto?
darron,

Ah ah sì, avrei fatto di tutto. Vedi che questo progetto era un compito scolastico e volevamo concentrarci sul farlo funzionare, non far sì che uno dei due EE del team imparasse una nuova piattaforma di microcontrollori. Sto pensando di entrare in ARM e micro più avanzati questa estate in realtà. L'altro EE del nostro team aveva effettivamente preso una classe in FPGA ... Gli
urlerò

Risposte:


4

Comprendo che volevi scegliere un ambiente di sviluppo che ti fosse familiare in modo tale da poter andare in moto, ma penso che il compromesso hardware / software potrebbe averti ostacolato attaccando Arduino e non scegliere una parte che avesse tutto le periferiche hardware di cui avevi bisogno e scrivendo invece tutto in C guidato da interrupt.

Sono d'accordo con il suggerimento di @Matt Jenkins e vorrei approfondirlo.

Avrei scelto un UC con 2 UART. Uno collegato all'Xbee e uno collegato alla telecamera. L'uC accetta un comando dal server per avviare una lettura della telecamera e può essere scritta una routine per trasferire i dati dal canale UART della telecamera al canale XBee UART su una base byte per byte - quindi nessun buffer (o al massimo solo un piccolo uno) necessario. Avrei cercato di eliminare tutti gli altri UC tutti insieme selezionando una parte che soddisfacesse anche tutte le esigenze del tuo PWM (8 canali PWM?) E se avessi voluto restare con 2 diversi UC che si prendessero cura dei loro rispettivi assi, forse un un'interfaccia di comunicazione diversa sarebbe stata migliore come tutte le altre UART sarebbero state prese.

Qualcun altro ha anche suggerito di passare a una piattaforma Linux integrata per eseguire tutto (incluso openCV) e penso che sarebbe stato qualcosa da esplorare. Sono stato lì prima, però, un progetto scolastico di 4 mesi e devi solo farlo al più presto, non posso essere bloccato dalla paralisi dall'analisi - spero che sia andato bene per te!


EDIT # 1 In risposta ai commenti @JGord:

Ho realizzato un progetto che implementava l'inoltro UART con un ATmega164p. Ha 2 UART. Ecco un'immagine da una cattura dell'analizzatore logico (analizzatore logico USB Saleae) di quel progetto che mostra l'inoltro UART: acquisizione dell'analizzatore

La linea superiore è i dati di origine (in questo caso sarebbe la tua videocamera) e la linea di fondo è il canale UART che viene inoltrato a (XBee nel tuo caso). La routine scritta per fare ciò ha gestito l'interruzione della ricezione UART. Ora, crederesti che mentre questo forwarding UART è in corso potresti configurare felicemente i tuoi canali PWM e gestire anche le tue routine I2C? Lasciami spiegare come.

Ogni periferica UART (comunque per il mio AVR) è composta da un paio di registri a scorrimento, un registro dati e un registro controllo / stato. Questo hardware farà le cose da solo (supponendo che tu abbia già inizializzato il baud rate e simili) senza alcun intervento se:

  1. Entra un byte o
  2. Un byte viene inserito nel suo registro dati e contrassegnato per l'output

È importante qui il registro a scorrimento e il registro dei dati. Supponiamo che un byte stia arrivando su UART0 e vogliamo inoltrare quel traffico all'output di UART1. Quando un nuovo byte è stato spostato nel registro di spostamento di ingresso di UART0, viene trasferito nel registro di dati UART0 e viene attivato un interrupt di ricezione UART0. Se hai scritto un ISR per esso, puoi prendere il byte nel registro dati UART0 e spostarlo nel registro dati UART1 e quindi impostare il registro di controllo per UART1 per iniziare il trasferimento. Ciò che fa è dire alla periferica UART1 di prendere qualunque cosa tu abbia appena inserito nel suo registro dati, metterlo nel suo registro di spostamento di uscita e iniziare a spostarlo. Da qui, puoi tornare dal tuo ISR e tornare a qualsiasi compito che il tuo uC stava facendo prima che fosse interrotto. Ora UART0, dopo aver semplicemente cancellato il registro a scorrimento e aver cancellato il registro dati, può iniziare a spostarsi in nuovi dati se non lo ha già fatto durante l'ISR e UART1 sta spostando il byte appena inserito - tutto ciò accade su proprio senza il tuo intervento mentre il tuo uC è spento per fare qualche altra attività. L'intero ISR richiede microsecondi per essere eseguito poiché stiamo spostando solo 1 byte di memoria, e questo lascia un sacco di tempo per spegnersi e fare altre cose fino all'arrivo del byte successivo su UART0 (che richiede 100's di microsecondi). e UART1 sta spostando il byte che hai appena inserito - tutto ciò accade da solo senza il tuo intervento mentre il tuo uC è spento per fare qualche altra attività. L'intero ISR richiede microsecondi per essere eseguito poiché stiamo spostando solo 1 byte di memoria, e questo lascia un sacco di tempo per spegnersi e fare altre cose fino all'arrivo del byte successivo su UART0 (che richiede 100's di microsecondi). e UART1 sta spostando il byte che hai appena inserito - tutto ciò accade da solo senza il tuo intervento mentre il tuo uC è spento per fare qualche altra attività. L'intero ISR richiede microsecondi per essere eseguito poiché stiamo spostando solo 1 byte di memoria, e questo lascia un sacco di tempo per spegnersi e fare altre cose fino all'arrivo del byte successivo su UART0 (che richiede 100's di microsecondi).

Questa è la bellezza di avere periferiche hardware: basta scrivere in alcuni registri mappati in memoria e da lì si prenderà cura di tutto il resto e segnalerà la tua attenzione attraverso interruzioni come quella che ho appena spiegato sopra. Questo processo avverrà ogni volta che un nuovo byte entra in UART0.

Notate come ci sia solo un ritardo di 1 byte nell'acquisizione della logica poiché stiamo "bufferizzando" 1 byte solo se volete pensarlo in quel modo. Non sono sicuro di come tu abbia elaborato la tua O(2N)stima: suppongo che tu abbia ospitato le funzioni della libreria seriale Arduino in un ciclo di blocco in attesa di dati. Se consideriamo l'overhead di dover elaborare un comando "read camera" su uC, il metodo guidato da interrupt è più simile a O(N+c)dove ccomprende il ritardo a singolo byte e l'istruzione "read camera". Questo sarebbe estremamente piccolo dato che stai inviando una grande quantità di dati (dati immagine giusto?).

Tutti questi dettagli sulla periferica UART (e tutte le periferiche su uC) sono spiegati a fondo nel foglio dati ed è accessibile in C. Non so se l'ambiente Arduino ti offre un accesso così basso da poter iniziare ad accedere registra - e questo è il punto - se non lo sei, sei limitato dalla loro implementazione. Hai il controllo di tutto se l'hai scritto in C (ancora di più se fatto in assembly) e puoi davvero spingere il microcontrollore al suo vero potenziale.


Immagino di non averlo spiegato bene. Il problema con il mettere il micro tra la fotocamera e l'XBee era che il micro non poteva fare nient'altro mentre stava ottenendo i dati dell'immagine a meno che tutto il resto non fosse interrotto dai timer. Inoltre, se si presume che ottenere un'immagine con N pixel abbia impiegato 5 secondi, quando si inserisce il micro ora occorrono ~ 10 secondi. Certo è ancora O (N) ma il suo tempo di esecuzione 2N che in questo caso è stato sufficiente a rovinare il nostro obiettivo di frequenza di aggiornamento. In definitiva lo spazio di memoria non era davvero il fattore limitante, era soprattutto la velocità. Sospiro, sembra l'unica vera risposta ...
NickHalden,

era usare hardware più avanzato. Speravo che qualcuno potesse suggerire qualcosa di veramente intelligente che potesse servire da trucco per tutti i miei anni come EE. Oh bene, suppongo che almeno significhi che non ero troppo stupido per pensare al trucco = P Oh e ha funzionato abbastanza bene.
NickHalden,

@JGord, per coincidenza ho fatto un progetto che utilizza l'inoltro UART nel modo che sto descrivendo tra un chip-card e una lavatrice con un ATmega164 jonathan-lee.ca/DC18-Smart-Card.html sto parlando utilizzando gli ISR ​​di UART per spostare un singolo byte da un registro dati UART all'altro registro dati di UART e quindi tornare. L'hardware UART agisce in modo indipendente e sposta i dati da lì. L'ISR impiega microsecondi e una volta tornato, l'UC è libero di fare tutto ciò che vuole fino a quando non viene spostato il byte successivo. Una spiegazione migliore arriva in una modifica, ogni volta che torno a casa.
Jon L

Interessante. Quindi il tuo suggerimento sarebbe di far parlare il server solo con il micro? Quindi che micro relè comanda da server a telecamera e risposte da telecamera a server? Quindi in UART0 ISR (fine a parlare con il server) ho potuto verificare se si tratta o meno di un comando della videocamera. Se sì, esegui il mirroring su UART1 sulla fotocamera. In caso contrario, non eseguire il mirroring e modificare invece un valore (angolo laterale, altezza, ecc. Con una variabile rispetto alla quale il mio circuito di controllo controlla.) Quindi l'ISR di UART1 sarebbe semplicemente rispecchiare sempre i dati dell'immagine su UART0. Sì, credo che funzionerebbe. Quindi
ho

e abbastanza canali PWM sarebbero stati la strada da percorrere. Quindi ora supponiamo che il server abbia inviato una richiesta get_data a cui il micro dovrebbe rispondere con una sequenza di 6 byte in cui invia altezza, direzione della bussola e alcuni ECC. Per qualche ragione il server legge 6 byte ma non ha il protocollo corretto, ovvero i byte dei messaggi di inizio e fine non si allineano. Chissà perché? Getteresti semplicemente il messaggio e richiedi di nuovo o cosa? Perché se nel buffer fosse presente un byte fittizio, il messaggio a 6 byte non si allinea più? Consiglieresti di svuotare dopo un messaggio non riuscito?
NickHalden,

1

Perché non è stato possibile reindirizzare i dati della telecamera attraverso µC? Non intendo fare il buffering delle immagini, ma inoltrare i dati UART attraverso il µC in modo che possa decidere cosa dovrebbe essere rispedito e cosa no?

Più semplice se hai un µC con due UART al suo interno, ma potrebbe essere emulato nel software.


Ah, sì, era una delle cose che avevo lasciato fuori ... modifica delle domande ora. Buona idea, però, siamo rimasti piuttosto entusiasti quando abbiamo pensato anche a quello.
NickHalden,

1

Mi è venuta in mente un'altra opzione, ma potrebbe essere un po 'ingombrante e troppo pesante per il tuo progetto:

  • Perché non utilizzare un server USB wireless, collegato a un piccolo hub USB, con adattatori USB-> RS232 per fornire più canali di controllo alle diverse parti del sistema?

Sì, ingombranti, ma se li spogli e forse usi USB invece di RS232 dove possibile, potresti probabilmente cavartela ...

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.