Aller au contenu


Photo

FourBarQuad525 - Toujours plus rapide


  • Veuillez vous connecter pour répondre
182 réponses à ce sujet

#101 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 7 030 messages
  • Gender:Male

Posté 03 décembre 2020 - 06:56

Le code est très simple, il suffit d'ajouter la fonction delay() à la fin de la boucle du Forward().

La fonction delayMicroseconds() n'est pas adapté, ici, car trop rapide.

 

void Forward(){
  for(int i=0;i<lgTab;i++){
    InverseKinematics(FRx[i],FRy[i],FRLS,FRRS);
    InverseKinematics(BLx[i],BLy[i],BLLS,BLRS);
    InverseKinematics(FLx[i],FLy[i],FLLS,FLRS);
    InverseKinematics(BRx[i],BRy[i],BRLS,BRRS);
  }
  delay(Speed);
}
 


#102 pat92fr

pat92fr

    Membre passionné

  • Membres
  • PipPipPip
  • 796 messages
  • Gender:Male

Posté 03 décembre 2020 - 08:11

Si l'objectif est de faire une courte pause entre chaque point, le delay devrait se trouver dans la boucle.

 

Patrick.



#103 pat92fr

pat92fr

    Membre passionné

  • Membres
  • PipPipPip
  • 796 messages
  • Gender:Male

Posté 03 décembre 2020 - 08:18

Je n'ai pas l'environnement Arduino et de carte pour vérifier, mais je t'invite quand même à faire évoluer ton code pour cadencer ta boucle de manière plus précise. Voici une façon de faire qui ne modifie pas trop ton code initial :

void Forward(
{
  static unsigned long last_time = micros();
  for(;;)
    if(micros()>=last_time+Speed)
      {
        last_time += Speed;
        InverseKinematics(FRx[i],FRy[i],FRLS,FRRS);
        InverseKinematics(BLx[i],BLy[i],BLLS,BLRS);
        InverseKinematics(FLx[i],FLy[i],FLLS,FLRS);
        InverseKinematics(BRx[i],BRy[i],BRLS,BRRS);
      }
}

Speed est exprimée en microsecondes. Le temps de calcul de IK n'intervient plus dans la cadence.

 

Patrick.



#104 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 7 030 messages
  • Gender:Male

Posté 03 décembre 2020 - 09:15

Si l'objectif est de faire une courte pause entre chaque point, le delay devrait se trouver dans la boucle.

Si un delay() se trouve à la fin et à l'intérieur de la boucle, seul la dernière patte en "profite" et la synchronisation ne fonctionne plus.

 

Dans ton exemple, je suppose que tu as oublié de mettre un delay() après chaque appel à InverseKinematics(). Mais là, je ne comprends pas ton code.

Ou alors, tu supposes qu'on doit appeler le delay() qu'une seule fois, ce qui ne fonctionne pas.

 

Mon code complet est plus haut. Ecrivez exactement les modifications que toi et Sandro vous souhaitez, et je les testerai.



#105 pat92fr

pat92fr

    Membre passionné

  • Membres
  • PipPipPip
  • 796 messages
  • Gender:Male

Posté 03 décembre 2020 - 09:23

Si le délai est dans la bouche, le comportement sera le suivant :

1) les 4 pattes bougent pratiquement en même temps,

2) puis une pause,

3) puis elles bougent (toujours synchronisées),

4) puis pause,

etc.

 

Au sujet de mon fragment de code, je ne l'ai pas compilé. Il se peut qu'il y ait une erreur de syntaxe ou un bug, mais dans le principe, c'est pratiquement comme ca que sont codés tous les quadripèdes.

Ca s'applique au tien sans aucun doute. Il existe une façon plus élégante de coder ce type de fonctionnement en utilisant un timer et un callback.

 

Patrick.



#106 Sandro

Sandro

    Membre chevronné

  • Modérateur
  • PipPipPipPip
  • 1 321 messages
  • Gender:Male

Posté 04 décembre 2020 - 12:12

Bonsoir,

je confirme les dires de pat92fr :

tu lances le mouvement des 4 pattes, puis tu fais une pause pour leur laisser le temps de se rendre à la position indiquée.

 

Pour les modifications du code :

- tu enlèves le "delayMicroseconds" de InverseKinematics

- tu remplace la fonction forward par :

void Forward(){
  static unsigned long last_time = micros();
  for(int i=0;i<lgTab;i++){
    InverseKinematics(FRx[i],FRy[i],FRLS,FRRS);
    InverseKinematics(BLx[i],BLy[i],BLLS,BLRS);
    InverseKinematics(FLx[i],FLy[i],FLLS,FLRS);
    InverseKinematics(BRx[i],BRy[i],BRLS,BRRS);

    //on attends de manière à avoir une durée d'exactement Speed avant de lancer la consigne suivante
    while(micros()-last_time<=Speed)
    {
      //ne rien faire : on attend
    }
    last_time += Speed;
  }
}

(j'ai vérifié, ça compile).

 

Prends une trajectoire qui fonctionne, et fait la modif : à priori, ça devrait continuer à fonctionner sans problème

 

Bonne soirée

Sandro
 


Aidez-nous à vous aider : partagez toutes les informations pertinentes : description précise du problème, contexte, schéma de câblage, liens vers la documentation des composants, votre code (ou encore mieux un code minimal reproduisant le bug), ...

Vous recevrez ainsi plus de réponses, et elles seront plus pertinentes.


#107 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 10 175 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 04 décembre 2020 - 01:04

La seule raison qui pourrait expliquer que cela ne fonctionne pas comme espéré, c'est que le temps d'attente avec cette méthode n'est plus assez long ... En faisant une seule fois l'attente pour les 4 pattes, au lieu d'une attente par patte, pour un même temps d'attente dans les deux cas, en attendant une fois par pattes tu attends 4 fois plus longtemps ... 
Donc en faisant ce test il faudra penser à allonger le temps d'attente pour prendre ce facteur de 4 en considération ... 


Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !
En attendant qu'une bibliothèque de fichiers 3D soit mise en place n'hésitez pas à demander si vous avez besoin du fichier 3D d'un des produits de la boutique... On l'a peut être ! 
Si vous souhaitez un robot pilotable par internet n'hésitez pas à visiter www.vigibot.com et à lire le sous forum dédié à vigibot!

 

Les réalisations de Mike118  

 

 

 


#108 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 7 030 messages
  • Gender:Male

Posté 04 décembre 2020 - 08:19

Ok, je n'avais compris le principe. Maintenant, j'ai compris.

J'ai testé avec un Speed=15000, puis j'ai baissé la valeur tout doucement par pas de 500. La distance augmente régulièrement, puis en dessous de 11000, ça régresse.

 

Voici le résultat :

// 04/12/2020 - symetric  Chapeau Chinois SandroV4.3 - Ay=150, a1=112, c1=88, Speed=11000, 385cm 
// 04/12/2020 - symetric  Chapeau Chinois SandroV4.3 - Ay=145, a1=112, c1=88, Speed=11000, 375cm 
 
Cela a l'air un peu moins bon qu'avec la version précédente, sans être vraiment significatif.
// 03/12/2020 - symetric  Chapeau Chinois SandroV4.3 - Ay=150, a1=112, c1=88, Speed=1900, 395cm 
// 03/12/2020 - symetric  Chapeau Chinois SandroV4.3 - Ay=145, a1=112, c1=88, Speed=1900, 375cm 
 
Ce matin, je ne serai pas chez moi. Je ne pourrai pas tester.


#109 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 7 030 messages
  • Gender:Male

Posté 04 décembre 2020 - 09:15

J'ai fait d'autres tests avec des mouvements au pas de 5mm.

Les résultats sont au mieux, équivalents.



#110 pat92fr

pat92fr

    Membre passionné

  • Membres
  • PipPipPip
  • 796 messages
  • Gender:Male

Posté 04 décembre 2020 - 09:27

Bonsoir,

 

Tu as une carte Arduino Mega dans ton robot ?

 

Patrick.



#111 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 7 030 messages
  • Gender:Male

Posté 05 décembre 2020 - 07:30

Tu as une carte Arduino Mega dans ton robot ?

Non, désolé.

J'utilise des Uno ou des Nano.

Pourquoi ?



#112 pat92fr

pat92fr

    Membre passionné

  • Membres
  • PipPipPip
  • 796 messages
  • Gender:Male

Posté 05 décembre 2020 - 09:08

Bonjour,

 

Les cartes Uno et Nano ont un tout petit micro-controleur, qui ne dispose que de quelques périphériques de type TIMER avec une sortie PWM (symbole ~ au niveau du connecteur).

 

http://reso-nance.org/wiki/lib/exe/fetch.php?w=600&tok=0371f2&media=http%3A%2F%2Fwww.gammon.com.au%2Fimages%2FArduino%2FTimer_and_Counter_pins.jpg

 

Je constate qu'il n'y en a pas assez pour tes 8 servos.

 

La génération des signaux PWM pour 8 servos, se fait alors, en partie en matériel et en partie en logiciel, à l'aide de la bibliothèque <Servo> de Arduino. 

 

Dans ton dernier message, je comprends que tu as une période de 11ms en dessous de laquelle, les performances régressent. C'est peut etre la bibliothèque Servo qui atteint ses limites.

 

De mémoire, elle génère les impulsions PWM les unes après les autres pour chaque servo à piloter. La position médiane d'un servo est  à 1.5ms, et tu as 8 servos, ca fait 12ms pour envoyer 8 consignes à 8 servos en moyenne.

 

Voila, le risque, c'est que ton code envoie plus de consignes que la bibliothèque <Servo> n'est capable de transmettre en PWM aux servo. Et du coup, les performances s'écroulent en dessous d'une certaine période (Speed).

 

Je ne maitrise pas les Arduino, et il faudrait investiguer cet éventuel problème, et si cela se confirme, il te faudra changer de manière de faire dans ton code et dans ton excel.

 

Par exemple, tu devrais rester sur un rythme fixe, juste au dessus de la limite de performance de Servo, probablement autours de 16ms et 20ms pour 8 servos (entre 8x2ms et 8x2.5ms).

 

Pour aller plus vite, il te faudra alors jouer sur la distance entre chaque point qui compose la trajectoire, en prenant un intervalle de temps fixe entre ces points : tu pourras toujours augmenter le pas (distance) et la vitesse (distance entre deux points), tout en conservant une cadence compatible de <Servo>.

 

Si quelqu'un connait bien en Arduino, ca aiderait !

 

Bonne chance,

Patrick.



#113 Sandro

Sandro

    Membre chevronné

  • Modérateur
  • PipPipPipPip
  • 1 321 messages
  • Gender:Male

Posté 05 décembre 2020 - 02:22

Bonjour,

j'ai un peu creusé la question (sur un arduino Uno, mais ça ne devrait absolument rien changer (même microcontroleur, même fréquence)).

 

- les servos utilisent tous le même timer : timer1, le seul timer 16 bits (un timer 8 bits ne suffirait pas pour avoir 180 valeurs sur une petite partie de la période totale (sauf à rajouter un compteur d'overflow en soft))

 

- l'instruction servo.write prends entre 48 et 49µs, indépendament du nombre de servos actifs. Écrire une nouvelle valeur pour chacun des 8 servos prends donc moins de 0.4ms. Donc il faudrait que tu donnes plus de 1000 consignes à chaque moteur chaque seconde pour n'utiliser que 50% du temps de calcul.

 

- la génération des signaux pour 8 servos prends environ 0.5% du temps d'exécution : ce n'est donc à priori pas l'élement limitant.

 

Donc à priori, je dirais que la libraire servo n'est pas en cause (à voir pour le calcul de cinématique inverse)


Aidez-nous à vous aider : partagez toutes les informations pertinentes : description précise du problème, contexte, schéma de câblage, liens vers la documentation des composants, votre code (ou encore mieux un code minimal reproduisant le bug), ...

Vous recevrez ainsi plus de réponses, et elles seront plus pertinentes.


#114 pat92fr

pat92fr

    Membre passionné

  • Membres
  • PipPipPip
  • 796 messages
  • Gender:Male

Posté 05 décembre 2020 - 02:41

- l'instruction servo.write prends entre 48 et 49µs, indépendament du nombre de servos actifs. Écrire une nouvelle valeur pour chacun des 8 servos prends donc moins de 0.4ms. Donc il faudrait que tu donnes plus de 1000 consignes à chaque moteur chaque seconde pour n'utiliser que 50% du temps de calcul.

- la génération des signaux pour 8 servos prends environ 0.5% du temps d'exécution : ce n'est donc à priori pas l'élement limitant.

 

 

Un servo demande une impulsion de 0.5 à 2.5ms, plus un temps de pause entre deux impulsions. Comment as tu réussi à envoyer 1000 consignes par seconde à un servo ?

 

En fait, tu dois pouvoir appeler la fonction de la librairie <servo> 1000 fois par seconde (l'appel de fonction est très court), mais le servo commande n'en recevra pas autant, à cause des limites de l'interface PWM (1000 consignes x 1.5ms, ca fait 1.5s, ca ne peut pas rentrer dans une seconde).

 

Je me demandais si <Servo> était vraiment capable d'envoyer 8 impulsions de manière simultanée, avec un seul TIMER. Si ce n'est pas le cas, et si Servo envoie 8 impulsions les unes à la suite des autres, on est plus proche de 50 consignes par seconde par servo. Le code Arduino de Servo est open source ?

 

Patrick.



#115 pat92fr

pat92fr

    Membre passionné

  • Membres
  • PipPipPip
  • 796 messages
  • Gender:Male

Posté 05 décembre 2020 - 02:55

#define REFRESH_INTERVAL    20000     // minumim time to refresh servos in microseconds 

Ok, c'est réglé. Servo met à jour la position des servo avec une période de 20ms dans le code. Donc, par défaut, il échantillonne la position donnée par l'utilisateur au rythme de 20ms, quelque soit le nombre d'appels à la fonction <Servo>. On doit pouvoir changer cette constante dans le code d'Oracid, mais je pense qu'il y a une valeur minimale qui est de l'ordre de 16 ms.

 

Je pense que c'est un facteur limitant à prendre en compte. Lorsque la valeur de Speed de Oracid passe en dessous de 20ms, les servos ne reçoivent même plus toutes les consignes (certaines sont perdues). Le comportement devient difficilement prédictible, ca peut etre mieux ou moins bien, ca devient difficile de comprendre ce qu'il se passe vraiment.

 

Patrick.



#116 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 7 030 messages
  • Gender:Male

Posté 05 décembre 2020 - 03:13

La fonction InverseKinematics() avec les commandes des 2 servos, mais sans delayMicroseconds(), prend le plus souvent 560us, mais les valeurs peuvent aller de 508us à 572us.



#117 Sandro

Sandro

    Membre chevronné

  • Modérateur
  • PipPipPipPip
  • 1 321 messages
  • Gender:Male

Posté 05 décembre 2020 - 03:54

Du coup, je suggère qu'on fixe une fois pour toute la "période" (speed) :

- soit un petit peu plus que 20ms, par exemple 21ms

- soit 25ms : c'est la plus petite valeur qui permette un nombre entier de consignes par seconde (40) tout en restant >20ms

- soit on expérimente avec le define qu'a trouvé Patrick, pour trouver jusqu'à quelle fréquence on peut commander les servos en modifiant les defines. D'après le lien suivant, certain servos peuvent descendre jusqu'à moins de 3ms : http://techniquemodelisme.free.fr/Modelisme/servomoteurs.htm

 

Une fois la fréquence d'asservissement fixée, alors on aura un seul paramètre à changer pour expérimenter : la trajectoire (bon, une trajectoire, c'est plein de paramètres)


Aidez-nous à vous aider : partagez toutes les informations pertinentes : description précise du problème, contexte, schéma de câblage, liens vers la documentation des composants, votre code (ou encore mieux un code minimal reproduisant le bug), ...

Vous recevrez ainsi plus de réponses, et elles seront plus pertinentes.


#118 pat92fr

pat92fr

    Membre passionné

  • Membres
  • PipPipPip
  • 796 messages
  • Gender:Male

Posté 05 décembre 2020 - 04:09

Oui, 20ms c'est le plus simple, ca fait 50 consignes par seconde par servo.

 

Oracid parvient à faire un cycle complet en 440ms environ, ca fait 22 consignes par cycle. Donc une trajectoire en 22 points, c'est déjà bien.



#119 Sandro

Sandro

    Membre chevronné

  • Modérateur
  • PipPipPipPip
  • 1 321 messages
  • Gender:Male

Posté 05 décembre 2020 - 05:07

20ms, je sais pas trop : est-ce une bonne idée d'avoir une fréquence de consignes égale à la fréquence des servos?

D'un coté, on reste "en phase", d'un autre, avec un peu de mal chance, on risque de "rater" un cycle à quelques micro-secondes près.

 

Je pense que l'idéal serait de tester de réduire significativement la durée du define, de manière à réduire la part d'aléatoire sur le respect des consignes envoyées.


Aidez-nous à vous aider : partagez toutes les informations pertinentes : description précise du problème, contexte, schéma de câblage, liens vers la documentation des composants, votre code (ou encore mieux un code minimal reproduisant le bug), ...

Vous recevrez ainsi plus de réponses, et elles seront plus pertinentes.


#120 pat92fr

pat92fr

    Membre passionné

  • Membres
  • PipPipPip
  • 796 messages
  • Gender:Male

Posté 05 décembre 2020 - 06:02

J'ai un analyseur logique, mais je n'ai pas de carte Arduino, alors je fais un croquis de ce que la carte pourrait produire comme signaux de sortie si j'ai bien compris le fonctionnement de servo.cpp :

 

Oracid "attach()" ses 8 servo aux sorties D2 à D9.

 

Servo.png

 

A chaque fois que la routine d'interruption de servo.cpp est appelée par le TIMER, elle active une sortie (un des 8 servos, à tour de rôle), programme le TIMER avec la largeur d'impulsion du servo en cours de traitement, puis rend la main. Lorsque l'impulsion du dernier servo a été générée, elle programme le TIMER pour attendre jusqu'à la prochaine période de 20ms (valeur de REFRESH_INTERVAL).

 

La période REFRESH_INTERVAL doit être supérieure ou égale à la somme des largeurs d'impulsion maximales des huit servos. Les MG92b prennent jusqu'à 2.5ms, ca fait 20ms. Tous les servos ne sont pas à 2.5ms, donc on peut réduire un peu, tout en conservant une marge. En moyenne, si tous les servos sont au neutre à 1.5ms, ca fait 12ms.

 

Donc la marge d'optimisation reste faible :

- 20ms c'est le maximum jamais atteint (8x 2.5ms) 

- 16ms est peut etre un bon compromis (8x 2.0ms)

- 12ms c'est trop peu (8x 1.5ms)

 

Si on baisse trop cette valeur, alors la routine active les 8 servos les uns après les autres, et recommence toute de suite avec le premier servo, sans faire de pause.

La période n'est plus constante, elle varie en fonction de la somme des largeurs d'impulsion.

 

Donc, on peut peut-être améliorer la cadence de quelques ms sans trop de risque en jouant sur le REFRESH_INTERVAL.

 

Une solution pour vraiment augmenter la cadence aurait peut etre été de prendre une carte avec 8 vraies sorties PWM (Mega ou autre). La Mega est un peu plus lourde par contre.

 

Enfin,il y a un léger risque de déphasage entre la routine d'It de servo.cpp et le code forward() de Oracid, D'ailleurs, ca doit déjà être le cas aujourd'hui depuis un certain temps avec les robots à base d'Arduino. Et cela même si on configure le délai dans forward() et le REFRESH INTERVAL de servo.h avec la même valeur (20ms par défaut , ou mieux si on trouve une plus petite valeur). Certains servos risquent d'être décalés de 20ms. Il faudrait modifier le code de servo.cpp pour émettre un signal, au moment du traitement de la pause pour REFRESH INTERVAL par exemple, et le capturer dans la boucle forward() pour les synchroniser parfaitement. C'est faisable, mais à distance et sans carte Arduino, je passe mon chemin ! Désolé, c'était juste une idée en l'air :-)

 

Patrick.






0 utilisateur(s) li(sen)t ce sujet

0 members, 0 guests, 0 anonymous users