Programme de commande de moteur pas à pas avec accélération

Bonjour à tous,

Je dois commander un moteur pas à pas de taille Nema 23 (200 pas) avec un driver MS542. Le driver est configuré en 400 pas
Il s'agit d'une simple rotation avec une variation de vitesse.

J'ai fait des essais avec AccelStepper et avec le programme ConstantSpeed.pde
Si j'ai bien compris, avec AccelStepper et une vitesse constante, il n'est pas possible de gérer les accélérations --> commande "stepper.runSpeed();"
Les accélérations sont gérées dans le cas ou la distance entre en jeux et non la vitesse --> commande "stepper.run();"

Finalement, j'ai fait un petit programme avec une boucle.

J'ai une variable de vitesse de démarrage "VitesseDemar1" qui est placée dans le loop.
A chaque passage de la boucle, la variable augmente d'un pas "PasAccel1" jusqu'à atteindre la vitesse de marche du moteur "VitesseMax1"

Ce programme simple fonctionne parfaitement, mon moteur se lance et accélère sans problème.
Reste à faire la même chose pour la décélération.

Qu'en pensez-vous ?

const int Pul1 = 26; // pas du moteur
const int Dir1 = 28; // direction du moteur
int CapteurMarche = 1; // interuuption N°1 --> pin N°3 mega 2560
int CapteurArret = 2; // interuuption N°2 --> pin N°21  mega 2560
const int Enable = 30; // enable driver

int pulseWidthMicros1 = 5;  // variable delaymicroseconds
float VitesseMax1 = 400 ;// variable delaymicroseconds -- >  pour la vitesse de fonctionnement maximun. Plus la valeur est grande, plus rapide est la vitesse
float VitesseDemar1 = 1000; // variable delaymicroseconds --> pour la vitesse de démarrage
float Vitesse1 = VitesseDemar1; // varable utilisée dans la fonction "moteur1"
float PasAccel1 = 0.7;  // pas pour l'acceleration
volatile int VolatileMarche = LOW; // varle volatile utilisée dans l'interruption



void setup() 
{ 
  Serial.begin(9600);

  pinMode(Dir1, OUTPUT);
  pinMode(Pul1, OUTPUT);
  pinMode(Enable, OUTPUT); 
  digitalWrite(Dir1, HIGH);
  digitalWrite(Enable, HIGH); // desactive enable

}

void loop() 
{ 
   attachInterrupt(CapteurMarche,CycleMarche, CHANGE); // interruption bouton marche
   attachInterrupt(CapteurArret,CycleArret, CHANGE); // interruption bouton arret 
   
   
   if (VolatileMarche  == HIGH) // test si l'interruption est activée
     {
        if (Vitesse1 < VitesseDemar1) // test si la variable vitesse est < que la vitesse de démarrage
         { 
            VitesseDemar1 = VitesseDemar1 - PasAccel1; // ajustement à chaque boucle de la vitesse
            Vitesse1 = VitesseDemar1; // renseigne la vitesse pour la fonction Moteur1
            Moteur1(); // lance la fonction qui fait tourner le moteur
          }
  else
     {
       Vitesse1 = VitesseMax1; // dans ce cas la vitesse du moteur = la vitesse maximum
       Moteur1();// lance la fonction qui fait tourner le moteur
      }  
   } 
}



void CycleMarche() // fonction interruption
{
  Serial.println("Moteur marche"); // message
  VolatileMarche = HIGH ; // active la variable
  digitalWrite(Enable, LOW); // active enable
}



void CycleArret() // fonction interruption
{
  Serial.println("Moteur arret"); // message
  VolatileMarche = LOW ; // désactive la variable
  digitalWrite(Enable, HIGH); // desactive enable
  digitalWrite(Pul1, LOW); // desactive la sortie Pul du driver
  VitesseDemar1 = 1000;  // recharge la variable de vitesse de démarrage
}


void Moteur1()
{
    digitalWrite(Pul1, HIGH); // Active la sortie Pul du driver
    delayMicroseconds(pulseWidthMicros1); // pause en Microsecondes
    digitalWrite(Pul1, LOW);  // desactive la sortie Pul du driver
    delayMicroseconds(Vitesse1); // pause en Microsecondes
}

le rapport cyclique devrai etre voisin de 50%, mais tu gere la vitesse en jouant sur la durée de l'état bas.

tu devrai plutot adapter genre

void Moteur1()
{
    pulseWidthMicros1=Vitesse1/2;
    digitalWrite(Pul1, HIGH); // Active la sortie Pul du driver
    delayMicroseconds(pulseWidthMicros1); // pause en Microsecondes
    digitalWrite(Pul1, LOW);  // desactive la sortie Pul du driver
    delayMicroseconds(pulseWidthMicros1); // pause en Microsecondes
}

en fonction de la vitesse du moteur et de ton code, tu risque d'avoir quand meme des dérapages car cette fonction n'est pas elle meme cyclique a la vitesse du moteur.

Bonsoir Jean,

Si j'ai bien compris tes explications, je fais :

void Moteur1()
{
    pulseWidthMicros1  = Vitesse1 *0.5;
    digitalWrite(Pul1, HIGH); // Active la sortie Pul du driver
    delayMicroseconds(pulseWidthMicros1); // pause en Microsecondes
    digitalWrite(Pul1, LOW);  // desactive la sortie Pul du driver
    delayMicroseconds(pulseWidthMicros1); // pause en Microsecondes
}

Bien je n'ai aucun changement de vitesse ou dans le comportement du moteur ?

@+ Pierre

Bonjour a tous,
Pierre

  • il faut utiliser les balises codes (#) pour inserer du ... code et pas les balises "quote".
  • les drivers comme ceux que tu utilises sont commandes par des impulsions. Le MS542 a besoin d'une impulsion de plus de 1,5uS. Il n'est nulle part question d'un rapport cyclique de 50%.
  • tu es tres confiant de demarrer un moteur pas-a-pas dans une application PROFESSIONNELLE qui fonctionne dans un milieu INDUSTRIEL avec une seule transition sur une entree. Moi, je choisirais plutot : 2 boutons enfonces simultanement pendant 1 seconde. Ceinture et bretelles. !!
  • on place les instructions attachinterrupt dans le setup plutot que dans la boucle.
  • prend l'habitude de ne pas placer dans tes fonctions d'interruptions des instructions longues a executer comme un Serial.println. Ici ce n'est pas important mais c'est une bonne habitude a prendre.
    Jacques

N.B.: 2 petits liens vers un peu de lecture, il faudra peut-etre t'inscrire pour consulter mais cela vaut la peine
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=37891&highlight=stepper+motor+controller

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=78603

Bonjour JME87,

J'ai modifié la balise de mon premier message :slight_smile:

Pour le temps d'impulsion pour connaître le temps optimal, hier, j'ai demandé l'avis de Leadshine.
J'attends la réponse.


Un extrait du manuel du MS542

Remarque:
(1) t1: ENA doit être positionné au moins 5?s avant l’apparition du signal DIR. Si la
fonction Enable n’est pas utilisée, les broches ENA+ et ENA- n’ont pas à être
raccordée.
(2) t2: Le signal DIR doit être positionné au moins 5?s avant l’apparition d’un pulse sur
l’entrée PUL ;
(3) t3: Les pulses sur l’entrée PUL doivent durer au moins 1.5?s ;
(4) t4: Le niveau bas des pulses doit avoir une durée minimum de 1.5?s

Merci pour tes remarques pour mon code, autant prendre de bonnes habitudes dès le début.

avec une seule transition sur une entree.

Que veux-tu dire par une seule transition ?

Je vais regarder tes liens ...

Bonjour a tous,
pas besoin de demander au fabricant, il n'y a pas de temps optimal.
L'entree de ton driver est un opto-coupleur. Le minimum c'est 1,5uS pour etre sur que le transistor est sature. Il n'y a pas de maximum. L'electronique de ton driver est sensible a la transition du signal pulse pas a sa duree.

Si ton uP recoit une "crasse", perturbation ou autre sur son entree, le moteur se met en marche. Pas de securite !!. Le minimum a faire serai de confirmer la pression sur le bouton en allant lire l'etat de ce bouton 1 fois ou 2 apres quelques dizaine de milisecondes pour verifier qu'il a bien ete enfonce.

A mon avis tu inverse les priorites, c'est dans une routine d'interruption declenchee par le timer qu'il faut generer tes impulsions et laisser la boucle principale gerer les "basses besognes" comme la lecture des boutons par pooling et la modification de la "duree" du timer pour gerer acceleration et deceleration, l'envoi de messages sur la console etc. Comme ton programme est fait, tu ne peux rien ajouter de consequent dans ta boucle sans perturber le fonctionnement du moteur.

Jacques

Bonsoir Jacques,

Si ton uP recoit une "crasse", perturbation ou autre sur son entree, le moteur se met en marche. Pas de securite !!. Le minimum a faire serai de confirmer la pression sur le bouton en allant lire l'etat de ce bouton 1 fois ou 2 apres quelques dizaine de milisecondes pour verifier qu'il a bien ete enfonce.

J'avais donné un exemple avec un bouton poussoir qui lançait le moteur mais en réalité, c'est un capteur qui commande l'interruption.

Par contre, c'est une sage précaution et simple à mettre en œuvre, certainement un retour d'expérience. J'en prends note ! Merci.

c'est dans une routine d'interruption declenchee par le timer qu'il faut generer tes impulsions

Là je comprends le principe mais tu n'as pas un bout de code pour me mettre sur la piste ?
Je connais les interruptions classique à partie d'une entrée mais à partir d'un timer ?

Je viens de trouvé ça :

http://www.mon-club-elec.fr/pmwiki_mon_club_elec/pmwiki.php?n=MAIN.ArduinoInitiationInterruptionsTemporisationTimer2UneSeconde

C'est au sujet de "MsTimer2"

Bonjour Jme87,

Si tu passes par là, peux-tu svp m'indiquer si je suis sur la bonne voie pour la routine d'interruption avec "MsTimer2" ?

Merci d'avance.

Pierre

Bonjour Pierre,
je te conseille plutot ceci comme lecture :
https://code.google.com/p/stepper-robot-arduino-lib/wiki/Reference
et cette librairie gere l'acceleration qui est parametrable !
Jacques

Bonjour Jacques,

Merci pour le lien, je ne connaissais pas.
Je vais essayer mais j'aimais bien la simplicité de mon code et tenant compte de tes remarques.
Mes tests sont plutôt concluant.

je ne connais pas le MS542, néanmoins, j'ai utilisé plusieurs drives de différentes puissances et marques. ce qui s'y dit (a la valeur des durée pres, exemple de la GAC de RTA):
"...step input: signal must be present for at least 30µS and should be ideally 50% duty cycle
dir input: for at least 50µS berfore 1st step and at least 50µS after last step ..."
Les cartes GAC gèrent le demi-pas et quart de pas. à 1500trs/minutes, on a 50µS pour générer un pas dans les cas les plus défavorables.
D'autres models vont jusqu'au 32eme de pas. à 1500trs/minutes, on a 6,25µS pour générer un pas dans les cas les plus défavorables.
je suppose que les FPGA montés necessitent un front montant et descendant. j'ai deja eu des problemes sur les seuils limites (perte de pas).
tous les fabricant n'ont pas obligatoirement des entrées optocouplés. et quand bien meme, ca dépend du traitement hard et soft en aval.
et encore une fois, ca ne concerne pas le MS542.

voici un exemple de routine de positionnement, avec rampe d'acelleration/decelleration, et vitesse max:

//////////////////////////////////////
// TRAIN DE PULS AVEC RAMPES        //
void pls3(int pin, int pinSens, int acel, int fmax, int plsNbr1, int sens) {
// modifie posActu, la position en cours du moteur
  int factu = acel;
  int plsacel = plsNbr1/2;
  int finacel = plsNbr1/2;

  pinMode (pin, OUTPUT);
  digitalWrite (pin, 0);
  pinMode (pinSens, OUTPUT);
  digitalWrite (pinSens, sens);
  delay(1);
  
  for (int pls=0; pls<plsNbr1; pls++) {
//500+500=1sec
//factu mini=50 pour optimiser a basse vitesse
    if (factu<50) {factu=50;}
    if (factu>fmax) {factu=fmax;}
    digitalWrite (pin, 1);
    delayMicroseconds(100000/factu);
    digitalWrite (pin, 0);
    delayMicroseconds(100000/factu);
    if (sens==1) {posActu++;}
    if (sens==0) {posActu--;}
    
    //accelerate
    if (factu<fmax){
      if (pls<(plsNbr1/2)){
        factu=factu+acel;
        plsacel=pls;
      }
    }

    //decelerate
    if (pls>(plsNbr1-plsacel-1)){
      if (factu>acel){
        factu=factu-acel;
      }
    }

  }
  digitalWrite (pin, 0); // optional, au cas ou ...
  delay(1);
}

Bonjour à tous

voilà une image pour bien comprendre mon problème ... en fait je veux savoir comment je pourrais faire la commande (asservissement) d'un moteur pas à pas bipolaire Nema 42 par une carte arduino uno car j'ai envie d'asservir le système du broche de ma perceuse sensitive... le moteur doit être à vitesse variable et qui suit les consignes reçus par les capteurs de position alors si vous pourriez m'aider à faire le code de mon moteur et les capteurs de présence (capacitifs)

Merci d'avance pour votre aide
Hamdi

Bonjour Hamdibelhaj,

pour ce genre d'application, pas besoin d'un asservissement sur un moteur pas a pas.

Si le moteur, l'alimentation et les drivers sont bien dimensionnes, tout va rouler tout seul.

Bon courage.

Jacques

oui jacques mais j'ai besoin du code sur arduino et comment la carte va lire les capteurs et comprendre le fonctionnement du système?
Merci

Re

Quel codeurs ??

A mon avis, le plus simple est de placer un vernier digital sur la colonne de ta foreuse. Ces verniers sont equipes d'une sortie digitale qui peut etre exploitee par l'Arduino. Apres, laisse aller ton imagination, l'Arduino s'occupe de tout a la condition d'etre programme correctement. Il faut evidemment ajouter affichage, clavier .....

Que desires-tu faire : travail repetitif, percage de precision, ..... ?

Jacques