Problème valeur <90° servomoteur

Bonjour à toutes et à tous, je travail sur un petit robot avec 3 servomoteurs permettant de déplacer un stylo sur les axes X Y et Z.

Sur la photo :
le servomoteur 1 : axe Z sur pin 6
le servomoteur 2 : axe Y sur pin 5
le servomoteur 3 : axe X sur pin 3

Les servomoteur sont en liaison pignon-crémaillère (3 fois la même)

Je voudrais faire un "M" :

Avec le code ci-dessous le M se fait correctement :

#include <Servo.h>

Servo servoX, servoY, servoZ;

const int servoXPin = 3; // Axe X
const int servoYPin = 5; // Axe Y
const int servoZPin = 6; // Axe Z

const int penUp = 120;   // Position stylo relevé
const int penDown = 70; // Position stylo abaissé

const int startX = 90;    // Position de départ (axe X, bas)
const int startY = 90;    // Position de départ (axe Y, gauche)
const int topX = 150;     // Position en haut (axe X)
const int centerY = 120;  // Centre de "M" (axe Y)
const int centerX = 120;  // Centre de "M" (axe X)
const int rightY = 150;   // Droite de "M" (axe Y)

const int stepDelay = 20; // Délai entre chaque étape (ms)
const int stepSize = 1;   // Incrément de déplacement (degrés)


void stylhaut() {
  servoZ.write(penUp); 
  delay(1000); 
}

void stylbas() {
  servoZ.write(penDown); 
  delay(1000); 
}


void moveTo(int targetX, int targetY) {
  static int currentX = startX;
  static int currentY = startY;

  while (currentX != targetX || currentY != targetY) {
    if (currentX != targetX) {
      int stepX = (targetX > currentX) ? stepSize : -stepSize;
      currentX += stepX;
      if ((stepX > 0 && currentX > targetX) || (stepX < 0 && currentX < targetX)) {
        currentX = targetX;
      }
      servoX.write(currentX);
    }

    if (currentY != targetY) {
      int stepY = (targetY > currentY) ? stepSize : -stepSize;
      currentY += stepY;
      if ((stepY > 0 && currentY > targetY) || (stepY < 0 && currentY < targetY)) {
        currentY = targetY;
      }
      servoY.write(currentY);
    }

    delay(stepDelay);
  }
}



void setup() {
  servoX.attach(servoXPin);
  servoY.attach(servoYPin);
  servoZ.attach(servoZPin);

  moveTo(startX, startY);
}

void loop() {
  stylhaut();
  
  moveTo(startX, startY);
  
  stylbas();
  
  moveTo(topX, startY);
  moveTo(centerX, centerY);
  moveTo(topX, rightY);
  moveTo(startX, rightY);
  
  stylhaut();

  
  moveTo(startX, startY);
  
  while (true);
}

Mais j'aimerais alors modifier ce code pour faire un M plus grand mais lorsque je modifie mes valeur, 2 cas :
entre 90 et 180° pas de problème
mais si je descend une valeur en dessous de 90°, j'obtiens cette forme :


(startX et startY = 50°, centerX et centerY = 90°, topX et rightY = 130°)

je suis parti du principe que les point sont positionnés comme ceci : (le robot se trouve en centerY topX comme sur les photos des M)
_________startY_____centerY______rightY
startX____O___________O____________O

centerX__O___________O____________O

topX_____O___________O____________O

Auriez-vous une solution ?

PS : je suis débutant en arduino et je me suis beaucoup aidé de chatgpt... j'ai déchiffré chaque lignes du code et je pense le comprendre mais je bloque sur ce détail.

Merci de votre aide sur ce sujet qui est un peu long.

Pouvez vous nous montrer la liaison mécanique entre le servo (rotation) et la crémaillère ? Les servos sont ils montés correctement ? N'atteignez vous pas simplement des limites physiques à votre montage ?

Faites un code tout bête qui va à l'angle 0° pour l'axe X, abaisse le crayon et fait un moveTo vers 180° puis levez le crayon

répétez en déplaçant le papier pour l'axe Y.

➜ Cela vous donnera le débattement maximal de votre montage.

Bonjour mathismrgt25

Peut être que la largeur d'impulsion du signal n'est pas adaptée à ton servo.
Par défaut:

pour 0 ° c'est 544 microsecondes.
pour 180 ° c'est 2400 microsecondes.

Tu peux essayer ça en faisant myservo.write(ici des microsecondes);

En bidouillant tu va trouver les valeurs extrêmes pulseMin et pulseMax
que tu reportes ici:
myservo.attach(pinDuServo, pulseMin, pulseMax);
Veux tu un programme pour ce faire?

Des informations ici.

Cordialement
jpbbricole

Alors, pour les servos en général et plus particulièrement pour les petits modèles bon marché, il n'y a de garantie ni sur le débattement, ni sur la relation durée de l'impulsion position angulaire.
Donc, comme le suggère @jpbbricole il faut chercher ces valeurs empiriquement. Voir même faire une calibration pour chaque servo.
Ne pas oublier que la méthode write() peut recevoir soit un angle soit une durée d'impulsion. Donc la calibration pourait prendre cette forme

  • partir avec une impulsion de valeur moyenne autour de 1400
  • diminuer la largeur de l'impulsion jusqu'à trouver la butée
  • relever la valeur
  • partir avec une impulsion de valeur moyenne autour de 1400
  • augmenter la largeur de l'impulsion jusqu'à trouver la butée
  • relever la valeur

Tu peux passer ces valeurs à attach() lors de l'initialisation des servos.
Au passage, tu peux aussi relever la valeur de l'angle pour vérifier la linéarité du mouvement.

Il est souvent plus facile de travailler directement en durée d'impulsion plutôt qu'en valeur angulaire. Typiquement dans ton cas tu peux facilement trouver la relation entre position des axes et durée d'impulsion. Au passage, tu gagnes en précision, en effet l'argument passé à write() étant un entier lorsque tu travailles avec des angles tu est limité à 180 pas en utilisant des durées d'impulsions tu passes à un peu moins de 2000 pas.

Bonjour @J-M-L, merci de votre réponse.
Vous pouvez voir sur la photo ci-dessous la course maximum des servomoteurs réalisée avec le programme suivant (j'ai obtenu les points en remplacant la valeur (180 et 0) et le servomoteur (MX et MY) dans le void setup) :

#include "Servo.h"

Servo servoMY; // création de l'objet "servo"
Servo servoMX; // création de l'objet "servo"
Servo servoMZ; // création de l'objet "servo"

void setup() {

   servoMY.attach(5); // attache le servo au pin spécifié Y
   servoMX.attach(3); // attache le servo au pin spécifié X
   servoMZ.attach(6); // attache le servo au pin spécifié Z
   Serial.begin(9600);

   servoMY.write(180); // demande au servo de se déplacer à cette position
   delay(1000); // attend 1000 ms entre changement de position

}

void loop() {
   
   
  
}

Je penses donc que nous pouvons exclure la course trop faible de mes servos.

Merci d'avoir prit le temps de répondre.

En réponse à vos messages @jpbbricole et @fdufnews.

J'ai trouver les valeur d'impulsion maximum et minimum avec un programme trouvé :

#include <Servo.h>

Servo myservo;
int pulseWidth;  // Largeur d'impulsion actuelle
const int pinServo = 5; // Pin où le servo est connecté

void setup() {
  myservo.attach(pinServo);
  Serial.begin(9600);
  Serial.println("Servo test: utilisez les commandes '+' et '-' pour ajuster la largeur d'impulsion.");
  pulseWidth = 1500; // Valeur neutre (environ 90° pour la plupart des servos)
  myservo.writeMicroseconds(pulseWidth);
}

void loop() {
  if (Serial.available()) {
    char input = Serial.read();

    if (input == '+') {
      pulseWidth += 50; // Augmenter la largeur d'impulsion
    } else if (input == '-') {
      pulseWidth -= 50; // Réduire la largeur d'impulsion
    }

    // Limiter la largeur d'impulsion entre des valeurs sûres
    pulseWidth = constrain(pulseWidth, 500, 2500);

    myservo.writeMicroseconds(pulseWidth);
    Serial.print("Largeur d'impulsion : ");
    Serial.println(pulseWidth);
  }
}

Elles sont donc de 544 et 2400 microsecondes comme précisé dans vos message. Au delà de ces valeur mon servo ne bouge plus.

J'ai donc essayé de programmer en microsecondes le programme de base :

#include <Servo.h>

// Déclaration des servos
Servo servoX, servoY, servoZ;

// Broches des servos
const int servoXPin = 3; // Axe X
const int servoYPin = 5; // Axe Y
const int servoZPin = 6; // Axe Z

// Positions stylo (axe Z)
const int penUp = 110;   // Position stylo relevé
const int penDown = 80; // Position stylo abaissé

// Déplacement des axes (calibrage en degrés)
const int startX = 544;    // Position de départ (axe X, bas)
const int startY = 544;    // Position de départ (axe Y, gauche)
const int topX = 2400;     // Position en haut (axe X)
const int centerY = 928;  // Centre de "M" (axe Y)
const int centerX = 928;  // Centre de "M" (axe X)
const int rightY = 2400;   // Droite de "M" (axe Y)

// Temps et vitesse
const int stepDelay = 20; // Délai entre chaque étape (ms)
const int stepSize = 10;   // Incrément de déplacement (degrés)

//position relevé
void stylhaut() {
  servoZ.write(penUp); 
  delay(1000); 
}

//position écriture
void stylbas() {
  servoZ.write(penDown); 
  delay(1000); 
}

// Fonction pour déplacer les axes X et Y simultanément
void moveTo(int targetX, int targetY) {
  static int currentX = startX;
  static int currentY = startY;

  // Déplacement synchronisé
  while (currentX != targetX || currentY != targetY) {
    if (currentX != targetX) {
      int stepX = (targetX > currentX) ? stepSize : -stepSize;
      currentX += stepX;
      if ((stepX > 0 && currentX > targetX) || (stepX < 0 && currentX < targetX)) {
        currentX = targetX;
      }
      servoX.writeMicroseconds(currentX);
    }

    if (currentY != targetY) {
      int stepY = (targetY > currentY) ? stepSize : -stepSize;
      currentY += stepY;
      if ((stepY > 0 && currentY > targetY) || (stepY < 0 && currentY < targetY)) {
        currentY = targetY;
      }
      servoY.writeMicroseconds(currentY);
    }

    delay(stepDelay);
  }
}


void setup() {
  // Attacher les servos à leurs broches respectives
  servoX.attach(servoXPin);
  servoY.attach(servoYPin);
  servoZ.attach(servoZPin);
  Serial.begin(9600);
  // Initialiser la position
  stylhaut();       // Relever le stylo
  moveTo(startX, startY); // Déplacer à la position de départ
}

void loop() {
  // Dessiner la lettre "M"


  moveTo(startX, startY);
  stylbas(); // Abaisser le stylo

  moveTo(topX, startY);

  moveTo(centerX, centerY);

  moveTo(topX, rightY);

  moveTo(startX, rightY);

  // Fin du dessin
  stylhaut();

  moveTo(startX, startY);

  // Stopper après un dessin
  while (true);
}

Mais le résultat n'est toujours pas concluant :joy:

Ais-je bien compris ce que vous me demandez?

Le problème se trouve dans la fonction de tracé
J'ai fait une capture des valeurs de currentx et currenty
et j'obtiens ça

Une couleur différente pour chaque appel à moveto

Ce sont les 4 petits points verts sur la feuille ?

Sinon vous bougez x et y et même temps mais ils n’arrivent pas en même temps au point d’arrivée, donc ça ne fait pas des droites comme vous le pensez.
Il faut modifier la formule pour avoir le même nombre d’étapes en x et y

(Ça fonctionne si on est sur un axe ou à 45° et il y a la même distance à parcourir)

Oui, ce sont les 4 points vert sur la feuille.
Je me suis placer à 90° sur le servo Y pour venir "palper" le X 0 et X 180
Pareil pour Y.

J'ai fais pour que les distances soit équivalente car effectivement lors d'un premier essaie, la pointe au centre du M descendais jusqu'en bas. La distance n'étant pas équivalente, la trait était comme scindé en 2.
Mais sur le code que je vous ai partager, les distances sont normalement égales.

Il faudrait un vrai algorithme de tracé de segment
Comme l'algorithme de Bresenham par exemple qui est couramment utilisé dans les librairies graphiques

Le problème viens donc du 1er trait (bleu) qui se fait trop au centre du M? Et donc pour le orange les distances X et Y sont différentes...
Mais ou est donc le problème :joy:

Le problème c'est le tracé orange pas le bleu.
L'erreur est là

const int centerY = 928;  // Centre de "M" (axe Y)
const int centerX = 928;  // Centre de "M" (axe X)

La valeur n'est pas 928 mais 1472
Et là, c'est bon

Erreur de ma part...

Mais le programme ne fonctionne toujours pas.
Je vous remets le code utilisé et la photo du M réalisé.

#include <Servo.h>

// Déclaration des servos
Servo servoX, servoY, servoZ;

// Broches des servos
const int servoXPin = 3; // Axe X
const int servoYPin = 5; // Axe Y
const int servoZPin = 6; // Axe Z

// Positions stylo (axe Z)
const int penUp = 110;   // Position stylo relevé
const int penDown = 90; // Position stylo abaissé

// Déplacement des axes (calibrage en degrés)
const int startX = 544;    // Position de départ (axe X, bas)
const int startY = 544;    // Position de départ (axe Y, gauche)
const int topX = 2400;     // Position en haut (axe X)
const int centerY = 1472;  // Centre de "M" (axe Y)
const int centerX = 1472;  // Centre de "M" (axe X)
const int rightY = 2400;   // Droite de "M" (axe Y)

// Temps et vitesse
const int stepDelay = 20; // Délai entre chaque étape (ms)
const int stepSize = 10;   // Incrément de déplacement (degrés)

//position relevé
void stylhaut() {
  servoZ.write(penUp); 
  delay(1000); 
}

//position écriture
void stylbas() {
  servoZ.write(penDown); 
  delay(1000); 
}

// Fonction pour déplacer les axes X et Y simultanément
void moveTo(int targetX, int targetY) {
  static int currentX = startX;
  static int currentY = startY;

  // Déplacement synchronisé
  while (currentX != targetX || currentY != targetY) {
    if (currentX != targetX) {
      int stepX = (targetX > currentX) ? stepSize : -stepSize;
      currentX += stepX;
      if ((stepX > 0 && currentX > targetX) || (stepX < 0 && currentX < targetX)) {
        currentX = targetX;
      }
      servoX.writeMicroseconds(currentX);
    }

    if (currentY != targetY) {
      int stepY = (targetY > currentY) ? stepSize : -stepSize;
      currentY += stepY;
      if ((stepY > 0 && currentY > targetY) || (stepY < 0 && currentY < targetY)) {
        currentY = targetY;
      }
      servoY.writeMicroseconds(currentY);
    }

    delay(stepDelay);
  }
}


void setup() {
  // Attacher les servos à leurs broches respectives
  servoX.attach(servoXPin);
  servoY.attach(servoYPin);
  servoZ.attach(servoZPin);
  Serial.begin(9600);
  // Initialiser la position
  stylhaut();       // Relever le stylo
  moveTo(startX, startY); // Déplacer à la position de départ
}

void loop() {
  // Dessiner la lettre "M"


  moveTo(startX, startY);
  stylbas(); // Abaisser le stylo

  moveTo(topX, startY);

  moveTo(centerX, centerY);

  moveTo(topX, rightY);

  moveTo(startX, rightY);

  // Fin du dessin
  stylhaut();

  moveTo(startX, startY);

  // Stopper après un dessin
  while (true);
}

Il y a aussi deux autres problèmes.

Tu fais

  • currentx=startx et currenty=starty
  • et au premier passage tu demandes de déplacer la plume à startx et starty

Le while est faux et tu ne bouges pas la plume

De même dans les cas ou currentx ou currenty sont égaux à leur cible tu ne fais pas bouger le servo pour l'axe concerné.
Ce qui peut aussi poser problème à l'initialisation.