Perte de données sur liaison série

Bonjour à tous,

Je reviens vous embêter avec mon programme qui avance bien, mais pas autant que je le voudrais.
Pour mémoire il s'agit d'un projecteur laser. L'idée est d'utiliser un laser similaire à ceux que l'on utilise pour faire du son et lumière, et de s'en servir pour balayer chaque pixel d'une image, le but étant d'insoler un support sensible aux UV, notamment certains procédés photographiques anciens.

Voilà pour la fonction. Pour la manière de faire, j'ai une carte arduino qui pilote un laser en PWM, et une paire de galvos via un DAC 16bits.

le programme est découpé en quatre sous programmes:
Serial gère la réception des données. C'est une implémentation maison, assez proche de celle de GRBL, qui permet le flow control avec Xon et Xoff.
Planner analyse les coordonnées reçues, les enregistrent, les préparent et les mettent à disposition du driver.
Driver gère l'envoi en temps réel (ou presque) des coodonnées (puissance laser et coordonnées X et Y)
I2C s'occupe d'envoyer les coordonnées au DAC.

Je crois que j'ai fait pas mal d'optimisations, la quasi totalité des calculs sont appelés depuis la boucle principale. J'ai mis en place des buffers qui permettent de prendre le plus d'avance possible et de vider les ISR, pour le driver (coordonnées de positionnement d'avance) et le serial (vidage du buffer RX aussitôt que possible)

Malgré cela j'ai des pertes de données: si je monte le baudrate au-dessus de 28800, les points affichés ne correspondent pas à ce qu'ils devraient être. Comme je ne vois pas ce que je pourrais améliorer d'autre, je suis prêt à prendre les idées que vous pourriez avoir.

Je ne pense pas que le problème vienne du coté PC ,Qt étant visiblement un ensemble de librairie réputées.
Cela peut venir des variables, qui utilisent autour de 71% de la mémoire RAM. Mais je ne sais pas dans quelle mesure cela peut jouer.

Et vous, qu'en pensez-vous?

PS: je joins le code, mais il est aussi disponible sur gituhub.

arduino-projecteur-laser.zip (19.1 KB)

bonjour,
via quelle appli envoie tu les données?
si tu réduis la vitesse, cela est il plus stable?
sous quelle forme sont les données, style gcode?

Bonjour et merci d'avoir pris le temps de répondre.

Les données sont envoyées via une application que j'ai écrite en C++, majoritairement basée sur Qt, et notamment sur la classe QSerialPort.
Je gagne effectivement en stabilité si je descend la vitesse de transfert, en-dessous de 28800 bauds c'est à peu près stable, et à partir de 14400 il n'y a plus aucun problème. Dans ce cas-là le problème est autre: une image de 2000x1500 pixels représentant 3 millions de pixels, le temps de travail devient énorme. Et le temps d'insolation résultant est bien supérieur à ce que nécessitent les procédés photo en question.

Les données sont effectivement structurées à la manière du Gcode. De type X2000Y238L233M1S400.
J'étais d'abord parti su un formatage type json, mais la quantité de données à traiter m'a incité à passer à quelque chose de plus compact.

Tous les essais que j'ai fait en envoyant des coordonnées à la main via le terminal arduino montrent qu'elles sont bien prises en compte.

Je ne sais plus quoi faire. J'ai vidé tous les ISR, dans celui du driver il ne reste qu'une incrémentation de variable et un drapeau, et dans celle de de la liaison série la copie du registre RX dans mon buffer, et le test pour le contrôle de flux.

Je me demande si le problème n'est pas du coté de l'application Qt, mais je trouve assez peu d'info. J'ai simplement fait, après initialisation du port série, une boucle qui envoie une instruction write() pour chaque valeur à prendre en compte. Si j'ai bien compris (et ça semble être le cas) la boucle envoie les données dans un buffer interne, qui gère l'envoi en arrière plan en prenant en charge le contrôle de flux.

ce que tu peux essayer, si ton appli le permet évidemment :
générer un fichier gcode et l'envoyer via une auttre appli style repeaterhost, mach3, gcodesender ou autre programme pouvant envoyer du gcode.
ca éliminera le coté arduino dans ce cas.

gcodesender ICI

C'est ce que j'avais pensé faire, j'ai essayé avec Gcode sender. Le problème c'est qu'il me demande de choisir un type de carte. Avec GRBL et XLCD je ne peux pas envoyer de fichier, car il attend que la carte dise qu'elle a fini de démarrer. Avec TinyG ça semble marcher: j'ai quatre chaînes de coordonnées qui sont expédiées. Elles le sont au format json, mais en principe mon programme vire les caractères qui ne lui vont pas. Cela dit le laser ne bouge pas, donc il y a peut-être un truc à régler de ce coté-là. je vais chercher.

Bon, j'avais une erreur dans le parser. Un test avec c='-' au lieu de c=='-'
Cela étant, avec Gcode sender ça ne fonctionne pas. Est-ce qu'il implémente le Xon/Xoff? Je n'ai pas trouvé l'info dans la doc?

j'avais fait un truc sous linux pour envoyer un fichier vers usb, mais je ne retrouve pas l'équivalent windows
cat nomfichier.txt>>/dev/ttyACM0

Ca tombe bien, je développe sur linux! ;D

Bonsoir,

Un idée qui ne vaut peut-être pas grand chose ? (je n'ai pas tout compris à ton problème)

Comme je n'avais pas d'analyseur à mettre sur la liaison série, un jour j'ai utilisé une autre carte Arduino
pour capter Tx et Rx d'une liaison qui posait problème.
J'avais pu relier TX et RX via 2 diodes car les données n'étaient jamais simultanées.

Bonjour à tous, et merci de prendre le temps de vous pencher sur mes petits problèmes.
geryko, merci pour l'idée: elle est excellente, et précisément je cherchais une manière de "sniffer" les communications entre le PC et l'Arduino. Cette solution vaudra que je l'essaie.

Cela étant, j'ai découvert différentes choses.
En faisant un echo des caractères bruts reçus par la liaison série, il s'avère que la totalité est reçue. J'ai un caractère NewLine qui se glisse de temps en temps au milieu d'une chaine, je ne sais pas d'où il vient, mais il est possible que ce soit dû à la manière dont je récupère ces données dans mon programme coté PC (fonction de lecture d'une ligne avec un timeout). Je n'y prête donc pas plus d'attention que ça pour le moment.

Curieusement, si coté PC je fais une tentative de lecture des données reçues après chaque envoi (même si aucune n'est reçue), les problèmes arrivent plus tard, plusieurs centaines de chaînes de commandes passent sans problème.

Encore plus curieusement, si j'augmente la fréquence de l'ISR qui demande la mise à jour des coordonnées des galvos, le système est plus efficace, et j'ai moins de coordonnées perdues, là aussi. Pourtant cela augmente le temps de processeur dévolu au calcul de position, au détriment du serial... Qui passe du coup surtout du temps à attendre.

Etant donné ces constats, je vais essayer d'implémenter un genre de système de gestion des priorités des appels aux fonctions, avec des points d'arrêts dans celles qui sont susceptibles de bloquer le processeur longtemps. Un peu comme ce que l'on trouve chez GRBL et TinyG. Je vais faire un peu d ereverse engeenering. :wink:

Cela dit, j'ai pu, en implémentant la solution d'une fonction principale dans le serial, qui appelle la copie des données entrantes ou bien le parsing de ses données en fonction de l'état enregistré, obtenir une communication un peu plus stable à 155200 bauds.

Merci à vous pour vos pistes de réflexion!

Bon, j'ai reformatté le programme, avec un système qui donne une priorité aux différentes fonctions. Et véréifié, en envoyant des paquets de données via coolterm sur OSX, que le flow control fonctionne correctement. Pas de données perdues. En revanche quand même des comportements bizarres dans le cas de coordonnées très rapprochées.

Je crois que la clef est là: dans le cas de coordonnées de test, le point de départ A et le point de départ B sont éloignés. Donc pour une coordonnée reçue, le serial fait son travail, le planner fait le sien en calculant les deltas entre la position de départ et la position d'arrivée, puis le driver a toutes les ressources pour lui pour remplir son buffer et afficher ses coordonnées en temps réel.
Dans le cas d'un envoi d'image (c'est à dire le cas qui m'intéresse), les coordonnées sont très proches les unes des autres. Dont je pense que la part processeur dédiée à la liaison série devient prépondérante, ou en tout cas plus importante. D'ailleurs si je demande plus d'une certaine vitesse sur une successions de points, dans le cas de points éloignés elle s'applique sans problème, mais dans le cas de points rapprochés ça se traîne, et parfois ça donne l'impression de brouter...

Bref, je n'ai pas l'impression que ce problème soit soluble en l'état, à moins d'augmenter la puissance de calcul.

PS: j'en ai profité pour remplacer tous les calculs mettant en œuvre des floats ou des double par des long en fixpoint, la différence est impressionnante.

Bonjour,
Je te suggère de passer en arm, plus rapide, plus puissant, et permettant de gérer les priorités des interruptions.
Cela te simplifiera la vie !

Merci du conseil! J'y pense sérieusement, j'ai regardé du coté de l'arduino Due (qui n'est plus au catalogue, pour une raison qui m'échappe un peu), de l'arduino Zero et aussi des teensy, qui sont tentantes. D'autant qu'il y a un petit module stereo pour les teensy qui pourrait peut-être remplacer avantageusement le DAC 16 bits qui est utilisé dans mon projet. je vais creuser le sujet un peu plus.

En bridant un peu les fonctionnalités de mon projet (c'est à dire en n'affichant que les points envoyés par le programme coté PC, plutôt que ces points-là et les points intermédiaires de la trajectoire), j'arrive à insoler des images.
Je vous en joints une... La résolution n'est pas géniale, mais pour ne pas perdre trop de temps en comparant différentes manières de formater les données, j'ai utilisé des images assez petite.

Il y a aussi un point qui m'a empêché d eregarder du coté des cartes plus rapide jusque là: l'architecture étant différente, ça veut dire qu'une partie du programme doit être réécrite puisque l'architecture est différente. Probablement rien d'insurmontable - je suis en train de parcourir la doc des SAM3X - mais devoir partir sur des bases vierges pour les ISR est bien sûr un frein!

une partie du programme doit être réécrite

pour certains c'est un plaisir, une fin en soi ...

Oui, je sais. Je tombe d'ailleurs moi-même facilement dans le piège. :slight_smile:

J'ai trouvé un article assez complet (ici) sur l'utilisation des Timers de la Due, si on ajoute à ça la doc Atmel qui est bien faite, et l'analyse des bibliothèques standard Arduino, je devrais m'en sortir. :slight_smile:
J'ai commandé deux Due lundi soir, je devrais les avoir prochainement.