ACTIVITE ARDUINO/PYTHON : Contrôler un moteur pas à pas bipolaire avec Arduino

Objectif : Explorer les différentes options pour contrôler un moteur pas à pas bipolaire. Pour chaque exemple, nous nous limitons à un seul usage (un tour horaire, puis deux tours anti-horaires). Il existe de nombreux articles à ce sujet, celui-ci a pour but de compiler les informations de la manière la plus simple possible, et en essayant de ne pas avoir recours aux bibliothèques.

Pour comprendre le fonctionnement d’un moteur pas à pas, quelques explications sur ce site : https://electrotoile.eu/moteur-pas-a-pas-sequence-commande-arduino.php

Un moteur bipolaire est constitué d’un aimant permanent (rotor) qui tourne d’un pas sous l’action de deux paires de bobines (stator) reliées entre elles de part et d’autre du rotor (il n’y a donc finalement que deux bobines car une paire de bobines reliées en série correspond à une seule bobine !).

Sans rentrer dans les détails (que je ne maîtrise pas !), voici les 4 étapes pour faire tourner le moteur pas par pas :

Chacune de ces étapes permet de faire tourner le moteur d’un pas. Pour un moteur 200 pas, il faudrait donc répéter cette opération 50 fois.

Pour ce moteur NEMA, l’association des bobines A/C correspond aux fils bleu et rouge et celle des bobines B/D aux fils noir et vert.

Il est facile d’identifier les paires de bobines à l’aide d’un ohmmètre (une valeur de quelques ohms devrait s’afficher si on relie l’ohmmètre aux deux fils d’une même paire de bobines).

Nous allons utiliser plusieurs types de shields disponibles dans le commerce. Un shield est une extension de la carte Arduino (que l’on branche par dessus la carte). Il présente l’avantage de ne pas avoir besoin de tout « câbler à la main ». Les composants sont déjà assemblés entre eux et connectés aux pins de la carte Arduino.

Remarque

Pour chacune des applications dans cet article, nous avons besoin d’une source d’alimentation externe car l’alimentation de la carte Arduino ne suffit pas (manque de puissance).

Nous utilisons ici une alimentation stabilisée ELC

1. Utilisation d’un driver L293 seul sans shield

Tout d’abord, nous allons utilisons un driver sans shield (circuit intégré qui permet d’alimenter et de contrôler les deux bobines avec deux ponts en H). Cela nécessite plus de câblage, mais ce type de montage peut permettre plus de souplesse dans certains cas.

Voici le montage :

Montage pour un seul moteur pas à pas L293D seul (sans shield)
const int pinBobine1A = 8 ;
const int pinBobine1C = 9 ;
const int pinBobine2B = 10 ;
const int pinBobine2D = 11 ;


float Tps =8;
int Tour=0; // Déclaration variable nombre de tour

void setup() {
    pinMode(pinBobine1A, OUTPUT); // 
    pinMode(pinBobine1C, OUTPUT); // 
    pinMode(pinBobine2B, OUTPUT); // 
    pinMode(pinBobine2D, OUTPUT); //

  

    Serial.begin(9600);
}




void loop(){
    sens_normal();
    stop();
   delay(100);
    sens_inverse();
   delay(100);
    sens_inverse();
   delay(100);
  


 
}

void sens_normal(){
    for (Tour = 0; Tour < 50; Tour++){   // Boucle pour faire 1 tour complet (Moteur 200 pas/4 = 50)
       // Commande moteur pas à pas Bipolaire 4 fils en Mode Wave | Sens Normal
       // Pas n°1 | 
       digitalWrite(pinBobine1A, LOW);
       digitalWrite(pinBobine1C, HIGH);  
       digitalWrite(pinBobine2B, LOW);
       digitalWrite(pinBobine2D, LOW);

       delay(Tps);
  
      // Pas n°2 | 
       digitalWrite(pinBobine1A, LOW);
       digitalWrite(pinBobine1C, LOW);   
       digitalWrite(pinBobine2B, LOW);
       digitalWrite(pinBobine2D, HIGH);
       delay(Tps); 

       // Pas n°3 | 
       digitalWrite(pinBobine1A, HIGH);
       digitalWrite(pinBobine1C, LOW);  
       digitalWrite(pinBobine2B, LOW);
       digitalWrite(pinBobine2D, LOW);
       delay(Tps); 

       // Pas n°4 | 
       digitalWrite(pinBobine1A, LOW);
       digitalWrite(pinBobine1C, LOW);   
       digitalWrite(pinBobine2B, HIGH);
       digitalWrite(pinBobine2D, LOW);
       delay(Tps); 
        }

  
}


void sens_inverse(){
    for (Tour = 0; Tour < 50; Tour++){ // Boucle pour faire 1 tour complet (Moteur 200 pas/4 = 50)
      // Commande moteur pas à pas Bipolaire 4 fils en Mode Wave | Sens Inverse
      // Pas n°1 |
      digitalWrite(pinBobine1A, LOW);
      digitalWrite(pinBobine1C, LOW);   
      digitalWrite(pinBobine2B, HIGH);
      digitalWrite(pinBobine2D, LOW);
      delay(Tps); 


      // Pas n°2 |
      digitalWrite(pinBobine1A, HIGH);
      digitalWrite(pinBobine1C, LOW);  
      digitalWrite(pinBobine2B, LOW);
      digitalWrite(pinBobine2D, LOW);
      delay(Tps); 

      // Pas n°3 |
      digitalWrite(pinBobine1A, LOW);
      digitalWrite(pinBobine1C, LOW);   
      digitalWrite(pinBobine2B, LOW);
      digitalWrite(pinBobine2D, HIGH);
      delay(Tps); 

      // Pas n°4 |
      digitalWrite(pinBobine1A, LOW);
      digitalWrite(pinBobine1C, HIGH);  
      digitalWrite(pinBobine2B, LOW);
      digitalWrite(pinBobine2D, LOW);
      delay(Tps); 

      delay(Tps);
  




        }
  
}

void stop(){
    digitalWrite(pinBobine1A, LOW);
     digitalWrite(pinBobine1C, LOW);  
     digitalWrite(pinBobine2B, LOW);
     digitalWrite(pinBobine2D, LOW);
}

  

2. Utilisation d’un motorshield L298P

L’Arduino Motor Shield Rev3 dispose d’un circuit intégré L298P qui est équipé de ponts en H. Ce composant permet d’inverser les tensions au bornes des bobines et de supporter des tensions et courants élevés. On peut utiliser ce type de driver pour piloter un moteur pas à pas ou aussi un moteur à courant continu.

Voici le montage :

Montage pour un seul moteur pas à pas L298P motorshield
//Parameters
 const int directionA  = 12;
 const int directionB  = 13;
 const int rateA  = 3;
 const int rateB  = 11;
 int millisBtwnSteps = 2000;
 int rate =255 ;
 void setup() {
   //Init Serial USB
   Serial.begin(9600);
   Serial.println(F("Initialize System"));
   //Init Motor Shield
   pinMode(directionA, OUTPUT); //Initiates Motor Channel A pin
   pinMode(directionB, OUTPUT); //Initiates Motor Channel B pin
 }
 void loop() {
 testStepperMS();
 }
 void testStepperMS() { /* function testStepperMS */
   //// Test stepper
   Serial.println("Move stepper 1 step clockwise");
   stpCW(50);
   delay(1000);
   Serial.println("Move stepper 2 step counter clockwise");
   stpCCW(100);
   delay(1000);
 }
 void stpCW(int nbstep) { /* function stpCW */
   //// Move stepper clockwise
   for (int i = 0; i < nbstep; i++) { 
     digitalWrite(directionA, HIGH);  //Set direction of CH A
     analogWrite(rateA, 255);   
     analogWrite(rateB, 0);   
     delayMicroseconds(millisBtwnSteps); 

     digitalWrite(directionB, LOW);//Set direction of CH B
     analogWrite(rateB, 255);   
     analogWrite(rateA, 0);   
     delayMicroseconds(millisBtwnSteps);

     digitalWrite(directionA, LOW);   //Set direction of CH A
     analogWrite(rateA, 255);   
     analogWrite(rateB, 0);   
     delayMicroseconds(millisBtwnSteps); 

     digitalWrite(directionB, HIGH);   //Set direction of CH B
     analogWrite(rateB, 255);   
     analogWrite(rateA, 0);   
     delayMicroseconds(millisBtwnSteps);
 
 }
 }
 void stpCCW(int nbstep) { /* function stpCCW */
   //// Move stepper counter-clockwise
   for (int i = 0; i < nbstep; i++) {

     digitalWrite(directionA, HIGH);   //Set direction of CH A
     analogWrite(rateA, 255);   
     analogWrite(rateB, 0);   
     delayMicroseconds(millisBtwnSteps);

     digitalWrite(directionB, HIGH);   //Set direction of CH B
     analogWrite(rateB, 255);   
     analogWrite(rateA, 0);   
     delayMicroseconds(millisBtwnSteps); 

      digitalWrite(directionA, LOW);   //Set direction of CH A
     analogWrite(rateA, 255);   
     analogWrite(rateB, 0);   
     delayMicroseconds(millisBtwnSteps);

     digitalWrite(directionB, LOW);   //Set direction of CH B
     analogWrite(rateB, 255);   
     analogWrite(rateA, 0);   
     delayMicroseconds(millisBtwnSteps);

 }
 }

3. Utilisation d’un motorshield CNC Shield V3

Le CNC Shield V3 est une autre carte d’extension pour Arduino UNO ou Mega qui offre la particularité de pouvoir contrôler 4 moteurs pas-à-pas, en branchant par dessus des drivers de type A4988.

https://www.gotronic.fr/art-shield-cnc-pour-uno-ard-cnc-k1-27753.htm

Montage pour un seul moteur pas à pas (sur l’emplacement Y)

Voici le code Arduino :

const int enPin=8;
 const int stepPin = 3; //Y.STEP
 const int dirPin = 6; // Y.DIR
 const int stepsPerRev=200;
 int pulseWidthMicros = 100;   // microseconds
 int millisBtwnSteps = 1000;
 void setup() {
   Serial.begin(9600);
   pinMode(enPin, OUTPUT);
   digitalWrite(enPin, LOW);
   pinMode(stepPin, OUTPUT);
   pinMode(dirPin, OUTPUT);
   Serial.println(F("CNC Shield Initialized"));
 }
 void loop() {
   Serial.println(F("Running clockwise"));
   digitalWrite(dirPin, HIGH); // Enables the motor to move in a particular direction
   // Makes 200 pulses for making one full cycle rotation
   for (int i = 0; i < stepsPerRev; i++) {
       digitalWrite(stepPin, HIGH);
       delayMicroseconds(pulseWidthMicros);
       digitalWrite(stepPin, LOW);
       delayMicroseconds(millisBtwnSteps);
   }
   delay(1000); // One second delay
   Serial.println(F("Running counter-clockwise"));
   digitalWrite(dirPin, LOW); //Changes the rotations direction
   // Makes 400 pulses for making two full cycle rotation
   for (int i = 0; i < 3*stepsPerRev; i++) {
       digitalWrite(stepPin, HIGH);
       delayMicroseconds(pulseWidthMicros);
       digitalWrite(stepPin, LOW);
       delayMicroseconds(millisBtwnSteps);
   }
   delay(1000);
 }

Remarque

Il est possible d’améliorer la résolution du moteur au 1/2, 1/4, 1/8 ou 1/16 en connectant des cavaliers (jumpers) sur les broches M0, M1 et M2. Ceci peut être utile pour des mouvements à faible vitesse. Pour un tour complet, il faudrait penser à multiplier le nombre d’étapes par le facteur de résolution (2, 4, 8 ou 16) dans les boucles for.

Tableau de positionnement des jumpers pour les différentes résolutions

4. Utilisation d’un motorshield Arduino/Velleman L293D

Le Motorshield L293D est une autre carte d’extension pour Arduino UNO ou Mega, il dispose de deux ponts en H L293D et d’un registre à décalage (ce composant permet d’augmenter le nombre d’entrées/sorties de la carte d’Arduino)

https://www.velleman.eu/products/view?id=439178&country=be&lang=fr

Remarque

Il est plus aisé d’utiliser une bibliothèque pour ce shield car l’utilisation du registre à décalage complexifie le code. Nous utiliserons ici la bibliothèque d’Adafruit AFMotor.h, disponible ici

Procédure d’installation de la bibliothèque :

– Dans la page Github télécharger le fichier dans Code / Download ZIP.

– Puis dans l’application Arduino, à partir du menu [Croquis][Inclure une librairie][Ajouter la librairie .ZIP], installer la librairie «Adafruit-Motor-Shield-library-master.zip »

Montage pour un seul moteur pas à pas L293D motorshield

Voici le code Arduino :

// Adafruit Motor shield library
 // copyright Adafruit Industries LLC, 2009
 // this code is public domain, enjoy!
# include <AFMotor.h>
 // Connect a stepper motor with 200 steps per revolution (1.8 degree)
 // to motor port #2 (M3 and M4)
 AF_Stepper motor(200, 2);
 void setup() {
   Serial.begin(9600);           // set up Serial library at 9600 bps
   Serial.println("Stepper test!");
 motor.setSpeed(100);  // 100 rpm   
 }
 void loop() {
   Serial.println("Running clockwise");
   motor.step(200, FORWARD, SINGLE); 
   delay(1000);
 Serial.println("Running counter-clockwise");
   motor.step(400, BACKWARD, SINGLE); 
   delay(1000);
 }

BONUS : Programmation avec Python du motorshield CNC Shield V3

Vous trouverez ci-dessous un code Python permettant de faire tourner le moteur pas à pas avec le shield cnc. Pour cela, il faut que la bibliothèque Nanpy soit téléversée préalablement sur la carte Arduino (pour plus de précisions sur la programmation de la carte Arduino en Python, voir l’article Programmer-en-python-pour-la-carte-arduino)

Donc, pour exécuter ce code avec Python, il faut donc :



 #########################################  IMPORTATION DES BIBLIOTHEQUES ET MODULES  #############################################################################
 import serial
 import serial.tools.list_ports # pour la communication avec le port série
 from nanpy import ArduinoApi    # importation des bibliothèques pour communication avec Arduino
 from nanpy import SerialManager
 from nanpy import Servo  # pour utiliser le servomoteur
 from time import sleep    # pour faire des "pauses" dans l'exécution du programme
 

 #########################################  COMMUNICATION AVEC CARTE ARDUINO ET DEFINITION DES BROCHES ET VARIABLES  #######################################################
 # Fonction pour la récupération du port COM venant de la carte Arduino
 def recup_port_Arduino() :
     ports = list(serial.tools.list_ports.comports())
     for p in ports:
         if 'Arduino' in p.description or  'CDC'in p.description or 'USB' in p.description :
             mData = serial.Serial(p.device,9600)
     print(mData.is_open) # Affiche et vérifie que le port est ouvert
     return (mData.name) # Retourne le nom du port
 

 port = recup_port_Arduino()
 connection = SerialManager(device=port) #indiquer le bon port de la carte Arduino
 

 a = ArduinoApi(connection=connection) #connection à la carte Arduino, on précédera chaque instruction Arduino par a. (exemple a.pinMode(2,a.OUTPUT)
 

 

 

 

 

 #########################################   CODE ARDUINO  EN LANGAGE PYTHON    #################################################################################
 stepPin = 3 #Y.STEP
 dirPin = 6 # Y.DIR
 enPin=8
 stepsPerRev=200
 

 

 

 

 a.pinMode(stepPin, a.OUTPUT)
 a.pinMode(dirPin, a.OUTPUT)
 a.pinMode(enPin, a.OUTPUT)
 a.digitalWrite(enPin, a.LOW)
 print("CNC Shield Initialized")
 

 while True:
     print("Running clockwise")
     a.digitalWrite(6, a.HIGH) # Enables the motor to move in a particular direction
     #Makes 200 pulses for making one full cycle rotation
 

     for i in range (int(stepsPerRev/2)):
         a.analogWrite(stepPin, 25)
 

 

     a.analogWrite(stepPin, 0)
     sleep(1)#One second delay
     print("Running counter_clockwise")
     a.digitalWrite(dirPin, a.LOW) #Changes the rotations direction
 

     a.analogWrite(stepPin, 0)
 

     # #Makes 400 pulses for making two full cycle rotation
     for i in range(stepsPerRev):
         a.analogWrite(stepPin, 25)
 

     a.analogWrite(stepPin, 0)
     sleep(1) #One second delay
 


Quelques remarques … car j’ai eu des difficultés à obtenir des résultats !

Les contrôles à l’oscilloscope m’ont permis d’y voir plus clair dans les défauts :


– Je n’arrive pas à obtenir une vitesse élevée car en passant par nanpy, les périodes des impulsions n’arrivent pas à descendre en dessous de 30 ms ! On pourrait se contenter d’une vitesse lente mais le moteur émet un bruit désagréable. Pour pallier ce problème de bruit, il faudrait changer la résolution du moteur, c’est possible en ajoutant des cavaliers sur M0, M1 et M2 (voir tableau dans la section 2)

– J’ai trouvé une solution alternative pour retrouver de la vitesse : utiliser l’analogWrite au lieu du digitalWrite. On pourrait choisir un PWM avec un rapport cyclique de 10% (analogWrite réglé à 25). L’avantage du PWM est qu’il est réglé par défaut à environ 500 Hz, donc une période de 2 ms, ce qui permet de faire tourner assez vite le moteur … mais on perd le contrôle de la vitesse :-(. A priori la fréquence du PWM peut se régler mais ça a l’air un peu compliqué…

– Je me suis rendu compte (toujours à l’oscilloscope) que lorsqu’on demande une impulsion PWM, deux sont envoyées ! Donc il faut prendre ce facteur 2 en compte dans le script.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *