Domanda:
Qual è il modo più veloce per comunicare periodicamente tramite seriale?
SagiCZ
2014-04-16 01:33:26 UTC
view on stackexchange narkive permalink

Userò un Arduino come ponte tra un client Java di alto livello del computer e alcune unità di calcolo hardware di basso livello (ad esempio altri Arduino). Devo trovare una soluzione per comunicare periodicamente in entrambi i modi. Dovrebbe essere possibile inviare ingressi analogici digitali al PC e ricevere uscite analogiche digitali dal PC tramite seriale.

Ho requisiti molto specifici per far funzionare il mio progetto:

  1. Latenza più bassa possibile in entrambi i modi. Un'elevata latenza causerebbe instabilità dei sistemi controllati.

  2. Periodo di aggiornamento estremamente costante, possibilmente definito dall'utente. La variazione della frequenza di campionamento causerebbe imprecisioni nel controllo.

Ho bisogno di una soluzione molto veloce possibile per la frequenza di aggiornamento di 100 Hz e superiore.

Finora ho ho provato a inviare dati tramite Firmata al mio client java utilizzando l'interruzione del tempo.

  ISR (TIMER1_COMPA_vect) {Firmata.sendAnalog (analog, analogRead (analog));}  

I messaggi tuttavia non arrivano a una velocità molto costante e talvolta vengono mantenuti per due o più volte il periodo di campionamento, il che è inaccettabile. Per le frequenze più alte questo accade ancora più spesso. Sospetto una specie di tampone. Hai idea di dove dovrei cercare i colli di bottiglia? Sarebbe utile progettare il mio protocollo di comunicazione e abbandonare Firmata?

EDIT: Guarda il diagramma allegato. Le linee blu sono "ingressi", le linee verdi "uscite". In questo momento sto cercando di progettare "Arduino (Bridge)" enter image description here

Potresti voler usare semplicemente `millis ()` e `delay` per evitare variazioni nella frequenza di campionamento. Per la latenza, IMHO sarebbe meglio ottenere un baud rate molto alto. Potresti [ridurre gli errori con questa libreria qui] (http://www.nongnu.org/avr-libc/user-manual/group__util__crc.html).
Ho pensato che l'uso di interrupt temporizzato hardware dovrebbe essere significativamente più accurato. Quindi niente nel loop () può rallentare i messaggi. Ho sbagliato?
Hai ragione. Sarebbero più accurati, ma ricorda che, se vuoi che sia sempre coerente, dovrai aggiungere un po 'di "tempo di riempimento" alla maggior parte del codice per assicurarti che qualcosa di un po' più lungo non trattiene il prossimo iterazione. Non lo renderà * più veloce *, ma molto probabilmente regolerà bene il tempo tra le iterazioni. Dato che non conosco la tua applicazione, la soluzione nel primo commento è stata la più semplice :)
Un'interfaccia USB stessa potrebbe non essere adatta per un'applicazione che richiede una bassa latenza. È possibile che * solo a malapena * funzioni alla velocità necessaria con driver software molto efficienti su entrambe le estremità, ma dovresti considerare il fatto stesso di utilizzare l'USB per se stesso aggiungere un millisecondo o più di latenza e il tuo intervallo è inferiore a 10 ms. Normalmente, un loop di controllo dovrebbe essere spostato completamente all'esterno dell'USB, ovvero utilizzare una scheda incorporata più capace ("Arduino" o altro) ed eseguire l'intero loop di controllo su di esso, passando solo impostazioni / stato non in tempo reale tramite USB.
Capisco il tuo punto ma sarebbe impossibile per il mio progetto. Eseguirò una simulazione di un robot e del suo ambiente sul PC, quindi ho bisogno di comunicare periodicamente con l'unità di controllo del robot (per inviare input del sensore simulato e per ricevere comandi). Questo approccio è chiamato simulazione "hardware in loop" ed è il punto centrale del mio progetto.
Usare i timer hardware sarà ** WAY ** migliore di uno dei costrutti di arduino come `millis ()`. Sarà anche ** molto ** più deterministico per quanto riguarda i vincoli temporali. Ovviamente, se includi la latenza dell'USB (ovunque da 10-200 mS, imprevedibile e incontrollabile), probabilmente sei SOL.
@SagiCZ - quindi dovresti ospitare la simulazione su un sistema progettato per I / O a bassa latenza, ed evitare quindi di utilizzare USB nel percorso critico. Molte delle schede basate su ARM, ad esempio, possono farlo, e continuano a eseguire un sistema operativo desktop come Linux, forse permettendoti di spostare il tuo ambiente di simulazione con una semplice ricompilazione. In alternativa, se non riesci a trovare un hardware a bassa latenza per ospitare un dispositivo di test "hardware in the loop", forse dovresti abbandonare questo approccio ed eseguire una copia del programma del robot sul tuo PC.
Finora ho trovato la soluzione migliore, anche se devo provarla ancora un po '. Sposterò tutto il tempo o la simulazione critica della latenza su Arduino ed eseguirò le cose non critiche sul PC. In questo modo posso sfruttare la potenza di elaborazione e le capacità grafiche superiori dei computer e non perdere la precisione al microsecondo dei microcontrollori. Il client PC genererà e caricherà anche uno Sketch personalizzato per il "bridge" Arduino ogni volta che l'utente cambia la configurazione del robot.
Tre risposte:
Lesto
2014-04-16 04:05:37 UTC
view on stackexchange narkive permalink

Latenza più bassa possibile in entrambi i modi. Una latenza elevata causerebbe instabilità dei sistemi controllati.

il fatto che tu stia utilizzando un sistema operativo non in tempo reale introduce molta latenza imprevedibile, quindi l'uso di una macchina virtuale su quel sistema operativo aggiungere un po 'di più. La comunicazione veloce può essere ottenuta aumentando il baudrate, che diminuirà la latenza tra il campione e l'elaborazione. Anche l'invio di dati RAW invece della stringa sarà più veloce (nessuna conversione E meno invio di byte) e infine diminuirà l'overhead causato dal protocollo.

Ad esempio Firmata per impostazione predefinita usa un baudrate basso E aggiungerà molto di overhead per inviare CHE tipo di operazione sta facendo. Scrivere il proprio protocollo di comunicazione è meglio.

Ad esempio: analogRead usa 10 bit, quindi puoi inviare un int grezzo, 2 byte, 16 bit, quindi 6 bit vengono persi .. oppure puoi "comprimere" con un bit di operazione bit per bit 4 letti (40 bit) in 5 byte (40 bit, nessuna perdita di bit) invece di 8 (dimensione int * 4)!

AnalogRead è un'operazione molto lenta per impostazione predefinita su arduino (~ 200us ), ma può essere più veloce se imposti un prescaler più basso (attenzione, un prescaler più basso significa una minore precisione dell'ADC. al prescaler 16 la precisione garantita è di 8 bit, se la mia memoria è buona)

Estremamente costante, possibilmente periodo di aggiornamento definito dall'utente. La variazione della frequenza di campionamento causerebbe imprecisioni nel controllo.

Sul lato PC, se hai bisogno di una precisione micro / nano al secondo, hai bisogno di un sistema operativo in tempo reale , linux più recente il kernel aggiunge un nuovo programma speciale ma penso che sia ancora difficile trovare documentazione. Se una precisione inferiore va bene, usa un timer.

Sul lato arduino, l'utilizzo di un timer hardware è il modo migliore, vedi LeOS per un buon programmatore su arduino

Nota: HardwareSerial sul lato arduino usa l'interrupt per inviare i dati, quindi usarlo all'interno di un ISR (come stai facendo ora o con LeOS) è davvero usafe e soggetto a deadlock. Inoltre ISR si "rallenterà" a vicenda, poiché non possono essere eseguiti parallelamente; e analogRead è un'operazione davvero lenta..userei il timer per impostare un flag, e nel ciclo se vedo il flag, avvii analogread, Serial.write e infine Serial.flush (per essere sicuro di non riempire il buffer Serial , causando molti problemi nel ripristino della riprogrammazione di arduino)

"... ma può essere più veloce se imposti un prescaler inferiore ..." O se rinunci del tutto a `analogRead ()` e fai semplicemente che l'ADC scarichi i suoi risultati in una variabile `volatile` al termine della conversione e legga da quella variabile su richiesta.
Non vedo come l'adc letto interrupt finito possa aiutare, specialmente se non si imposta l'adc. O stai suggerendo di utilizzare register invece di snalogread
Si imposta l'ADC su trigger automatico (ad esempio, un timer) e l'ISR di completamento scarica il valore nella variabile. Quindi si legge da questa variabile invece di accedere direttamente all'ADC.
grazie, queste sono ottime idee .. Quando si tratta del lato client del PC, non sto seguendo il percorso RTOS quindi vorrei implementare il secondo approccio che hai suggerito -> "usa solo un timer". Potresti approfondirlo un po '? In che modo il timer mi aiuta rispetto all'ascolto ininterrotto della porta? Volevi dire che posso cronometrare l'elaborazione del messaggio per il costo di essere "in ritardo" di uno o due periodi? Probabilmente sarebbe accettabile.
+ SagiCZ stavo parlando di un'azione programmata. Per leggere l'evento dati o bloccare la chiamata sono la soluzione ottimale
E se programmassi la lettura in alcuni momenti particolari nel tempo ma i messaggi arrivassero tra quei momenti?
x arduino e pc: sono bufferizzati. viene utilizzato un buffer circolare, quindi quando il buffer è pieno, la lettura più recente sovrascrive la più vecchia
user3550029
2014-04-20 13:45:02 UTC
view on stackexchange narkive permalink

potresti essere più specifico con la tua domanda? Da quello che ho capito:

  • hai un programma java client che funge da "cervello" del tuo sistema
  • tuo il controllo è sensibile al tempo. Hai un PID o un altro circuito di regolazione in esecuzione sul PC
  • hai o prevedi di avere più schede di discussione sul tuo sistema e devi comunicare con loro.
  • hai a che fare con IO analogici

bridging

Se vuoi restare sulla porta seriale, devi usare RS485 o RS422. Quelli possono funzionare come un autobus. Il protocollo in cima a quello può essere Modbus. Nota che ha una latenza maggiore e hai bisogno di almeno 2 porte seriali sul tuo bridge

I2C. Bus a 2 fili a latenza inferiore. Orientato ai comandi, funziona a 400 kHz. Quindi hai tutto il tempo per inviare / ricevere dati

SPI ha una latenza ancora più bassa poiché i byte vengono inviati come stream .arduino mega ha 5 pin per esso. Nota che in questo caso devi occuparti dell'indirizzamento.

Arduino come molti microcontrollori è pessimo perché fa molte attività contemporaneamente come nel caso del bridging. In questo caso eviterei di usare un arduino

latenza

Come già risposto, la conversione ADC richiede tempo e non puoi annidare interrupt. La tua latenza minima può essere calcolata dalle specifiche di arduino .La latenza massima è un valore che aggiusti da solo. Conoscendo la quantità di tempo per inviare un byte sulla porta di comunicazione, puoi decidere tra opzioni come

  • eseguire la conversione AD in un ciclo al di fuori dell'interrupt , metti il ​​valore all'interno di una variabile e fai la comunicazione in modo sincrono all'interno di un interrupt
  • esegui la conversione AD basata sull'interrupt e la comunicazione nel ciclo principale dell'operazione

conclusioni

Non eseguire hardware in loop utilizzando un PC. La latenza e la mancanza di rigorose possibilità di temporizzazione peggiorate dall'uso di java lo rendono un vicolo cieco con la tua configurazione attuale.

Usa un micro controller più potente come un AVR32 con USB che fa funzionare il cervello del tuo sistema. Puoi anche usare un lampone pi o un beaglebone. Quelli eseguono distribuzioni Linux "standard" e quindi puoi usare un approccio simile a quello di un PC.

In ogni caso le linee guida sono:

  • cerca di stare il più vicino possibile al microcontrollore possibile ed evita le librerie di astrazione e i sistemi operativi
  • non utilizzare un PC per l'hardware in loop
  • prova a far eseguire il tuo compito il più possibile in modo sincrono
  • mantieni le tue routine di interrupt il più piccole possibile
Forse potrei dimenticarmi della seriale ma usare comunque Arduino e connessione USB. Penso che ci potrebbe essere un modo per impostare la connessione come un dispositivo HID o qualcosa di completamente diverso da Serial. La tua conclusione e i suggerimenti finali purtroppo ignorano il punto del progetto. Che è simulare con l'hardware nel loop ma anche visualizzare il robot simulato nel motore di gioco Java. Non credo che dovrebbe essere impossibile inviare i dati così velocemente. So che ad esempio le schede audio esterne hanno una latenza di circa 1 ms e inviano i dati con velocità di 20 kHz o simili tramite USB.
Ho modificato la domanda con un diagramma per aiutarti a capire.
ShanevanJ
2014-04-19 23:05:35 UTC
view on stackexchange narkive permalink

Ho avuto un grande successo rimuovendo seriale da un esempio del genere e impostando una rete chiusa e quindi Ethernet abilitando tutti i dispositivi arduino. Quindi ho un server DCHP e SNTP nella rete chiusa. Il PC ecc. Sono tutti nella rete chiusa. Quindi ho configurato un server MQTT (vedi www.mosquitto.org) e uso la libreria Arduino PubSub su arduino. Ottengo viaggi di andata e ritorno estremamente brevi con tempi di risposta abbastanza costanti e questo ha il vantaggio che più fonti possono pubblicare o iscriversi ai flussi di dati creati. Il server SNTP viene utilizzato in modo che tutti i dispositivi ottengano lo stesso riferimento temporale da utilizzare e io stampo l'ora di tutti i messaggi e posso quindi elaborare la sequenza dei messaggi ecc. Anche se sono fisicamente ritardati leggermente da una collisione Ethernet! sebbene in una rete chiusa ciò sia ridotto al minimo.

Sembra promettente. Hai usato lo scudo ethernet di Arduino? Potreste fornire maggiori dettagli o indicarmi una direzione per conoscere i dettagli di questa implementazione? L'abbandono della serie è stata la mia idea più recente :)
@SagiCZ Se stai cercando un protocollo di comunicazione basato su PubSub ad alte prestazioni, senza l'overhead del broker MQTT, dovresti dare un'occhiata al mio progetto, è praticamente quello, protocollo punto-punto, libreria C leggera e un python CLI per comunicare con il dispositivo, aprire grafici, ecc: https://github.com/Overdrivr/pytelemetrycli


Questa domanda e risposta è stata tradotta automaticamente dalla lingua inglese. Il contenuto originale è disponibile su stackexchange, che ringraziamo per la licenza cc by-sa 3.0 con cui è distribuito.
Loading...