Go Down

Topic: Programme de commande de moteur pas à pas avec accélération (Read 638 times) previous topic - next topic

lapenduledargent

Aug 18, 2014, 04:40 pm Last Edit: Aug 20, 2014, 06:48 am by lapenduledargent Reason: 1
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 ?

Code: [Select]


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
}





jean-I

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
Code: [Select]

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.

lapenduledargent

Bonsoir Jean,

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

Code: [Select]
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

JMe87

#3
Aug 19, 2014, 11:02 pm Last Edit: Aug 19, 2014, 11:13 pm by JMe87 Reason: 1
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

lapenduledargent

#4
Aug 20, 2014, 07:10 am Last Edit: Aug 20, 2014, 08:01 am by lapenduledargent Reason: 1
Bonjour JME87,

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

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

Quote
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.

Quote
avec une seule transition sur une entree.


Que veux-tu dire par une seule transition ?


Je vais regarder tes liens ...


JMe87

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

lapenduledargent

Bonsoir Jacques,

Quote
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.

Quote
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 ?


lapenduledargent

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"

lapenduledargent

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

JMe87

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

lapenduledargent

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.

jean-I

#11
Aug 28, 2014, 06:33 pm Last Edit: Aug 29, 2014, 02:17 pm by jean-I Reason: 1
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:
Code: [Select]

//////////////////////////////////////
// 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);
}


Go Up