Piloter un servo avec 2 gp2d120

Bonjour

Je lis avec assiduité votre forum depuis plusieurs semaines,mais malgre tout je n'est pas trouver de solution a mon probleme!
je suis en train de faire une tondeuse robot autonome,mais j'ai un probleme pour la direction,je voudrai commander le servo de direction en fonction des informations de distance des 2 capteurs IR montes a l'avant gauche et droit.
Je me suis inspiré d'un programme qui commande un servo avec un potentiometre,(cela marche avec 1 IR)mais comment faire quand il y a 2 entrées a prendre en compte?

Yankee76

Salut,
je te conseille de jeter un coup d'oeil sur la librairy servo (si ce n'est pas déjà fait), mais je ne vois pas trop ton problème, peux tu poster ton code source ?

Merci de me repondre

Ce que je veut faire:
le capteur droit commande le sevo pour tourner a gauche.
le capteur gauche commande le servo pour tourner a droite.

Le probleme:
le capteur droit commande le servo sur toute sa course
le capteur gauche est ignoré

Le programme:

  #include <Servo.h>
Servo myservo;

int irG = A4;                                  //entree ir gauche A4
int irD = A5;                                  //entree ir droit A5
int nombre_mesureG=10;                 //nombre de mesure a G
int nombre_mesureD=10;                 //nombre de mesure a D
int irValueG =0;                              // variable to store the value coming from the sensor G
int irValueD =0;                              // variable to store the value coming from the sensor D
int moyenneG;                                   
int moyenneD;

void setup(){

myservo.attach(10);

}
   
  void loop() { 
           //pilotage de la direction avec les capteurs infrarouge
  for (int i=0; i<nombre_mesureD; i++);           //
  for (int i=0; i<nombre_mesureG; i++);           //
  irValueD = analogRead(irD);                         //lit la valeur de ir droit
  irValueD=irValueD+irD;                                 //
  irValueG = analogRead(irG);                         //lit la valeur de ir gauche
  irValueG=irValueG+irG;                                //
  moyenneD=irValueD/nombre_mesureD;               //
  moyenneG=irValueG/nombre_mesureG;               //
  irValueG = map(irValueG,0,1023,0,30);           //transforme le 0,1024 en 0,30°
  irValueD = map(irValueD,0,1023,30,60);         //transforme le 0,1024 en 30,60°
  myservo.write(moyenneG);                         //positionne le servo
  myservo.write(moyenneD);                         //positionne le servo
  delay(200);                                             //delay pour que le servo aille en position
  irValueG=0;                                      //
  irValueD=0;                                      //
  
  }

Tes boucles for ( int i) ne font rien
Qu'elle était ton intention avec ces boucles ?

Un servo se pilote avec un paramètre d'angle entre 0 et 180 degrés. On peut supposer que entre 0° et 90° la tondeuse tourne plus ou moins à gauche, à 90° elle va tout droit et que de 90° à 180° elle tourne plus ou moins à droite.

Ce que je veut faire:
le capteur droit commande le sevo pour tourner a gauche.
le capteur gauche commande le servo pour tourner a droite.

Ca ne veut rien dire.

Ton est avant tout un problème de spécification.
Il faut que tu commence par écrire ce que tu veux faire, ou du moins ce que tu veux que ta tondeuse fasse:
Quand veux-tu tourner à gauche ?
Quand veux tu aller tout droit ?
Quand veux tu tourner à droite ?

Commence à répondre à ces questions en français, après tu devrais trouver comment l'écrire en C.

Merci de me faire d'avancer.

Les boucles(for...):
J'ai recuperé ca sur le net,je croyait que cela servait pour calculer la moyenne des distances!

C'est vrai que j'ai ete un peu trop bref sur la description du fonctionnement.

Le capteur situé a l'avant gauche,mesure la distance avec un eventuel obstacle(idem pour l'avant droit)
Quand un obstacle entre dans le "faiseau" IR, le servo tourne(dans le sens opposé) proportionnellement a la distance.
Si il n'y a rien,le servo reste au neutre.

Avec mon programme actuel,a la mise sous tension,le servo vas en butée(au lieu de rester au neutre),et a des tremblemments,par contre si j'approche ma main du capteur droit,le servo ce deplace legerement vers le neutre;Si j'appoche ma main du capteur de gauche,il ne se passe rien(surement parce que le servo est deja en butée).
a mon avis,le probleme est de faire rester le servo au neutre quand il n'y a rien dans le champ des capteurs!

Yankee76

ps:Je debute,il y a 3 semaines je ne savait meme pas ce q'etait le C/c++

Continue à réfléchir

Tu mesures 2 distances à des obstacles, l'une sur la gauche, l'autre sur la droite.

J'ai l'impression que tu veux te diriger du coté opposé à l'obstacle le plus proche.
Peut être plus l'obstacle et proche, plus tu veux t'éloigner.

Un algorithme simple pourrait être :

  • mesurer la distance à l'obstacle à gauche
  • mesurer la distance à l'obstacle à droite
  • choisir le coté où l'obstacle est le plus prêt
  • m'en éloigner de manière inversement proportionnelle à la proximité de l'obstacle.

Si on cherche à traduire cela en C, ca donne quelque chose du genre :

void loop()
{
      irValueD = analogRead(irD); //lit la valeur de ir droit (valeur haute = objet proche, valeur faible = objet lointain)
      irValueG = analogRead(irG); //lit la valeur de ir gauche
      if ( irValueD > irValueG )
      {
            // obstacle plus prêt à droite, on part à gauche (angle < 90°)
            angle = 90 + map( irValueD, 0, 1023, 0, 30 );
      }
      else
      {
            // obstacle plus prêt à gauche, on part à droite (angle > 90°)
            angle = 90 + map( irValueG, 0, 1023, 0, 30 );
      }
      servo.write( angle );
}

Cela devrait un peu plus se rapprocher de ce que tu veux faire.

Si tu veux faire un calcul de moyenne au lieu d'une lecture unique, tu peux faire comme cela :

#define NB_MESURES     10

void loop()
{
      irValueD = 0;
      irValueG = 0;
      for ( int i = 0 ; i < NB_MESURES ; i++ =
      {
          irValueD += analogRead(irD); // on fait la somme des mesures
          irValueG += analogRead(irG); // on fait la somme des mesures
      }
      irValueD /= NB_MESURES; // on divise par le nombre de mesures pour avoir la moyenne
      irValueG /= NB_MESURES;
      if ( irValueD > irValueG )
      {
            // obstacle plus prêt à droite, on part à gauche (angle < 90°)
            angle = 90 + map( irValueD, 0, 1023, 0, 30 );
      }
      else
      {
            // obstacle plus prêt à gauche, on part à droite (angle > 90°)
            angle = 90 + map( irValueG, 0, 1023, 0, 30 );
      }
      servo.write( angle );
}

Cette méthode ne regarde qu'un coté donc quand on est proche de 2 obstacles, on risque de faire ping-pong.
Une autre approche est de combiner les 2 valeurs de façon a déterminer la direction.
L'un des valeurs va tirer à gauche, l'autre va tirer à droite.

void loop()
{
      irValueD = analogRead(irD); //lit la valeur de ir droit (valeur haute = objet proche, valeur faible = objet lointain)
      irValueG = analogRead(irG); //lit la valeur de ir gauche
      angle = irValueD - irValueG; // la valeur va tendre vers 1023 si un objet est proche à droite ou à -1023 si l'objet est proche a gauche.
                                               // mais si il y a des objets des 2 cotés, elle va tendre du coté de l'objet le plus proche.
      angle = map( angle, -1023, 1023, 60, 120 );
      servo.write( angle );
}

Mais le mieux est de passer vers un modèle d'asservissement PID (Proportionnel Intégrale Dérivée) qui est surement le plus efficace.

Ca fonctionne ,c'est nickel,j'ai utiliser ta deuxieme solution( j'ai chercher pendant 5 minutes pourquoi il y avait une erreur pendant la compiltion ,le sigle = au lieu de ) ).
Par contre le servo a un peu la tremblote autour du neutre,pourquoi ?
Il me semble avoir lu que les IR avaient des petites imperfections(une histoires de condo a rajouter il me semble).
Ou un paramètre du progamme a modifier peut etre?(j'ai rejouter du delay sans grand resultat).

Le PID ca me dit quelque chose,je regarde dans cette voie.

Merci Barbudor.

Yankee76

C'est normal. Les valeurs ne sont pas stable donc ca gigotte de tous les cotés.
j'avais la même chose sur mon robot suiveur de ligne au début jusqu'à ce que je découvre le PID.
La PID à changé ma vie XD Allez Lou Y'a

Sur ce robot j'ai sur le devant un barreau de 5 capteurs IR pour suivre la ligne au sol (GG: GaucheGauche, G: Gauche; C: Centre, D: Droite et DD: DroiteDroite). Je récupère 5 valeurs analogiques entre 0 et 1023 que je combine de la facon suivante :

capteur = (-2000 * capteurGG - 1000 * capteurG + 1000 * capteurD + 2000 * capteurDD)/(capteurGG + capteurG + capteurC + capteurD + capteurDD);

Un espèce de calcul de barycentre dont le but est d'obtenir une valeur entre -2000 et +2000 qui indique le cap du robot par rapport à la ligne suivie.
Ensuite je me suis inspiré du code ici Pololu - 7.c. Advanced Line Following with 3pi: PID Control pour faire mon propre programme.
Une différence par rapport à toi est que je contrôle 2 moteurs donc la différence de vitesse entre els 2 moteurs fait tourner alors que toi tu piote un servo. Ca devrait être simple.

@Barbudor
Que se passe t'il avec ton robot suiveur si tu fais un 8 avec ta ligne ? XD

Il se coupe en 3 XD

A voir. je pense que ca dépend de l'angle des traits au moment du croisement.
Si les traits se croisent à 90°, je pense qu'il continue tout droit. Sinon, il se peut qu'il s'accroche au trait qui fait l'angle le plus fermé avec la direction d'où il vient.
Jamais essayé !

barbudor:
Jamais essayé !

J'en imagine un qui est en train d'imprimer un grand huit..... XD

Apres quelle heures de recherche sur le PID,j'ai encore plus d'interrogations !

Faut-il utiliser une lib PID ?
A quoi correspondent les valeurs -1000 ,-2000 etc...?
Et je n'ai pas trouvé d'explications claires en Français sur l'architecture du programme !

Yankee76

Faut-il utiliser une lib PID ?

Pas nécessairement un lib. Dans mon cas je l'ai codé moi même en direct.

Le fonctionnement général d'un asservissement est celui ci :

La consigne c'est la contrainte que tu donne à ton système : une direction, une vitesse, une position ....
L'action ou commande c'est ce qui sort de l'asservissement et qui agit sur le système.
Le retour c'est la mesure que tu effectue sur le système et qui vient dire à l'asservissement où tu est par rapport à ta consigne.

Dans mon robot :

  • Retour : position par rapport à la ligne (<0 : trop a gauche, 0: ok, >0 trop à droite)
  • Action : contrôle des moteurs
  • Consigne : rester sur la ligne c'est à dire position 0

Dans ton cas :

  • Retour : mesure des obstacles à gauche et a droite
  • Action : le servo
  • Consigne : rester entre 2 obstacles

La consigne et le retour sont 2 valeurs de même signification.
Il faut une et une seule valeur.
Dans mon robot j'ai 5 lectures de capteurs, toi tu en as 2.
J'ai créé une formule qui me retourne une valeur unique qui exprime la position du robot par rapport à la ligne noir (-2000 : trop a gauche, -1000 : à gauche, 0 : centré, 1000 : à droite, 2000 : trop à droite). Cette formule applique des pondérations à la valeur retournée à chaque capteur.
Dans ton cas, tu peux faire comme dans mon exemple la différence entre le capteur droit et le capteur gauche. Plus la valeur se haute en valeur absolue, plus tu as un obstacle prêt. Plus la valeur est négative, plus l'obstacle est sur la gauche. Plus la valeur est négative, plus l'obstacle est sur la droite.

Dans ton cas, si on applique les formules de PID de Pololu ca devrait donner quelque chose du genre :

unsigned int position = irDroite - irGauche;
// Proportionel
int proportional = ((int)position);
// Derivée
int derivative = proportional - last_proportional;
// Intégral
integral += proportional;
 // Remember the last position.
last_proportional = proportional;

// Ca c'est la formule magique qu'il faudra probablement fignoler du coté des coefficients pour atteindre le résultat souhaité.
int power_difference = proportional/20 + integral/10000 + derivative*3/2;
// On cale (max est l'angle max de débattement, soit 30°)
if(power_difference > max)
    power_difference = max;
if(power_difference < -max)
    power_difference = -max;

// On applique le résultat à la commande du servo
servo.write( 90 + power_difference );

En gros ca devrait être à peu prêt ca.
Il faut ajuster certainement les valeurs des coefficients pour que l'action s'applique correctement et non pas trop violemment.

En effet,c'est pas ce qu'il y a de plus simple!!!
Je vais essayer de mettre tout ça dans mon programme et de comprendre comment ça marche!

Merci pour le coup de main.

Yankee76

Me revoila!

C'est sur qu'il n'y a plus de tremblements puisque que le servo ne bouge plus du tout !!!!!
J'ai encore quelques lacunes,je suis pratiquement sur que j'ai mal fait quelque chose dans les "INT" si quelqu'un pouvais me filer au coup de main,se serai sympa.

 #include <Servo.h>
Servo myservo;


int irG = A4;                                  //entree ir gauche A4
int irD = A5;                                  //entree ir droit A5
int last_proportional=0;                       //test
int integral=0 ;                               //test
int max = 30;                                  // angle max du servo



void setup(){

myservo.attach(10);
     
}
   
  void loop()
{
         
     unsigned int position = irD - irG;
                 // Proportionel
int proportional = ((int)position);
                // Derivée
int derivative = proportional - last_proportional;
                // Intégral
integral += proportional;
              // Remember the last position.
last_proportional = proportional; 
      
    // Ca c'est la formule magique qu'il faudra probablement fignoler du coté des coefficients pour atteindre le résultat souhaité.
int power_difference = proportional/20 + integral/10000 + derivative*3/2;
// On cale (max est l'angle max de débattement, soit 30°)
if(power_difference > max)
    power_difference = max;
if(power_difference < -max)
    power_difference = -max;       
       // On applique le résultat à la commande du servo
       myservo.write( 90 + power_difference );
      delay (100);
}

essaye avec int position au lieu de unsigned int. Il faut que la valeur puisse prendre des valeurs négatives.

Merci de prendre du temps pour m'aider.

je viens de faire le changement, mais toujours aucune reaction.

Est-ce que par hasard tu n'aurais pas oublié de rajouter le code pour lire les entrées analogiques ?

Tu ne fais pas la différence entre un "code de principe" et un vrai code ?

EDIT: Je viens d'essayer avec des portars sur les entrées analogiques, mon servo semble réagir correctement.
Ensuite comme je l'ai dit, il y a certainement du tunning a faire sur les paramètres du PID.

int position = analogRead( irD ) - analogRead( irG );

Je commence a comprendre.
Donc le code du PID,c'est "un code de principe",je pensai que c'etait un code "definitif".
Non,je n'ai pas oublier les entrées analogique.
Je vais voir pour faire un vrai code.

Yankee76

int irG = A4;                                  //entree ir gauche A4
int irD = A5;                                  //entree ir droit A5

unsigned int position = irD - irG;

Prend un peu de recul et essaye de comprendre ce que tu fais et ce qu'on te donne.
Ce code que tu as posté ne faisait pas les lecture analogique !
Donc je me permet de douter
En remplaçant ca par la ligne

 int position = analogRead( irD ) - analogRead( irG );

Ca marche a peut prêt sur ma maquette

Maintenant comme je dit, ca a bien marché pour mon robot mais les coeff peuvent devoir être optimiser.

La il va falloir appeler les pros de l'automatisme.