Lenteur moteur pas à pas

Bonjour tous le monde !

Alors voila, dans une partie de mon projet, je dois piloter un microscope sur 4 axes, XYZR.

Pour se faire chaque axe est munis de moteur pas a pas nema 17.

Les axes XY commandé par un joystick.

J’utilise un Arduino méga + shield CNC v3 + 4 Drivers TMC2209.

J’avais plus ou moins réussis en utilisant la bibliothèque AccelStepper.

Dans la version précédant de mon projet, j’utilisais des drivers A4988 en 1/16 pas.

J’ai maintenant changé pour des TMC2209 en 1/67 pas (interpolé en 1/256 pas).

Je ne pouvais donc plus utilisé la bibliothèque AccelStepper car elle est limité en vitesse (4000 pas/s?)

J’ai alors entrepris d’apprendre à piloté les moteurs sans bibliothèque, mais avec des commandes d’impulsions. :face_with_monocle:

J’ai réussi, mais je rencontre un problème…

Le code fait en sorte que plus on appuie sur le joystick, plus le moteur tourne vite (dans un 1er temps j’ai juste écris le code pour l’axe X de la position centrale, à une extrémité).
Mais le problème, c'est que lorsque le joystick arrive en buté, cela fonctionne, mais malgré les 5millis/s le moteur tourne doucement :face_with_diagonal_mouth: :

// Configuration des broches pour les moteurs pas à pas
const int moteurXStepPin = 2;
const int moteurXDirPin = 5;
const int moteurYStepPin = 3;
const int moteurYDirPin = 6;
const int moteurVitesseRapide = 5;
const int moteurVitesseLente = 2000;
const int enPin = 8; // pin 8 pour activer les 4 drivers sur shield CNC

// Configuration des broches pour le joystick
const int joystickXPin = A8; // Broche analogique pour l'axe X
const int joystickYPin = A9; // Broche analogique pour l'axe Y


//===================================================================================================================================//
//======================================================    VOID SETUP   ============================================================//
//===================================================================================================================================//

void setup() {

  // Initialisation du moniteur série
  Serial.begin(9600);

  // Configuration des broches en sortie pour les moteurs
  pinMode(moteurXStepPin, OUTPUT);
  pinMode(moteurXDirPin, OUTPUT);
  pinMode(moteurYStepPin, OUTPUT);
  pinMode(moteurYDirPin, OUTPUT);

  // Configuration du joystick en entrée
  pinMode(joystickXPin, INPUT);
  pinMode(joystickYPin, INPUT); 

  // Activation des 4 pins "EN" des 4 drivers du shield CNC
  pinMode(enPin, OUTPUT);
  digitalWrite(enPin,LOW);

}



//===================================================================================================================================//
//=======================================================    VOID LOOP   ============================================================//
//===================================================================================================================================//

void loop() {

  // Lecture des valeurs du joystick
  int joyX = analogRead(joystickXPin);
  //int joyY = analogRead(joystickYPin);

  //Mappage du Joysitck pour les vitesses moteur
  // X moyenne a 522 -> Zone morte de 16 -> (514/530)
  // Y moyenne a 539 -> Zone morte de 16 -> (531/547)
  int vitesseXpos = map(joyX, 530, 945, moteurVitesseLente, moteurVitesseRapide);
  //int vitesseXneg = map(joyX, 514, 107, moteurVitesseLente, moteurVitesseRapide);
  //int vitesseYpos = map(joyY, 547, 950, moteurVitesseLente, moteurVitesseRapide);
  //int vitesseYneg = map(joyY, 531, 125, moteurVitesseLente, moteurVitesseRapide);



  // Si joyX sup 530, alors faire avancer moteur x en positif
  if (joyX > 530){
    digitalWrite(moteurXDirPin, HIGH);  // Direction moteur
    digitalWrite(moteurXStepPin, HIGH); // Une impulsion
    delayMicroseconds(vitesseXpos);     // Vitesse
    digitalWrite(moteurXStepPin, LOW);  // Fin impulsion
    delayMicroseconds(vitesseXpos); 
    //Serial.println(vitesseXpos);
  }


  
}

Par exemple quand je met juste cela dans le loop, le moteur tourne rapidement :

    digitalWrite(moteurXDirPin, HIGH);  // Direction moteur
    digitalWrite(moteurXStepPin, HIGH); // Une impulsion
    delayMicroseconds(5);     // Vitesse
    digitalWrite(moteurXStepPin, LOW);  // Fin impulsion
    delayMicroseconds(5); 

Alors je me suis dit que peut être la condition if ralentissait de quelques millis/s le programme, donc j’ai essayer en mettant juste ce code dans le loop, avec une condition basique, et là le moteur tourne rapidement aussi, donc cela ne vient pas d’une condition :

  if (0==0){
    digitalWrite(moteurXDirPin, HIGH);  // Direction moteur
    digitalWrite(moteurXStepPin, HIGH); // Une impulsion
    delayMicroseconds(5);     // Vitesse
    digitalWrite(moteurXStepPin, LOW);  // Fin impulsion
    delayMicroseconds(5); 
    //Serial.println(vitesseXpos);
  }

C’est lorsque je demande à la condition d’aller chercher la valeur de « joyX » que le moteur tourne lentement arrivé en buté du joystick… :

void loop() {

  // Lecture des valeurs du joystick
  int joyX = analogRead(joystickXPin);
  //int joyY = analogRead(joystickYPin);

  //Mappage du Joysitck pour les vitesses moteur
  // X moyenne a 522 -> Zone morte de 16 -> (514/530)
  // Y moyenne a 539 -> Zone morte de 16 -> (531/547)
  int vitesseXpos = map(joyX, 530, 945, moteurVitesseLente, moteurVitesseRapide);
  //int vitesseXneg = map(joyX, 514, 107, moteurVitesseLente, moteurVitesseRapide);
  //int vitesseYpos = map(joyY, 547, 950, moteurVitesseLente, moteurVitesseRapide);
  //int vitesseYneg = map(joyY, 531, 125, moteurVitesseLente, moteurVitesseRapide);



  // Si joyX sup 530, alors faire avancer moteur x en positif
  if (joyX > 530){
    digitalWrite(moteurXDirPin, HIGH);  // Direction moteur
    digitalWrite(moteurXStepPin, HIGH); // Une impulsion
    delayMicroseconds(vitesseXpos);     // Vitesse
    digitalWrite(moteurXStepPin, LOW);  // Fin impulsion
    delayMicroseconds(vitesseXpos); 
    //Serial.println(vitesseXpos);
  }


  
}

Je n’ai pas un très haut niveau en code.

Quand pensez vous, suis-je sur une mauvaise piste ? Une mauvaise façon de faire ? :face_with_monocle:

Merci a tous ! :+1:

Quand le joystick est rajouté, cela inclut deux instructions entre deux pas:
− analogread qui attend la fin de la conversion
− map qui convertit les nombres en entiers longs et qui fait entre autre une division qui prends aussi beaucoup de temps.

Une première démarche importante serait de ne lire le joystick qu'une fois de temps en temps au lieu d'à chaque pas. Du coup, tous les pas auraient la bonne temporisation sauf un de temps en temps.

On peu difficilement s'affranchir du digitaread, la solution pourrait être de faire le déclenchement manuel (passer par les registres!) juste après une lecture. Ainsi quand on ferait la lecture, la donnée serait prête sans que l'on ait besoin d'attendre.

Pour map, je conseille vivement de la boycotter, car c'est un gaspillage important de code et de temps. Certains ne jurent que par map et accelstepper, qui ne sont pas performant.
vitesseXpos = map(joyX, 530, 945, moteurVitesseLente, moteurVitesseRapide);
est équivalent à
vitesseXpos = (joyX - 530) * (2000 - 5) / (945 - 530)
avec un calcul fait sur des entiers longs. map fait donc deux conversion int <-> long des multiplications et une division. Le nombre (2000 - 5) / (945 - 530) vaut quasiment 4,8.
Si on peut se contenter d'une approximation à 5, on pourrait remplacer map par
vitesseXpos = (joyX-530)*5
ce qui serait beaucoup plus rapide. Même peut être par
vitesseXpos = joyX-530;
vitesseXpos += (vitesseXpos << 2); // multiplication par 5 (on ajoute 4 fois le nombre)
Il faudrait alors peut être ajuster 530 vu que l'on a changé la pente de le transformation.

Si le temps d’execution de digitalread est penalisant, avec une carte mega qui utilise un micro atmel avr il est possible d’uitliser la bibliothèque digitalWriteFast/digitalreadfast qui ne prend que deux cycles horloge au lien de plus de 60 pour les fonction digital classiques.

Quand le joystick est a fond que doit-il se produire : le moteur continue a pleine vitesse ou autre chose ?

Merci pour vos réponse! :+1:
J'ai maintenant quelques pistes à explorer :face_with_monocle:
Si je résume :

  • map prend du temps
  • Essayer de lire le joystick qu'une fois de temps en temps
  • Essayer de le remplacer par un calcule
  • Déclenchement manuel (registres)
  • Bibliothéque digitalWriteFast/digitalreadfast

Il n'y a plus qu'à!

Le joystick contrôlera un plateau sous la lentille du microscope, sur axe X et Y. Le joystick a fond, fera tourner le moteur à fond en continue (je n'ai pas besoin de la vitesse maximum du moteur), jusqu'à relâchement du joystick ou arrivé contre le fin de course. J'ai besoin que la vitesse sois progressive, dans le sens où plus on appuie sur le joystick, plus le moteur tourne vite.

Merci pour vos réponse, je vais explorer tous ça! :+1:

Bonjour jon_01

Avant tout ça, fais un print à cet endroit:

	if (joyX > 530){
Serial.println(vitesseXpos);
		digitalWrite(moteurXDirPin, HIGH);  // Direction moteur
		digitalWrite(moteurXStepPin, HIGH); // Une impulsion
		delayMicroseconds(vitesseXpos);     // Vitesse
		digitalWrite(moteurXStepPin, LOW);  // Fin impulsion
		delayMicroseconds(vitesseXpos);
		//Serial.println(vitesseXpos);
	}

et tu verras pourquoi ton moteur ralenti, un moment donné, ton temps devient négatif.

1
1
1
1
1
5
5
1
-4
-4
-9
-9
-14
-19
-23
-28
-28
-28
-28
-28

La fonction map() n'est pas en cause mais ta formulation.

Cordialement
jpbbricole

pour que cela fonctionne bien il faut être sûr que joyX reste entre 530 et 945.
Si vous dépassez, la fonction map vous donne juste une fonction affine et donc vous allez dépasser les bornes

si vous voulez garantir que le résultat ne sort pas de l'intervalle vous pouvez rajouter l'appel à constrain ensuite

  int vitesseXpos = map(joyX, 530, 945, moteurVitesseLente, moteurVitesseRapide);
  vitesseXpos = constrain(vitesseXpos, moteurVitesseLente, moteurVitesseRapide);

bien sûr c'est du calcul en plus donc du temps ...

Merci jpbbricole, J-M-LJackson.

Oui j'ai aussi vu que parfois les valeurs passaient en négatif.

En faisant un print comme jpbbricole le préconise, et que je met en buté le joystick, j'ai (sa va très vite) environ entre 20/60, et si je force un peut sur le joystick cela passe en négatif. Avec un print le moteur est encore plus lent :sloth:, mais j'ai pus voir quand les valeurs passaient en négatif.

J-M-LJackson, un constrain est une bonne idée pour bloqué la valeur à max 5, j'ai essayé, et cela ne semble pas trop ralentir le moteur, mais il ne tourne pas plus vite...

J'aurai bien posté une vidéo pour vous montrer, mais j'ai l'impression que l'on ne peut plus poster directement de vidéo sur le forum.

Je vais creuser les idées proposé plus haut. :+1:

vous la postez sur votre compte youTube si vous en avez un et ensuite vous postez le lien youTube dans le forum. ça affichera directement le lecteur vidéo dans le post.

1 Like

Salut vileroi, je me penche sur un calcule :abacus: plutot que faire un map.

Quand je regarde la doc de Map, map équivaut à :

  • (x-inMini) * (outMax-outMin) / (inMax-inMini) + outMini

Dans mon cas :

  • (joyX-530) * (5-2000) / (945-530) + 2000

Lors de mes essais, je suis tombé sur un os :bone:

J'ai par exemple essayé ce calcule :

  • (945-530) * (5-2000) / (945-530) + 2000

:bone: = Arduino me répond : 2057 et une calculette : 5

J'ai oublié des parenthèses quelque part? :thinking:

c'est la différence entre faire les calculs avec des entiers sur 16 bits ou 32 bits. la fonction map() utilise des long donc 32 bits

essayez ce code

void setup() {
  Serial.begin(115200);
  int x = (945 - 530) * (5 - 2000) / (945 - 530) + 2000;
  long y = (945L - 530L) * (5L - 2000L) / (945L - 530L) + 2000L;
  Serial.println(x);
  Serial.println(y);
}

void loop() {}

je pense que vous verrez 2057 et 5

(par défaut en C++ le compilateur prend un int comme type pour un nombre écrit "en dur" (un literal) et sur UNO un int est sur 16 bits. Si vous mettez L à la fin du nombre, vous dites au compilateur que vous voulez que ce soit un Long donc sur 32 bits et là il n'y a plus de débordement dans les calculs

A ok merci J-M-L! :+1:
Effectivement j'obtient 2057 et 5.
Très bien je continue mes recherche, merci!

En remplaçant le map et si on passe par les entiers long, c'est la même chose. On doit avoir 5. Mais que l'on utilise map ou la formule ci dessus, le temps de calcul est à peu près le même, on a toujours la division. C'est pour cela qu'en faisant

On va beaucoup plus vite. Mais 5est une approximation...

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.