Accéder au contenu principal

Hack du RoboSapien en Infra-Rouge

Mon fils a eu un RoboSapien V1 il y a une dizaine d'années. Il prenait la poussière sur le haut d'une armoire, jusqu'à ce que j'ai envie de le ramener à la vie. Il était temps, les piles étaient en train de commencer à couler et vu le nombre de servo moteurs qu'il contient, ses jours étaient comptés. Mais non, j'ai réussi à contenir mon irrésistible envie de tout démonter et j'ai décidé de passer par la télécommande Infrarouge pour le piloter.
Le protocole est assez similaire de celui d'une télécommande infrarouge classique avec cependant quelques petites différences. Il est correctement expliqué sur les sites http://www.aibohack.com/robosap/ir_codes.htm et http://www.markcra.com/robot/ir_codes.php. Je vais traduire en français pour ceux qui auraient un peu de difficulté. Il existe des librairies Arduino, mais comme d'habitude, pas toujours de librairie en C#.
Voici les choses importantes à connaitre sur le protocole :
  1. Le protocole envoie un code sur 8 bits précédé par une entête
  2. Les signaux sont envoyés sur la base 1/1200 secondes (0,833 ms)
  3. Le signal est normalement à 1 (HIGH) au début. Un signal envoyé pendant 65758 ns.
  4. L'entête est à 0 (DOWN) pendant 8*1/1200 = 8*833 ns = 6664 ns
  5. Les données sont envoyées sur 8 bits
    1. Pour la valeur 0, mettre le signal à HIGH pendant 0,833 ms et à DOWN pendant 0,833 ms
    2. Pour la valeur 1, mettre le signal à HIGH pendant 4*0,833 ms et à DOWN pendant 0,833 ms.
  6. A la fin, mettre le signal HIGH pendant 65758 ns.


Voici la librairie en C#. Je me suis inspiré de la librairie de décodage des signaux IR de Sony et j'ai rajouté les commandes pour envoyer le signal.

 using System;  
 using Microsoft.SPOT;  
 using System.Threading;  
 using ITEADStudio;  
 using Microsoft.SPOT.Hardware;  
 using GHIElectronics.NETMF.FEZ;  
 using GHIElectronics.NETMF.Hardware;  
 namespace DemoIR  
 {  
   public class RoboSapienIR : IDisposable  
   {  
     static bool running = false;  
     static Thread thread;  
     // Infra Red  
     static OutputCompare remoteGenerator;  
     static PinCapture cap;  
     const int DataLength = 8;  
     private readonly long[] _pulsesDown;  
     private readonly long[] _pulsesUp;  
     const int StartSequence = 60000;  
     const int LogicalOne = 20000;  
     static int[] bits = new int[] { 128, 64, 32, 16, 8, 4, 2, 1 };  
     const int bit0 = 833; //757;  
     const int bitDown = 833; //952;  
     const int bit1 = 4 * bit0; //3366;  
     const int bitHeaderUp = 65758;  
     const int bitHeaderDown = 8 * bit0;  
     public enum Commands  
     {  
       // No Shift  
       TurnRight = 0x80, //$80 - turn right (on left side of remote)  
       RightArmUp = 0x81, //$81 - right arm up (upper left button on remote)  
       RightArmOut = 0x82, //$82 - right arm out  
       TiltBody = 0x83, //$83 - tilt body right  
       RightArmDown = 0x84, //$84 - right arm down  
       RighArmIn = 0x85, //$85 - right arm in  
       WalkForward = 0x86, //$86 - walk forward  
       WalkBackward = 0x87, //$87 - walk backward  
       TurnLeft = 0x88, //$88 - turn left (on right side of remote)  
       LeftArmUp = 0x89, //$89 - left arm up (upper right button on remote)  
       LeftArmOut = 0x8A, //$8A - left arm out  
       TiltBodyLeft = 0x8B, //$8B - tilt body left  
       LeftArmDown = 0x8C, //$8C - left arm down  
       LeftArmIn = 0x8D, //$8D - left arm in  
       Stop = 0x8E, //$8E - stop  
       // Green Shift commands  
       RightTurnStep = 0xA0, //$A0 - right turn step  
       RightHandThump = 0xA1, //$A1 - right hand thump  
       RightHandThrow = 0xA2, //$A2 - right hand throw  
       Sleep = 0xA3, //$A3 - sleep  
       RightHandPickUp = 0xA4, //$A4 - right hand pickup  
       LeanBackward = 0xA5, //$A5 - lean backward  
       ForwardStep = 0xA6, //$A6 - forward step  
       BackwardStep = 0xA7, //$A7 - backward step  
       LeftTurnStep = 0xA8, //$A8 - left turn step  
       LeftHandThump = 0xA9, //$A9 - left hand thump  
       LeftHandThrow = 0xAA, //$AA - left hand throw  
       Listen = 0xAB, //$AB - listen  
       LeftHandPickup = 0xAC, //$AC - left hand pickup  
       LeanForward = 0xAD, //$AD - lean forward  
       Reset = 0xAE, //$AE - reset  
       ExecuteMaster = 0xB0,//$B0 - Execute (master command program execute)  
       Wakeup = 0xB1, //$B1 - Wakeup  
       Right = 0xB2, //$B2 - Right (right sensor program execute)  
       Left = 0xB3, //$B3 - Left (left sensor program execute)  
       Sonic = 0xB4, //$B4 - Sonic (sonic sensor program execute)   
       // Orange shift commands        
       RightHandStrike3 = 0xC0,//$C0 - right hand strike 3  
       RightHandSweep = 0xC1, //$C1 - right hand sweep  
       Burp = 0xC2, //$C2 - burp  
       RightHandStrike2 = 0xC3, //$C3 - right hand strike 2  
       High5 = 0xC4, //$C4 - high 5  
       RightHandStrike1 = 0xC5,//$C5 - right hand strike 1  
       Bulldozer = 0xC6, //$C6 - bulldozer  
       Oops = 0xC7, //$C7 - oops (fart)  
       LeftHandStrike3 = 0xC8, //$C8 - left hand strike 3  
       LeftHandSweep = 0xC9, //$C9 - left hand sweep  
       Whistle = 0xCA, //$CA - whistle  
       LeftHandStrike2 = 0xCB, //$CB - left hand strike 2  
       Talkback = 0xCC, //$CC - talkback  
       LeftHandStrike1 = 0xCD, //$CD - left hand strike 1  
       Roar = 0xCE, //$CE - roar  
       AllDemo = 0xD0, //$D0 - All Demo  
       PowerOff = 0xD1, //$D1 - Power Off (drop snow-globe and say "Rosebud")  
       Demo1 = 0xD2, //$D2 - Demo 1 (Karate skits)  
       Demo2 = 0xD3, //$D3 - Demo 2 (Rude skits)  
       Dance = 0xD4 //$D4 - Dance   
     }  
     public RoboSapienIR(Cpu.Pin inputPin, Cpu.Pin outputPin)  
     {  
       cap = new PinCapture(inputPin, Port.ResistorMode.Disabled);  
       remoteGenerator = new OutputCompare(outputPin, false, 300);  
       _pulsesDown = new long[DataLength];  
       _pulsesUp = new long[DataLength];  
     }  
     /// <summary>  
     /// Blocking method for input pulse measuring  
     /// </summary>  
     /// <param name="port">Input port</param>  
     /// <param name="state">State to measure length</param>  
     /// <returns>Pulse length in ticks</returns>  
     private static long PulseIn(PinCapture port, bool state, out long up, out long down)  
     {  
       var lowTime = DateTime.Now;  
       while (port.InternalPort.Read() != state) { }  
       var startTime = DateTime.Now;  
       while (port.InternalPort.Read() == state) { }  
       var delta = DateTime.Now - startTime;  
       up = startTime.Ticks - lowTime.Ticks;  
       down = delta.Ticks;  
       return up;  
     }  
     public int ReadSignal()  
     {  
       Debug.Print("Capturing...");  
       long headerUp;  
       long headerDown;  
       // wait for start sequence  
       while ((headerUp = PulseIn(cap, false, out headerUp, out headerDown)) < StartSequence) { }  
       // receive data  
       for (int i = 0; i < DataLength; i++)  
       {  
         PulseIn(cap, false, out _pulsesUp[i], out _pulsesDown[i]);  
       }  
       Debug.Print("Head #0" + "= " + headerUp);  
       Debug.Print("Head #1" + "= " + headerDown);  
       for (int i = 0; i < _pulsesDown.Length; i++)  
       {  
         Debug.Print("Edge #" + i + "= " + _pulsesUp[i]);  
         Debug.Print("Down #" + i + "= " + _pulsesDown[i]);  
       }  
       Debug.Print("Done programing!");  
       int command = -1;  
       DecodePulses(_pulsesUp, out command);  
       Debug.Print("Command = " + command);  
       return command;  
     }  
     /// <summary>  
     /// Decodes received pulses using SONY protocol  
     /// </summary>  
     /// <param name="pulses">Array with pulses</param>  
     /// <param name="command">Command decoded</param>  
     public static void DecodePulses(long[] pulses, out int command)  
     {  
       command = 0;  
       byte mask;  
       // Decode command  
       for (int i = 0; i < pulses.Length; i++)  
       {  
         mask = (pulses[i] > LogicalOne) ? (byte)1 : (byte)0;  
         //mask <<= i;  
         //command |= mask;  
         command += mask * bits[i];  
       }  
     }  
     public void RSPlay(Commands command)  
     {  
       RSPlay((uint)command);  
     }  
     public void RSPlay(uint command)  
     {  
       Debug.Print("Send RS signal " + command);  
       uint[] rsbuffer = new uint[19];  
       rsbuffer[0] = bitHeaderUp;  
       rsbuffer[1] = bitHeaderDown;  
       for (int i = 1; i < 9; i++)  
       {  
         if ((command & 128) != 0)  
           rsbuffer[i * 2] = bit1;  
         else  
           rsbuffer[i * 2] = bit0;  
         command <<= 1;  
         rsbuffer[i * 2 + 1] = bitDown;  
       }  
       rsbuffer[18] = bitHeaderUp;  
       remoteGenerator.SetBlocking(false, rsbuffer, 0, rsbuffer.Length, 100, true, 38000);  
     }
     public void Dispose()  
     {  
       sensor = null;  
       stop();  
     }  
     public void stop()  
     {  
       running = false;  
     }  
     public string ToString(long command) {  
       switch (command)  
       {  
         case 0x80 : return "Turn right (on left side of remote)";  
         case 0x81 : return "Right arm up (upper left button on remote)";  
         case 0x82 : return "Right arm out";  
         case 0x83 : return "Tilt body right";  
         case 0x84 : return "Right arm down";  
         case 0x85 : return "Right arm in";  
         case 0x86 : return "Walk forward";  
         case 0x87 : return "Walk backward";  
         case 0x88 : return "Turn left (on right side of remote)";  
         case 0x89 : return "Left arm up (upper right button on remote)";  
         case 0x8A : return "Left arm out";  
         case 0x8B : return "Tilt body left";  
         case 0x8C : return "Left arm down";  
         case 0x8D : return "Left arm in";  
         case 0x8E : return "Stop";  
         case 0x90 : return "P (Master Command Program)";  
         case 0x91 : return "P>> (Program Play, the one on the bottom)";  
         case 0x92 : return "R>> (Right sensor program)";  
         case 0x93 : return "L>> (Left sensor program)";  
         case 0x94 : return "S>> (Sonic sensor program)";  
         case 0xA0 : return "Right turn step";  
         case 0xA1 : return "Right hand thump";  
         case 0xA2 : return "Right hand throw";  
         case 0xA3 : return "Sleep";  
         case 0xA4 : return "Right hand pickup";  
         case 0xA5 : return "Lean backward";  
         case 0xA6 : return "Forward step";  
         case 0xA7 : return "Backward step";  
         case 0xA8 : return "Left turn step";  
         case 0xA9 : return "Left hand thump";  
         case 0xAA : return "Left hand throw";  
         case 0xAB : return "Listen";  
         case 0xAC : return "Left hand pickup";  
         case 0xAD : return "Lean forward";  
         case 0xAE : return "Reset";  
         case 0xB0 : return "Execute (master command program execute)";  
         case 0xB1 : return "Wakeup";  
         case 0xB2 : return "Right (right sensor program execute)";  
         case 0xB3 : return "Left (left sensor program execute)";  
         case 0xB4 : return "Sonic (sonic sensor program execute)";  
         case 0xC0 : return "Right hand strike 3";  
         case 0xC1 : return "Right hand sweep";  
         case 0xC2 : return "Burp";  
         case 0xC3 : return "Right hand strike 2";  
         case 0xC4 : return "High 5";  
         case 0xC5 : return "Right hand strike 1";  
         case 0xC6 : return "Bulldozer";  
         case 0xC7 : return "Oops (fart)";  
         case 0xC8 : return "Left hand strike 3";  
         case 0xC9 : return "Left hand sweep";  
         case 0xCA : return "Whistle";  
         case 0xCB : return "Left hand strike 2";  
         case 0xCC : return "Talkback";  
         case 0xCD : return "Left hand strike 1";  
         case 0xCE : return "Roar";  
         case 0xD0 : return "All Demo";  
         case 0xD1 : return "Power Off (drop snow-globe and say \"Rosebud\")";  
         case 0xD2 : return "Demo 1 (Karate skits)";  
         case 0xD3 : return "Demo 2 (Rude skits)";  
         case 0xD4: return "Dance";  
         default: return "Command not found";  
       }  
     }  
   }  
 }  

Commentaires

  1. Hello,
    Comment faire pour connecter le robosapien v1 au PC ?

    RépondreSupprimer
    Réponses
    1. Bonjour Diprog59,

      Solution 1 - Connexion Infra Rouge (IR) non intrusive
      Il y a une dizaine d'année, la plupart des laptops étaient équipés d'une interface Infra Rouge. Sur un PC fixe, il était également possible de s'équiper d'un dongle Infrarouge (voir l'ActiSYS 220 que j'ai acheté à l'époque). Cette interface supportait essentiellement le protocol série IRDA, mais il était possible en "grugeant" un peu de simuler un protocol IR de télécommande en envoyant des caractères. Il fallait cependant jouer sur les bits d'arrêts, la vitesse de transmission, etc.
      Aujourd’hui, il existe des extensions PC telles que le PCIR (http://www.intolect.com/pcirdetail.htm) ou l'USB-UIRT (http://www.usbuirt.com/order.htm) qui permettent à la fois de recevoir et d'émettre. Leur prix varie entre 35 et 50€.
      Personnellement, j'utiliserai plutôt un micro-contrôleur tel que le Fez-Panda II ou le FEZ Cerbuino Bee avec une interface Bluetooth ou une interface Ethernet (pour le Cerbuino Bee). Le PC aurait ainsi juste à transmettre en Bluetooth ou via ethernet le code à envoyer en IR par le micro-contrôleur. J'essaierai de poster le dispositif correspondant la semaine prochaine.

      Solution 2 - Ouvrir le Robosapiens pour le contrôler directement à partir d'un micro-contrôleur
      Certains de mes collègues de Bell Labs l'ont fait pour les besoins d'une démo il y a 4 ans environ. Ils ont collé un Arduino au dos d'un RS et piloté le tout à partir d'un PC connecté à une XBOX Kinect. Le robot avait un long fil à la patte, mais le résultat était bluffant. Cela demande un peu plus d'expertise et d'avoir le courage de démonter son Robosapiens. Tu pourras trouver des exemples sur http://playground.arduino.cc/Main/RoboSapienIR et http://www.markcra.com/robot/hackrobos.php, mais c'est vraiment très intrusif. L'approche IR me semble beaucoup plus douce.

      ftoure

      Supprimer

Enregistrer un commentaire

Posts les plus consultés de ce blog

Utilisez votre tablette Android comme second écran pour Linux (Raspberry Pi, MK908II)

Les tablettes Android atteignent désormais des prix qui défient toute concurrence. On trouve désormais des modèles à 39 € TTC en super marché, soit à peine plus cher que le Raspberry PI, mais avec un écran. Ces modèles souvent mono-core 1Ghz ou 1,4 Ghz avec 512 ou 1Go de mémoire ne sont très probablement pas utilisables pour une utilisation régulière sur Internet et ne sont en aucun point comparables à leur équivalent de marque (Samsung, Sony, LG, HTC, Lenovo, etc). Plusieurs tutoriels indiquent comment connecter utiliser une tablette Android comme second écran ( http://www.linux-magazine.com/Online/Blogs/Productivity-Sauce/Use-an-Android-Device-as-Screen-and-Input-for-Raspberry-Pi ). Ces méthodes utilisent généralement l'USB Tethering qui n'est malheureusement disponible que sur les téléphones ou tablettes avec un accès mobile (3G ou 4G) inclus. Dans ce billet, je vais vous montrer comment se connecter à une tablette en utilisant le mode Debug adb (Android Debug Bridge

Supprimer les partitions Raspberry sur une carte SD sous Windows avec Diskpart

Si vous souhaitez récupérer une ancienne carte SD utilisée pour démarrer un Raspberry pour un autre usage (appareil photo, etc), il vous faudra supprimer les deux partitions créées au moment de l'écriture de l'image sur la carte SD. Vous pouvez voir les partition en sélectionnant Menu Windows/Ordinateur/bouton droit "Gérer". Voici un exemple du résultat final. Vous pouvez supprimer la partition Unix de 7 Gb (ou 4Gb en fonction de la taille de votre carte) en sélectionnant la partition puis en faisant "bouton droit Supprimer". Laissez juste une partition pour pouvoir faire les autres manipulations avec DISKPART. Démarrez l'outil DISKPART en ligne de commande. Une nouvelle fenêtre s'ouvrira. Microsoft DiskPart version 6.1.7601 Copyright (C) 1999-2008 Microsoft Corporation. DISKPART> list disk   N° disque  Statut         Taille   Libre    Dyn  GPT   ---------  -------------  -------  -------  ---  ---   Disque 0    En ligne        238 G octe

Emulateur Raspberry Pi sous Windows

Si vous souhaitez développer ou tester des applications pour Raspberry Pi, vous pouvez, soit compiler directement l'application sur Raspberry, soit la développer sous Linux ou Windows et la compiler pour Raspberry. La seconde solution est souvent plus simple car elle permet de disposer d'un environnement de compilation complet tel qu'Eclipse pour le développement. Une fois l'application développée, il faut la tester sur Raspberry. Là, il faut copier l'application en utilisant un client FTP ou SCP, puis se connecter en SSH et lancer l'exécutable. Il existe un autre moyen de tester une application Raspberry sans avoir à l'allumer. Il suffit de passer par un émulateur tel que QEMU qui permet de lancer un OS pour processeur ARM sous Linux ou Windows. L'émulateur sous Windows 1. Récupérez l'émulateur à l'adresse suivante : http://sourceforge.net/projects/rpi-emulator-win32/ . 2. Dézippés le contenu de l'image Rpi-Occidentalis-v02-qemu.7z av