MOVEO - le bras robotique pour apprendre & Chat GPT 4.o
Cette fois, on va causer avec Chat GPT !
Dans l'étape précédente, j'ai passé en revue les principaux éléments mécaniques et électroniques du bras MOVEO, avec l'ambition d'utiliser Chat-GPT 4.o pour générer un code Arduino pour le MEGA ; l'idée de manœuvre est simple : si je suis clair et complet dans ma demande, j'obtiendrais un code propre, commenté et évolutif, comme sait le faire l'IA. J'ai également choisi d'intégrer mon Arduino dans un environnement ROS2 : l'IA devra me fournir les bases pour tester rapidement les implémentations Arduino. Cependant, je le répète une nouvelle fois : si vous n'êtes pas à l'aise avec le code Arduino, vous ne pourrez pas juger de la pertinence des propositions de l'IA, ni de la complétude de ses propositions ; c'est encore plus vrai avec ROS2, qui demande de passer par une démarche d'apprentissage plus académique (je recommande les excellents cours de Edouard RENARD sur Udemy - c'est en anglais mais le sous-titrage est efficace, et l'anglais d'Edouard très bon).
Pour faire cela, je vais devoir construire un prompt, c'est à dire préparer ma requête envers l'IA. Un prompt est un phrase, une question, correctement construite lexicalement parlant ; on doit y trouver, à minima : ce que l'on attend en retour + les éléments caractéristiques. Il est toujours possible d'affiner progressivement les réponses en apportant des précisions, mais dans le cas présent, je veux aller au but le plus directement possible. Notez qu'avec l'IA, il est possible de configurer son projet progressivement, en questionnant ou en suggérant, une fois le contexte installé.
Ce que je veux obtenir :
- un croquis pour Arduino MEGA 2560, permettant de piloter un bras MOVEO sous ROS2
Les caractéristiques de mon projet :
- le bras a 6 DOF, dont un grip commandé par un servo,
- je veux intégrer des codeurs AS5600 sur chaque stepper, et ils seront multiplexés via un TCA9548A,
- les steppers seront commandés via des drivers TB6560,
- le sketch permettra l'envoi direct de commandes simples via le port série de l'Arduino,
- les drivers seront configurés en 1/8e de pas avec 1,8° par pas (classique),
- la 1ère articulation est mue par 2 moteurs qui sont commandés séparément (un driver chacun) mais avec une seul commande.
Voici une proposition de prompt :
Je veux un croquis complet commenté en français pour Arduino MEGA 2560, permettant de piloter un bras MOVEO sous ROS2 ; le bras possède 6 DOF (la BASE, l'articulation 1 mue par 2 moteurs qui sont commandés séparément (un driver chacun) mais avec une seul commande, l'articulation 2, le GRIP-ROTATO, l'articulation 3, le GRIP actionné par un servo standard) ; je veux intégrer des codeurs AS5600 sur chaque stepper, et ils seront multiplexés via un TCA9548A ; les steppers seront commandés via des drivers TB6560 ; le sketch comportera l'intégration dans ROS2 mais aussi l'envoi direct de commandes simples via le port série de l'Arduino pour des tests limités; les drivers seront configurés en 1/8e de pas avec 1,8° par pas.
Ce qui donne le résultat suivant :
Voici un croquis complet en français pour contrôler un bras robotique MOVEO avec 6 degrés de liberté (DOF) à l'aide d'un Arduino MEGA 2560 sous ROS2. Le bras est équipé de plusieurs moteurs pas à pas, un servo moteur pour le grip, et des codeurs AS5600 pour la rétroaction. Les codeurs sont multiplexés via un TCA9548A, et les moteurs pas à pas sont pilotés par des drivers TB6560 configurés en 1/8e de pas.
Le croquis :
#include <Wire.h>
#include <AS5600.h>
#include <TCA9548A.h>
#include <AccelStepper.h>
#include <Servo.h>
#include <rcl/rcl.h> // ROS2 C client library
#include <rclc/rclc.h>
// Déclarations pour ROS2
rcl_publisher_t publisher;
rcl_subscription_t subscription;
// Déclarations pour moteurs pas à pas
AccelStepper stepperBase(1, 2, 5); // Pin STEP, DIR
AccelStepper stepperJoint1a(1, 3, 6);
AccelStepper stepperJoint1b(1, 4, 7);
AccelStepper stepperJoint2(1, 8, 9);
AccelStepper stepperGripRotate(1, 10, 11);
AccelStepper stepperJoint3(1, 12, 13);
Servo gripServo;
// Déclarations pour AS5600 via TCA9548A
TCA9548A tca;
AS5600 encoder;
int encoderAddresses[] = {0, 1, 2, 3, 4, 5};
// Configuration des moteurs pas à pas
#define STEPS_PER_REVOLUTION 200
#define MICROSTEPS 8
#define STEPS_PER_DEGREE (STEPS_PER_REVOLUTION * MICROSTEPS / 360.0)
// Variables globales pour positions
long targetPosition[6] = {0, 0, 0, 0, 0, 0};
// Configuration initiale
void setup() {
Serial.begin(115200);
Wire.begin();
// Initialisation du TCA9548A et des encodeurs
tca.begin();
for (int i = 0; i < 6; i++) {
tca.openChannel(i);
encoder.begin();
encoder.setDirection(1); // 1 pour sens normal, -1 pour inverse
tca.closeChannel(i);
}
// Configuration des moteurs pas à pas
stepperBase.setMaxSpeed(1000);
stepperJoint1a.setMaxSpeed(1000);
stepperJoint1b.setMaxSpeed(1000);
stepperJoint2.setMaxSpeed(1000);
stepperGripRotate.setMaxSpeed(1000);
stepperJoint3.setMaxSpeed(1000);
stepperBase.setAcceleration(500);
stepperJoint1a.setAcceleration(500);
stepperJoint1b.setAcceleration(500);
stepperJoint2.setAcceleration(500);
stepperGripRotate.setAcceleration(500);
stepperJoint3.setAcceleration(500);
// Configuration du servo
gripServo.attach(14); // Broche du servo
// Initialisation ROS2
rcl_allocator_t allocator = rcl_get_default_allocator();
rclc_support_t support;
rclc_support_init(&support, 0, NULL, &allocator);
// Création d'un node ROS2
rcl_node_t node;
rclc_node_init_default(&node, "moveo_controller", "", &support);
// Création d'un publisher ROS2
rclc_publisher_init_default(
&publisher,
&node,
ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, String),
"moveo_status"
);
// Création d'un subscriber ROS2 pour recevoir les commandes
rclc_subscription_init_default(
&subscription,
&node,
ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, String),
"moveo_commands"
);
// Démarrer le spin
rclc_executor_t executor;
rclc_executor_init(&executor, &support.context, 1, &allocator);
rclc_executor_add_subscription(&executor, &subscription, &msg, &subscription_callback, ON_NEW_DATA);
}
// Boucle principale
void loop() {
// Lecture des commandes série pour les tests manuels
if (Serial.available() > 0) {
String command = Serial.readStringUntil('\n');
processCommand(command);
}
// Mise à jour de la position des moteurs pas à pas
stepperBase.run();
stepperJoint1a.run();
stepperJoint1b.run();
stepperJoint2.run();
stepperGripRotate.run();
stepperJoint3.run();
// Mise à jour du ROS2
rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100));
}
// Fonction pour traiter les commandes reçues via le port série ou ROS2
void processCommand(String command) {
// Parsing des commandes (format simple pour l'exemple)
if (command.startsWith("G1")) {
// Ex: G1 X100 Y200 Z300
long positions[6];
parseG1Command(command, positions);
setTargetPositions(positions);
} else if (command.startsWith("GRIP")) {
// Commande pour le servo du grip
int angle = command.substring(5).toInt();
gripServo.write(angle);
}
}
// Fonction pour définir les positions cibles des moteurs
void setTargetPositions(long positions[]) {
targetPosition[0] = positions[0];
targetPosition[1] = positions[1];
targetPosition[2] = positions[2];
targetPosition[3] = positions[3];
targetPosition[4] = positions[4];
targetPosition[5] = positions[5];
stepperBase.moveTo(targetPosition[0]);
stepperJoint1a.moveTo(targetPosition[1]);
stepperJoint1b.moveTo(targetPosition[1]); // Synchronisation de Joint1a et Joint1b
stepperJoint2.moveTo(targetPosition[2]);
stepperGripRotate.moveTo(targetPosition[3]);
stepperJoint3.moveTo(targetPosition[4]);
}
// Fonction de rappel pour les messages ROS2
void subscription_callback(const void * msgin) {
const std_msgs__msg__String * msg = (const std_msgs__msg__String *)msgin;
processCommand(String(msg->data.data));
}
// Fonction pour parser les commandes G1 (Ex: G1 X100 Y200 Z300)
void parseG1Command(String command, long positions[]) {
command.replace("G1 ", "");
char* token = strtok(command.c_str(), " ");
int i = 0;
while (token != NULL) {
positions[i] = atol(token);
token = strtok(NULL, " ");
i++;
}
}
Explications
- Bibliothèques et Déclarations:
- Wire.h, AS5600.h, TCA9548A.h: Pour communiquer avec les codeurs via I2C.
- AccelStepper.h: Pour contrôler les moteurs pas à pas.
- Servo.h: Pour contrôler le servo moteur du grip.
- rcl/rcl.h, rclc/rclc.h: Pour ROS2.
- Initialisation:
- setup(): Initialise la communication série, les périphériques I2C, les moteurs, et ROS2.
- Boucle principale (loop):
- Écoute les commandes série pour des tests manuels.
- Met à jour les positions des moteurs pas à pas.
- Gère les messages ROS2.
- Fonctions supplémentaires:
- processCommand: Traite les commandes reçues via le port série ou ROS2.
- setTargetPositions: Définit les positions cibles des moteurs.
- subscription_callback: Fonction de rappel pour gérer les messages reçus via ROS2.
- parseG1Command: Parse les commandes G1 pour extraire les positions.
Ce sketch fournit un exemple de base pour contrôler un bras robotique MOVEO avec 6 DOF. Vous devrez adapter le code selon les spécificités de votre matériel et de votre configuration ROS2.
Dans ce code, la fonctions "processCommand(String command){}" m'interpelle, je demande donc des explications.
La fonction processCommand est conçue pour traiter les commandes reçues, que ce soit via le port série (pour des tests manuels) ou via ROS2 (pour les commandes de contrôle du bras). Elle analyse la commande reçue et exécute les actions appropriées sur le bras robotique. Voici une explication détaillée de son fonctionnement :
void processCommand(String command) {
// Parsing des commandes (format simple pour l'exemple)
if (command.startsWith("G1")) {
// Ex: G1 X100 Y200 Z300
long positions[6];
parseG1Command(command, positions);
setTargetPositions(positions);
} else if (command.startsWith("GRIP")) {
// Commande pour le servo du grip
int angle = command.substring(5).toInt();
gripServo.write(angle);
}
}
1. Vérification du Type de Commande
La fonction commence par déterminer le type de commande en vérifiant les préfixes de la chaîne command :
- Commande de type "G1" :
Les commandes "G1" sont des commandes de déplacement. Dans le contexte des CNC (Contrôle Numérique par Ordinateur) et des imprimantes 3D, "G1" est une commande standard pour le mouvement linéaire. Ici, elle est utilisée pour indiquer que le bras doit se déplacer à une certaine position.
- Commande de type "GRIP" :
Les commandes "GRIP" sont spécifiques à la manipulation du grip du bras robotique, en particulier pour ouvrir ou fermer la pince du robot.
2. Traitement des Commandes "G1"
Lorsque la commande commence par "G1", la fonction :
Exemple de commande : G1 X100 Y200 Z300
Analyse : La commande est extraite, et les valeurs de position pour chaque articulation sont lues et stockées.
Fonction parseG1Command : Cette fonction analyse la commande G1 pour extraire les positions cibles de chaque articulation du bras.
Fonction setTargetPositions : Une fois les positions cibles extraites, setTargetPositions est appelée pour définir ces positions comme objectifs pour les moteurs pas à pas, initiant ainsi le mouvement du bras vers ces positions.
3. Traitement des Commandes "GRIP"
Lorsque la commande commence par "GRIP", la fonction :
Exemple de commande : GRIP 90
Analyse : La commande contient un angle pour le servo moteur du grip.
Action : L'angle est extrait et converti en entier. Cet angle est ensuite passé à la méthode write de l'objet gripServo pour positionner le servo à l'angle spécifié. Par exemple, gripServo.write(90); positionne le servo à 90 degrés, ce qui pourrait correspondre à une position ouverte ou fermée de la pince.
Conclusion
La fonction processCommand est une partie cruciale du code car elle permet de contrôler le bras robotique de manière flexible, que ce soit via des commandes série pour des tests simples ou via des messages ROS2 pour un contrôle plus avancé et automatisé. Elle interprète les commandes reçues et agit en conséquence pour piloter les différents moteurs du bras robotique.
Une autre question me taraude : pourquoi ne pas recourir à la bibliothèque "rosserial", grand classique dans l'univers ROS ; réponse :
L'utilisation de rosserial est une option courante pour les systèmes embarqués utilisant ROS1, car elle permet de communiquer avec des microcontrôleurs via une connexion série en utilisant le protocole ROS. Cependant, il y a plusieurs raisons pour lesquelles rosserial pourrait ne pas être la meilleure option ou pourquoi il pourrait ne pas être utilisé dans ce sketch particulier pour un système basé sur ROS2:
1. Compatibilité avec ROS2
rosserial a été conçu principalement pour ROS1 et n'a pas de support officiel complet pour ROS2. ROS2 utilise un middleware DDS (Data Distribution Service) pour la communication, ce qui diffère fondamentalement de l'infrastructure ROS1 utilisée par rosserial. Bien qu'il existe des efforts pour porter rosserial vers ROS2 (par exemple, ros2arduino), ils ne sont pas aussi bien intégrés ou largement utilisés que pour ROS1.
2. Flexibilité et Support des Fonctionnalités Avancées
ROS2 offre des fonctionnalités avancées telles que la qualité de service (QoS), la communication réseau distribuée et la sécurité intégrée. Ces fonctionnalités peuvent être limitées ou non supportées par rosserial. En utilisant une implémentation native ROS2, comme une communication directe avec des messages ROS2 standard via une bibliothèque compatible, on peut tirer parti de ces fonctionnalités.
3. Performance et Latence
rosserial utilise une communication série, qui peut introduire une latence significative et une bande passante limitée, surtout lorsqu'il s'agit de données fréquentes ou volumineuses comme les états des joints ou les trajectoires de mouvement. ROS2, en utilisant DDS, peut exploiter des liaisons réseau plus rapides et gérer des communications plus complexes et plus robustes.
4. Complexité et Maintenance
Intégrer rosserial peut ajouter de la complexité, notamment en termes de configuration et de maintenance, surtout si des modifications ou des ajouts doivent être faits pour prendre en charge de nouvelles fonctionnalités ou protocoles. Développer une solution native ROS2 permet une meilleure intégration dans l'écosystème ROS2, facilitant ainsi l'utilisation des outils ROS2 existants et l'intégration de nouveaux composants.
5. Prototypage et Développement
Dans un contexte de prototypage ou de développement, utiliser des bibliothèques et des outils directement compatibles avec ROS2 peut accélérer le développement et simplifier le processus d'intégration, tout en garantissant que les meilleures pratiques et les normes actuelles de ROS2 sont suivies.
Conclusion
En résumé, bien que rosserial soit une option viable pour des applications ROS1 ou des scénarios spécifiques, l'absence de support officiel complet pour ROS2, la nécessité de fonctionnalités avancées, et les avantages d'utiliser une infrastructure native ROS2 justifient souvent de ne pas l'utiliser dans des projets ROS2, surtout pour des systèmes complexes comme le contrôle d'un bras robotique avec plusieurs degrés de liberté et des capteurs avancés.