Tutoriel Arduino moteur pas a pas
Commande d’un moteur pas pas bipolaire sur Arduino
1. Introduction
Ce document montre comment commander un petit moteur pas pas avec un Arduino. On consid?re le cas du moteur pas pas bipolaire. De nombreux mod?les au format NEMA17 sont disponibles sur le marchØ, avec des pas de 1.9 ou 0.8 degrØs (respectivement 200 et 400 pas par tour).
2. Principe du moteur pas pas et de la commande
2.a. Moteur rØluctance variable et moteur hybride
Dans un moteur pas pas rØluctance variable ([1]), les bobines d’excitation sont sur le stator. Le rotor est en acier de forte permØabilitØ, et n’a pas d’aimantation permanente. Le stator et le rotor sont munis d’encoches. La gure suivante montre un dØtail avec quelques encoches. La reprØsentation est rectiligne (au lieu de circulaire).
Dans la position reprØsentØe, les dents des p les A du stator sont en vis- -vis des dents du rotor, alors que celle des p les B sont dØcalØes. Les p les A sont excitØs par un bobinage formant la phase A (en rouge sur la gure). Dans la con guration reprØsentØe, un courant circule dans la phase A mais pas dans la phase B. Le champ magnØtique gØnØrØ dans les p les A induit une aimantation dans le rotor en acier. La con guration stable est celle oø les dents du rotor sont alignØes avec celles du stator. Si l’on tente d’Øcarter le rotor de cette position, un couple de rappel s’exerce. Lorsqu’on coupe le courant dans la phase A et qu’on alimente le bobinage de la phase B, le rotor tourne pour obtenir l’alignement avec les dents des p les B. Si le rotor comporte 100 dents, cela fait une rotation de 1,8 degrØs (moteur 200 pas par tour).
Les moteurs pas pas sont souvent de type hybride ([2]), avec une aimantation permanente du rotor. Le rotor d’un moteur hybride est constituØ de deux roues dentØes dØcalØes. La premi?re roue est un p le sud magnØtique et se trouve en vis vis des p les A du stator. La seconde roue est un p le nord magnØtique et se trouve en vis vis des p les B du stator. La gure suivante reprØsente un moteur hybride dans la con guration oø la phase A est alimentØe. Les dents du p le sud du rotor sont alors alignØes avec celle des p les A du stator.
Stator A | Stator B |
Stator A | Stator B |
Dans un moteur hybride, il y a un faible couple mŒme en l’absence de courant, qui bloque le rotor dans une position d’alignement, soit avec les p les A soit avec les p les B. Ce couple permet souvent de bloquer le moteur au repos sans lui fournir du courant ( condition que la charge au repos soit faible).
Un moteur pas pas bipolaire comporte 4 ls d’alimentation, deux pour la phase A, deux pour la phase B. Un ohmm?tre permet facilement de repØrer les deux paires.
Pour les moteurs unipolaires, qui comportent deux bobines en sens inverse par p le (deux pour le p le A, deux pour le p le B), voir Commande d’un moteur pas pas unipolaire sur Arduino.
2.b. Excitation des phases
Dans le premier mode d’excitation, la rotation du rotor se fait lorsqu’on coupe le courant dans une phase tout en dØclenchant l’alimentation de l’autre phase. Dans ce mode, le courant ne circule que dans une phase la fois (mode One Phase ON, Full Step). Le sens du courant est inversØ chaque alternance. Ce mode est aussi appelØ excitation ondulØe.
La rotation se fait tr?s rapidement, avec un temps de l’ordre de la milliseconde, juste apr?s le changement des courants (il faudra tout de mŒme tenir compte du temps d’Øtablissement du courant). La durØe des pas est gØnØralement de plusieurs dizaines de millisecondes. Le courant est maintenu de mani?re bloquer le rotor dans sa position. Si la charge est faible, on peut aussi couper le courant entre les pas, apr?s avoir appliquØ une impulsion assez longue pour provoquer la rotation. Par exemple, si les pas sont espacØs d’une seconde, on peut appliquer des impulsions de 50 millisecondes suivies de plages de courant nul.
Le second mode (Two Phase ON, Full Step) consiste alimenter les deux phases en mŒme temps, et changer le sens du courant chaque pas. Avec ce mode, le couple moteur est plus important, mais la dissipation est Øvidemment plus grande (dans le moteur est dans le circuit de commande). Ce mode est aussi appelØ excitation standard.
Le troisi?me mode permet de faire des dØplacements par demi-pas (mode Half Step) :
3. Pont en H transistor bipolaire
3.a. Pont en H
Un pont en H permet d’alimenter un bobinage du moteur (une phase) en permettant de changer le sens du courant. Voici le schØma de principe d’un pont en H :
Le bobinage est reprØsentØ par une rØsistance et une inductance. Lorsque V a = 5 V et V b = 0, les transitors Q2 et Q3 sont passants et un courant positif circule dans le moteur de B vers A. Inversement, lorsque V a =0 et V b =5 V , un courant positif circule de A vers B. Lorsque les deux tensions de commande sont zØro, le courant dans le bobinage est nul. Les diodes permettent au courant de ce rØduire continßment lorsque les transistors sont bloquØs (diodes de roue libre)
Le temps de commutation des transistors est nØgligeable devant le temps d’Øtablissement du courant dans la bobine, qui est de l’ordre de L/R = 4 ms. La simulation LTSPICE e ectuØe consiste faire varier V a sous forme d’impulsions carrØes, tout en maintenant V b =0.
Voici le courant dans la bobine obtenu par cette simulation :
La durØe des pas est de 100 ms (en rØalitØ, il y a une inversion du sens du courant qui n’est pas introduite dans la simulation). On voit bien le rØgime transitoire d’Øtablissement du courant. Pour obtenir le couple maximal, il faut que la durØe des impulsions soit supØrieure au temps d’Øtablissement du courant.
3.b. Circuit avec arduino
Le circuit L298N comporte deux ponts en H transistors bipolaires, pouvant en principe dØlivrer chacun jusqu’ 2 A, sous rØserve que la chaleur dissipØe soit bien ØvacuØe. Voici un circuit complet avec les connexions un arduino. Ces connexions permettent d’utiliser conjointement la platine Arduino Motor Shield.
Le pont A sert alimenter la phase A, le pont B alimenter la phase B. Les deux sorties du pont A sont OUTPUT1 et OUTPUT2. Le pont A est pilotØ par les trois entrØes logiques INPUT1, INPUT2 et ENABLEA. Lorsque ENABLEA=1, un sens du courant est obtenu avec INPUT1=1 et INPUT2=0, alors que le sens opposØ est obtenu avec INPUT1=0 et INPUT2=1. Lorsque ENABLEA=0, le pont est inactif (le courant est nul).
Voici la table de correspondance entre les sorties de l’arduino utilisØes et les entrØes du L298 :
. D2 : INPUT 1
. D4 : INPUT 2
. D6 : ENABLEA
. D10 : INPUT 3
. D7 : INPUT 4
. D5 : ENABLE B
Les entrØes ENABLEA et ENABLEB sont pilotØes par des sorties PWM de l’Arduino, ce qui permet Øventuellement d’appliquer un signal PWM pour limiter le courant moyen, comme on le fait pour les moteurs courant continu.
3.c. Signaux de commande
On convient de noter les niveaux logiques dans l’ordre INPUT1, INPUT2, INPUT3, INPUT4. Voici les niveaux appliquer pour e ectuer un pas moteur dans un sens (on change l’ordre pour le sens opposØ).
Mode pas entier, une phase alimentØe la fois (One Phase ON, Full Step) :
. 1000
. 0010
. 0100
. 0001
Mode pas entier, deux phases alimentØes en mŒme temps (Two Phase ON, Full Step) :
. 1010
. 0110
. 0101
. 1001
Mode demi-pas :
. 1000
. 1010
. 0010
. 0110
. 0100
. 0101
. 0001
. 1001 noter qu’il existe des circuits logiques (comme le L297), qui permettent de gØnØrer ces sØquences en synchronisation avec un signal d’horloge. Dans le cas prØsent, on utilisera l’arduino pour gØnØrer les sØquences, ce qui reprØsente une charge de travail minime pour celui-ci. Le vØritable intØrŒt d’un circuit de commande comme le L297 est de permettre la rØgulation de courant par hachage, que l’on verra plus loin.
3.d. Programmation sur arduino
Le programme suivant montre comment piloter un moteur pas pas avec le circuit ci-dessus.
On commence par dØ nir les sorties utilisØes et la variable globale etape, qui mØmorisera l’Øtape de la sØquence d’impulsion en cours.
// commande d’un moteur pas pas bipolaire avec un L298
#define INPUT1 2
#define INPUT2 4
#define ENABLEA 6
#define INPUT3 10
#define INPUT4 7 #define ENABLEB 5 int etape;
Les trois fonctions suivantes appliquent les signaux logiques pour l’une des Øtapes, pour les trois modes dØcrits plus haut :
void pas_une_phase(int etape) {
switch(etape) {
case 0 : // 1000
digitalWrite(INPUT1,HIGH); digitalWrite(INPUT2,LOW); digitalWrite(INPUT3,LOW); digitalWrite(INPUT4,LOW); break;
case 1: // 0010
digitalWrite(INPUT1,LOW); digitalWrite(INPUT2,LOW); digitalWrite(INPUT3,HIGH); digitalWrite(INPUT4,LOW); break;
case 2: // 0100
digitalWrite(INPUT1,LOW); digitalWrite(INPUT2,HIGH); digitalWrite(INPUT3,LOW); digitalWrite(INPUT4,LOW); break;
case 3: // 0001
digitalWrite(INPUT1,LOW); digitalWrite(INPUT2,LOW); digitalWrite(INPUT3,LOW); digitalWrite(INPUT4,HIGH); break;
}
}
void pas_deux_phases(int etape) {
switch(etape) {
case 0 : // 1010
digitalWrite(INPUT1,HIGH); digitalWrite(INPUT2,LOW); digitalWrite(INPUT3,HIGH); digitalWrite(INPUT4,LOW); break;
case 1: // 0110
digitalWrite(INPUT1,LOW); digitalWrite(INPUT2,HIGH); digitalWrite(INPUT3,HIGH); digitalWrite(INPUT4,LOW);
break;
case 2: // 0101
digitalWrite(INPUT1,LOW); digitalWrite(INPUT2,HIGH); digitalWrite(INPUT3,LOW); digitalWrite(INPUT4,HIGH); break; case 3: // 1001
digitalWrite(INPUT1,HIGH); digitalWrite(INPUT2,LOW); digitalWrite(INPUT3,LOW); digitalWrite(INPUT4,HIGH); break;
}
}
void pas_demi_pas(int etape) {
switch(etape) {
case 0 : // 1000
digitalWrite(INPUT1,HIGH); digitalWrite(INPUT2,LOW); digitalWrite(INPUT3,LOW); digitalWrite(INPUT4,LOW); break; case 1: // 1010
digitalWrite(INPUT1,HIGH); digitalWrite(INPUT2,LOW); digitalWrite(INPUT3,HIGH); digitalWrite(INPUT4,LOW); break; case 2: // 0010
digitalWrite(INPUT1,LOW); digitalWrite(INPUT2,LOW); digitalWrite(INPUT3,HIGH); digitalWrite(INPUT4,LOW);
break; case 3: // 0110
digitalWrite(INPUT1,LOW); digitalWrite(INPUT2,HIGH); digitalWrite(INPUT3,HIGH); digitalWrite(INPUT4,LOW); break; case 4: //0100 digitalWrite(INPUT1,LOW); digitalWrite(INPUT2,HIGH); digitalWrite(INPUT3,LOW); digitalWrite(INPUT4,LOW);
break; case 5: //0101 digitalWrite(INPUT1,LOW); digitalWrite(INPUT2,HIGH); digitalWrite(INPUT3,LOW); digitalWrite(INPUT4,HIGH); break;
case 6: //0001 digitalWrite(INPUT1,LOW); digitalWrite(INPUT2,LOW); digitalWrite(INPUT3,LOW); digitalWrite(INPUT4,HIGH); break; case 7: //1001 digitalWrite(INPUT1,HIGH); digitalWrite(INPUT2,LOW); digitalWrite(INPUT3,LOW); digitalWrite(INPUT4,HIGH); break;
}
}
Les fonctions suivantes incrØmentent ou dØcrØmentent la variable etape selon le sens de mouvement choisi et appliquent les signaux pour l’Øtape atteinte.
void pas_une_phase_sens_1() {
etape++; if (etape>3) etape = 0; pas_une_phase(etape);
}
void pas_une_phase_sens_2() {
etape--; if (etape
}
void pas_deux_phases_sens_1() {
etape++; if (etape>3) etape = 0; pas_deux_phases(etape);
}
void pas_deux_phases_sens_2() {
etape--; if (etape
}
void pas_demi_pas_sens_1() {
etape++; if (etape>7) etape = 0; pas_demi_pas(etape);
}
void pas_demi_pas_sens_2() {
etape--; if (etape
}
La fonction suivante applique un signal PWM sur les entrØes ENABLEA et ENABLEB du L298. Dans la plupart des cas, on utilisera soit la valeur 255 soit la valeur
0.
void pwm(int pwm) {
analogWrite(ENABLEA,pwm); analogWrite(ENABLEB,pwm);
}
Voici la fonction d’initialisation :
void setup() { pinMode(INPUT1,OUTPUT); pinMode(INPUT2,OUTPUT); pinMode(INPUT3,OUTPUT); pinMode(INPUT4,OUTPUT); pinMode(ENABLEA,OUTPUT); pinMode(ENABLEB,OUTPUT); analogWrite(ENABLEA,0); analogWrite(ENABLEB,0);
etape = 0;
}
Voici un exemple avec des blocs de 100 pas suivi d’un temps d’attente de 3 secondes. La durØe d’application du courant est choisie, et la durØe totale du pas (en millisecondes). Pour faire tourner le moteur une vitesse bien prØcise, on calculera la durØe du pas en tenant compte du nombre de pas par tour du moteur. Dans cet exemple, le courant de repos est nul, ce qui suppose que la charge au repos du moteur soit faible. Si une charge importante doit Œtre appliquØe, il faut choisir un courant de repos maximal (255). Cela conduit une dissipation importante dans le L298 (qui doit Œtre muni d’un radiateur). On peut aussi choisir d’appliquer un PWM (par exemple une valeur de 100), ce qui rØduit le courant moyen mais a pour consØquence l’Ømission d’un son la frØquence du PWM (environ 1000 Hz).
void loop() {
int k;
unsigned int duree_courant = 10; unsigned long duree_pas = 50; unsigned long temps;
for (k=0; k
while (millis()-temps
// autre chose faire pendant le pas moteur
}
pwm(0); // courant de repos, en fonction de la charge
while (millis()-temps
// autre chose faire pendant le pas moteur
}
}
pwm(0); // courant de repos, en fonction de la charge delay(3000);
}
Il est possible de faire tourner le moteur avec une durØe de pas de l’ordre de la milliseconde. Si la durØe est assez courte, le moteur tourne de mani?re continue tout en restant en synchronisme avec les impulsions : c’est le mode de rotation en survitesse. Voici comment faire un tour complet avec un moteur de 200 pas par tour, en utilisant le mode demi-pas :
duree_pas = 2 pwm(255); // courant max for (k=0; k
while (millis()-temps
}
Dans le cas d’un moteur fort courant (plus de 1 A), on peut rØduire la dissipation en rØduisant la durØe d’application du courant, tout en restant au dessus de 5 ms. Le reste du temps, on applique un courant nul, ou bien un courant PWM de valeur moyenne faible.
3.e. Utilisation du motor shield arduino
Le Motor-Shield o ciel Arduino est plut t prØvu pour piloter un ou deux moteurs courant continu, mais il peut Œtre utilisØ pour un moteur pas pas de faible puissance de la mani?re suivante (Attention, la biblioth?que Stepper n’est pas utilisable pour cela).
Voici le branchement d’un moteur pas pas bipolaire sur ce shield :
Pour dØterminer les ls de chaque phase, il faut utiliser un ohmm?tre. L’alimentation du moteur est faite directement sur le shield par un bloc secteur 12 V . La liaison Vin a ØtØ coupØe de mani?re Øviter une alimentation accidentelle par le port USB.
Le programme ci-dessous reprend les fonctions du prØcØdent pour le motor shield arduino :
// commande moteur pas pas bipolaire avec Arduino-MotorShield
#define DIRA 12
#define DIRB 13
#define BRAKEA 9
#define BRAKEB 8
#define PWMA 3
#define PWMB 11 #define VAL 100 int etape;
void pas_une_phase(int etape) { // pas One Phase ON
switch (etape) {
case 0 : // 1000
digitalWrite(BRAKEA,LOW); digitalWrite(BRAKEB,HIGH); digitalWrite(DIRA,HIGH); break;
case 1 : // 0010
digitalWrite(BRAKEA,HIGH); digitalWrite(BRAKEB,LOW); digitalWrite(DIRB,HIGH); break;
case 2 : // 0100
digitalWrite(BRAKEA,LOW); digitalWrite(BRAKEB,HIGH); digitalWrite(DIRA,LOW); break;
case 3 : // 0001
digitalWrite(BRAKEA,HIGH); digitalWrite(BRAKEB,LOW); digitalWrite(DIRB,LOW); break;
}
}
void pas_deux_phases(int etape) { // pas One Phase ON
switch (etape) {
case 0 : // 1010
digitalWrite(BRAKEA,LOW); digitalWrite(BRAKEB,LOW); digitalWrite(DIRA,HIGH); digitalWrite(DIRB,HIGH); break;
case 1 : // 0110
digitalWrite(BRAKEA,LOW); digitalWrite(BRAKEB,LOW); digitalWrite(DIRA,LOW); digitalWrite(DIRB,HIGH); break;
case 2 : // 0101
digitalWrite(BRAKEA,LOW); digitalWrite(BRAKEB,LOW); digitalWrite(DIRA,LOW); digitalWrite(DIRB,LOW); break;
case 3 : // 1001
digitalWrite(BRAKEA,LOW); digitalWrite(BRAKEB,LOW); digitalWrite(DIRA,HIGH); digitalWrite(DIRB,LOW); break;
}
}
void pas_demi_pas(int etape) {
switch(etape) {
case 0 : // 1000
digitalWrite(BRAKEA,LOW); digitalWrite(BRAKEB,HIGH); digitalWrite(DIRA,HIGH); break; case 1: // 1010
digitalWrite(BRAKEA,LOW); digitalWrite(BRAKEB,LOW); digitalWrite(DIRA,HIGH); digitalWrite(DIRB,HIGH); break; case 2: // 0010
digitalWrite(BRAKEA,HIGH); digitalWrite(BRAKEB,LOW); digitalWrite(DIRB,HIGH); break; case 3: // 0110
digitalWrite(BRAKEA,LOW); digitalWrite(BRAKEB,LOW); digitalWrite(DIRA,LOW); digitalWrite(DIRB,HIGH); break; case 4: //0100 digitalWrite(BRAKEA,LOW); digitalWrite(BRAKEB,HIGH); digitalWrite(DIRA,LOW); break; case 5: //0101 digitalWrite(BRAKEA,LOW); digitalWrite(BRAKEB,LOW); digitalWrite(DIRA,LOW); digitalWrite(DIRB,LOW);
break; case 6: //0001 digitalWrite(BRAKEA,HIGH); digitalWrite(BRAKEB,LOW); digitalWrite(DIRB,LOW); break; case 7: //1001 digitalWrite(BRAKEA,LOW); digitalWrite(BRAKEB,LOW); digitalWrite(DIRA,HIGH); digitalWrite(DIRB,LOW); break;
}
}
void pas_une_phase_sens_1() { etape++;
if (etape>3) etape = 0; pas_une_phase(etape);
}
void pas_une_phase_sens_2() { etape--; if (etape
}
void pas_deux_phases_sens_1() {
etape++; if (etape>3) etape = 0; pas_deux_phases(etape);
}
void pas_deux_phases_sens_2() {
etape--; if (etape
}
void pas_demi_pas_sens_1() { etape++; if (etape>7) etape = 0; pas_demi_pas(etape);
}
void pas_demi_pas_sens_2() { etape--;
if (etape
}
void pwm(int pwm) { analogWrite(PWMA,pwm); analogWrite(PWMB,pwm);
}
void setup() {
pinMode(DIRA,OUTPUT); pinMode(DIRB,OUTPUT); pinMode(BRAKEA,OUTPUT); pinMode(BRAKEB,OUTPUT); pinMode(PWMA,OUTPUT); pinMode(PWMB,OUTPUT); analogWrite(PWMA,0); analogWrite(PWMB,0);
etape = 0;
}
void loop() {
int k;
unsigned int duree_courant = 10; unsigned long duree_pas = 20; unsigned long temps;
for (k=0; k
temps = millis(); pas_deux_phases_sens_1(); pwm(255); // courant max
while (millis()-temps
// autre chose faire pendant le pas moteur
}
pwm(0); // courant de repos, en fonction de la charge
while (millis()-temps
// autre chose faire pendant le pas moteur
}
}
pwm(0); // courant de repos, en fonction de la charge delay(3000);
}
Le L298 du motor shield arduino ne poss?de pas de radiateur, c’est pourquoi son utilisation pour les moteurs dont le courant est supØrieur 1 A est dØconseillØe (il y a de toute mani?re une coupure thermique dans le L298).
Sur ce shield, les bornes CURRENT SENSING A et B du L298 sont branchØes sur une rØsistance de 1,65 ?, ce qui permet d’obtenir le courant circulant dans les phases A et B (bornes A0 et A1 de l’arduino). Voici l’enregistrement l’oscilloscope des ces tensions pour des pas de durØe 50 ms, avec un moteur consommant 300 mA sous 12 V :
Le mode utilisØ est le mode 1 (une phase alimentØ la fois). Le sens du courant n’appara t pas. On voit que cette durØe de pas est largement su sante pour l’Øtablissement du courant. La che du constructeur donne pour une phase du moteur L = 90 mH et
R =40?, ce qui fait un temps L/R =2,5 ms, en accord avec cet oscillographe. Voici le rØsultat pour une durØe de pas de 20 ms :
Pour une durØe de 10 ms :
Pour une durØe de 5 ms :
Pour ces deux derni?res durØes, le courant maximal n’est pas atteint au cours d’un pas, mais le moteur tourne bien. Dans ce rØgime de fonctionnement, le moteur Ømet un si ement dont la frØquence est celle des pas.
4. RØgulation du courant par hacheur
4.a. Principe
Les enregistrements de courant ci-dessus montrent un inconvØnient du mode de commande prØcØdent : le temps de montØe du courant dans la bobine (rapport L/R) limite l’utilisation haute frØquence (la limitation n’est pas mØcanique). D’autre part, lorsque le courant augmente dans une phase, le courant dans l’autre phase descend zØro trop lentement, ce qui rØduit le couple. Par ailleurs, ce type de commande ne permet pas de rØguler le courant pendant l’impulsion.
Pour diminuer le temps de montØe du courant, on peut ajouter une rØsistance en sØrie avec chaque phase et augmenter la tension d’alimentation. Cependant, cette solution n’est pas e cace car une partie importante de l’Ønergie est dissipØe dans cette rØsistance ajoutØe.
La rØgulation du courant par hachage consiste couper le courant d?s qu’il dØpasse un seuil (rØglable). Le courant est rØtabli peu de temps apr?s la coupure. La base de temps est fournie par un oscillateur dont la frØquence est ajustØe par un choix de R et C. Le pont en H est alimentØ avec une tension beaucoup plus grande que la tension nominale du moteur, par exemple 36 V pour un moteur de 12 V , ce qui permet d’augmenter la pente de montØe du courant. Voici quoi ressemble la courbe de courant avec la rØgulation par dØcoupage :
Les variations du courant pendant le dØcoupage ont un pØriode infØrieure celle de l’oscillateur. Plus l’oscillateur est rapide, plus ces variations ont une amplitude faible. En ajustant le seuil de courant, on peut ainsi rØguler l’intensitØ atteinte pendant l’impulsion de commande. Cela permet de rØduire notablement la dissipation lorsque la charge appliquØe au moteur le permet. Le mŒme dispositif peut d’ailleurs Œtre appliquØ un moteur courant continu pour rØguler le courant.
La commande par dØcoupage permet d’augmenter la frØquence des pas tout en gardant un couple important.
RØfØrences
[1] A. Hughes, B. Drury, Electric motors and drives, (Elsevier, 2013) [2] T. Wildi, Electrotechnique, (DeBoeck UniversitØ, 2000)