bunex-industries

Galvos laser XY

Un faisceau laser est dévié en X et en Y à l'aide deux miroirs orientables contrôlés par un Arduino. Le très brillant spot lumineux se déplace sur un écran et dessine une forme plusieurs fois par seconde, la persistance rétinienne donne l'impression d'un trait continu, une forme, un mot apparaît. Cette technologie de pointage précis d'un laser est utilisée dans le spectacle et dans l'industrie pour la gravure — voire la découpe. (l'appareil pour le son-sur-film, encore lui, n'est pas très éloigné.)

Si on s'en tient à des courbes "régulières", c'est relativement facile, on s'en sort avec des miroirs tournants, et on génère par exemple des courbes de lissajous.

Avec des haut-parleurs et des miroirs, on peut faire des choses plus intéressantes comme celle-ci, j'en dirai quelques mots.

La voie la plus porteuse réside toutefois dans l'emploi de miroirs oscillants galvanométriques. Je me suis lancé dans la construction de deux d'entre eux, de leurs drivers de puissance et d'un contrôleur. C'est un projet assez délicat, je le savais plein d'embûches, mais pourtant accessible et intéressant, voici ce que je peux dire de mes travaux :

Exigences

Pour avoir des formes bien lisibles, il faut qu'elles soient répétées en boucle très rapidement et à l'identique. Cependant, le miroir et tout l'assemblage vibrant possède une masse et donc une inertie qui s'oppose aux changement de vitesse (ceci d'autant plus que les mouvements sont rapides constituant ainsi un filtre passe bas mécanique). L'énergie cinétique des pièce mobiles devient importante à grande vitesse et des problèmes d'ordre dynamique viennent fatalement compromettre la précision du positionnement.

On devine qu'il va y avoir des questions de temps de réaction et de forts effets d'inertie : le miroir déplacé soudainement va avoir tendance à continuer sur sa lancée si rien n'est entrepris pour le freiner. Il peut y avoir des vibrations parasites, des fréquences de résonance qui rendent le fonctionnement instable à certains régimes.

Ce problème de la vitesse, en relation avec le moment cinétique (l'énergie cinétique en rotation) est central. Il faut donc fabriquer des pièces mobiles aussi légères que possible (c.à.d avec un petit moment d'inertie). La masse d'un corps variant avec le cube de ses dimensions, on s'efforcera de construire des pièces très petites. Plus les déviations demandées sont grandes et brusques (fonctionnement à haut régime) plus ces effets seront sensibles.

Boucle ouverte ou fermée

Il y a quelques années, j'avais fabriqué un laser-show assez rudimentaire à l'aide de haut-parleurs contrôlés en PWM par un Arduino. Un miroir était fixé de manière adéquate sur la membrane de chaque HP et les orientations étaient modifiées selon l'excitation des HP :

Malheureusement, il y a peu de chances que ce dispositif réponde de manière analogue à la consigne envoyée par le contrôleur. Ce dernier n'a pas connaissance de l'orientation réelle des miroirs. On dit d'un tel système qu'il est en boucle ouverte. Il n'y a pas rétro-action des effets sur les causes. Le contrôleur est donc incapable de corriger le pointage pour forcer les miroirs à s'orienter correctement.

À haute vitesse, les effets d'inertie, d'emballement, d'oscillations ou resonance deviennent prépondérants. Ce succédané de galvo-laser donne toutefois des résulats et se comporte plutôt bien quant à la vitesse, mais la précision est plutôt mauvaise. De plus, la membrane d'un haut parleur n'est pas conçue pour des déplacements importants, limitant de ce fait fortement l'amplitude des déviations.

On peut obtenir de bien meilleures performances, c'est à dire des dessins géométriquement corrects et rapidement exécutés en mettant en oeuvre un système en boucle fermée. C'est ce que j'ai tenté ici, voici un modèle de principe du galvo-miroir (mais d'autres configurations sont bien sûr possibles) :

L'arbre traverse le boîtier de part en part, il est soutenu par des roulements à billes. Le miroir est monté à son extrémité gauche, l'aimant est placé en son milieu, et à droite se situe le capteur optique de position angulaire. La bobine encadre l'aimant (elle est scindée en deux pour laisser passer l'arbre). Selon le courant qui la traverse (son intensité, son sens), elle crée un champ magnétique qui imprime un couple sur l'arbre et le force à tourner de manière à aligner les lignes du champ de l'aimant avec celles de la bobine.

Il est important de noter que ce type de galvo n'a aucun dispositif de rappel. Lorsqu'aucun courant ne traverse la bobine, l'arbre est libre de tourner. Cela est très différent du dispositif à haut-parleur où la membrane tend à ramener le miroir vers une position "zéro".

Ma principale référence trouvée en ligne est elm-chan.org, merci bravo pour ce partage ! J'ai toutefois pris des directions sensiblement différentes tant pour la fabrication que pour l'électronique.

Réalisation

J'ai commencé par construire deux galvos dont voici 2 photos :

La finition ne fait pas rêver, je le reconnais, cela dit les points importants selon moi sont bien là :

On aperçoit sur la droite le bloc de mesure optique de la position. J'ai employé un composant que je connaissais bien : le CNY70. C'est un capteur infrarouge en réflexion, prévu pour les courtes distances. Il est équipé d'une led IR et d'un phototransistor dans le même boîtier. Le tout est placé derrière un filtre qui atténue beaucoup la lumière ambiante.

Nous allons voir que ce projet demande beaucoup d'efforts de mise au point, cela explique entre autres les nombreuses reprises du hardware. Par exemple, il a fallu de multiples essais pour limiter la course de l'arbre et optimiser la réponse du capteur.

Régulation électronique

Le projet dont je me suis principalement inspiré propose un schéma électronique pour les drivers de ces galvos. Ce montage est "tout analogique", c'est à dire que la mesure de la position, sa comparaison avec la commande, le calcul de l'erreur et du couple à envoyer aux galvos est réalisé par une série d'ampli-op. J'ai tenté de reproduire ce système dans un premier temps mais j'ai fini par préférer une approche partiellement numérique. Toutefois le principe de régulation en boucle fermée est similaire, voici le schéma synoptique :

Une commande de position est d'abord comparée à la position actuelle afin de déterminer une erreur. Plus cette erreur est grande (en valeur absolue), plus l'effort à envoyer aux galvos pour la rattraper le sera aussi. Cette première phase donne donc un terme qui est (inversement) proportionnel à l'erreur.

Si on s'en tenait là, et compte tenu des effets d'inertie évoqués plus haut, le galvo ne parviendrai jamais ou très difficilement à atteindre la position désirée. En effet, rien n'est prévu pour freiner l'arbre à l'approche de la position cible. Il va donc la dépasser, l'erreur va changer de signe, le couple également, l'arbre va être rappelé en arrière : tout est réuni pour que le système oscille autour de la position ce qui n'est évidemment pas souhaitable.

Il faut donc connaître la vitesse de variation de cette erreur et la prendre en compte pour provoquer le freinage ad hoc nécessaire pour atteindre la position demandée avec le minimum d'oscillation.

L'erreur est donc constamment comparée à l'erreur précédente afin de calculer un terme correctif. Mathématiquement parlant, nous devons calculer la dérivée de l'erreur par rapport au temps, ce qui est très faisable avec des AOP ou — comme je l'ai fait — avec un Arduino.

Mon approche peut donc être schématisée plus précisément comme il suit :

Concernant les divers paramètres ajustables, voici ce qui apparaît comme nécessaire a minima :

À propos de l'ampli de puissance

J'ai préféré monter un ampli à transistors plutôt que d'employer un AOP de puissance (type LM675T par exemple). Il n'est sans aucun doute pas parfait mais ma foi il a le mérite de la simplicité, ne demande aucun composant rare ou cher et son fonctionnement est très compréhensible ce qui est important dans ma démarche. Il s'agit d'un ampli DC (c'est à dire dépourvu de condensateurs de liaisons) de classe B "push-pull" assez classique dont voici le schéma (il en faut un pour chaque galvo bien sûr :-) :

Il s'agit surtout d'un amplificateur de courant étant donné que le gain en tension vaut moins 1 (notez le circuit de contre-réaction de la sortie vers l'entrée inverseuse).

Les transistors de puissance sont montés sur des radiateurs et le tout est ventilé.

Aspects logiciels

Les performances finales sont assez directement dépendantes du programme, sachant que les capacité de l'Arduino ne sont pas infinies. Comme indiqué plus haut, il faudra plusieurs exécutions de la boucle pour parvenir à un positionnement satisfaisant ce qui "coûte" beaucoup.

Compte tenu de la vitesse souhaitée, il faut configurer l'Arduino pour qu'il exécute ses conversion analogiques/numériques à (relativement) haute fréquence, ce qui n'est pas le cas par défaut. Les routines suivantes permettent à l'Arduino Nano d'effectuer plus de 50,000 lectures par seconde :


// Au début du sketch
#define FASTADC 1
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif


// Puis dans la fonction setup()
#if FASTADC
    // set prescale to 16 : readings at 55 KHz => 20 µs 
    sbi(ADCSRA, ADPS2) ;
    cbi(ADCSRA, ADPS1) ;
    cbi(ADCSRA, ADPS0) ;
#endif

Toujours dans l'esprit de maximiser les performances, l'écriture des sorties pour la conversion numérique analogique se fait via les fonction de "digitalWriteFast" implémentées comme il suit :


// Pins configurées en digital output :
int PINS[8] = {2, 3, 4, 5, 6, 7, 8, 9}; 

// fonction d'écriture rapide via la manipulation des registres.
void digitalWriteFast(uint8_t pin, uint8_t x) {
  if (pin / 8) { // pin >= 8
    PORTB ^= (-x ^ PORTB) & (1 << (pin % 8));
  }
  else {
    PORTD ^= (-x ^ PORTD) & (1 << (pin % 8));
  }
}

Un autre sujet important est la solution pour le dessin. Je me suis pour l'instant concentré sur l'affichage de caractères. Chaque caractère est représenté par un tableau de points correspondants aux coordonnées XY sommets de la lettre. Des coordonnées spéciales (999,999 / 777,777) dans ces tableaux indiquent le lever ou baisser de stylo (en réalité l'allumage ou l'extinction du laser) :


double A[] = {777,777,18,144,999,999,45,72,72,144,777,777,28,117,999,999,61,117};
double B[] = {777,777,27,108,999,999,54,108,63,117,63,135,54,144,27,144,27,72,54,72,63,81,63,99,54,108};
double C[] = {777,777,72,72,999,999,45,72,27,90,27,126,45,144,72,144};
double D[] = {777,777,27,72,999,999,27,144,54,144,63,135,63,81,54,72,27,72,27,72};
double E[] = {777,777,63,72,999,999,27,72,27,108,27,144,63,144,777,777,27,108,999,999,45,108};
double F[] = {777,777,27,144,999,999,27,108,27,72,63,72,777,777,45,108,999,999,27,108};
double G[] = {777,777,72,72,999,999,36,72,27,81,27,135,36,144,63,144,72,135,72,108,54,108};
double H[] = {777,777,27,72,999,999,27,108,27,144,777,777,63,72,999,999,63,108,63,144,777,777,63,108,999,999,27,108};
double I[] = {777,777,45,72,999,999,45,144,777,777,36,144,999,999,45,144,54,144,777,777,36,72,999,999,45,72,54,72};
double J[] = {777,777,18,72,999,999,45,72,72,72,777,777,18,144,999,999,36,144,45,135,45,72};
double K[] = {777,777,27,72,999,999,27,144,777,777,63,72,999,999,27,117,63,144};
double L[] = {777,777,27,72,999,999,27,144,63,144};
double M[] = {777,777,18,144,999,999,18,72,45,108,72,72,72,144};
double N[] = {777,777,27,144,999,999,27,72,72,144,72,72};
double O[] = {777,777,27,81,999,999,27,135,36,144,63,144,72,135,72,81,63,72,36,72,27,81,27,81};
double P[] = {777,777,27,144,999,999,27,108,27,72,54,72,62,81,63,99,54,108,27,108};
double Q[] = {777,777,45,126,999,999,51,132,58,139,65,146,72,153,777,777,18,81,999,999,18,135,27,144,54,144,63,135,63,81,54,72,27,72,18,81,18,81};
double R[] = {777,777,63,144,999,999,27,108,54,108,63,99,63,81,54,72,27,72,27,108,27,144};
double S[] = {777,777,63,81,999,999,54,72,36,72,27,81,27,99,36,108,54,108,63,117,63,135,56.25,141.75,54,144,36,144,27,135};
double T[] = {777,777,18,72,999,999,45,72,72,72,777,777,45,144,999,999,45,72};
double U[] = {777,777,27,72,999,999,27,135,36,144,54,144,63,135,63,72};
double V[] = {777,777,27,72,999,999,45,144,63,72};
double W[] = {777,777,18,72,999,999,36,144,45,117,54,144,72,72};
double X[] = {777,777,27,72,999,999,45,108,63,144,777,777,27,144,999,999,45,108,63,72};
double Y[] = {777,777,27,72,999,999,45,108,777,777,63,72,999,999,45,108,27,144};
double Z[] = {777,777,18,72,999,999,63,72,18,144,63,144};

Cependant ces coordonnées ne seront pas directement données au système de positionnement telles quelles. En effet, un long segment doit être découpé en plusieurs étapes (interpolation linéaire) sans quoi la vitesse acquise sera trop importante et la précision grandement amoindrie. Quoiqu'il en soit, voici à quoi ressemble ce jeu de caractères :

Résultats !

Voici une capture vidéo des résultats obtenus jusqu'ici.

À noter que le temps d'exposition a été par moment augmenté (jusqu'à 1/3 de sec.) de manière à visualiser le mot entier. Un grand Merci à Alain Clément dit Pinpin pour son aide et ses conseils pour ce petit tournage !

Limites et perspectives

J'estime le respect de la géométrie des caractères assez satisfaisant, tout comme la "répétabilité" des tracés (l'image ne "danse" pas trop). L'amplitude est bonne avec angle de projection horizontal d'environ 30° (et facilement extensible).

En revanche, un simple mot de 4 lettre comme ci-dessus n'est répété que 3.5x par seconde ce qui est encore trop lent. J'aimerai atteindre un taux de rafraîchissement de 10 images par secondes au moins. Évidemment, cela restera toujours très lié à la complexité du tracé.

Je ne pense pas pouvoir faire plus rapide avec un Arduino. Des optimisations plus profondes du code peuvent sans doute être appliquées mais avec ses 16 MHz de fréquence d'horloge, il n'y a pas beaucoup à gagner de ce côté là. Il faut un micro-contrôleur plus rapide comme les Teensy 3.2 ou même le Teensy 4.0 par exemple. Le sketch Arduino pourra être utilisé tel quel sur ces cartes ce qui rendra ce remplacement très simple.

Ce projet reste plus une "preuve de faisabilité" plutôt qu'un appareil fonctionnel mais sa conception, sa fabrication et sa mise au point m'ont apporté beaucoup de savoir-faire dans les 3 domaines de l'électronique, la mécanique et l'informatique.

Cette approche vectorielle de l'affichage laser appelle évidemment son complémentaire "raster", c'est à dire un balayage ligne par ligne comme dans un tube cathodique et c'est aujourd'hui une voie que je souhaite explorer. À ce sujet, voici en guise de démo (:-) une petite vidéo des recherches en cours !


Voici le schéma de principe :

Merci pour votre curiosité !