Etape 12 : P.3, Code : cinématique Inverse et autre Code Bits
Dans un premier temps, nous avons considéré, y compris les calculs de cinématiques inverses dans notre code Objective-C, mais cela s’est avéré pour être une mauvaise idée parce qu’il y a beaucoup de calculs à faire lors de l’initialisation, et après qu’ils sont fait une fois, il n’y a pas besoin de les répéter encore une fois. Après tout, le fait que les objets ne se déplacent est le fondement de notre système de contrôle. Au lieu de cela, nous avons des angles de coder en dur.
Une pièce très importante du code est une méthode qui va ralentir le bras comme il approche de sa destination, au lieu de bousculer il complètement et en espérant qu’il ne démarre pas oscillant (peut-être dans un monde idéal avec servos idéales).
Tout d’abord, nous faisons une pause l’exécution pour un peu de temps pour permettre le servo passer à l’angle que nous leur donner (la première fois cette méthode est appelée, la volonté de servo pas encore être en mouvement) :
[NSThread sleepForTimeInterval:0.03] ;
Ensuite, nous comparons l’asservissement de position angulaire actuelle à la position de fin désirée - si elle a assez (ce que nous avons définie à 2 degrés), proche puis définissez la variable qui stocke l’angle actuel égale à la position de fin désirée (vous verrez pourquoi c’est logique dans un instant) :
Si (abs(targetAngle-angle) < = 2) {angle = targetAngle;}
Maintenant, calculer un dixième de la différence entre l’angle actuel et l’angle de la cible - si l’angle actuel est égal à l’angle de la cible, il retournera zéro :
double diff = (targetAngle - angle) * 0,1 ;
Maintenant, déplacez le servo à la position angulaire actuelle plus d’un dixième de la différence - si l’angle actuel est égal à l’angle de la cible, ce qui cause le servo déplacer tout le chemin jusqu'à l’angle de la cible :
[auto moveActualTo:(angle + diff)] ;
Voir la description de la vidéo ci-dessous pour obtenir une meilleure compréhension de comment tout pourquoi cela est fait. Vous pouvez trouver ce code dans la méthode calculateChange dans Motor.m, qui est fixé dans le cadre d’un fichier zip à l’étape suivante.
La première version de notre code utilisé seulement l’électro-aimant (c’est ce que nous avons pris au tournoi régional - la vidéo a été prise vers 06:00 le jour de, après une nuit blanche à faire fonctionner!) et attend quelque chose comme ceci :
Description de la vidéo YouTube :
« C’est le premier test complet de nos bras robotique pour l’Olympiade de Science du Massachusetts. Nous avons eu une des coordonnées mal dans notre programme (Oui, il s’est avéré être un problème de logiciel - beaucoup mieux qu’un bug aléatoire « MOSFET ne tourne pas »!), donc il n’a pas pu ramasser un des clous, mais nous l’avons eu fixe dans le temps pour l’événement régional plus tard ce jour-là et a remporté la première place par un glissement de terrain !
L’un des troubles nous avions précédemment (et ayant beaucoup d’équipes au tournoi régional) a été oscillant servos et servos qui aller trop vite et le bras oscillant dans des chemins imprévisibles. Pour résoudre ce problème, nous avons écrit un algorithme qui ralentit les servos car ils s’approchent de leur destination finale en lui donnant un flux continu de coordonnées qui sont exponentiellement plus étroite et plus près de l’angle de la cible jusqu'à ce que le servo se trouve quelques delta, habituellement un ou deux degrés, de cet angle et assez proche juste rejoindre directement les débats. Chaque itération est calculée en trouvant un dixième de l’angle que le servo doit encore se rendre afin de terminer l’étape. Au lieu d’utiliser la récursivité pour avoir la méthode de calcul à l’angle à s’appeler lui-même jusqu'à la fin de l’étape, qui est le choix évident, nous avons utilisé un NSTimer dans son propre thread pour éviter d’avoir à ajouter des délais à notre programme de suivre les calculs avec les servos (les servos se déplacent beaucoup plus lentement que l’ordinateur peut cracher des angles).
La raison pour laquelle nous utilisons décroissance exponentielle plutôt que simplement ralenti le mouvement linéaire est que le bras se comporte essentiellement comme un oscillateur harmonique amorti (si l'on suppose que le terme proportionnel au contrôleur de PID interne du servo domine autour de la valeur de consigne, qui semble correspondre à ce que j’ai observé lorsque le bras est oscillant), donc si nous conduire linéairement, il commencer avec une énergie cinétique initiale une fois que c’est au lieu de destination et oscillent autour de lui plutôt que venir à l’arrêt. La seule solution (si nous insistons sur le conduire linéairement) est de ralentir le bras pour réduire l’amplitude de l’oscillation jusqu'à ce que nous considérons acceptable, qui n’est pas souhaitable si l'on considère qu’il s’agit d’une compétition chronométrée. Au lieu de cela, si nous forcer à l’angle suivant avec dans un motif de façon exponentielle en décomposition, il devrait y arriver avec très peu d’énergie cinétique, avec erreur causée uniquement par le fait que nous lui donnons des données discrètes.
Toutes les positions qui les servos doivent atteindre sont calculée à l’aide de cinématique inverse et puis réglée individuellement en utilisant le mode manuel de notre programme aussi exacte que possible. Une des choses que nous travaillons sur des États le mois prochain est contrôle cinématique inverse en temps réel qui permettre de déplacer l’effecteur de la dépendance dans n’importe quel chemin précis que nous choisissons, plutôt que de simplement déplacer les servos à chaque angle un à la fois comme nous le faisons maintenant.
Même si nous n’avons qu’un électro-aimant et non pas une pince universelle ou la griffe, permettant de ramasser des objets (ainsi nous obligeant à laisser la moitié de la grille de jeu intacte), contrôle automatique signifie que ce bras sont capables de ramasser les objets rapidement et sans erreur humaine, afin que nous plaçons avec succès 100 % des objets que nous avons pour objectif de placer chaque fois que nous courons le bras".