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 :
- Le protocole envoie un code sur 8 bits précédé par une entête
- Les signaux sont envoyés sur la base 1/1200 secondes (0,833 ms)
- Le signal est normalement à 1 (HIGH) au début. Un signal envoyé pendant 65758 ns.
- L'entête est à 0 (DOWN) pendant 8*1/1200 = 8*833 ns = 6664 ns
- Les données sont envoyées sur 8 bits
- Pour la valeur 0, mettre le signal à HIGH pendant 0,833 ms et à DOWN pendant 0,833 ms
- Pour la valeur 1, mettre le signal à HIGH pendant 4*0,833 ms et à DOWN pendant 0,833 ms.
- 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";
}
}
}
}
Hello,
RépondreSupprimerComment faire pour connecter le robosapien v1 au PC ?
Bonjour Diprog59,
SupprimerSolution 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