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.
- Le moteur utilisé ici est un moteur bipolaire 200 pas (angle pour un pas de 1,8°) NEMA 23: https://www.gotronic.fr/art-moteur-23hs30-2804s-18357.htm
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 :
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 :
//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
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.
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 »
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 :
- Téléverser nanpy sur la carte Arduino, ( pour rappel, si ce n’est pas fait, dans [Croquis][Inclure une librairie][Ajouter la librairie .ZIP], installer la librairie « Nanpy_Arduino.zip » bibliothèque disponible sur ce lien https://drive.google.com/file/d/1ZF6odibIBuOAS1ESFEJPiq33Z3tjzOk0/view ) :
- Exécuter le code ci dessous
######################################### 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.
Lien Github pour ces scripts :
https://github.com/jonasforlot/python-arduino/tree/main/Moteur%20pas%20%C3%A0%20pas