Développement pour robot NXT par bluetooth

Chapitres traités   

Cette étude va nous permettre de voir comment piloter le robot NXT MINDSTORM en envoyant des commandes bluetooth. L'ensemble des commandes seront exécutées à partir d'un développement en C++ sous QT Creator qui va être en relation avec un service Java, présent sur la même machine ou non, qui s'occupera d'interpréter les commandes souhaitées afin de les communiquer en bluetooth avec le robot choisi.

Choix du chapitre Installation du programme LEJOS NXJ et réglages du bluetooth sous Linux

Afin que tout fonctionne correctement, il faut être sûr que tout soit installé correctement. Voici la procédure à suivre :
Sous Linux Ubuntu
  1. Récuperer tout d'abord le logiciel, sous forme d'archive, leJOS_NXJ_0.9beta.tar.gz. et placez-le directement dans votre home.
  2. Décompressez cette archive afin que le dossier lejos_njx soit automatiquement créé :

  3. Bien entendu, pour la suite, il est préférable d'avoir installé la JDK et même de préciser sa localisation en réglant les variables d'environnement JAVA_HOME, PATH et par la même occasion NXJ_HOME dans le fichier .profile :

  4. En l'état actuel des choses, pour vous puissiez développer des applications sur le robot NXT, vous devez pouvoir télécharger vos programmes par bluetooth. Vous devez donc installer les paquets libusb-dev et libbluetooth-dev.

  5. Pour terminer la phase d'installation, vous devez générer une archive qui prend en compte l'aspect physique de votre matériel de communication par bluetooth. Pour cela, vous devez lancer le build avec l'outil ant en vous plaçant dans le répertoire lejos_nxj/build en mode console. Tapez alors la commande ant.

  6. Si ant n'est pas installé, comme c'est mon cas, pensez à installer le paquet dénommé tout simplement ant.



  7. Vous avez maintenant la possibilité de communiquer avec le robot choisi par bluetooth, sans cable USB. Il suffit de placer une clé bluetouth sur votre ordinateur et de prendre en compte le robot avec lequel vous désirez transférer vos informations diverses :


 

Choix du chapitre Service Java : Commandes directes par bluetooth

Ce chapitre explique comment piloter le robot par liaison bluetooth. Effectivement, il existe une possibilité qui consiste à envoyer des commandes directes qui sont instantanément interprétées par ce dernier. Dans ce cas de figure, vous n'avez pas besoin de déployer un programme spécifique à l'intérieur même du robot. Cette fonctionnalité est directement intégré dans la brique NXT, quelque soit d'ailleurs le firmware intallé.

  1. Voici à ce sujet le document PDF concernant les commandes autorisées par les robot mindstorms NXT.
  2. Ainsi que le document propre à la commande du Capteur Ultrasonic.
Il est possible d'utiliser uniquement le service Java tout seul, sans programme spécifique écrit en C++ au moyen de Qt Creator. Dans ce cas là, c'est beaucoup plus difficile à mettre en oeuvre puisque vous devez maîtriser les commandes bluetooth que vous venez de découvrir dans le document ci-dessus. Cela peut tout de même être intéressant afin d'effectuer des tests direct pour contrôler ou pas un disfonctionnement.

Quelque soit votre motivation, il est de toute façon nécessaire de lancer ce service Java pour le robot NXT concerné puisse récupérer le flux bluetooth relatif aux commandes souhaitées. Cliquez sur l'icône prévue à cet effet dans le répertoir général "btsiris".

Après avoir valider le lancement, voici l'interface visuelle que vous obtenez.

La première valeur hexadécimale de votre requête bluetooth indique la taille de la réponse. Si vous ne souhaitez pas de réponse, il suffit de placer le code hexadécimal 00.

Cette application fait également office de service qui permet de récupérer la requête bluetooth souhaitée, sous forme également de chaîne de caractères avec la structure que nous venons de voir, et de la soumettre instantanément au robot actuellement connecté. Là aussi, si une réponse est démandée, une chaîne de caractères est renvoyée au client dans le même format.

Vous avez ci-dessous le code Java correspondant. Attention toutefois, vous devez passer par la commande nxjpc afin de permettre l'exécution d'une application sur le PC qui pilote le robot à distance nxjpc nxtbluetooth/NXTBluetooth. (ce qui est automatiquement réalisé par le fichier de commande dénommé "robot").

nxtbluetooth.NXTBluetooth.java
package nxtbluetooth;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.*;
import java.net.*;
import java.util.Scanner;
import lejos.pc.comm.*;
import javax.swing.*;

public class NXTBluetooth extends JFrame {
    private JTextArea notification= new JTextArea("Connectez-vous !\n", 20, 50);
    private JTextField iRobot = new JTextField("00:16:53:0C:BC:BF");
    private JTextField iRequete = new JTextField("03 03 F4 01 F4 01");
    private JToolBar boutons = new JToolBar();
    private NXTComm bluetooth;
    private boolean ouvert = false;
    private String retour;
    private ServerSocket service;

    private NXTBluetooth() {
        setTitle("Robot NXT");
        notification.setBackground(Color.YELLOW);
        iRobot.setBackground(Color.RED);
        iRequete.setBackground(Color.ORANGE);
        add(boutons, BorderLayout.NORTH);
        boutons.add(iRobot);
        boutons.add(new AbstractAction("Bluetooth") {
            @Override
            public void actionPerformed(ActionEvent e) {
                connexion();
            }
        });
        boutons.add(new AbstractAction("Stop") {
            @Override
            public void actionPerformed(ActionEvent e) {
                deconnexion();
            }
        });
        boutons.add(iRequete);
        boutons.add(new AbstractAction("Soumettre") {
            @Override
            public void actionPerformed(ActionEvent e) {
                commande();
            }
        });
        boutons.add(new AbstractAction("Effacer") {
            @Override
            public void actionPerformed(ActionEvent e) {
                notification.setText("");
            }
        });        
        add(new JScrollPane(notification));
        pack();
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
    }
    
    private void connexion() {
        try {
            if (!ouvert) {
                NXTInfo infoBluetooth = new NXTInfo(NXTCommFactory.BLUETOOTH, "NXT", iRobot.getText());
                bluetooth = NXTCommFactory.createNXTComm(NXTCommFactory.BLUETOOTH);      
                ouvert = bluetooth.open(infoBluetooth);
                if (ouvert) { 
                    notification.append("Communication avec le robot "+iRobot.getText());
                    new Thread(new Serveur()).start();
                }
                else notification.append("ATTENTION problème avec "+iRobot.getText());
                notification.append("\n");    
            }
        } 
        catch (NXTCommException ex) {
            notification.append("ATTENTION probleme de BLUETOOTH...\n");
        }
    }
    
    private void deconnexion() {
       if (ouvert) 
       try {
          bluetooth.close();
          ouvert = false;
          notification.append("BLUETOOTH interrompu...\n");
          if (service!=null) {
              service.close();
              notification.append("Service (5588) interrompu...\n");
          }
       } 
       catch (IOException ex) { }     
    }
    
    private void commande() {
        try {
            if (ouvert) {
                String[] hexa = iRequete.getText().split("\\s");
                byte[] octets = new byte[hexa.length];
                for (int i=0; i<octets.length; i++) octets[i] = (byte) Integer.parseInt(hexa[i], 16);  
                byte nombreRetour = octets[0];    
                if (nombreRetour>0) {
                    octets[0] = 0;
                    byte[] reponse = bluetooth.sendRequest(octets, nombreRetour);
                    notification.append("Commande : ("+iRequete.getText()+") -- Retour : (");
                    StringBuilder chaine = new StringBuilder();
                    for (byte octet : reponse) chaine.append(octet+" ");
                    chaine.deleteCharAt(chaine.length()-1);
                    retour = chaine.toString();
                    notification.append(retour+")\n");
                }
                else {
                    octets[0] = (byte) 0x80;
                    bluetooth.sendRequest(octets, 0);
                    retour = "sans";
                    notification.append("Commande : ("+iRequete.getText()+")\n");
                }
            }
            else notification.append("ROBOT non connecté...\n");
        } 
        catch (Exception ex) {
            notification.append("ATTENTION : Commande non valide pour le ROBOT\n");
        }
    }

    private class Serveur implements Runnable {
        @Override
        public void run() {
            try {
                service = new ServerSocket(5588);
                notification.append("Service (5588) en action...\n");
                while (ouvert) {
                   notification.append("Attente d'un nouveau client...\n");
                   Socket client = service.accept();
                   String adresse = client.getInetAddress().getHostAddress();
                   notification.append("----------------------------- Connexion : "+adresse+" : \n");
                   Scanner requete = new Scanner(client.getInputStream());
                   PrintWriter reponse = new PrintWriter(client.getOutputStream(), true);
                   boolean fin = false;
                   do {
                       String soumission = requete.nextLine();
                       fin = soumission.equalsIgnoreCase("fin");
                       if (!fin) {
                           iRequete.setText(soumission);
                           commande();
                           reponse.println(retour);
                       }
                   } 
                   while(!fin);
                   client.close();
                   notification.append("----------------------------- Deconnexion\n");
                }
            }
            catch (IOException ex) {
                notification.append("Perte de la communication...\n");
            }            
        }        
    }
    
    public static void main(String[] args) throws Exception {
        new NXTBluetooth();
    }
}

 

Choix du chapitre Développement en C++ sous Qt Creator

Nous allons maintenant voir comment implémenter nos différentes commandes d'exécution afin que le robot puisse réagir en conséquence.

En préalable, il est absolument nécessaire que le service Java que nous venons de découvrir soit activé, soit sur le poste sur lequel vous êtes, soit sur un autre poste du réseau local.

Afin de donner les bonnes directives au robot, vous devez ensuite ouvrir le projet QTRobot qui possède déjà toute l'ossature globale du pilotage à distance au travers du service Java, dont voici la localisation :

Je vous propose de découvrir l'ossature globale du projet, d'une part avec le diagramme de classes et ensuite avec le diagramme de composants :

Diagramme de classes du projet QTRobot :

  1. La classe la plus importante est la classe Robot qui est un thread, puisqu'elle hérite de la classe QThread. L'intérêt de ce choix, c'est qu'il est possible d'agir en même temps sur l'IHM alors que des commandes sont en train d'être envoyées.
  2. Vous remarquez la présence de méthodes de haut niveau qui permet de commander simplement le robot, comme demarrerMoteur(), arreterMoteur(), finDeCourse(), distance(), etc.
  3. De façon interne, ces méthodes ont la responsabilité de générer correctement le flux d'octets au travers du réseau (au travers des classes QTextStream et QTcpSocket) correspondant aux requêtes à soumettre pour le bluetooth, qui est collecté par le service Java.
  4. Lorsque vous désirez proposer des commandes particulières au robot, vous devez les soumettre à la méthode run() de la classe Actions. Cette classe est spécialement prévue pour cette tâche. Elle hérite de la classe Robot de telle sorte que nous pouvons utiliser les méthodes d'actions directement et donc simplement.
  5. Par ailleurs, comme la classe Actions est en réalité un QThread, c'est bien dans la méthode run() qu'il faut préciser l'enchainement des actions pour qu'elles s'exécutent dans un thread séparé.

Si cela vous intéresse, vous pouvez cliquer sur certains composants afin de découvrir le code source correspondant :

  1. Une fois que vous avez placer vos différentes commandes dans la méthode run() de la classe Actions, vous pouvez compiler le projet et l'exécuter :

  2. La première chose à faire est de préciser l'adresse IP du service Java, celui qui va communiquer réellement avec le bon robot, et de cliquez ensuite sur le bouton "Connecter" :

  3. Si tout se passe bien, vous n'avez plus qu'à cliquer sur le bouton "Exécuter commandes" afin de vérifier que le robot exécute bien les actions que vous avez soumises. Par ailleurs, si vous renseignez la méthode alerte(), le message correspondant s'affiche bien dans la barre d'état de votre IHM, ce qui permet de faire une trace des commandes en cours de fonctionnement.

Description des méthodes utiles de la classe Robot
  1. playTone(fréquence, durée) : joue un son sur le robot en spécifiant la fréquence en hertz et la durée du son en millisecondes.
  2. demarrerMoteur(moteur, vitesse) : demarre le moteur spécifié (MoteurA, MoteurB ou MoteurC suivant la connexion au port du robot) à une vitesse comprise entre -100 et 100, ce qui sous entend que vous pouvez reculer.
  3. arreterMoteur(moteur) : arrête le moteur spécifié en cours de rotation.
  4. finDeCourse() : retourne la valeur true ou false suivant que le capteur de fin de course est actuellement actionné ou pas.
  5. distance() : retourne la valeur en centimètre de la distance entre le robot et son obstacle. si le distance est considéré comme très grande par le capteur ultrason, la valeur renvoyée est alors de -1.
  6. sleep(secondes) : met en attente le thread des actions pendant x secondes.
  7. msleep(millisecondes) : met en attente le thread des actions pendant x millisecondes.
  8. alerte(message) : propose un message à la barre d'état de l'IHM pour tracer les différentes commandes proposées.

 

Choix du chapitre Travaux pratiques d'apprentissage des commandes du robot

Nous allons maintenant commencer par piloter ce robot à distance au travers d'un ensemble de travaux pratiques qui vont nous permettre de bien maîtriser les différentes méthodes récupérées par héritage.

Jouer des sons sur le robot

Pour le premier exercice, je vous propose simplement de jouer une suite de sons dans le robot. En vous aidant d'Internet, récupérer les bonnes fréquences en hertz des différentes notes de musique afin de pouvoir monter la gamme de Do Majeur (do, ré, mi, fa, sol, la, si, do) à l'octave 2. Chaque note sera jouer pendant deux dizièmes de seconde. Par ailleurs, il serait souhaitable que la note actuelle soit affichée sur la barre d'état de l'IHM.

Je vous rappelle toute la procédure à suivre, dans l'ordre, pour réaliser correctement vos tests des travaux pratiques qui vous sont demandés :
  1. Dans un premier temps, placez l'ensemble des instructions souhaitées dans la méthode run() de la classe Actions.
  2. Compiler et exécuter l'ensemble du projet.
  3. Si ce n'est pas déjà fait, démarrer votre service Java et connectez-vous par bluetooth sur le bon robot (qui doit être bien entendu en activité).
  4. A l'aide de votre programme C++, connectez-vous ensuite au service Java.
  5. Cliquez enfin sur le bouton "Exécuter commandes" et vérifiez le fonctionnement.

Une petite considération technique avant de commencer à coder. J'aimerais que nous puissions changer la durée commune de chaque note, très rapidement sans qu'il soit nécessaire de faire la modification sur chaque note en particulier.

Correction de ce premier TP à consulter uniquement après avoir fait les différents tests: notes.pdf.

  1. Que remarquez-vous sur la fréquence du premier do par rapport à la fréquence du deuxième do ?
  2. Au niveau des fréquences de toutes les notes de la gamme, d'après-vous que doit-on faire globalement pour monter la gamme à l'octave 3 ?

A partir de la réponse à ces deux questions, je vous propose de modifier votre codage de telle sorte que nous puissions monter la gamme, toujours en Do Majeur, sur quatre octaves consécutives, de l'octave 1 à l'octave 4.

Pour cela, je vous invite à créer une énumération des notes de la quatrième octave, qui va servir de référence, du Do jusqu'au Si. Ensuite, à l'aide d'une itérative, vous devez jouer chacune de ces notes proposées par l'énumération, en commençant bien par l'octave 1.

Correction de ce TP à consulter uniquement après avoir fait les différents tests: octaves.pdf.

Commander les moteurs du robot

Nous allons maintenant voir comment faire déplacer le robot, vers l'avant ou vers l'arrière, qu'il sache tourner à gauche ou à droite, qu'il sache faire des quart de tour ou même des demi-tours complets.

Cette rubrique concerne donc uniquement le pilotage des moteurs A et C, sachant que le moteur A est à droite du robot et que le moteur B se situe à gauche (relativement au sens de la marche).

Je propose de réaliser un certain nombre de scénarii en vous rappellant juste que deux méthodes sont nécessaire pour faire bouger le robot ou l'arrêter, respectivement demarrerMoteur() et arreterMoteur().
Aller vers l'avant ou reculer

Dans le premier TP sur ce sujet, je vous propose tout simplement de savoir avancer et reculer suivant deux vitesses différentes dont l'une est au ralenti sachant que le ralenti sera toujours la moitié de la vitesse normale.

Durant vos différents test, vous avez la possibilité de changer la vitesse normale sachant qu'il est souhaitable de ne pas dépasser la valeur 70 pour éviter de trop consommer d'énergie (penser aux batteries). Dans ce premier test, je vous demande de respecter le scénario suivant.

  1. Le robot doit dans un premier temps avancer au ralenti pendant 2 secondes.
  2. Durant la seconde suivante, le robot continu d'avancer, mais en vitesse normale.
  3. Durant la seconde qui suit, le robot doit de nouveau avancer au ralenti.
  4. Pour terminer, le robot doit reculer pour revenir en position initiale et s'arrêter.

Correction de ce TP à consulter uniquement après avoir fait différents tests en modifiant la valeur de la vitesse normale : avancer.pdf.

Tourner à droite ou à gauche

Vous allez maintenant faire tourner le robot à droite ou à gauche en proposant des vitesses de rotation différentes sur les deux moteurs, sachant que pour ce scénario la vitesse lente doit être le tiers de la vitesse normale.

Prenez une vitesse normale de 50 et respectez le scénario suivant :

  1. Le robot doit dans un premier temps avancer en vitesse normale pendant 1 seconde.
  2. Ensuite, toujours dans la même vitesse, tourner à droite également pendant 1 seconde.
  3. Tourner ensuite à gauche pendant 9 secondes afin de revenir sur nos pas.
  4. Enfin, le robot doit de nouveau avancer normalement pendant 2 secondes pour revenir en position initiale et finalement s'arrêter.

Correction de ce TP à consulter uniquement après avoir fait différents tests en modifiant la valeur de la vitesse normale : tourner.pdf.

Quart de tour à droite ou à gauche

Toujour dans le changement de direction, je vous propose cette fois-ci de faire en sorte que le robot fasse des quarts de tour. Cela sous-entends qu'un moteur reste à l'arrêt pendant que l'autre tourne.

Cette fois-ci, c'est un peu plus délicat à mettre en oeuvre. En effet, quelque soit la vitesse choisie, le robot doit impérativement faire le quart de tour souhaité. Vous allez faire des expériences en choisissant le temps convenable pour réaliser un quart de tour, avec une vitesse de 50 et une vitesse de 25.

Prenez la méthode msleep() plutôt que la méthode sleep() durant la phase du quart de tour et trouvez la relation qui existe pour spécifier le bon temps quelque soit la vitesse choisie. Pour bien tester la quart de tour, prenez une référence (par exemple un mur) et respectez le scénario suivant :

  1. Le robot doit dans un premier temps avancer en vitesse normale pendant 1 seconde.
  2. Ensuite, toujours dans la même vitesse, faites un quart de tour à droite.
  3. Le robot continu tout droit pendant une nouvelle seconde et s'arrête.

Afin de bien évaluer la relation que vous avez trouvée pour proposer le bon temps pendant la phase de quart de tour, effectuez des tests avec des valeurs de vitesses autres que 25 et 50 : quart.pdf.

Prendre en compte les capteurs de fin de course et ultrason

Dans cette session, nous allons utiliser les capteurs afin d'évaluer la distance du robot par rapport à un obstacle éventuel et de l'arrêter si nécessaire.

  1. Le robot dispose d'un capteur de fin de course placé à l'avant qui permet donc d'arrêter le robot si ce dernier touche un obstacle.
  2. Il contient également un détecteur ultrason qui permet d'évaluer la distance en cm sachant qu'à partir d'une certaine distance, le robot ne distingue plus aucun obstacle.
Comme précédemment, nous allons effectuer un certain nombre de tests qui vont nous permettre de bien maîtriser ces deux capteurs.
Fin de course

Avec ce capteur, nous allons réaliser juste deux petits tests, au préalable sans tenir compte des moteurs, et ensuite en faisant avancer le robot vers un obstacle à vitesse réduite.

  1. Pour le premier test, lorsque vous démarrer votre programme, la barre d'état de l'IHM doit afficher "Capteur non actif". Le programme continu indéfiniment jusqu'à ce que vous actionnez à la main sur le fin de course du robot. Le programme se termine alors avec le message "Capteur actif" dans la barre d'état : finDeCourseSeul.pdf.

    Afin de réaliser ce test correctement, et surtout afin d'éviter de polluer la liaison bluetooth par un énorme flot d'octets, je vous invite à interroger le robot tous les 20 millièmes de seconde.

  2. Une fois que le premier test est validé, vous aller maintenant faire avancer le robot en vitesse réduite (20 à 30) près d'un obstacle. Dès que ce dernier est atteint, le robot et le programme s'arrête définitivement (interroger le capteur tous les 30 millièmes de seconde) : finDeCourseAvecMoteurs.pdf.
Capteur ultrason

Le capteur Ultrason sert à évaluer la distance du robot par rapport à un obstacle placé devant lui. La mesure de la distance est exprimée en centimètre, sachant que lorsque la distance est trop grande, le capteur considère qu'il n'existe aucun obstacle et renvoit dont la valeur -1.

Là aussi, nous allons réaliser un certain nombre de test qui vont nous permettre de maîtriser ce capteur ultrason avec ou sans activation des moteurs.

  1. Pour le premier test, la barre d'état de l'IHM doit afficher la distance du capteur ultrason en permanence par rapport à un obstacle avec une fréquence de rafraîchissement de l'ordre de 20 ms. Au départ, placez l'obstacle relativement près du capteur. Le programme continu indéfiniment jusqu'à ce que vous éloignez l'obstacle suffisamment pour qu'il ne soit plus pris en compte, le programme s'arrête alors instantanément.

    Profitez-en pour évaluer la distance maximale que le capteur est capable de mesurer. Il est donc nécessaire que le dernier affichage dans la barre d'état nous montre la dernière distance prise en compte avant l'arrêt définitif : distance.pdf.

  2. Pour ce deuxième test, nous pilotons le robot et réglons sa vitesse de progression en fonction de la distance de l'obstacle. Si la distance du robot est au delà de 50, la vitesse d'avance doit également être de 50. Si la distance est comprise entre 20 et 50, la vitesse d'avance doit correspondre à la distance, ce qui nous permet de faire ralentir le robot à l'approche de l'obstacle. Lorsque le robot s'approche trop près de l'obstacle à 20 cm du bord, le robot s'arrête définitivement : distanceMoteurs.pdf.

 

Choix du chapitre Restructuration de la classe Actions

Pour conclure cette petite étude, je vous propose de structurer la classe Actions de façon plus convaincante en proposant des méthodes et un attribut qui vont nous permettre de piloter le robot encore plus simplement.

Par exemple, vous allez créer une méthode qui permet de faire un quart de tour à droite, dénommé quartDeTourDroite(). Voici l'ensemble des méthodes qui sont à mettre en oeuvre.

Voici le diagramme de classes modifié :

Descriptif de l'attribut et de l'ensemble des méthodes à mettre en oeuvre
  1. vitesse : est le seul attribut qui propose une vitesse d'avance qui sera utile pour l'ensemble des méthodes de la classe. Vous pouvez faire évoluer cette vitesse à tout moment. Pour chacune des méthodes ci-dessous, il s'agira systématiquement de la nouvelle vitesse par défaut. Au départ cette vitesse doit être réglée à 50.
  2. avancer() : cette méthode permet de faire avancer le robot, droit devant, à la vitesse par défaut.
  3. reculer() : cette méthode permet de faire reculer le robot, de façon linéaire, à la vitesse par défaut.
  4. arreter() : arrête les deux moteurs du robot.
  5. tournerADroite() : cette méthode fait tourner le robot à droite à la vitesse par défaut, sachant que le moteur de droite tourne trois fois moins vite que celui de gauche.
  6. tournerAGauche() : cette méthode fait tourner le robot à gauche à la vitesse par défaut, sachant que le moteur de gauche tourne trois fois moins vite que celui de droite.
  7. quartDeTourDroite() : cette méthode fait faire un quart de tour à droite au robot à la vitesse par défaut.
  8. quartDeTourGauche() : cette méthode fait faire un quart de tour à gauche au robot à la vitesse par défaut.
Travail à faire

Pour valider ces différentes fonctionnalités, je vous propose de respecter les travaux suivant dans l'ordre indiqué :
.

  1. Faites la déclaration de l'attribut et des méthodes associés à la classe Actions dans le fichier en-tête. Profitez-en pour définir complètement le constructeur : actions_h.pdf.
  2. Définir ensuite toutes les méthodes dans le fichier source actions.cpp en prenant en compte toutes les comportements désirés pour faire évoluer le robot dans ces trajectoires.
  3. Pour terminer, dans la méthode run(), vous allez tester ces méthodes en faisant faire un parcourt à votre robot. Par exemple, faites-lui faire une trajectoire qui suit un carré complet à la vitesse de 30, dont chaque côté dure 2 secondes : actions_cpp.pdf.