Reducir el retraso entre el arduino y un boceto de procesamiento en mi computadora

13

Actualmente estoy en el proyecto # 14 del libro del proyecto Arduino.

Estoy tratando de controlar un boceto de procesamiento en mi computadora portátil usando mi Arduino. Esto se logra mediante el uso de un potenciómetro para controlar el fondo de una imagen.

Código Arduino:

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

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

Procesando:

//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);
}

Ahora, mientras el código funciona, y el color de fondo cambia cuando giro el potenciómetro, hay un gran retraso entre girar el potenciómetro y ver que el fondo cambia de color, y los valores del Arduino / potenciómetro cambian en el monitor en serie del procesamiento.

Lo que he intentado:

  • Cambiar la velocidad de la comunicación en serie

Me di cuenta de que cuando disminuyo la velocidad de la comunicación en serie, por ejemplo, alrededor de 100, el retraso entre girar el potenciómetro y verlo cambiar en mi computadora portátil disminuye a aproximadamente 1 segundo. Sin embargo, cuando disminuyo la velocidad de la comunicación en serie aún más, por ejemplo, un valor de 1, el retraso aumenta nuevamente.

Por otro lado, a la velocidad estándar de 9600, el retraso es enorme, aproximadamente 5 segundos antes de que los cambios en el potenciómetro aparezcan en la computadora portátil / procesamiento.

¿Por qué la disminución de la velocidad de comunicación (hasta cierto punto) disminuye el retraso de tiempo y el aumento aumenta el retraso de tiempo? Además, ¿hay alguna forma de hacerlo casi instantáneo?

Kenneth .J
fuente
3
Estás enviando una lectura cada vez alrededor del Arduino loop(). Es muy posible que su programa de procesamiento no se ejecute lo suficientemente rápido como para mantenerse al día. Intenta retrasar el loop()código Arduino para ralentizarlo; por ej delay(50).
Peter Bloomfield
Hola Peter, gracias por la pronta respuesta, agregar un pequeño retraso realmente resolvió mi problema. Sin embargo, solo otra pequeña pregunta, ¿hay alguna manera de que pueda determinar la velocidad de mi programa de procesamiento en el futuro para evitar que esto vuelva a suceder, o conseguir una mejor computadora portátil / velocidad de procesamiento resuelve el problema? Además, ¿por qué ingresar una velocidad de comunicación de 250 o 300 desordena las lecturas del arduino? (Las lecturas que obtengo son alternativas entre la lectura y cero Ej. 147,0,147,0)
Kenneth .J

Respuestas:

11

Está generando una lectura cada vez que Arduino loop(), por lo que parece probable que su programa de procesamiento no se ejecute lo suficientemente rápido como para mantenerse al día. Intente retrasar el loop()código Arduino para ralentizarlo, por ejemplo:

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

Hasta donde sé, el procesamiento tiene como objetivo ejecutarse a una velocidad de fotogramas constante, que puede modificar utilizando la frameRate()función. De manera predeterminada, es de 60 cuadros por segundo, aunque puede funcionar más lentamente en sistemas más antiguos (o donde está ejecutando un programa intensivo). Puede verificar qué tan rápido se está ejecutando leyendo la frameRatevariable.

La introducción de un retraso de 50 milisegundos en el bucle Arduino significa que se actualizará un poco menos de 20 veces por segundo. Eso significa que debe ser lo suficientemente rápido para fines de interfaz de usuario, pero también debe estar dentro de las capacidades de su programa de procesamiento.

En lo que respecta a la velocidad de transmisión (velocidad de comunicación), es probable que el ajuste por cantidades arbitrarias tenga resultados impredecibles. Esto se debe a que el hardware solo admitirá velocidades específicas, e intentar usar cualquier otra cosa puede hacer que los datos aparezcan confusos en el otro extremo. La Serial.begin()documentación tiene más información sobre las velocidades de transmisión admitidas.

Peter Bloomfield
fuente
14

Como ya se señaló, su Arduino está diciendo demasiado, demasiado rápido. Agregar delay()lo ralentizará, pero aún así sigue gritando en el procesamiento. Idealmente, desea que Processing solicite el valor cuando sea conveniente y luego reciba una respuesta de su Arduino.

Introduzca SerialEvent().

A diferencia de loop()en su Arduino y draw()en el procesamiento, todo en el interior serialEvent()solo se produce cuando hay algo nuevo en el búfer en serie. Entonces, en lugar de Procesar haciendo preguntas lo más rápido posible y su Arduino gritando aún más rápido, pueden tener una conversación agradable, educada (asíncrona).

Tanto Processing como Arduino tienen un evento serial. Esto es serialEvent () en Arduino y esto es serialEvent () en Processing. Usando serialEvent en ambos lados, esto es lo que sucedería:

  1. El procesamiento envía un carácter a la conexión en serie. Esto podría ser cualquier carácter, pero si predeterminamos uno, podemos filtrar cualquier solicitud no deseada causada, por ejemplo, por una señal ruidosa. Para este ejemplo, Venviemos un mensaje cada vez que queramos una nueva lectura de su potmeter. Después de enviar el personaje, continuamos nuestro negocio como de costumbre. ¡No estoy esperando una respuesta aquí!

  2. En el lado de Arduino no sucede nada, hasta que recibe datos en el búfer en serie. Comprueba si el personaje entrante es un V, y por suerte, lo es. El Arduino lee el valor del potmeter una vez, emite ese valor en serie una vez y vuelve a relajarse, maximizando la relajación. Protip: termina el valor con un carácter ( *en nuestro caso). Esto te ayudará en el siguiente paso.

  3. El procesamiento está haciendo su negocio de píxeles de interfaz regular cuando, de repente, hay una perturbación en la fuerza de nuevos datos en el búfer en serie. Cambia a serialEvent(), y comienza a leer los datos en serie, hasta que *se encuentra nuestra terminación . Sabiendo con certeza que este fue el último carácter que vale la pena leer, ahora podemos almacenar el valor entrante en una variable que almacena la lectura de Arduino.

  4. Eso es. Processing ahora conoce el nuevo valor del sensor y continúa con todo lo que le decimos que haga. Mientras tanto, su Arduino disfruta del clima o contempla su existencia hasta que haya datos seriales entrantes.

Tom
fuente
1
Y mientras lo hace, coloque un condensador en paralelo con su potenciómetro. Esto suaviza los pequeños cambios en la entrada de su DAC, posiblemente evitando el movimiento nervioso en el procesamiento.
Tom
¡Gracias por esta buena respuesta (y un poco antropomórfica)!
Zeta.Investigador el
En realidad, hacer preguntas por USB puede ser una idea. Esto se debe a que el USB tiene mucha más latencia que un puerto serie, por lo que hacer una pregunta y esperar una respuesta es una operación que requiere más tiempo de lo que sería, especialmente en comparación con lo que se podría hacer a altas velocidades de transmisión. Está bien dejar que el Arduino funcione un poco más rápido (aunque no debería saturar la parte en serie del enlace); El problema es que el boceto de procesamiento debe drenar los datos de Arduino a medida que estén disponibles, y mantener el último valor completo para usar cuando lo necesite.
Chris Stratton
7

Su circuito de sondeo se ejecuta a toda velocidad de su procesador y escribe en el puerto serie en cada ronda.

De esta manera, está escribiendo con más frecuencia en el puerto serie de lo que puede manejar.

El puerto escribe los datos tan rápido como lo configuró, y los datos del búfer que ingresan desde su programa demasiado rápido , para escribirlos lo antes posible. Si el búfer está lleno, solo deja caer nuevos datos.

Lo importante aquí es que mantendrá el orden de los valores: es un búfer FIFO , que funciona en orden Primero en entrar / Primero en salir.

Lo que sucede es:
el bucle llena el búfer del puerto y lo mantiene lleno al 100%.
Si gira el potenciómetro, el valor modificado se escribe al final del búfer , el puerto funciona lo más rápido posible para escribir todos los elementos en el búfer, que todavía tienen el valor anterior.

Y finalmente, el valor que le interesa. El valor más actual que queríamos ver de inmediato fue al final de la FIFO, y primero en entrar / primero en salir también significa último en / último en salir. Lo contrario de lo que queremos.

La frecuencia máxima que tiene sentido para leer sus datos es la frecuencia con la que puede escribirlos, por lo que debe usar al menos un retraso que sea lo suficientemente largo como para escribir los bytes a la velocidad del puerto actual.


Como otra medida independiente para evitar este tipo de retraso en general,
también puede establecer el búfer de escritura del puerto al mínimo.

Eso provocaría que los datos se eliminen mucho antes, en lugar de almacenar mucho en el búfer primero.

Por supuesto, en muchas aplicaciones eso no es lo que necesita; Con mala suerte, podría funcionar de todos modos al principio y volverse inestable en algunas situaciones cuando el tiempo cambia en función de cosas como la carga del procesador, y solo hay algunas muestras de datos aleatorios que se eliminan. Un búfer grande generalmente se comporta de manera mucho más determinista, por lo tanto, use un búfer grande de forma predeterminada .

Volker Siegel
fuente
Idea correcta, pero no del todo correcta en la afirmación "Si el búfer está lleno, simplemente deja caer nuevos datos". Una vez que se llena el búfer, los datos no se descartan, sino que se bloquean las escrituras hasta que haya espacio en el búfer saliente. Esto significa que la entrada y la salida pronto terminan fluyendo a la misma velocidad promedio, pero que hay un valor de latencia de búfer entre ellas.
Chris Stratton
6

En lugar de enviar constantemente datos en serie, solo envíe datos cuando el valor del potenciómetro haya cambiado por encima de un cierto umbral.

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);
  }
}
Daniel Eisterhold
fuente
1
Eso loop()no está llenando el búfer de salida con muestras iguales, eso es bueno. Pero aún funciona a toda velocidad del procesador, que puede ser 100 veces más rápido de lo necesario. Eso significa que aún puede llenar el búfer hasta el límite rápidamente si la entrada cambia con frecuencia, por ejemplo, por el ruido de arriba threshold, o un cambio continuo en alta resolución (que no es el caso en la aplicación de ejemplo aquí)
Volker Siegel
0

Dos soluciones simples que están garantizadas para trabajar para cualquier persona que todavía esté buscando:

  1. Aumente el retraso a 50 a 100 milisegundos.

  2. Añadir esto después de la Serial.begin(9600)de setup();

    Serial.setTimeout(50);

El segundo paso es el más importante. Funcionó para mí solo después de agregar el código anterior. Esto no se menciona muy a menudo en muchos otros foros que he buscado cuando tuve exactamente el mismo problema.

Rishi Swethan
fuente
Esto está algo equivocado. El método setTimeout () se aplica a la entrada, no a la salida; consulte la documentación en arduino.cc/en/Serial/SetTimeout
Chris Stratton