Riduzione del ritardo tra l'arduino e uno schizzo di elaborazione sul mio computer


13

Sono attualmente al progetto n. 14 del libro del progetto Arduino.

Sto cercando di controllare uno schizzo di elaborazione sul mio laptop usando il mio Arduino. Ciò si ottiene utilizzando un potenziometro per controllare lo sfondo di un'immagine.

Codice Arduino:

void setup(){
  Serial.begin(9600);
}

void loop(){
  Serial.write(analogRead(A0)/4);
}

In lavorazione:

//imports serial library
import processing.serial.*;
//setups the serial object
Serial myPort;
//creates an object for the image
PImage logo;
//variable to store background color
int bgcolor = 0;

void setup(){
  colorMode(HSB,255);
  logo = loadImage("http://arduino.cc/logo.png");
  size(logo.width,logo.height);
  println("Available serial ports");
  println(Serial.list());
  myPort = new Serial(this,Serial.list()[0],9600);
}
//equivalent of arduino's loop function
void draw(){
  if(myPort.available() > 0)
  {
    bgcolor = myPort.read();
    println(bgcolor);
  }

  background(bgcolor,255,255);
  image(logo,0,0);
}

Ora, mentre il codice funziona e il colore di sfondo cambia mentre giro il potenziometro, c'è un enorme ritardo tra la rotazione del potenziometro e la visualizzazione del colore di cambiamento dello sfondo, ei valori di Arduino / potenziometro cambiano sul monitor seriale dell'elaborazione.

Cosa ho provato:

  • Modifica della velocità della comunicazione seriale

Ho notato che quando diminuisco la velocità della comunicazione seriale, ad esempio circa 100, il ritardo tra l'accensione del potenziometro e il vederlo cambiare sul mio laptop diminuisce a circa 1 secondo. Tuttavia, quando diminuisco ulteriormente la velocità della comunicazione seriale, ad esempio un valore di 1, il ritardo aumenta nuovamente.

D'altro canto, alla velocità standard di 9600, il ritardo è enorme, all'incirca 5 secondi ++ prima che i cambiamenti nel potenziometro compaiano sul laptop / elaborazione.

Perché la riduzione della velocità di comunicazione (fino a un certo punto) diminuisce il ritardo e aumentandolo aumenta il ritardo? Inoltre, c'è un modo per renderlo quasi istantaneo?


3
Stai trasmettendo una lettura ogni volta in Arduino loop(). È del tutto possibile che il tuo programma di elaborazione non sia abbastanza veloce da tenerlo aggiornato. Prova a ritardare loop()nel tuo codice Arduino per rallentarlo; es delay(50).
Peter Bloomfield,

Ciao Peter, grazie per la pronta risposta, l'aggiunta di un piccolo ritardo ha davvero risolto il mio problema. Solo un'altra piccola domanda, c'è un modo in cui posso determinare la velocità del mio programma di elaborazione in futuro in modo da evitare che ciò accada di nuovo o ottenere una migliore velocità di elaborazione / laptop risolva il problema? Inoltre, perché l'inserimento di una velocità di comunicazione di 250 o 300 confonde le letture dall'arduino (le letture che ottengo sono alternate tra la lettura e zero Ad esempio 147,0,147,0)
Kenneth .J

Risposte:


11

Stai trasmettendo una lettura ogni volta in Arduino loop(), quindi sembra probabile che il tuo programma di elaborazione non sia abbastanza veloce da tenerlo aggiornato. Prova a ritardare loop()nel tuo codice Arduino per rallentarlo, ad esempio:

void loop(){
    Serial.write(analogRead(A0)/4);
    delay(50);
}

Per quanto ne so, Processing ha lo scopo di eseguire un framerate coerente, che è possibile modificare utilizzando la frameRate()funzione. Per impostazione predefinita, è di 60 fotogrammi al secondo, anche se potrebbe funzionare più lentamente su sistemi meno recenti (o dove si esegue un programma intensivo). Puoi controllare la sua velocità leggendo la frameRatevariabile.

Introdurre un ritardo di 50 millisecondi nel ciclo di Arduino significa che si aggiornerà poco meno di 20 volte al secondo. Ciò significa che dovrebbe essere abbastanza veloce ai fini dell'interfaccia utente, ma dovrebbe anche rientrare nelle capacità del tuo programma di elaborazione.

Per quanto riguarda la velocità di trasmissione (velocità di comunicazione), è probabile che la regolazione di importi arbitrari abbia risultati imprevedibili. Questo perché l'hardware supporterà solo velocità specifiche e il tentativo di utilizzare qualsiasi altra cosa può far apparire confusi i dati dall'altra parte. La Serial.begin()documentazione contiene ulteriori informazioni sulle velocità di trasmissione supportate.


14

Come già sottolineato, il tuo Arduino sta dicendo troppo in fretta. L'aggiunta delay()lo rallenterà, ma continua a urlare durante l'elaborazione. Idealmente, si desidera che Elaborazione richieda il valore quando è conveniente e quindi riceve una risposta dal proprio Arduino.

Enter SerialEvent().

A differenza di loop()Arduino e draw()di Processing, tutto al suo interno serialEvent()si eccita solo quando c'è qualcosa di nuovo nel buffer seriale. Quindi, invece di elaborare le domande il più rapidamente possibile e il tuo Arduino che urla ancora più velocemente, possono avere una conversazione piacevole, educata (asincrona).

Sia Processing che Arduino hanno un serialEvent. Questo è serialEvent () su Arduino e questo è serialEvent () in elaborazione. Usando serialEvent su entrambi i lati, questo è ciò che accadrebbe:

  1. L'elaborazione invia un carattere alla connessione seriale. Potrebbe trattarsi di qualsiasi personaggio, ma se ne predeterminiamo uno possiamo filtrare eventuali richieste indesiderate causate, ad esempio, da un segnale rumoroso. Per questo esempio, inviamo un messaggio Vogni volta che vogliamo una nuova lettura del tuo potmeter. Dopo che il personaggio è stato inviato, continuiamo i nostri affari come al solito. Non aspettando una risposta qui!

  2. Sul lato Arduino non succede nulla, fino a quando non riceve i dati nel buffer seriale. Controlla se il personaggio in arrivo è un V, e fortunatamente noi lo è. Arduino legge il valore del potenziometro una volta, trasmette quel valore al seriale una volta e torna a rilassarsi, al massimo rilassandosi al massimo. Protip: termina il valore con un carattere ( *nel nostro caso). Questo ti aiuterà nel prossimo passaggio.

  3. L'elaborazione sta svolgendo la sua normale attività di pixel di interfaccia quando all'improvviso c'è un disturbo nella forza di nuovi dati nel buffer seriale. Passa a serialEvent(), e inizia a leggere i dati seriali, fino a quando non *viene rilevata la nostra chiusura . Sapendo con certezza che questo è stato l'ultimo personaggio che vale la pena leggere, ora possiamo memorizzare il valore in arrivo in una variabile che memorizza la lettura di Arduino.

  4. Questo è tutto. L'elaborazione ora conosce il nuovo valore del sensore e continua con qualsiasi cosa gli diciamo di fare. Nel frattempo, il tuo Arduino si sta godendo il tempo o sta contemplando la sua esistenza fino a quando non ci sono dati seriali in arrivo.


1
E mentre ci sei, metti un condensatore in parallelo con il tuo potmetro. Ciò attenua i piccoli cambiamenti nell'input del DAC, impedendo possibilmente il movimento nervoso nell'elaborazione.
Tom,

Grazie per questa bella (e un po 'antropomorfa) risposta!
Zeta.Investigator

In realtà, fare domande tramite USB può essere un'idea. Questo perché l'USB ha una latenza molto maggiore rispetto a una porta seriale, quindi porre una domanda e attendere una risposta è un'operazione che richiede più tempo di quanto sarebbe altrimenti, soprattutto rispetto a ciò che potrebbe essere fatto con baud rate elevati. Lasciare Arduino un po 'più veloce va bene (anche se non dovrebbe saturare la parte seriale del collegamento); il problema è che lo schizzo di elaborazione deve drenare i dati di Arduino non appena disponibili e conservare l'ultimo valore completo da utilizzare quando ne ha bisogno.
Chris Stratton,

7

Il ciclo di polling viene eseguito alla massima velocità del processore e scrive sulla porta seriale in ogni round.

In questo modo, stai scrivendo molto più spesso sulla porta seriale di quanto possa gestire.

La porta scrive i dati più velocemente di quanto li hai configurati e bufferizza i dati che arrivano dal tuo programma troppo velocemente , per scriverli il prima possibile. Se il buffer è pieno, rilascia solo nuovi dati.

Ciò che è importante qui è che manterrà l'ordine dei valori: è un buffer FIFO , che funziona in ordine di First In / First Out.

Quello che succede è:
il loop riempie il buffer delle porte e lo mantiene pieno al 100%.
Se si ruota il potenziometro, il valore modificato viene scritto alla fine del buffer , la porta funziona il più velocemente possibile per scrivere tutti gli elementi nel buffer, che hanno ancora il vecchio valore.

E infine il valore che ti interessa. Il valore più attuale che volevamo vedere immediatamente era alla fine del FIFO, e primo in / primo in uscita significa anche ultimo in / ultimo in uscita. Il contrario di ciò che vogliamo.

La frequenza massima che ha senso leggere i tuoi dati, è la frequenza che puoi scriverli, quindi dovresti usare almeno un ritardo che è abbastanza lungo da scrivere i byte alla velocità della porta corrente.


Come altra misura indipendente per prevenire questo tipo di ritardo in generale,
è possibile impostare al minimo il buffer di scrittura della porta.

Ciò causerebbe l'eliminazione dei dati molto prima, invece di eseguire prima il buffering.

Naturalmente, in molte applicazioni non è quello che ti serve; Con sfortuna, potrebbe funzionare comunque all'inizio e diventare instabile in alcune situazioni in cui i tempi cambiano in base a cose come il carico del processore e ci sono solo alcuni dati casuali che vengono eliminati. Un buffer di grandi dimensioni si comporta generalmente in modo molto più deterministico, quindi per impostazione predefinita utilizzare un buffer di grandi dimensioni .


Giusta idea, ma non proprio sull'affermazione "Se il buffer è pieno, rilascia solo nuovi dati". Una volta riempito il buffer, i dati non vengono eliminati, ma il blocco di scrittura fino a quando non c'è spazio nel buffer in uscita. Ciò significa che l'input e l'output finiranno presto per fluire alla stessa velocità media, ma che tra loro c'è una latenza di un buffer.
Chris Stratton,

6

Invece di inviare costantemente dati seriali, inviare i dati solo quando il valore del potenziometro è cambiato oltre una certa soglia.

int oldValue = 0;
const int threshold = 5;

void setup()
{
  Serial.begin(9600);
  pinMode(A0, INPUT)
}

void loop()
{
  if(oldValue >= analogRead(A0)+threshold || oldValue <= analogRead(A0)-threshold)
  {
    Serial.println(analogRead(A0));
    oldValue = analogRead(A0);
  }
}

1
Questo loop()non sta riempiendo il buffer di output con campioni uguali, va bene. Ma funziona ancora alla massima velocità del processore, che può essere 100 volte più veloce del necessario. Ciò significa che può comunque riempire rapidamente il buffer fino al limite se l'ingresso cambia frequentemente, ad esempio da un rumore sopra threshold, o un cambiamento continuo in alta risoluzione (che non è il caso nell'applicazione di esempio qui)
Volker Siegel,

0

Due soluzioni semplici che sono garantite per funzionare per chiunque sia ancora alla ricerca: -

  1. Aumenta il ritardo da 50 a 100 millisecondi.

  2. Aggiungi questo dopo il Serial.begin(9600)in setup();

    Serial.setTimeout(50);

Il secondo passo è il più importante. Ha funzionato per me solo dopo aver aggiunto il codice sopra. Questo non è menzionato molto spesso in molti altri forum che ho visto quando ho avuto lo stesso identico problema.


Questo è in qualche modo sbagliato. Il metodo setTimeout () si applica all'input, non all'output - vedere la documentazione su arduino.cc/en/Serial/SetTimeout
Chris Stratton,
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.