Rubriques:
Conception





Au commencement de ce projet je ne savais pas exactement comment m'y prendre pour actioner une machine à partir d'un pc: quelle interface ? quels moteurs ? quel soft ? ...  après quelques recherches sur l'internet il semblait évident que le couple port parallèle et moteur pas à pas s'imposait pour les raisons suivantes:

    - aspect "standard" du port // (présent sur tout les pc).
    - rapidité de programmation et de mise en oeuvre de la commande des moteurs pas à pas.
    - coût réduit

Comme je le précise sur la page d'accueil, cette rubrique à pour seul but d'expliquer de manière claire (du moins je l'espère) les différents mécanismes qui permettent une découpe numérique. 
 

Les spécifications

Principe de base: 
Pour découper un noyau d'aile en mousse on utilise un fil métallique de quelque 10eme de mm de diamètre, tendu et monté en court-circuit sur une alimentation variable afin d'être chauffé par effet Joule à une température suffisante pour faire fondre le bloc de polystyrène.

Considerons maintenant les 2 points du fil espacés de l'envergure de l'aile, l'un des points décrit le profil de l'emplanture, et dans le même temps l'autre décrit le profil du saumon. Je souligne dans le même temps, car de ces 4 mots découlent la principale difficulté du projet. 
Quand on a décrit l'intrados et l'extrados, on en ressort le noyau d'aile.

La méthode manuelle consite à fabriquer des gabaris de découpe et de faire glisser le fil sur ces gabaris de manière à peu près synchrone.
Tous les modélistes connaissent les difficultés de mise en oeuvre: chaque nouveau type d'aile entraine la fabrication de nouveaux gabaris. Le respect exact  du profil passe par une qualité de gabaris et une synchronisation de la découpe emplanture/saumon, qu'il est très difficile d'obtenir manuellement.

La machine CNC permet de s'affranchir de ces problèmes: les gabaris et les déplacements sont assurés par le couple logiciel + machine.

Le logiciel assure la découpe d'aile d'une envergure quelconque, possèdant une flèche, une corde variable et un vrillage donné ( ceci dans la limite des dimensions mécanique de la machine).

Enfin, le logiciel est compatible avec les formats des fichiers de coordonnées de profils les plus courant (.dat) et permet la sauvegarde des fichiers découpe d'ailes.
 

Schématiquement voici à quoi j'ai aboutit:

Organigramme logiciel

Toutes les questions que vous vous posez suite à la lecture de ce schéma sont, je l'espère, dans les paragraphes qui suivent.
 
 

Solution Technique

OS: Linux 

Plus la peine de vanter les mérites de ce système d'exploitation. Ouvert, libre c'est une base de développement fantastique. Le logiciel a été développé sur une RedHat 7.2, et fonctionne aujourd'hui sur une Mandrake 9.2.


Interface graphique: Tcl/Tk

Tcl/tk est un langage script pour application graphique que j'ai eu l'occasion de connaitre de par mes activités professionelles. Très puissant, facile d'aprentissage, de nombreuses documentations et sources existent sur le web.

Le role de l'interface est de passer des coordonnées des profils aux coordonnées de découpe. 
Les coordonnées de découpes sont les couples de vecteurs d'interpolation linéaire u(x,y) et v(x,y) qui décrivent les courbes des profils.
Ils sont ensuite transmis au driver de la carte interface machine les uns après les autres pour décrire les mouvements complet de la machine.

Voici quelques captures d'écran de la version 1.1 du logiciel:

Fenêtre Principale


 

Graph du profil emplanture

Graph du profil saumon


 

Les coordonnées du profil emplanture

Coordonnées Profil


Interface Matériel: Le Port parallèle
Présent sur tous les pc, il est très facilement programmable.  Sous Linux, en langage C, on lit et on écrit directement les états des bits du port // par les commandes inb <lecture> et outb <ecriture>.
Il faut par contre autoriser au préalable ces opérations par la commande ioperm.
Attention seul le user root peut faire un ioperm car c'est une opération très dangereuse, on imagine facilement ce qui se passerait si n'importe quel user pouvait accéder directement aux d'entrées sorties du pc. 

Pour commencer, il faut connnaitre l'adresse de base du port // sur le PC, en géneral 0x378, mais en cas de doute vérifier dans le setup ou lancer les commandes: 

echo > /dev/lp0  (puis crtl-c) (force la prise en compte du port // par le système )
et 
cat /proc/ioports
qui vous donne la liste des plages d'entrées sortie des périphériques:
....
0378-037a : parport0
.....
cherchez la ligne correspondant au "parport0", et recuperez l'adresse, ici 378h.


Enfin il faut connaitre le mapping du port // :

Port //

En résumé:
A l'adresse de base nous trouvons un port en écriture dont les 8 bits sont accessibles , à l'adresse de base+1 un port en lecture dont seulement 6 bits sont accessibles et enfin à l'adresse de base +2 un port en écriture dont 4~5 ? bits sont accessibles.
Ceci est largement suffisant pour la commande de la machine.

Le petit plus:

Je fais remarquer aux non-initiés, que les commandes d'i/o ne permettent pas de commander ou de connaitre l'état d'un seul bit d'un port, on y accède par octet.
Pour isoler un bit en particulier, il faut masquer les autres: le bit n, aura un masque sera égal à 2^n :

n bit= 76543210
2^0 = 00000001
2^1 = 00000010
2^2 = 00000100
Un exemple:

Lecture:

Supposont qu'inb(adresse_base+1) renvoie la valeur entiere 233 soit 11101001 en binaire

Pour connaitre la valeur du bit 3 on fait un ET logique entre l'état du port et le masque: 2^3 soit 00001000 en binaire.

11101001&&00001000
=00001000

L'opération vaudra 0 si 0 Volt est appliqué à la broche 4 du port // (b3 à 0), et vaudra 2^3 si vous y appliquez 5 volts. 

Ecriture:
Cette fois il faut lire le port pour connaitre sont état et donc la valeur de tous les bits du port, et si ce n'est pas possible connaitre sont état préalable (c'est le dernier état appliqué par le logiciel) ETAT_PREV. 

Mise à 1 d'un bit:

On applique le masque du bit n à ETAT_PREV et on ecrit ce nouvel état par outb(adresse_base,ETAT):

ETAT = ETAT_PREV  ||  2^n  ( OU logique ) 

ex bit 4: 

ETAT_PREV=10001010
n=4 => masque bit4=0x10 en hexa soit en binaire: 00010000 

10001010 || 0001000
= 10011010
et hop le bit est à 1

Mise à 0 d'un bit:
si l'on veut mettre le bit n à 0  on prend le complément du masque ( opération ~ en c ) et l'on fait un ET logique:
ETAT = ETAT_PREV  &&  ~2^n (et logique )
ex bit 4: 
ETAT_PREV=10111010
n=4 => masque bit4=0x10 en hexa
soit en binaire: 00010000 
son complement vaut: 11101111
10111010 &&  11101111
= 10101010
et hop le bit est à 0


Pour ceux qui comme moi, ne parlent pas l'hexabinarodécimal couramment, une calculatrice de type scientifique est souhaitable.

Maintenant que les opérations de base sont décrites, on imagine toutes les possibliltés qui s'offrent à nous.

La programmation:

Les header à ne pas oublier:
#include <unistd.h>
#include <asm/io.h>
un define pour l'adresse de base:
#define ADR_BASE       0x378
les fonctions de base d'entree / sortie
int out_port(int adresse, int port, int val)
  {
         outb(val,adresse + port);
         return(0);
  }

int in_port(int adresse, int port)
 {
         int val;
         val = inb(adresse + port);
         return(val);
 }

dans le main, placer la commande d'autorisation à l'init :
if (ioperm(ADR_BASE,2, 1)) // => depuis l'adresse de base ADR_BASE sur  2 octets , mettre à 1 ( autoriser), les accès i/o .
{
           perror("ioperm");
           exit(EXIT_FAILURE);
}
on peut alors appeler les fonctions out_port et in_port.
J'ai créer un programme de base io.c qui donne un bon exemple de ce que l'on peut faire avec un port parallèle, vous le retrouverez dans la rubrique download.
Une étape plus loin a été de développer un testeur de servo avec une interface tk qui s'appuie sur la RTC (Real Time clock) tout comme le driver cnc.
Moteurs: Pas à Pas
Au début, je pensais utiliser des servomoteurs, j'ai même developpé une interface pour les piloter via le port // qui est resté au stade de testeur de servo. Et puis je me suis vite rendu compte que je ne pourrai jamais asservir correctement ces moteurs en vitesse relative, point indispensable à la réussite du projet.
Je me suis donc orienté vers des moteurs pas à pas, très intéressant, bien plus facile a commander qu'un servo avec un pc. Par contre si un servo peut se commander avec 1 bit (en modulation de largeur de pulse) un moteur pas à pas nécessite au minimum 2 bits (1 bit d'horloge et 1 bit de sens).
Quant à l'asservissement en vitesse relative, pas de problème il suffit d'envoyer les tops d'horloges "intelligement", le driver de la carte interface va se charger de ça grâce à l'algorithme de Breseham.
 


Le driver carte interface: langage c

Si l'interface et les calculs sont écrits en tcl/tk , il était impossible d'écrire la partie driver dans ce langage pour des questions évidentes de rapidité d'éxecution. 
Le driver pilotera la carte interface via le port parallèle: il reçoit les coordonnées des vecteurs à décrire et met en forme les signaux sur le port // pour la commande des moteurs.

Dans un premier temps le driver a été développé en 2 parties:
Une partie client et une partie serveur auxquelles je dédiais respectivement les tâches 1) d'algorithmie 2) de commande de la carte via le port //, ces 2 programmes communiquant aux travers de fifos pour la "buffersation". 
Tous ceci fonctionnait très bien (du moins sur le papier) mais dès les premiers tests de découpe je me suis rendu compte de ralentissement des moteurs au passage entre chaque nouveau vecteur à cause de rides dans le polystyrène !!! 
Explication: en fait l'interface graphique communiquait avec la partie cliente à chaque vecteur, et cette fois sans "bufferisation" d'où une perte de performance à chaque appel.

Puisque c'est ainsi pourquoi ne pas ré-écrire le driver ? Et tant qu'on y est pourquoi ne pas utiliser des  threads ?
L'idée a donc été de considérer ces programmes précédant comme 2 threads d'un même programme, qui communiqueraient cette fois non plus par fifo mais par un tableau à référence circulaire à double index: un index pour l'écriture et l'autre pour la lecture obéïssants aux rêgles suivantes: la lecture ne dépasse jamais l'écriture et l'écriture ne dépasse pas l'indexe écriture. Tout ça modulo la taille du tableau of course.
 

thread 1: algorithme de  bresenham

Une première thread est dédiée à la lecture de la FIFO in (messages en provenance de l'interface) , elle y recoit les 2 vecteurs u(x,y) v(x,y) d'interpolation linéaire des courbes à décrire avec la machine.

Elle transforme ensuite ces vecteur en instructions élémentaires: "pas" et "direction" pour chacun des moteurs qu'elle transmets à un tableau circulaire.

Pour ce faire le programme repose sur l'algorithme de Bresenham.

Dans un plan discret, cet algorithme permet de trouver les points du système de coordonnées les plus proches des courbes (ou plus exactement des vecteurs d'interpolation linéaire)

Ce paragraphe s'appuie sur un article de Mme Marie-Renée Josserand (Maatel) (ÉLECTRONIQUE - No. 38, Mai 1994),
j'ai ensuite modifié l'algorithme pour qu'il s'applique non pas à un vecteur mais à 2.

Graphe d'un profil dans le repère discret

Seuls les points de croisement sont accessibles.

Description de l'algorithme original pour 1 vecteur (2 moteurs)

 
Extrapolation d'un vecteur dans un repere discret:
Soit u(A,B) un vecteur de ce repere ( A et B entier) ramené dans le premier octan: A=abs(A), B=abs(B) , permutation A B si A<B)

Représentation du vecteur U dans le repère discret

pour decrire le vecteur dans ce systeme:

on pose VAR=2B-A
Faire A fois

Si VAR < 0
VAR=VAR+2B
On avance d'un pas suivant l'axe majeur
sinon
VAR=VAR+2B-2A
on avance simultanément d'un pas suivant l'axe majeur
et suivant l'axe mineur
fin si
fin faire

on decrit aisement tout types de vecteurs en ayant memorisé les opérations
effectuées pour le ramener dans le premier cadran.

en clair si A etait < B         on permute axe majeur mineur
si A < 0                        on recule suivant cet axe
si B < 0                        on recule suivant cet axe


Application de l'algorithme pour 2 vecteurs ( == 4 moteurs)

Dans la suite on considere u(A,B),v(C,D) vecteurs du repere discret

- les 2 vecteurs sont ramenés dans le premier octan de la meme maniere
- de plus si C > A alors on permute les deux vecteurs.

pour decrire les vecteurs dans ce systeme:

on pose         VAR_U=2B-A
on pose         VAR_V=2D-C
on pose         R = C/A
on pose         JS=-1

DE I=1 a I<A I=I+1

( Traitement du 1 eme vecteur )                ( Traitement du 1 eme vecteur )
Si ( VAR_U < 0 )
VAR_U=VAR_U+2B
on avance d'un pas suivant l'axe majeur 1
sinon
VAR_U=VAR_U+2B-2A
on avance simultanément d'un pas suivant l'axe majeur 1 et suivant l'axe mineur 1
fin si

J= entier ( R * I )
si ( J different de JS && J >= à 1 )    ( nouveau pas pour le 2eme vecteur )
( Traitement du 2 eme vecteur )

Si ( VAR_V < 0 )
VAR_V=VAR_V+2D
on avance d'un pas suivant l'axe majeur 2
sinon
VAR_V=VAR_V+2D-2C
on avance simultanément d'un pas suivant l'axe majeur 2
et suivant l'axe mineur 2
fin si
JS=J                    (memorisation du pas effectue)
fin si
fin DE

Le vecteur v est decrit au fur et a mesure de la description de u: Ils sont décrits dans le meme temps ( ceci permet de s'affranchir du casse tête qu'aurait été la synchronisation d'un bras par rapport à l'autre). 
De meme que pour un vecteur seul, on decrit aisement tout types de vecteurs en ayant memorisé les opérations effectuées pour les ramener dans le premier cadran.

pour u & v

si A etait < B                  on permute axe majeur1 mineur1
si A < 0                        on recule suivant cet axe
si B < 0                        on recule suivant cet axe

si C etait < D                  on permute axe majeur2 mineur2
si C < 0                        on recule suivant cet axe
si D < 0                        on recule suivant cet axe

nb: dans tout ceci j'ai considéré que les axes majeur1&2 mineur1&2 etaient echantillonnés avec le meme pas ( sinon il faudrait introduire  un facteur d'echelle dans le calcul J = entier ( R * I ) ce que je n'ai pas fait car toutes mes mécaniques sont identiques)

Chaque itération de l'algorithme est transmise à la thread de commande du port grâce à un tableau circulaire.

Format du message du tableau : pas1:pas2:pas3:pas4:

 ex:
 -1:+0:+0:+1:
 -1 pas moteur 1
 1  pas moteur 2
 0  pas moteur 3
 1  pas moteur 4
thread 2: commande port parallèle

C'est le thread qui va réellement attaquer le port parallèle ( par la commande outb). Il applique un mot binaire, suivant les instructions reçues dans le tableau circulaire, au rythme de ça propre horloge (cadencée de manière très précise par la RTC ) :
 

             __   __   __   __      __   __
            | |  | |  | |  | |     | |  | |
            | |  | |  | |  | |     | |  | |
            | |  | |  | |  | |     | |  | |
          __| |__| |__| |__| |_____| |__| |__     pas
 

          __                      ________________      sens
            |                    |
            |____________________|

        Mapping port //

        voie 1:         pas: pin 2 sens: pin 3
        voie 2:         pas: pin 4 sens: pin 5
        voie 3:         pas: pin 6 sens: pin 7
        voie 4:         pas: pin 8 sens: pin 9

        masse:          pin 22
 

thread 3: commande de la chauffe

La modulation de température est obtenue par hachage de la tension appliquée au fil:  Pour cela le soft fait varier le rapport cyclique du créneau appliqué à un MOSFET utilisé en mode commutateur.
 

   0% ___________
        __   __
       |  | |  |
  66% _|  |_|  |_
        __    __
       |  |  |
  50% _|  |__|
        ________
       |
 100% _|

Hard: Carte commande moteur
But: transformer les 8 bits d'entrée (horloge et sens des 4 moteurs fourni par la port // ) en des signaux compatibles avec la commande des moteurs pas à pas. Je précise qu'il seront du type "unipolaire".
L'alimentation sera assurée par une alim. de PC. 

Pour rendre à César ce qui lui appartient je précise que je suis parti d'un existant: infos , j'ai ensuite modifié le schéma  pour mes besoins

J'ai représenté par la suite le schéma pour un moteur. Pour les trois autres je vous laisse deviner.

  • Le schéma est des plus simple: mise en forme des signaux via 2 transistor NPN ( passage de TTL 5v à 12 v ).
  • Le bit d'horloge et le bit de sens attaquent un 4029 qui va transformer les signaux en une séquence binaire sur 2 bits qui comptera ou décomptera suivant le cas.
  • on relie les 2 bits de sortie du 4029 au 4028 qui "décodera" le mot binaire et alimentera une à une les bobines du moteur pas à pas.
  • ces signaux transitent via un 2803 pour la puissance et sont ensuite relié au bornier.
Commande Moteur Pas à Pas
schema de l'électronique de commande moteur pas à pas

Pas de composant programmable, tout est dans le soft. 

Voici le mapping utilisé pour commander la  carte depuis le soft:


et quelques photos de la  carte que réalisée en wrapping:
 
 


on reconnait les transistors, les 3 CI par voie, et le bornier d'alimentation type pc.

Telle qu'elle est faite, la carte est passive. Je m'explique: pour obtenir un cadencement à une certaine vitesse, une rampe d'accelération ou de descélération c'est au PC et au soft qu'il faut le demander.  
 

Mécanique: La rustica
Pour la réussite du projet, il fallait une machine facile à construire, pas d'usinage compliqué, pas de pièce introuvable en grande surface de bricolage, bref un must.


vue arrière de la machine démontée depuis l'oeil de la webcam

Decoupe: Principe de Projection / Prolongation
Posons le problème: La machine a des dimensions fixes: tant en largeur qu'en hauteur pour chaque chariot mais aussi en empatement. Les 2 plans que décrivent les points mobiles des chariots sont toujours à la même distance: c'est la largeur de la  table sur le schema ( D ). 

Or, ce que l'on se propose de faire, c'est de décrire le profil emplanture et le profil saumon de notre aile sur ces deux plans pour faire la découpe ( ici les plans rouge et vert ).

Dans ces conditions, pour des ailes d'envergure différente de la largeur de la machine (ici E ), nous n'obtiendrons une découpe correcte que si les ailes sont à profil, corde, incidence et épaisseur constant ( autant dire très peu d'ailes) .

Dans tous les autres cas il faut "prolonger / projeter " le profil saumon sur le plan du chariot de découpe.

On peut faire cela en applicant le théorème de Thalès, un rappel:

On applique cela au triangle orange de la figure suivante qui représente l'aile vue du dessus:

(X0n - N) / (X0n - M ) = (X0n-X'1n) / (X0n-X1n) = (X'1n-N) / (X1n-M)

 
d'où X'1n-N = (X0n-N) / (X0n-M ) x (X1n-M)  soit  X'1n-N= D/E(X1n - X0n) 
et  X'1n = D/E (X1n - X0n ) - X0n

On pourrait appliquer le meme principe pour trouver l'ordonnée Y'1n du point de prolongation.

En notation vectorielle cela donne: (en considérant les notation du  schéma 3D du dessus):

V'n= D/E (Un-Vn) + Un

Unvecteur décrivant le nieme point du profil emplanture dans le plan Po
Vn  vecteur décrivant le nieme point du profil saumon dans le plan P1
D distance entre les 2 plans de découpe
E envergure de l'aile.
 
Quelques exemples pour une machine de 750 mm d'empatement:

Sont représentés sur les graphes suivants, les chemins empreintés par le fil de la machine pour la de découpe d'une aile à 30° de flèche avec 10° de vrillage négatif  de 330 mm de corde emplanture  et 130 mm de corde de saumon ( trait vert ) et le point de projection ( trait bleu).
 

- trait rouge: cheminement du fil dans le plan de l'emplanture ( 1er chariot )
- trait vert:  cheminement du fil dans le plan du saumon
- trait bleu:  cheminement du fil dans le plan du 2eme chariot
Envergure=550mm

D=750mm E=550mm

Même aile mais avec une Envergure=450mm

D=750mm E=550mm

On voit clairement l'évolution de la forme décrite par le 2eme chariot de la machine ( en bleu ) pour que le profil saumon ( en vert ) reste le meme quelque soit l'envergure.


 

Crevasse de découpe
 

Dernier paramètre: le fil chaud a un diamètre de quelques 10ème de mm, il créé de  plus une crevasse de plusieurs dixièmes millimètres dans la mousse en fonction de la chaleur dissipée. 

L'épaisseur de mousse fondue au passage du fil est relative à la vitesse du fil, au matériau utilisé (type de mousse, densité), et bien sûr à la température du fil.

Dans le cas d'un rectangle voici ce qu'on obtient:

Le rectangle voulu est en pointillés rouges ( déplacement du fil ), le rectangle obtenu est de dimension inférieure et en tout état de cause non conforme à l'attendu.

Il faut donc implémenter un facteur correctif pour que le fil décrive, non pas le contour de la forme, mais une trajectoire tangeantant la forme à 1/2 largeur de crevasse ( E ):
 
 

Le schéma précédant représente la trajectoire du fil et  les points d'échantillonnages.

Chaque point An de coordonnées (xAn,yAn) correspondant de la trajectoire échantillonnée est remplacé par le point An" (xAn",yAn") tel que ( en notation vectorielle):

An"=An - ( E . tg(alpha) . Un )+ E  . En

où:

 
Un vecteur unitaire //  au segment  [An-1, An]
En vecteur unitaire _|_  au segment [An-1, An]
Un+1 vecteur unitaire //  au segment  [An, An+1]
En+1 vecteur unitaire _|_  au segment [An, An+1]
alpha moitié de l'angle formé par les deux vecteurs Un,Un+1
Nota: Comme on n'a pas accès directement à la valeur de l'angle Alpha on cherche tout naturellement à passer par celle de Beta grâce à un arccos ou un arcsin et diviser par 2.
Or ce qui nous intéresse en fin de compte ce n'est pas alpha mais tg(alpha) alors pour éviter les accumulations d'erreurs de calcul; et optimiser un peu, référons nous aux formules de trigonométrie, voici celle utilisée dans le soft:

tg(beta/2)=sin(beta) / (1+cos(beta) )

sin(beta) et cos(beta) sont obtenus par une simple division de la norme des vecteurs par chacune de leurs coordonnées d'où:

An"=An -  E . ( (sin(beta) / (1+cos(beta) ) ).Un  +  En )





Exemple de découpe


Mise à jour: 18/10/2004