Domanda:
Ordine di trasmissione seriale binario dei dati
mike
2014-06-23 13:38:35 UTC
view on stackexchange narkive permalink

Sto implementando un filtro per il mio sensore IMU e quindi voglio che i dati in tempo reale vengano visualizzati sul computer. Uso la comunicazione seriale binaria per facilitare la parte di invio per arduino (per quanto ne so il serial.print è piuttosto lento). Quindi ho diviso il mio int16_t in due byte e lo invio, come:

  Serial.write ((uint8_t) (gx >> 8)); Serial.write ((uint8_t) (gx & 0xFF));  

Dopodiché invio direttamente il numero successivo (3 in totale ormai, forse fino a 7 numeri da 2 byte in futuro) . Ho letto la cosa in matlab con:

  dt (k) = toc; tic; bindata ([1: 6], k) = fread (s, [6,1], " int8 '); time = cumsum (dt (1: k));  

Che legge 6 byte (3 numeri) e poi ricalco la rappresentazione binaria, li concateno e ottengo il numero originale (se qualcuno può suggerire un modo più semplice .. Ho trovato matlab piuttosto poco pratico qui).

Il problema è che i numeri si mescolano con il tempo. Alcuni quando un byte non viene letto o altro, quindi i byte vengono confusi e viene prodotto un numero senza senso. Un numero intero (2 byte) viene saltato per un campione esattamente. Invece di questo numero uno c'è due volte. Il prossimo campione l'ordine è incasinato (spostato, in modo che il primo numero sia il secondo). Questo processo viene visualizzato dopo forse 30 secondi, a volte alcuni minuti. Dopo la prima volta continua a spostarsi e saltare.

Qualcuno può dirmi cosa fare qui? Posso includere un qualche "punto di interruzione" / terminatore di riga, in cui il lettore (matlab) sa che siamo all'inizio del primo numero? O come viene fatto effettivamente?

Credo di dover aggiungere il mio obiettivo principale: voglio rendere l'invio il più veloce possibile per arduino. Non dovrebbero essere necessari calcoli aggiuntivi (se possibile). E: la ragione di questi cambiamenti sembra essere il ritardo (lentezza). Sospetto che sia una lettura matlab lenta, poiché ho visto script di elaborazione fluenti nella lettura HIL. Tuttavia, gli errori si sono fermati, poiché ho abbassato la velocità di trasmissione. Solo i numeri sbagliati sono ancora il problema.

Potrebbe esserci la possibilità di scorrere il fread e leggere semplicemente memorizzare i valori dopo un'intestazione aggiunta? Quindi diciamo che si verifica un ordine sbagliato. Quindi scarto tutto fino al successivo carattere / byte "a" e utilizzo i seguenti 6 byte per produrre i miei 3 valori. Quindi aspetto di nuovo una "a". Per questo dovrei eseguire il loop fread (s, [1,1], 'int8'); e cerca l'intestazione.

Codice arduino completo:

  // Programma per inviare i dati del giroscopio / accel tramite porta seriale // Programmi matlab corrispondenti: sensing.m e sensing_binary .m // 2 loop di sicurezza per garantire un tempo di campionamento costante // #define DEBUG # include "GY86.h" #include "Wire.h" GY86 gy86; int16_t ax, ay, az; int16_t gx, gy, gz; uint32_t currenttime = 0; uint32_t starttime = 0; uint32_t starttime2 = 0; // #define OUTPUT_ACCEL_COUNTS # define OUTPUT_GYRO_COUNTS // #define OUTPUT_ACCEL_BINARY // #define OUTPUT_GYRO_BINARYvoid setup () {Serial.beg; gy86.setup ();} void loop () {currenttime = millis (); if (currenttime-starttime > 9) {while (micros () - starttime2 < 9000) {} starttime2 = micros (); // legge le misurazioni gyro / accelerate dal dispositivo gy86.getSensorValues ​​(&ax, &ay, &az, &gx, &gy, &gz); // verifica delle costanti // gx = -29; // gy = 245; // gz = 17; #ifdef OUTPUT_GYRO_COUNTS Serial.print ((int) gx); Serial.print (F ("\ t")); Serial.print ((int) gy); Serial.print (F ("\ t")); Serial.print ((int) gz); Serial.print (F ("\ t")); # endif # ifdef OUTPUT_ACCEL_COUNTS Serial.print (ax); Serial.print (F ("\ t")); Serial.print (ay); Serial.print (F ("\ t")); Serial.println (az); Serial.print (F ("\ t")); # endif
#if definito (OUTPUT_ACCEL_COUNTS) || definito (OUTPUT_GYRO_COUNTS) Serial.print (F ("\ n")); # endif # ifdef OUTPUT_ACCEL_BINARY Serial.write ((uint8_t) (ax >> 8)); Serial.write ((uint8_t) (ax & 0xFF)); Serial.write ((uint8_t) (ay >> 8)); Serial.write ((uint8_t) (ay & 0xFF)); Serial.write ((uint8_t) (az >> 8)); Serial.write ((uint8_t) (az & 0xFF)); # endif # ifdef OUTPUT_GYRO_BINARY Serial.write ((uint8_t) (gx >> 8)); Serial.write ((uint8_t) (gx & 0xFF)); Serial.write ((uint8_t) (gy >> 8)); Serial.write ((uint8_t) (gy & 0xFF)); Serial.write ((uint8_t) (gz >> 8)); Serial.write ((uint8_t) (gz & 0xFF)); # endif starttime = currenttime; }}  
Tre risposte:
#1
+3
BrettAM
2014-06-24 03:23:22 UTC
view on stackexchange narkive permalink

Potresti aggiungere una sorta di intestazione / piè di pagina del pacchetto all'output seriale. Ad esempio, se hai inviato i caratteri "ab" prima dei byte per il tuo numero, potresti rifiutare i pacchetti che contengono solo 2 byte. il numero di serie sarebbe

"ab123ab123ab12ab123"

Il codice matlab potrebbe vedere un altro "ab" entrare prima che il pacchetto precedente finisse e ignorare il pacchetto che è finito come solo "ab12" , e poi rimettersi in carreggiata. Dovresti usare 2 byte per la tua intestazione per evitare collisioni con i dati effettivi. L'aggiunta di un checksum alla fine del pacchetto consentirebbe anche di rilevare errori di scambio di bit. CRC o fletcher16 funzionerebbero perfettamente in questo scenario, ma nel tuo caso un checksum potrebbe essere più del necessario.

Quindi devo aggiungere Serial.write ('ab') e quindi scrivere i bit numerici. E in matlab fread 2 + number-bits e controlla se i primi 2 sono ab. In caso contrario, rileggi. È questa la cosa che suggerisci? Cosa intendi per collisione con i miei dati .. Se leggo solo 1 + num-bit e si spostano, riceverei comunque un errore se non tutti i bit sono presenti o il primo non è "a"?
Per collisione intendo che se i tuoi dati fossero i caratteri "ab", rovineremmo una lettura. Hai ragione sul lato arduino, invia semplicemente "ab" prima dei dati. Il codice matlab dovrebbe rilevare che la sequenza "ab" è avvenuta e quindi cercare i bit di dati successivamente, ma se un altro "ab" accade prima che i bit di dati siano terminati, scarica ciò che è già arrivato e ricomincia. In questo modo si riallinea con i dati se viene eliminato un byte.
Collisione intendi la probabilità, che nei miei valori effettivi uno dei byte contenga l'intestazione per incidenza? Quindi un'intestazione più lunga ridurrebbe la possibilità di avere dati reali = stringa di terminazione. Corretta?
Esattamente. Con due byte c'è una probabilità su 2 ^ 16 che dati casuali generino l'intestazione. Un'intestazione più lunga di 2 byte che potrebbe comportare la trasmissione di meno dati poiché i pacchetti sono così piccoli; L'invio di un carattere in più per pacchetto costerebbe probabilmente più tempo che perderne uno su 65536 pacchetti.
Ok bene. Lo implementerei come Serial.write (219); Serial.write (128); Poiché ciò che viene inviato è binario (quindi lo stesso di alcuni char), e per matlab è più facile leggere (e confrontare) direttamente l'uint con 8 bit. Lo contrassegno come soluzione, poiché posso rimuovere gli spostamenti. L'intera cosa è tuttavia inutile, poiché c'è uno strano ritardo temporale che si somma. Vedi: http: //stackoverflow.com/questions/24368670/matlab-plot-serial-data-continously
#2
+3
Anonymous Penguin
2014-06-24 04:23:26 UTC
view on stackexchange narkive permalink

Modifica: sembra che il problema sia eccessivo. Ciò significa che la connessione USB non tiene il passo con i dati che tentano di essere inviati. Per risolvere questo problema, devi eseguire una delle seguenti operazioni (o entrambe):

  • Velocità in baud più alta. La velocità in baud è la frequenza con cui vengono inviati i dati. Da quello che ho sentito, qualsiasi valore superiore a 500.000 come velocità di trasmissione non è utile con le librerie Arduino.
    • L'IDE di Arduino sale solo così in alto. Prova un'applicazione come PuTTY per ottenere velocità di trasmissione più elevate sul monitor seriale.
    • Velocità molto elevate come questa sono più adatte su un cavo più corto che puoi gestire. Direi 4 piedi max , anche se dipende da molti fattori, inclusa la qualità del cavo. Un cavo più corto ha meno resistenza (quindi meno errori).
    • Il resto della risposta è ancora valido. Potresti voler aggiungere un semplice bit di parità per assicurarti che i dati non vengano danneggiati durante l'invio. L'aggiunta di due o tre caratteri riduce notevolmente il rischio di danneggiamento, ma a scapito di dimezzare la frequenza di campionamento e non verifica l'integrità dei dati. Non conosco la tua situazione esatta, quindi potrebbe non essere possibile aggiungere un bit.
  • Frequenza di campionamento più bassa: stai inviando troppo , quindi una soluzione semplice è semplicemente aggiungere delay (250); alla fine del ciclo in modo da non sovraccaricare la porta.

Risposta originale:

L'unica cosa che viene in mente molto efficiente è un bit di parità con un altro bit che è sempre l'opposto del bit di parità. Perché? Avere una cosa accidentale in cui gli ultimi due bit sono uno di fronte all'altro e si sommano tutti a un numero pari (ignorando l'ultimo bit) sarebbe davvero strano.

Un bit di parità è un bit in più, quindi tutti i bit sommati al bit di parità sono uguali a un numero pari. Se non è del tutto corretto, allora sai che c'è un problema. Funziona solo per numeri dispari di bit modificati, quindi non è infallibile. Un esempio è che hai i bit 10010110 . Ci sono quattro 1 , quindi è un numero pari, quindi il bit di parità sarà 0 . Se fosse un numero dispari, sarebbe 1 per rendere il conteggio totale un numero pari. Se il computer calcola che non si sommano correttamente (scusa il mio sciocco gioco di parole), allora è danneggiato e il computer può scartarlo.

Per implementare questo devi convertire i numeri e il bit di parità in ASCII e quindi contare gli 0 e gli 1. Puoi usare una funzione resto e dividere per due in modo che ci sarà un resto di 1 se è dispari, quindi è danneggiato. Personalmente prenderò l'ultimo bit e il numero x prima di esso, e continuerò a ripetere il ciclo finché non trovi una combinazione che soddisfi l'intera questione del bit di parità e rientri in un intervallo ragionevole che hai specificato nel codice.

Un'interruzione di riga sarebbe sufficiente, ma richiede più bit e scopre solo dati mancanti, non dati danneggiati.

Forse dovresti esaminare il perché c'è un problema . Potresti ridurre la lunghezza del cavo USB? Aggiornare il cavo / provarne uno diverso? Ridurre leggermente la velocità di trasmissione?

Il cablaggio è lungo forse 2 m. Il baude rate è 115200 ... quale sarebbe il vantaggio di un baude rate inferiore? E come faccio a sapere quanto mi serve?
Dato che pensavo che l'errore fosse nel mio codice matlab, ho già aperto una domanda SE per questo, poiché ora questo riguarda più il ritardo, più che gli scambi in bit, continuiamo lì. Il tuo suggerimento funziona! Il tempo di campionamento di 1/3 s fornisce valori di tempo validi / correnti! Pls mi aiuta a capire appieno da dove potrebbe provenire questo: http: //stackoverflow.com/questions/24368670/matlab-plot-serial-data-continously
@mike non è un problema di codice Matlab, è l'Arduino quindi. Stai inviando più dati di quelli che il chip seriale può gestire alla velocità di trasmissione specificata. So di aver detto di abbassarlo (pensavo che le informazioni si stessero corrompendo), ma è necessario aumentare la velocità di trasmissione e abbassare la frequenza di campionamento.
Sono possibili 25 Hz. È il più alto possibile. Tuttavia volevo eseguire il debug di un filtro quadricottero, ... il che non ha molto senso se eseguo 1/2 o anche 1/4 della frequenza di campionamento pianificata. (Cerco di ottenere i miei 100Hz, o almeno 50) .Inoltre se aggiungo più numeri questo fenomeno si verifica più spesso immagino? Perché è troppo pieno, giusto? Proverò a ottenere un cablaggio più corto, grazie per il consiglio!
@mike Sono contento di aver potuto aiutare! Ho modificato la mia domanda e ho ripulito i commenti qui.
#3
  0
user2973
2014-06-30 18:56:13 UTC
view on stackexchange narkive permalink

Se quello che vuoi è alta velocità, la velocità di trasmissione seriale è la tua preoccupazione principale. Presumo che tu stia utilizzando un Arduino con un chip convertitore da seriale a USB, come Uno o Mega.

Trasferendo a 115200 baud, invii 11520 byte / s con impostazioni seriali normali (1 start + 8 dati + 1 bit di stop = 10 bit per inviare 1 byte di dati). Con un Arduino in esecuzione a 16 MHz di clock, è possibile eseguire istruzioni 16M / 11520 = 1389 nel tempo necessario per trasmettere un byte. Quindi non devi preoccuparti molto delle routine lente della libreria, quello di cui ti devi preoccupare è di quanti byte invii. In questo modo hai ragione a non usare Serial.print, poiché convertirà il tuo int in ascii, rendendo tutti i numeri maggiori di 99 più lenti da trasmettere (per un uint16).

Quando invii dati con Serial .write viene prima memorizzato in un buffer circolare, quindi trasmesso in background da una routine di servizio di interrupt. Il buffer circolare è in genere di 64 byte. Una volta che il buffer è pieno, una chiamata a Serial.write si bloccherà fino a quando un byte non sarà stato trasmesso, quindi c'è di nuovo spazio nel buffer. Pertanto la temporizzazione nel tuo loop principale non è realmente necessaria, puoi rimuoverla e la tua frequenza di campionamento si adatterà alla tua velocità di trasmissione seriale.

Una volta che i dati hanno raggiunto il chip convertitore da seriale a USB, essere trasmesso sul bus USB a 12 Mbit / s. Non conosco la velocità di trasferimento dati esatta poiché c'è molto più overhead su USB che su seriale, ma puoi essere certo che il collo di bottiglia non è qui. Non penso che tu debba preoccuparti della lunghezza del cavo USB in quanto nel protocollo USB è integrato il rilevamento degli errori e la ritrasmissione e la velocità è molto più veloce del collegamento seriale.

Nel computer i dati possono essere memorizzati in diversi posti, tutto dipende dai driver del dispositivo e dalle librerie seriali utilizzate. Non sono un esperto qui. Senza controllo del flusso, i driver / librerie probabilmente non hanno altra scelta che eliminare i dati se stanno ricevendo più di quanto l'applicazione finale o il livello successivo possano elaborare. Forse matlab o la sua implementazione seriale o il tuo computer è in qualche modo lento (i dati non arrivano molto velocemente per un computer moderno). Quindi potresti abbassare il baud rate o provare a gestire i pacchetti persi.

Ho un suggerimento per gestire i pacchetti persi: riserva i bit x superiori in ogni byte che trasmetti per contenere il numero di quel byte la sua sequenza. Potresti f.ex. riserva 2 bit, consentendo di inviare fino a 4 byte di pacchetti appartenenti insieme. Ogni pacchetto di byte avrebbe 6 bit di dati, consentendo di inviare numeri con 24 bit di precisione. Per inviare un uint16:

  void send_int16 (uint16_t data) {Serial.write ((0 << 6) | (dati & 0b00111111)); dati >> = 6; Serial.write ((1 << 6) | (data & 0b00111111)); dati >> = 6; Serial.write ((2 << 6) | (data & 0b00111111));}  

Quindi in matlab dovresti aspettare una sequenza di pacchetti numerati 0, 1, 2 nei due più in alto bit, rimuovere la numerazione e unire i pacchetti insieme agli operatori bit a bit. Se si ottiene una sequenza non corretta, ad es. 0, 2, puoi scartarlo e attendere il byte successivo che inizia con uno zero.

Non ho davvero usato matlab, quindi sei da solo.



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...