Piloter un moteur pas à pas Nema 17 avec la librairie RpiMotorLib Python pour A4988 • Domotique et objets connectés à faire soi-même

Code source

La librairie RpiMotorLib pour Python 3 permet de piloter très simplement les moteurs pas à pas (pilote ULN2003,  L298N, A4988, DRV8825, A3967 ou TB6612FNG) et les moteurs à courant continu via le GPIO du Raspberry Pi. Les moteurs pas à pas sont très largement utilisés pour réaliser les mouvements des axes des imprimantes 3D, des graveurs laser et commandes numériques (CNC).

Pour ce tutoriel, nous allons utiliser un moteur pas à pas Nema 17 (précisément 17HS4401) piloté par un micro-contrôleur A4988, deux composants très largement employés dans les imprimantes 3D et CNC à assembler soi-même.

Installer les librairies RpiMotorLib et Flask

Pour pouvoir fonctionner, la librairie RpiMotorLib nécessite Python 3.5 ou supérieur. La version Desktop de Raspberry Pi OS est installée avec les versions 2.7 et 3.7 ou ultérieures. Pour vérifier que la version 3 est bien installée, exécutez la commande suivante dans un Terminal.

Si Python3 est correctement installé, vous devez obtenir le numéro de version en réponse.

python3 --version
Python 3.7.3

Pour installer les librairies Python, on aura besoin du script pip3. Pour vérifier qu’il est bien installé, exécuter cette commande qui doit vous renvoyer le chemin vers le script

pip3 --version

Si pip3 n’est pas installé, exécuter

sudo apt install python3-pip

Maintenant, on peut installer les librairies

sudo pip3 install rpimotorlib
python3 -m pip install Flask

Que peut-on piloter avec la librairie RpiMotorLib ?

La librairie développée par Gavin Lyons permet de piloter des moteurs pas à pas (stepper motors), des servo-moteurs et moteurs à courant continu. Pour chaque type de mouvement, plusieurs contrôleurs sont pris en charge. Voici la liste avec le renvoi vers la documentation en ligne pour le câblage et un petit exemple de code.

Moteurs pas à pas (Stepper motors)

Moteurs à courant continu (DC Motor)

Contrôleurs supportés par la librairie pour piloter les moteurs à courant continu

Liste des fonctions proposées par la librairie

Les fonctions proposées par la librairie varient en fonction de contrôleur utilisé et du type de moteur.

Classe Fonction Paramètres
Code source du fichier RpiMotorLib.py
BYJMotor print_cursor_spin Fonction dépréciée
motor_run(gpiopins, wait=.001, steps=512, ccwise=False, verbose=False, steptype=”half”, initdelay=.001
)
GPIOPins type liste de 4 entiers longs. Broches du GPIO sur lesquels sont connectées le contrôleur.

wait type float, temps d’attente (en secondes) entre chaque pas (steps)

steps. type int. Nombre de séquences d’étapes exécuter. La valeur par défaut est une révolution, 512 pour le 28BYJ-48.

ccwise sens antihoraire par défaut. True pour inverser le sens

verbose True pour activer la mise au point

steptype type string. half par défaut. Type de pilote. 3 options : full (fullstep), half (half step), wave (wave drive)

initdelay type float, 1ms par défaut. Délai d’attente après l’initialisation avant de lancer le mouvement.

A4988

Contrôleurs supportés

A4988

DRV8825

motor_go(clockwise=False, steptype=“Full”,steps=200, stepdelay=.005, verbose=False, initdelay=.05) clockwise sens de rotation dans le sens des aiguilles d’une montre par défaut. True pour inverser

steptype chaîne, par défaut Full. Type de moteur pas à pas. 5 options Full, Half, 1/4, 1/8, 1/16, 1/32

steps entier. Nombre de pas à exécuter, 200 par défaut

stepdelay type float, par défault 0.05 seconde. Temps d’attente entre chaque pas.

verbose True pour activer les messages de mise au point

initdelay type float, 0.05 s par défaut. Délai d’attente après l’initialisation avant de lancer le mouvement.

Code source du fichier rpi_dc_lib.py
L298NMDc

DRV8833NmDc

initialisation(pin_one, pin_two, freq=50, verbose=False, name=”DCMotorY”) pin_one type int, broche de direction connectée à IN1 ou IN3

pin_two type  int, broche PWM du GPIO pour la vitesse connectée à IN2 ou IN4

Freq fréquence en Hz par défaut 50

verbose True pour activer les messages de mise au point

Name aucune utilité

forward(duty_cycle=50) Marche avant
backward(duty_cycle=50) Marche arrière
stop(duty_cycle=0) Arrête le moteur
brake(duty_cycle=100) Freine le moteur
cleanup(clean_up=False) Libère le GPIO. A utiliser en cas de problème de communication
TranDc initialisation(pin, freq=50, verbose=False) Classe pour contrôler un moteur à courant continu via un transistor

pin type  int, broche PWM du GPIO pour la vitesse connectée à IN2 ou IN4

Freq fréquence en Hz par défaut 50

verbose True pour activer les messages de mise au point

TB6612FNGDc initialisation(pin_one, pin_two, pwm_pin, freq=50, verbose=False, name=”DCMotorY”) pin_one Broche GPIO connectée à AI1 ou BI1

pin_two Broche GPIO connectée à AI2 ou BI2

pwm_pin Broche GPIO connectée à PWA ou PWB

Freq fréquence en Hz par défaut 50

verbose True pour activer les messages de mise au point

Name aucune utilité

standby(standby_pin, standby_on=True) standby_pin Broche GPIO connectée à la broche standby du TB661FNG

Active / désactive le mode veille du contrôleur TB661FNG

forward(duty_cycle=50) Marche avant
backward(duty_cycle=50) Marche arrière
stop(duty_cycle=0) Arrête le moteur
brake(duty_cycle=100) Freine le moteur
cleanup(clean_up=False) Libère le GPIO. A utiliser en cas de problème de communication

Remarque concernant l’arrêt des mouvements des moteurs pas à pas

Le librairie RpiMotorLib n’offre aucune méthode pour stopper le mouvement en cours un pour les moteurs pas à pas.

On ne pourra pas arrêter en cas d’urgence ou manuellement les mouvements gérés par les micro-contrôleurs BYJMotor, A4988 et DRV8825.

Attention également à ne pas débrancher les broches durant un mouvement. Cela peut endommager le GPIO du Raspberry Pi ou du micro-contrôleur.

Circuit

Le GPIO du Raspberry Pi (quelque soit le modèle et la génération) ne délivre pas suffisamment de puissance pour alimenter directement un moteur Nema (ou n’importe quel autre moteur d’ailleurs).

La contrôleur A4988 dispose d’une entrée 12V qu’il suffira d’alimenter à l’aide d’une batterie ou sur le secteur. L’A4988 doit également être alimenté en 5V. Si vous n’avez qu’un seul accessoire connecté au GPIO, le Raspberry Pi peut délivrer suffisamment de puissance.

Il existe des cartes d’alimentation multi-tension permettant de délivrer suffisamment de puissance pour ce type de montage.

N’oubliez pas de connecter les broches Reset et Sleep à l’aide d’un jumper sinon le micro-contrôleur reste en veille. La librairie RpiMotorLib ne permet pas de gérer la sortie de veille uniquement lorsqu’un mouvement est demandé. Cela signifie que le moteur reste sous tension en permanence.

Voici le repérage des broches en détail

Alimentation 12V Broche A4988
+12V de la batterie ou de l’alimentation secteur VMOT
GND de la batterie ou de l’alimentation secteur GND
GPIO du Raspberry Pi (toute version)
5V du Raspberry Pi VDD
GND du Raspberry Pi GND
GPIO 21 STP
GPIO 20 DIR
Vers le moteur pas à pas

Généralement le moteur est livré avec un câble 4 broches  muni d’un connecteur au pas de 2,54mm. Si le déplacement est inversé, inverser le connecteur ou modifiez le code Python

1A, 1B, 2A, 2B
GPIO 14 MS1
GPIO 15 MS2
GPIO 18 MS3
Connecter les broches RESET et SLEEP ensembles

Schéma de câblage du circuit

Exemple de projet Python permettant de déplacer un Moteur Nema 17

Ouvrez l’éditeur de code Thonny Python IDE accessible depuis le menu Programmation. 

Coller le code Python ci-dessous

#! /usr/bin/python
# -*- coding:utf-8 -*-
from RpiMotorLib import RpiMotorLib
from flask import Flask, render_template_string, redirect, request

#define GPIO pins
GPIO_pins = (14, 15, 18)    # Microstep Resolution MS1-MS3 -> GPIO Pin
direction= 20               # Direction Pin, 
step = 21                   # Step Pin
distance = 80               # Default move 1mm => 80 steps per mm


# Declare an named instance of class pass GPIO pins numbers
mymotortest = RpiMotorLib.A4988Nema(direction, step, GPIO_pins, "A4988")

app = Flask(__name__)

#HTML Code 

TPL = '''

    
        
        

        
        

        Stepper Motor Controller
    
    
      
        
          
            
                Up
            
          
          
            
                Down
            
           
           
            
                
                  0.1 mm
                  1 mm
                  10 mm
                
              
        
         
    


'''
 
@app.route("/")
def home():
    return render_template_string(TPL)

@app.route("/setdistance", methods=["POST"])
def setdistance():
    global distance
    global distance
    distance = int(request.form["distance"])
    print("set distance to", distance)
    return redirect(request.referrer)
     
@app.route("/up", methods=["POST"])
def up():
    global distance
    print("Move up,", distance, "steps")
    mymotortest.motor_go(False, "Full" , distance, 0.01 , False, .05)
    return redirect(request.referrer)

@app.route("/down", methods=["POST"])
def down():
    global distance
    print("Move down,", distance, "steps")
    mymotortest.motor_go(True, "Full" , distance, 0.01 , False, .05)
    return redirect(request.referrer)
 
# Run the app on the local development server
if __name__ == "__main__":
    app.run()

Cliquer sur la flèche verte pour lancer le script

Ouvrir Chromium et saisir l’adresse http://127.0.0.1:5000 dans la barre d’adresse.

Utiliser le sélecteur pour choisir le déplacement à effectuer : 0.1mm, 1mm ou 10mm

Puis la direction du mouvement :

  • UP pour  monter
  • DOWN pour descendre

Voici une petite vidéo de démonstration

https://projetsdiy.fr/data/uploads/2020/10/piloter-stepper-motor-nema-a4988-raspberry-pi.mp4?_=1

Comme nous l’avons vu au paragraphe précédent, on doit attribuer 5 broches au micro-contrôleur A4988. MS1, MS2, MS3, step et inversion de direction.

On créé ensuite un objet RpiMotorLib.A4988Nema que l’on pourra ensuite utiliser dans le code du projet

GPIO_pins = (14, 15, 18)    # Microstep Resolution MS1-MS3 -> GPIO Pin
direction= 20               # Direction Pin,
step = 21 # Step Pin 
mymotortest = RpiMotorLib.A4988Nema(direction, step, GPIO_pins, "A4988")

Explication du code

Nous allons créer une interface Web pour piloter le moteur Nema à l’aide de la librairie Flask.

from flask import Flask, render_template_string, redirect, request

Ici la page HTML est stocké directement dans la chaîne TPL. On importe la feuille de style Bootstrap 4.0 directement depuis internet. Pour changer de version, rendez-vous ici.




Avec Flask, il suffit de quelques lignes de code pour créer un serveur Web

app = Flask(__name__)
if __name__ == "__main__":
  app.run()

Comme la plupart des framework HTML, Flask gère des points de sortie (endpoint), l’adresse de la page Web. Par défaut lorsqu’on arrive sur un site internet (la racine), la page se par /

On créé donc une route pour chaque page et chaque action soumise submit par un formulaire.

Le décorateur @ permet de spécifier un endpoint et la fonction (immédiatement après) à exécuter dès que la page est appelée par l’utilisateur. La méthode home renvoie la page principale dont le code source est stocké dans la chaine TPL au début du projet.

@app.route("/")
def home():
    return render_template_string(TPL)

Le rendu des pages Web peut être fait de 3 manières

  • Renvoyer directement le code HTML sous la forme d’une chaîne.
return "

Hello World

"
  • Le code HTML est stocké dans une chaîne de caractères.  Dans ce cas, on utilise la méthode render_template_string pour réaliser le rendu. C’est la solution utilisée dans le projet.
  • A partir d’un fichier HTML stocké dans un fichier séparé à l’aide de la méthode render_template

On envoi les commandes au code Python à l’aide de requêtes HTTP de type POST. Pour cela, il suffit de placer les commandes (les boutons en l’occurence) dans un Formulaire.

Ici, le code du bouton UP qui permet de lancer le déplacement du moteur vers le haut.

  Up

Lorsqu’on souhaite envoyer des paramètres au serveur, on peut par exemple utiliser l’argument value.

La méthode request.form([“name”]) permet de récupérer la valeur de l’argument coté serveur

distance = int(request.form["distance"])

On créé une nouvelle route /up qui accepte la méthode HTTP Post.

La distance à parcourir est stockée dans un variable globale distance. Pour récupérer la valeur de celle-ci dans la méthode up(), il faut faire précédé le nom de la variable par l’argument global afin d’indiquer à l’interpréteur Python qu’on souhaite accéder à la variable globale.

On utilise la méthode motor_go de la librairie RpiMotorLib pour exécuter le déplacement.  Voici les paramètres nécessaires

  • False indique le sens de rotation
  • “Full” type de moteur
  • Distance nombre de pas (step) à faire
  • 0.01 délai d’attente entre chaque pas
  • False exécution silencieuse de la fonction
  • 0.05 secondes délai d’attente avant de lancer le mouvement

Inutile ici de redessiner la page HTML, on renvoi simplement l’utilisateur sur la page d’origine à l’aide de la méthode redirect(request.referrer).

@app.route("/up", methods=["POST"])
def up():
    global distance
    print("Move up,", distance, "steps")
    mymotortest.motor_go(False, "Full" , distance, 0.01 , False, .05)
    return redirect(request.referrer)

Comment calculer le nombre de pas par millimètre parcouru ?

C’est un problème récurrent en impression 3D et CNC. Heureusement, on trouve de nombreux outils pour nous aider. Tout d’abord ce résumé posté par J-Max sur le forum du site lesimprimantes3d.fr nous donne quelques bons conseils

  • Transmission directe à courroie : steps_per_mm = (pas_moteur_par_tour * nb_de_micropas_driver) / (pas_courroie * nombre_de_dents_de_la_poulie)
  • Transmission directe à vis : steps_per_mm = (pas_moteur_par_tour * nb_de_micropas_driver) / pas_du_filetage
  • Extrudeur à entrainement direct (direct drive) : steps_per_mm = (pas_moteur_par_tour * nb_de_micropas_driver) / (diamètre_effectif_galet * pi)
  • Extrudeur à réduction (geared) : steps_per_mm = (pas_moteur_par_tour * nb_de_micropas_driver) * (Nb_dents_plateau / nb_dents_pignon) / (diamètre_effectif_galet * pi)

Vous pouvez également utiliser ces calculateurs en ligne proposés par Prusa, le fabricant d’imprimantes 3D Espagnol bien connu

Mises à jour

20/10/2020 Publication de l’article

English version

Avez-vous aimé cet article ?

[Total: 0 Moyenne: 0]