Réduction du décalage entre l'arduino et une esquisse de traitement sur mon ordinateur


13

Je suis actuellement sur le projet # 14 du livre de projet Arduino.

J'essaie de contrôler une esquisse de traitement sur mon ordinateur portable à l'aide de mon Arduino. Ceci est accompli en utilisant un potentiomètre pour contrôler l'arrière-plan d'une image.

Code Arduino:

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

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

En traitement:

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

Maintenant, alors que le code fonctionne et que la couleur d'arrière-plan change lorsque je tourne le potentiomètre, il y a un énorme décalage entre tourner le potentiomètre et voir la couleur changer d'arrière-plan, et les valeurs de l'Arduino / potentiomètre changent sur le moniteur série du traitement.

Ce que j'ai essayé:

  • Modification de la vitesse de communication série

J'ai remarqué que lorsque je diminuais la vitesse de communication série, par exemple autour de 100, le délai entre tourner le potentiomètre et le voir changer sur mon ordinateur portable diminuait à environ 1 seconde. Cependant, lorsque je diminue encore plus la vitesse de communication série, par exemple une valeur de 1, le retard augmente à nouveau.

D'un autre côté, à la vitesse standard de 9600, le retard est énorme, environ 5 secondes ++ avant que les changements de potentiomètre n'apparaissent sur l'ordinateur portable / traitement.

Pourquoi la diminution de la vitesse de communication (jusqu'à un certain point) diminue-t-elle le décalage temporel et l'augmentation augmente-t-elle le décalage temporel? De plus, est-il possible de le faire presque instantanément?


3
Vous produisez une lecture à chaque fois autour de l'Arduino loop(). Il est fort possible que votre programme de traitement ne fonctionne pas assez rapidement pour le suivre. Essayez de mettre un retard dans le loop()code Arduino pour le ralentir; par exemple delay(50).
Peter Bloomfield

Salut peter, merci pour la réponse rapide, l'ajout d'un petit retard a vraiment résolu mon problème. Juste une autre petite question cependant, existe-t-il un moyen de déterminer la vitesse de mon programme de traitement à l'avenir afin d'éviter que cela ne se reproduise, ou est-ce que l'obtention d'une meilleure vitesse d'ordinateur portable / de traitement résout le problème? Aussi, pourquoi l'entrée d'une vitesse de communication de 250 ou 300 perturbe-t-elle les lectures de l'arduino? (Les lectures que j'obtiens sont alternées entre la lecture et zéro, par exemple 147,0,147,0)
Kenneth .J

Réponses:


11

Vous produisez une lecture à chaque fois autour de l'Arduino loop(), il semble donc probable que votre programme de traitement ne fonctionne pas assez rapidement pour le suivre. Essayez de retarder le loop()dans votre code Arduino pour le ralentir, par exemple:

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

Autant que je sache, le traitement vise à fonctionner à un débit d'images constant, que vous pouvez modifier à l'aide de la frameRate()fonction. Par défaut, c'est 60 images par seconde, bien que cela puisse fonctionner plus lentement sur les anciens systèmes (ou lorsque vous exécutez un programme intensif). Vous pouvez vérifier sa vitesse d'exécution en lisant la frameRatevariable.

L'introduction d'un retard de 50 millisecondes dans la boucle Arduino signifie qu'il sera mis à jour un peu moins de 20 fois par seconde. Cela signifie qu'il devrait être assez rapide pour les besoins de l'interface utilisateur, mais devrait également être bien dans les capacités de votre programme de traitement.

En ce qui concerne le débit en bauds (vitesse de communication), l'ajustement par des quantités arbitraires risque d'avoir des résultats imprévisibles. C'est parce que le matériel ne prend en charge que des vitesses spécifiques, et essayer d'utiliser autre chose peut entraîner une altération des données à l'autre extrémité. La Serial.begin()documentation contient des informations supplémentaires sur les débits en bauds pris en charge.


14

Comme déjà souligné, votre Arduino en dit trop trop vite. L'ajout delay()le ralentira, mais il continuera de crier sur Traitement. Idéalement, vous voulez que Processing demande la valeur quand cela vous convient, puis reçoive une réponse de votre Arduino.

Entrez SerialEvent().

Contrairement à loop()votre Arduino et draw()en cours de traitement, tout ce qui se trouve à l'intérieur serialEvent()ne s'excuse que lorsqu'il y a quelque chose de nouveau dans le tampon série. Ainsi, au lieu que Processing pose des questions le plus rapidement possible et que votre Arduino crie encore plus vite, ils peuvent avoir une conversation agréable et polie (asynchrone).

Processing et Arduino ont tous deux un serialEvent. C'est serialEvent () sur l'Arduino et c'est serialEvent () dans Processing. En utilisant serialEvent des deux côtés, voici ce qui se passerait:

  1. Le traitement envoie un caractère à la connexion série. Cela peut être n'importe quel caractère, mais si nous en prédéterminons un, nous pouvons filtrer toutes les demandes indésirables causées par exemple par un signal bruyant. Pour cet exemple, envoyons Vchaque fois que nous voulons une nouvelle lecture de votre potmètre. Après l'envoi du personnage, nous continuons notre travail comme d'habitude. Ne pas attendre une réponse ici!

  2. Du côté de l'Arduino, rien ne se passe jusqu'à ce qu'il reçoive des données dans le tampon série. Il vérifie si le personnage entrant est un V, et nous avons de la chance. L'Arduino lit la valeur du potmètre une fois, envoie cette valeur en série une fois et revient à se détendre, en relaxant au maximum. Protip: termine la valeur par un caractère ( *dans notre cas). Cela vous aidera dans la prochaine étape.

  3. Le traitement fait son affaire de pixels d'interface habituels quand tout à coup il y a une perturbation dans la force de nouvelles données dans le tampon série. Il passe à serialEvent(), et commence à lire les données série, jusqu'à ce que notre terminaison *soit rencontrée. Sachant que c'était le dernier caractère à lire, nous pouvons maintenant stocker la valeur entrante dans une variable qui stocke la lecture de l'Arduino.

  4. C'est ça. Le traitement connaît désormais la nouvelle valeur du capteur et poursuit tout ce que nous lui demandons de faire. Pendant ce temps, votre Arduino profite de la météo ou envisage son existence jusqu'à ce qu'il y ait des données série entrantes.


1
Et pendant que vous y êtes, mettez un condensateur en parallèle avec votre potmètre. Cela atténue les petits changements dans l'entrée de votre DAC, empêchant peut-être un mouvement instable dans le traitement.
Tom

Merci pour cette réponse sympa (et un peu anthropomorphe)!
Zeta.Investigator

En fait, poser des questions sur USB peut être une idée. C'est parce que l'USB a beaucoup plus de latence qu'un port série, donc poser une question et attendre une réponse est une opération plus longue qu'elle ne le serait autrement, en particulier par rapport à ce qui pourrait être fait à des vitesses de transmission élevées. Laisser l'Arduino fonctionner un peu plus vite est bien (bien qu'il ne devrait pas saturer la partie série du lien); le problème est que l'esquisse de traitement doit drainer les données Arduino lorsqu'elles deviennent disponibles, et conserver la dernière valeur complète à utiliser lorsqu'elle en a besoin.
Chris Stratton

7

Votre boucle d'interrogation s'exécute à pleine vitesse de votre processeur et écrit sur le port série à chaque tour.

De cette façon, vous écrivez beaucoup plus souvent sur le port série qu'il ne peut en gérer.

Le port écrit les données aussi rapidement que vous les avez configurées et met en mémoire tampon les données provenant de votre programme trop rapidement , pour les écrire dès que possible. Si le tampon est plein, il laisse simplement tomber de nouvelles données.

Ce qui est important ici, c'est qu'il gardera l'ordre des valeurs: Il s'agit d'un tampon FIFO , fonctionnant dans l'ordre First In / First Out.

Ce qui se passe est la suivante:
la boucle remplit le tampon de port et le maintient à 100% plein.
Si vous tournez le potentiomètre, la valeur modifiée est écrite à la fin du tampon , le port fonctionne aussi vite que possible pour écrire tous les éléments du tampon, qui ont toujours l'ancienne valeur.

Et enfin la valeur qui vous intéresse. La valeur la plus récente que nous voulions voir immédiatement était à la fin du FIFO, et le premier entré / premier sorti signifie également le dernier entré / dernier sorti. Le contraire de ce que nous voulons.

La fréquence maximale de lecture de vos données est la fréquence à laquelle vous pouvez les écrire, vous devez donc utiliser au moins un délai suffisamment long pour écrire les octets à la vitesse actuelle du port.


Comme autre mesure indépendante pour éviter ce type de retard en général,
vous pouvez également régler la mémoire tampon d'écriture du port au minimum.

Cela entraînerait la suppression des données beaucoup plus tôt, au lieu de tamponner beaucoup en premier.

Bien sûr, dans de nombreuses applications, ce n'est pas ce dont vous avez besoin; Avec de la malchance, cela pourrait de toute façon fonctionner au début et devenir instable dans certaines situations lorsque le timing change en fonction de choses comme la charge du processeur, et il n'y a que quelques échantillons de données aléatoires qui sont supprimés. Un grand tampon se comporte généralement beaucoup plus déterministe, utilisez donc un grand tampon par défaut .


Bonne idée, mais pas tout à fait juste sur la déclaration "Si le tampon est plein, il laisse simplement tomber de nouvelles données.". Une fois que le tampon est rempli, les données ne sont pas supprimées, mais plutôt le bloc d'écriture jusqu'à ce qu'il y ait de l'espace dans le tampon sortant. Cela signifie que l'entrée et la sortie finissent bientôt par s'écouler au même taux moyen, mais qu'il y a une valeur de latence tampon entre elles.
Chris Stratton

6

Au lieu d'envoyer constamment des données série, n'envoyez des données que lorsque la valeur du potentiomètre a changé au-dessus d'un certain seuil.

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
Cela loop()ne remplit pas le tampon de sortie avec des échantillons égaux, c'est bien. Mais il fonctionne toujours à pleine vitesse du processeur, qui peut être 100 fois plus rapide que nécessaire. Cela signifie qu'il peut toujours remplir le tampon à la limite rapidement si l'entrée change fréquemment, par exemple du bruit au threshold- dessus , ou un changement continu en haute résolution (ce qui n'est pas le cas dans l'exemple d'application ici)
Volker Siegel

0

Deux solutions simples qui sont garanties de fonctionner pour tous ceux qui sont toujours à la recherche: -

  1. Augmentez le délai à 50 à 100 millisecondes.

  2. Ajoutez ceci après le Serial.begin(9600)in setup();

    Serial.setTimeout(50);

La deuxième étape est la plus importante. Cela n'a fonctionné pour moi qu'après avoir ajouté le code ci-dessus. Ce n'est pas mentionné très souvent dans de nombreux autres forums que j'ai consultés lorsque j'ai eu exactement le même problème.


C'est quelque peu faux. La méthode setTimeout () s'applique à l'entrée et non à la sortie - voir la documentation sur arduino.cc/en/Serial/SetTimeout
Chris Stratton
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.