Moteur pas a pas et vis à bille

Bonsoir,
J'ai relié une vis à bille a un moteur nema 34, ce système doit faire des aller-retours de 50 cms pour activer un bras d'une petite machine .
Avec le code , ça marche si je pars du début de la vis , il fait des allers retour comme il faut ; mais si il y a coupure de courant , le moteur repart du début et la vis voit son parcours décalé , et dépasse donc les limites .
En vérifiant le moniteur série,j'ai remarqué que c'est lui qui repart à 0 toujours dans le sens '' aller'' et que le moteur suit , quelque soit l'endroit où il s'est arrêter au niveau de la vis .
Mon problème est ; comment faire pour qu'il reprenne là où il en était avant la coupure de courant , et qu'il continue normalement , sans tout recommencer à zéro ?
Merci de vos conseils
voici le code :


//IL FAUT AU DEPART PARTIR SYNCHRONISER AVEX X=0  OU Y = 0       UTILISER LE MONITEUR SERIE +++++

//ET QUE SI COUPURE DE COURANT CA ARRETE DE COMPTER
//CAR SINON LE MOTEUR N'EST PLUS SYNCHRO AVEC LE COMPTAGE ET DEBORDE LA FIN DE LA VIS !!


#include <AccelStepper.h>
#include <MultiStepper.h>

// defines pins numbers
const int stepPin = 5;
const int dirPin = 2;
const int enPin = 8;

// declaration variable pour 'delayMicroseconds' pour faire varier la vitesse , on l'appellera v ( comme vitesse )
const int vitesse = 200;
// C'EST ICI QU'ON CHANGE 'VITESSE' POUR CHANGER LA VITESSE

//POUR AUGMENTER LA VITESSE, ON DOIT DIMINUER '' VITESSE ""  !!!

void setup() {
  Serial.begin(115200);
  // Sets the two pins as Outputs
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);

  pinMode(enPin, OUTPUT);
  digitalWrite(enPin, LOW);

}
void loop() {

  delay (500);
  digitalWrite(dirPin, LOW); // Enables the motor to move in a particular direction
  // Makes 200 pulses for making one full cycle rotation
  //                                                                                    x  LOW AVANCE            y HIGHT   RECULE

  for (long x = 0; x < 54000; x++) {
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(vitesse);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(vitesse);
    Serial.print("x : ");
    Serial.println(x);
    //    delay(100);
  }


  delay(5000); // One second delay

  digitalWrite(dirPin, HIGH); //Changes the rotations direction
  //Makes 400 pulses for making two full cycle rotation


  for (long y = 0; y < 54000; y++) {
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(vitesse);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(vitesse);
    Serial.print("y : ");
    Serial.println(y);

  }
  delay(5000);

}

Le fonctionnement que tu décris est normal, comment le moteur peut-il se rappeler de sa position à la coupure du courant ?

Il faut tu prévois une phase d'initialisation au lancement du programme ou à sa reprise .

Une butée de fin de course par exemple.
La fonction initialisation() ferait tourner le moteur dans un sens déterminé par toi, quand le contact du fin course est activé : blocage du moteur et initialisation des différentes variables.

Caractéristiques du moteur s'il te plaît.
Nema 34 indique juste la dimension de la semelle de fixation.
Ce que l'on peut supposer c'est que c'est un gros moteur et donc il doit avoir beaucoup de couple.

Peut être qu'un contact mécanique de fin de course serait trop fragile et qu'une détection optique ou magnétique serait plus appropriée.

Bonjour Cavok
Il n'est pas vraiment possible d'enregistrer, en permanence, la position de ton moteur pas à pas (MPAP), ça userai rapidement l'EEPROM.
Il te faut un contact de début de course, le mieux optique ou magnétique, afin de "tarer" ton système.
S'il y a coupure tu dois obligatoirement "retarer" ton système.
Il faut simplement enregistrer un flag qui serait à 1 si tout c'est bien passé et 0 si pas.
Je viens de terminer un pont transbordeur à vis et NEMA17 qui applique ces principes, si tu est intéressé?

Cordialement
jpbbricole

Bonsoir

Une possibilité de conservation de données à la coupure de l'alimentation sans l'usure rapide des EEPROM ou des Flash est maintenant offerte avec les FRAM ("ram ferromagnétique non volatile")

It's excellent for low-power or inconsistent-power datalogging or data buffering where you want to stream data fast while also keeping the data when there's no power. Unlike Flash or EEPROM there's no pages to worry about. Each byte can be read/written 10,000,000,000,000 times so you don't have to worry too much about wear leveling.

Adafruit propose un module (versions I2C ou SPI) et une librairie : https://www.adafruit.com/product/1895

5€ TVA incluse et port compris ici via Aliexpress (module avec une puce FRAM 32KB de Fujitsu )

https://fr.aliexpress.com/item/1005002593176010.html?spm=a2g0o.productlist.0.0.63456dd4iHLUuz&algo_pvid=a985e3f6-f089-4e50-96ca-b6e6ac511722&algo_exp_id=a985e3f6-f089-4e50-96ca-b6e6ac511722-0&pdp_ext_f=%7B%22sku_id%22%3A%2212000021313870411%22%7D

.....acheté mais pas encore testé , peut être une solution originale pour conserver la position du moteur ?

FRAM

1 Like

Bonsoir,
il y a aussi la possibilite de sauver des donnees au moment de la coupure de courant.
Une petite recherche avec Google (arduino save configuration at power down) et je trouve ceci :
arduino-power-down-auto-save
l'alimentation "de secours" est assuree par une grosse capa 1F.
C'est le premier exemple de la liste, il y en a plein.
Jacques

La même recherche en français donne sans doute le très bon tuto de Henri

Sinon oui, le détecteur de fin de course est une solution simple

+1 et j'ajouterai que la solution est fiable.
Avec la sauvegarde ça finira par merder un jour ou l'autre à cause d'un évènement qu'on n'aura pas prévu, ne serait ce que le cas ou quelqu'un appuie sauvagement sur le reset

Quant AccelStepper (ou tout autre moyen) envoie un ordre de se mettre sur une position donnée, le moteur va se mettre sur la position ou n'importe quelle position située à 4 pas près si je note les position pas0, pas1, pas2, pas3, pas0, pas2... et si on demande au moteur d'être sur le pas (ou micropas) pas0, le pas3 est en équilibre instable, les pas1 et pas3 vont forcer le moteur à aller au pas le plus proche. Si on est en micropas, les pas intermédiaires vont aussi foercer le moteur à aller au pas0.

Quand on met sous tension, AccelStepper (ou toute autre bibliothèque) se positionnera toujours sur le même pas, on va dire le pas0. Si il y a eu coupure su l'alimentation quand le moteur était sur le pas2, à la mise sous tension, il va faire 2 pas, mais on ne peut pas prévoir dans quel sens. Puis quand on lui dira de se positionne sur le pas2 (si on a mémorisé sur quel pas on était), le moteur va avancer de 2 pas. Si on a de la chance, le moteur se replacera au même pas, mais si on en a moins, il y aura une erreur pouvant aller jusqu'à 4 pas.
Si on n'a pas de chance, on se décalera de 4 pas à chaque coupure de tension (c'est la loi de Murphy)

Si on veut pouvoir reprendre sans fins de course le mouvement et que l'on ne veut pas de décalages, il faut donc:

  • mémoriser le pas ou on était lors de la coupure
  • ne pas utiliser AccelStepper qui ne permet pas de démarrer sur le bon pas parmi les 4
  • ne pas utiliser non plus des circuits comme les A4988 ou les drivers qui s'initialisent aussi toujours sur le même pas

 

Conclusion:
Dans la pratique, si on veut pouvoir repartir du point d'arrêt, il faut mémoriser en gros la position d'arrêt, et mettre au moins un capteur de position pour le zéro machine. Lors d'un arrêt dû à une coupure, on va redémarrer avec une erreur négligeable, mais à chaque retour, on refait le zéro machine et les erreurs ne peuvent se cumuler.

Bonjour

Dans mes systèmes de déplacements de moteurs pas à pas (MPAP), quand il faut sauver la position, j'ai une variable positionMpap qui, à la fin de chaque déplacement, est sauvée en EEPROM ou mieux en FRAM comme proposé par @al1fch.
Au départ de ce déplacement, positionMpap est mise à -1 et sauvée. Ainsi au redémarrage du système, si à la lecture de la mémoire positionMpap == -1, celà veut dire crash ou reset de l'Arduino en cours de déplacement, donc procédure de homing, donc présence d'un contact de début de course.
Comme signalé par @vileroi, le redémarrage peut engendrer une rotation de 1 ou 2 pas. Si c'est supporté par l'application...
De toute façon, dans un système nécessitant une haute précision, il y a toujours une procédure de homing au démarrage du système.

Cordialement
jpbbricole

Bonsoir,
Merci pour vos réponses.
68tjs
Caractéristiques du moteur :
Moteur pas à pas nema 34 1 axe, 965oz.in 7.5N.m 5.6A et commande de pilote DM860A,c'est pour tirer et pousser une trappe de 28kg ( matériel agricole gérant le passage de grain de blé ), qui doit s'ouvrir plus ou moins selon le débit souhaité , c'est pour cela que j'ai choisi un moteur pas a pas et une vis a bille ; pour l'instant j'ai le code simple 'ouvert ou fermé' mais ensuite je réglerai le degré d'ouverture.
détection optique ou magnétique serait plus appropriée en fin de course : je prends note

jpbbricole :
un pont transbordeur à vis et NEMA17 qui applique ces principes, si tu est intéressé? oui bien sûr, je suis intéressé , merci de me donner le lien

al1fch
les FRAM : j'ai regardé, ça a l'air adapté, mais tout est en anglais et je n'arrive pas encore à bien comprendre comment ça fonctionne et les branchements avec l’Arduino et surtout si c'est fiable en pratique concrète

JMe87
Merci , cela me parait intéressant , et ça a donner l'idée à JML ( que je remercie )de mettre le lien de Henri BACHETTI en français,explications très claire , exposé de manière pédagogique (enfin le top ! )

kamill
merci pour ton conseil judicieux de mise en garde, j'en tiendrai compte

vileroi
'Lors d'un arrêt dû à une coupure, on va redémarrer avec une erreur négligeable, mais à chaque retour, on refait le zéro machine et les erreurs ne peuvent se cumuler.' ce que tu dis ça me convient parfaitement, je ne suis pas à 1 mm près

jpbbricole :
'Au départ de ce déplacement, positionMpap est mise à -1 et sauvée. Ainsi au redémarrage du système, si à la lecture de la mémoire positionMpap == -1, celà veut dire crash ou reset de l'Arduino en cours de déplacement, donc procédure de homing, donc présence d'un contact de début de course.' : j'aurai besoin de plus d'explications , si je comprends l'idée, en pratique, j'ai des difficultés à savoir comment faire
Cordialement

Bonjour Cavok

Je ne peux pas, malheureusement, te donner de lien, ce développement s’étant réalisé essentiellement en messages privés. Mais je peux de donner un lien sur la vidéo de la réalisation ainsi que le montage que j’ai utilisé pour le développement.

Ton système est un va-et-vient qui fonctionne en continu, à l’allumage du système ou il est commandé ?

Dans ton cas, c'est assez simple puisqu'il n'y a que 2 positions possibles, au début ou à la fin. Il suffit de mémoriser dans quel sens le moteur doit tourner.
Je peux te faire un exemple en transposant le développement du transbordeur sur ton exemple du post #1

A+
Cordialement
jpbbricole

Bonjour jpbbricole ,
Merci pour les 2 vidéos , c'est exactement ce que je veux faire
Pourrais-tu me montrer le code que tu as utilisé ?
mon système est un va-et-vient qui fonctionne en continu, à l’allumage du système, avec des poses durant un temps programmé à l'avance (en fait un autre moteur devra fonctionné pendant ces pauses , avec un petit délai entre les 2 .
Puis-je te demandé si tu veux bien m'aider par tes conseils jusqu'à la fin de la réalisation , en corrigeant mes erreurs que je ferai forcément dans l'écriture de mon code . Si ça doit être en messages privés , ça ne me dérange absolument pas , par contre , je ne sais pas comment procéder pour cela .
amicalement.

Bonjour Cavok

Ce sera plus instructif que je te fasse un exemple en tenant compte de to code du post #1.
J'y mettrai un maximum de commentaires afin que tu puisses comprendre la "mécanique".
C'est aussi une façon d'apprendre :wink:

A+
Cordialement
jpbbricole

Bien sûr , Merci

Bonjour Cavok

Voilà un « Premier jet » de ton application.

Comme ton installation n’est certainement pas la même que la mienne, il y a 2 sections de paramètres qui sont sélectionnées en précompilation par la ligne :
#define programmeur // Si programme pour utilisateur mettre //

Si cette ligne est commentée (//), c’est tes paramètres qui sont compilés, ceux depuis :
#else //=========================== Paramètres propres à l'utilisateur, mettre //#define programmeur

Jusqu’à
#endif

J’espère y avoir mis assez de commentaires pour aider à ta compréhension, mais je suis toujours à ta disposition.
Je ne sait pas quel driver tu as mais, au vu des pin de ton programme, ça se gère comme un A4988.
Dans la section des paramètres tu peux régler l’état des pin et autres comme :
const int mpapDriverTypeDef = 1; // Type de driver 1 = A4988 AccelStepper Arduino Library, connecting Stepper Motors to Teensy

const int mpapDriverEnaOnStat = LOW; // Etat du signal ENA actif

const boolean mpapDriverDirReverse = false; // Pour inverser DIR

const int homeOnStat = LOW; // Etat du signal Home actif LOW = GND

Si tu mets ta console à 115200 tu y verras des informations sur le déroulement du programme.
Par prudence, pour les premiers essais, comme on ne sait pas dans quel sens va tourner ton MPAP, déconnectes la mécanique. Si le MPAP tourne dans le mauvais sens, c’est la variable mpapDriverDirReverse.
La fonction de homing, pour le moment, est commentée. As-tu le matériel, sais-tu comment monter tout ça ? Dans l’idéal, quand ton mobile est « at home » il doit donner un LOW sur homingPin, mais c’est paramétrable.
Pour le premier démarrage, le MPAP doit faire le va-et-vient en fonction de mpapPasAvant.
Pour tester la mémorisation :
Un reset pendant la pause, au redémarrage le mobile bouge « comme si de rien n’était »
Un reset pendant un déplacement, il y a toujours un homing au redémarrage.
Je te laisse essayer tout ça.

Ce ne serait pas mieux s’il y avait un contact pour faire du « hand shaking », à voire plus tard.

Le programme:

/*
    Name:       ARDFR_Cavok_VisAbilles.ino
    Created:	29.11.2021
    Author:     jpbbricole
*/

#include <AccelStepper.h>     // https://www.arduino.cc/reference/en/libraries/accelstepper/
                              // http://edge.rit.edu/edge/P14215/public/Software/P14215_Nick/Code/Arduino%20Library%20Folder/AccelStepper/doc/classAccelStepper.html
#include <EEPROM.h>     // Pour sauvegarde des paramètres en mémoire EEPROM

//#define programmeur     // Si programme pour utilisateur mettre //

#ifdef programmeur
const int mpapDriverDirPin = 5;     // Pin du signal DIR driver A4988
const int mpapDriverStepPin = 6;     // Pin du signal STEP
const int mpapDriverEnaPin = 7;     // Pin du signal ENA

const int mpapDriverTypeDef = 1;     // Type de driver 1 = A4988 https://www.pjrc.com/teensy/td_libs_AccelStepper.html
const int mpapDriverEnaOnStat = LOW;     // Etat du signal ENA actif
const boolean mpapDriverDirReverse = false;     // Pour inverser DIR

float mpapMaxSpeed = 800.0;     // Vitesse en pas/Sec.
float mpapAcceleration = mpapMaxSpeed*2.0 ;     // Accélération

long mpapPasAvant = 3000;     // Position avant en pas
long mpapPasArriere = 0;     // Position arrière en pas

unsigned long pauseInterMouvement = 5;     // Pause en secondes

const int homingPin = 8;     // Pin du contact home.
const int homeOnStat = LOW;     // Etat du signal Home actif LOW = GND
#else     //=========================== Paramètres propres à l'utilisateur, mettre //#define programmeur
const int mpapDriverDirPin = 5;     // Pin du signal DIR driver A4988
const int mpapDriverStepPin = 6;     // Pin du signal STEP
const int mpapDriverEnaPin = 7;     // Pin du signal ENA

const int mpapDriverTypeDef = 1;     // Type de driver 1 = A4988 https://www.pjrc.com/teensy/td_libs_AccelStepper.html
const int mpapDriverEnaOnStat = LOW;     // Etat du signal ENA actif
const boolean mpapDriverDirReverse = false;     // Pour inverser DIR

float mpapMaxSpeed = 800.0;     // Vitesse en pas/Sec.
float mpapAcceleration = mpapMaxSpeed*100.0 ;     // Accélération

long mpapPasAvant = 3000;     // Position avant en pas
long mpapPasArriere = 0;     // Position arrière en pas

unsigned long pauseInterMouvement = 5;     // Pause en secondes

const int homingPin = 8;     // Pin du contact home.
const int homeOnStat = LOW;     // Etat du signal Home actif LOW = GND
#endif

//------------------------------------ Moteur pas à pas (mpap)
AccelStepper mpap(mpapDriverTypeDef, mpapDriverStepPin, mpapDriverDirPin);     // Initialisation du mpap

//------------------------------------- Sens de déplacement 0 = en mouvement   1 = avant    2 = arrière
enum mpapDirIndex  : byte  {mpapDirArriere, mpapDirAvant, mpapDirInMove};

byte mpapDirStatus = mpapDirInMove;     

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

    //--------------------------------- Initialisation des entrées/sorties
    pinMode(mpapDriverEnaPin, OUTPUT);     // Pin du signal ENA du driver
    digitalWrite(mpapDriverEnaPin, mpapDriverEnaOnStat);     // Activer le moteur
    pinMode(homingPin, INPUT_PULLUP);     // Pour le contact de homing, active avec GND

    //--------------------------------- Initialisation mpap
	mpap.setMaxSpeed(mpapMaxSpeed);
    mpap.setAcceleration(mpapAcceleration);
    mpap.setPinsInverted(mpapDriverDirReverse, false, false);       // Inverser DIR si nécessaire 
	
	mpapDirStatus = EEPROM.read(0);     // Lecture du dernier sens de rotation enrtegistré

	Serial.print(F("EEPROM = ")); Serial.println(mpapDirStatus);

	switch (mpapDirStatus) 
	{
	case mpapDirArriere:     // Si dernier mouvement enregistré = arrière
		mpapDirStatus = mpapDirAvant;     // Donc prochain mouvement en avant
		mpap.setCurrentPosition(0);
		break;
	case mpapDirAvant:
		mpapDirStatus = mpapDirArriere;     // Donc prochain mouvement en arrière
		mpap.setCurrentPosition(mpapPasAvant);
		break;
	default:     // Si crash ou reset de l'Arduino
		homingStart();
		mpapDirStatus = mpapDirAvant;     // Ensuite prochain mouvement en avant
		mpap.setCurrentPosition(0);
		delay(2000);
		break;
	}	
}

void loop()
{
	if (mpapDirStatus == mpapDirAvant)     // Si prochain mouvement est en avant
	{
		mpapGoto(mpapPasAvant);
	}
	else
	{
		mpapGoto(mpapPasArriere);
	}
	Serial.print("Pause "); Serial.println(pauseInterMouvement);
	delay(pauseInterMouvement * 1000);
	
	mpapDirStatus = (mpapDirStatus == mpapDirAvant) ? mpapDirArriere : mpapDirAvant;     // Alterner les mouvements

}

//===================================== Moteur pas à pas (MPAP)
//------------------------------------- Goto pas
void mpapGoto(long gotoSteps)
{
	Serial.print("MPAP Goto\t" + String(gotoSteps));

    //digitalWrite(mpapDriverEnaPin, mpapDriverEnaOnStat);     // Activer le moteur

	mpapDirStatus = mpapDirInMove; EEPROM.update(0, mpapDirStatus);    // MPAP en mouvement et enregistrer
	mpap.runToNewPosition(gotoSteps);
	mpapDirStatus = digitalRead(mpapDriverDirPin); EEPROM.update(0, mpapDirStatus);     // Lire sens du mouvement et enregistrer

    //digitalWrite(mpapDriverEnaPin, !mpapDriverEnaOnStat);     // Désactiver le moteur

	Serial.println("\tOK");
}

//------------------------------------- Retour à la position de départ
void homingStart()
{
	Serial.println(F("\nHOMING"));
	Serial.println(F("DESACTIVE"));

    //if (digitalRead(homingPin) != homeOnStat)     // Si pas sur contact home
    //{
		//Serial.println(F("Retour <<"));
//
		//mpap.moveTo(-mpapPasAvant);
	//
		//while (digitalRead(homingPin) != homeOnStat)    // Tant que pas home
		//{
			//mpap.run();
		//}
		//mpap.stop();     // Stop aussitôt que possible
    //}
	delay(2000);
}

A+
Cordialement
Jpbbricole

attention au type. unsigned long serait mieux.

Si vous mettez 40 secondes de pause sur un AVR votre calcul plus tard dans delay(pauseInterMouvement * 1000) va déborder de ce qu'un int peut représenter


Sinon un enum va définir un type, autant en profiter. Au lieu de

enum mpapDirIndex {mpapDirArriere, mpapDirAvant, mpapDirInMove};
byte mpapDirStatus = mpapDirInMove; 

faites

enum mpapDirIndex : byte {mpapDirArriere, mpapDirAvant, mpapDirInMove} mpapDirStatus = mpapDirInMove; 

la procédure de homing est désactivée ? il faut bien sûr que @Cavok rajoute un détecteur de fin de course

Bonsoir J-M-L

Merci, c'est corrigé, je pensais économiser :blush:

Cordialement
jpbbricole

:wink:

sinon cette ligne là

dépend du driver et de l'ordre de l'énumération. peut être faire un truc comme cela:

  EEPROM.update(0, mpapDirInMove);    // noter en mouvement
  mpap.runToNewPosition(gotoSteps);
  EEPROM.update(0, mpapDirStatus);    // mouvement terminé

tout simplement ? (au lieu de modifier mpapDirStatus)

Merci,
Je travaille ça , et vous tiens au courant

Jpbbricole,
mon driver est un DM860A
Caractéristiques:

1.Adoption du DSP 32 bits, technologie de subdivision à onde sinusoïdale pure

  1. Faible bruit, faible vibration et faible élévation de température

  2. Tension 24-80VAC

  3. Avec 8 cales, réglage du courant de sortie, courant 2.4-7. 2a

  4. Avec 16 cales réglage de la subdivision microstep

  5. Demi-courant automatique, auto-test, surtension, sous-tension, protection contre les surintensités

  6. isolation optique interne, réponse en fréquence la plus élevée 200KHZ

  7. Convient pour moteur pas à pas nema 34,