Next

Prev

Prev-tail

Tail

Up

Chapter 26
Aldebaran Nao

 26.1 Introduction
 26.2 Starting up
 26.3 Accessing joints
  26.3.1 Advanced parameters
 26.4 Leds
 26.5 Camera
  26.5.1 Slots
 26.6 Whole body motion
 26.7 Other sensors
 26.8 Interfacing with NaoQi
  26.8.1 Accessing the NaoQi shared memory region
  26.8.2 Accessing standard NaoQi modules
  26.8.3 Binding new NaoQi modules in Urbi
  26.8.4 Writing NaoQi modules in Urbi
  26.8.5 More examples

26.1 Introduction

The Nao is a 60 centimeters tall humanoid robot built by Aldebaran Robotics. It has an onboard Geode processor running Linux, 25 degrees of freedom, two onboard cameras, speakers, microphones, accelerometers, ultrasound and IR sensors...

26.2 Starting up

Nao comes with an installed version of Urbi.

On some versions of Nao, Urbi is not automatically started upon start-up. If this is the case, you must take the following steps to activate Urbi:

Those lines might already be present but commented out.

You should be able to connect to your Nao on the port 54000 and send it urbiscript:

 
tts.say("Hello, I am Nao."), 
// Activate right arm. 
armR.load = 1; 
// Wave the arm: put it in position 
shoulderRollR.val = -0.3 time: 0.5s | 
shoulderPitchR.val = -1 speed: 0.9 | 
// Wave it 
timeout(6s) shoulderRollR.val = -0.4 sin:2s ampli:0.4; 
// And put it back down, using the uncompressed name this time 
robot.body.arm[right].shoulder.pitch.val = 2 speed:0.9; 
// Activate all motors. 
motors.on; 
// Stand 
motion.walkTo(0.1,0,0); 
// Start walking... 
tag: robot.walk(1), 
// ... and interrupt the movement 
sleep(2s) | tag.stop; 
// Get the list of all devices and the interfaces they implement: 
robot.dump;  

26.3 Accessing joints

All the Nao joints are accessible through their standard Nao and Urbi names, and respect the Urbi standard specifications for servo motors: Basically each motor has a load field that is linked to the motor stiffness, and a val field that can be used to read or write the motor position.

Some motor groups are also provided: they can be used exactly as joints, but any action performed on a group is sent to all the joints that are a member of this group.

 
// Activate all motors 
motors.load = 1; 
// Or only all the head motors 
head.load = 1; 
// Move headYaw to 0.8 in one seconds. Since this commands takes 
// one second to execute, terminate with a comma to be able 
// to send other commands while it executes. 
headYaw.val = 0.8 time: 1s, 
// Move headPitch to -0.5 as fast as possible. 
headPitch.val = -0.5; 
// Move headYaw continuously in a sinusoidal trajectory... 
tag: headYaw.val = 0 sin:2s ampli:0.5, 
// ...and stop the movement 
tag.stop;  

The two hands are purposefully not included in the ’motors’ group. Although you can control them using the val slot, it is recommended that you use the open() and close() methods, since they stop the motors once the movement is finished.

26.3.1 Advanced parameters

26.3.1.1 Trajectory generator period

The trajectory generators will issue write command at a period given by the System.period variable. The default is 10ms which is the native period of NaoQi.

26.3.1.2 Motor back-end method

Urbi can use two different methods to send the joint commands: DCM::send(), or ALMotor::setAngle(). The mode can be changed by calling motor.setDCMWrite(bool).

In setAngle() mode, each write operation to a joint will synchronously call the setAngle() NaoQi method.

In DCM mode, Urbi will set a hook on the DCM update, and send all commands in this hook using the send() method. Urbi will send commands S milliseconds in the future. S defaults to 50ms, and can be changed by calling ALMotor.setDCMShift(shiftValue).

26.3.1.3 Motor command debugging

The motors implements a debugging mode that can be activated by calling ALMotor::setTrace(1). While activated, it will write all sent commands to the file ‘/tmp/traj.log’. Each line will contain the motor number, the command timestamp, and the command position. Be careful not to let this feature active while not using it, as it would quickly fill-up the memory.

26.4 Leds

All Nao’s leds and led groups, as defined by NaoQi, are available as objects in urbiscript. You can set the led values by writing to the slots val (intensity between 0 and 1), r, g, b (color intensity between 0 and 1) or rgb (RGB value, one byte each, i.e. 0xFF0000 is red).

26.5 Camera

The Nao camera object is instantiated by default, under the standard name camera. It is deactivated by default, set its load field to 1 to activate. The default frame-rate is only 4fps, so you might want to increase it before activating the camera.

If you wish to use RTP instead of TCP to receive the camera image, you must:

RTP will then be used automatically.

26.5.1 Slots

26.6 Whole body motion

The whole body motion component of the NaoQi can be activated by reading and writing to the pos slot of both hands. This sets a target position in Cartesian space for the hand. The NaoQi will use its whole body to try to reach it. Be careful to only ask for reachable positions.

26.7 Other sensors

The following sensors are available through a urbiscript variable. Reading the variable will give the latest available sensor value.

26.8 Interfacing with NaoQi

26.8.1 Accessing the NaoQi shared memory region

Many NaoQi modules communicate through a shared hash table handled by the ALMemory module. This module is available under the stm name in Urbi, and has the following slots:

26.8.2 Accessing standard NaoQi modules

All standard NaoQi modules (ALMemory, ALLogger, ALMotion, ALFrameManager, ALTextToSpeech, ALAudioPlayer...) are available in Urbi. You can call all their methods directly.

26.8.3 Binding new NaoQi modules in Urbi

You can create a proxy on every NaoQi module, local or remote. The function ALProxy can be used to ease the process:

Here is an example with red ball detection and tracking module:

 
// Instantiate a proxy to RedBallDetection 
var Global.redBallDetection = ALProxy("RedBallDetection", [], []); 
// Instantiate a proxy to RedBallTracker 
var Global.redBallTracker = ALProxy("RedBallTracker", [], []); 
// Enable nao head motors 
robot.body.head.load = 1; 
// Launch the red ball tracking 
redBallTracker.startTracker (); 
sleep(60s); 
// Stop red ball tracking 
redBallTracker.stopTracker (); 
// reset head position 
headYaw.val = 0 speed: 0.8 & headPitch.val = 0 speed: 0.8;  

Another example with the vision toolbox module:

 
var vision = ALProxy("VisionToolbox", [], ["isItDark", 
  "takePicture", "takePictureRegularly", "setWhiteBalance", 
  "startVideoRecord", "startVideoRecord_adv" ]); 
switch(vision.isItDark) 
{ 
  case var x if x <= 4: 
    echo("We are outdoor"); 
  case var x if x <= 100: 
    echo("We are indoor, its bright"); 
  case var x if x > 100: 
    echo("Here is a shadowed place"); 
};  

The first argument to ALProxy is the name of the module without the ”AL”. The second argument is a list of alternate names for the module. The third argument is a list of method names that must be called asynchronously. You must pass in this list all the functions that takes a long(more than a few milliseconds) time to execute. Synchronous calls have less overhead, but will freeze the urbiscript interpreter for the duration of the call.

Most of the standard modules are loaded by the initialization script ‘URBI.INI’.

26.8.4 Writing NaoQi modules in Urbi

You can create independent modules capable of answering requests and services from other NaoQi modules, written in C ++, urbiscript or ruby. Let’s see an example:

 
// ---------------------------------- 
// Simple module creation samples 
// ---------------------------------- 
// the name of your variable and of the module must be the SAME! 
var urbiHelloWorld = ALModule.new("urbiHelloWorld"); 
 
// create a new method doing some interesting things 
function urbiHelloWorld.sayHello() 
{ 
  // Use ALTextToSpeech, instantiated by URBI.INI 
  tts.say("Hello"); 
}; 
 
// inform the world that theres a new method, and what its doing 
urbiHelloWorld.bindUrbiMethod("sayHello", "Nao will say Hello", [], []); 
 
// to test it outside of Urbi, you can for example type in a browser: 
// http://<Naos IP>:9559/?eval=urbiHelloWorld.version(); or 
// http://<Naos IP>:9559/?eval=urbiHelloWorld.ping(); and more naturally 
// http://<Naos IP>:9559/?eval=urbiHelloWorld.sayHello(); 
 
// ---------------------------------- 
// another module example 
// ---------------------------------- 
var mathematics = ALModule.new("mathematics"); 
 
function mathematics.add(a, b) 
{ 
  var res = a + b; 
  echo("mathematics.add => " + res); 
  return res; 
}; 
 
mathematics.setModuleDescription( 
  "A powerful module to make remote computing in urbiscript :)"); 
mathematics.bindUrbiMethod("add", "Compute the sum of two numbers", 
[["a",   "first number of the addition"], 
 ["b",   "second number of the addition"]], 
 ["sum", "the sum of the addition"] ); 
 
// to test it outside of urbi, you can for example enter in a browser: 
// http://<Naos IP>:9559/?eval=mathematics.add(2,3)  

26.8.5 More examples

Here is an example that shows how to declare in urbiscript a callback called by a NaoQi module:

 
// Instantiate a proxy to RedBallDetection 
var redBallDetection = ALProxy("RedBallDetection", [], []); 
// Instantiate a proxy to RedBallTracker 
var redBallTracker = ALProxy("RedBallTracker", [], []); 
// Create an urbi ALModule that contains a callback function 
var urbiRedBall = do (ALModule.new("urbiRedBall")) 
{ 
  function ballDetected(varName, value, message) { 
    echo("BallDetected was triggered. Got: %s, %s, %s" % [varName, value, message]); 
  }; 
  bindUrbiMethod("ballDetected", "called when ball is detected", [], []); 
}; 
// Trigger the callback function whenever a ball is detected. 
memory.subscribeToMicroEvent("redBallDetected", "urbiRedBall", 
                             "ballisdetected", "ballDetected"); 
// Launch the red ball tracking 
redBallTracker.startTracker ();  

Playing in urbiscript with nao eyes:

 
class Global.EyeLeds 
{ 
  function rotate(var inc, var r, var g, var b, 
                  var frequency = 80ms, var duration = 3) 
  { 
    var eye_ = this.led.asList; 
    var i = 0; var p = -inc; var j = inc; 
    timeout (duration) 
      every (frequency) 
      { 
        i = (i+inc)%8; p = (p+inc)%8; j = (j+inc)%8; 
        eye_[p].val = 0; 
        eye_[i].r = r; eye_[i].g = g; eye_[i].b = b; 
        eye_[j].r = r; eye_[j].g = g; eye_[j].b = b; 
      }; 
  }; 
 
  function redRotate()   { rotate(1, 1, 0, 0); }; 
  function greenRotate() { rotate(1, 0, 1, 0); }; 
  function blueRotate()  { rotate(1, 0, 0, 1); }; 
 
  function randomize(var duration = 3) 
  { 
    timeout(duration) 
      loop 
      { 
        led.r = random(100)/100; 
        led.g = random(100)/100; 
        led.b = random(100)/100; 
      }; 
  }; 
 
  function redBlink(var duration = 3) { 
    timeout(duration) led.r = 0.5 sin:1s ampli:0.5; 
  }; 
  function greenBlink(var duration = 3) { 
    timeout(duration) led.g = 0.5 sin:1s ampli:0.5; 
  }; 
  function blueBlink(var duration = 3) { 
    timeout(duration) led.b = 0.5 sin:1s ampli:0.5; 
  }; 
 
  function color(var r, var g, var b) { 
    led.r = r; led.g = g; led.b = b; 
  }; 
 
  function red()   { color(1, 0, 0); }; 
  function green() { color(0, 1, 0); }; 
  function blue()  { color(0, 0, 1); }; 
  function black() { color(0, 0, 0); }; 
 
  function __unit(slot) 
  { 
    eyeR.black & eyeL.black; 
    voice.say(slot) & 
    eyeR.getSlot(slot).apply([eyeR]) & 
    eyeL.getSlot(slot).apply([eyeL]); 
  }; 
 
  function __test() 
  { 
    voice.say("Eyes test procedure"); 
    __unit("red"); 
    __unit("green"); 
    __unit("blue"); 
    __unit("redBlink"); 
    __unit("greenBlink"); 
    __unit("blueBlink"); 
    eye.black; 
    voice.say("blink yellow") & 
    eyeL.greenBlink & eyeL.redBlink & 
    eyeR.greenBlink & eyeR.redBlink; 
    __unit("randomize"); 
    __unit("redRotate"); 
    __unit("greenRotate"); 
    __unit("blueRotate"); 
    eye.black; 
  }; 
}; 
 
do (eyeL) 
{ 
  addProto(EyeLeds); 
 
  function rotateRight(var r, var g, var b, var frequency = 50ms, 
                       var duration = 1s) { 
    rotate(-1, r, g, b, frequency, duration) 
  }; 
 
  function rotateLeft(var r, var g, var b, var frequency = 50ms, 
                      var duration = 1s) { 
    rotate(1, r, g, b, frequency, duration) 
  }; 
}; 
 
do (eyeR) 
{ 
  addProto(EyeLeds); 
 
  function rotateRight(var r, var g, var b, var frequency = 50ms, 
                       var duration = 1s) { 
    rotate(1, r, g, b, frequency, duration) 
  }; 
 
  function rotateLeft(var r, var g, var b, var frequency = 50ms, 
                      var duration = 1s) { 
    rotate(-1, r, g, b, frequency, duration) 
  }; 
}; 
 
EyeLeds.__test;  

This example shows how to to set nao into it’s initial standing pose:

 
var HeadYawAngle       = 0; 
var HeadPitchAngle     = 0; 
var ShoulderPitchAngle = 80; 
var ShoulderRollAngle  = 20; 
var ElbowYawAngle      = -80; 
var ElbowRollAngle     = -60; 
var WristYawAngle      = 0; 
var HandAngle          = 0; 
var HipYawPitchAngle   = 0; 
var HipRollAngle       = 0; 
var HipPitchAngle      = -25; 
var KneePitchAngle     = 40; 
var AnklePitchAngle    = -20; 
var AnkleRollAngle     = 0; 
var Head = []; 
var LeftArm = []; 
var RightArm = []; 
var LeftLeg = []; 
var RightLeg = []; 
 
switch (ALMotion.getRobotConfig[1][0]) 
{ 
  case "naoAcademics": 
    Head     = [HeadYawAngle, HeadPitchAngle]; 
    LeftArm  = [ShoulderPitchAngle, +ShoulderRollAngle, 
                +ElbowYawAngle, +ElbowRollAngle, WristYawAngle, HandAngle]; 
    RightArm = [ShoulderPitchAngle, -ShoulderRollAngle, 
                -ElbowYawAngle, -ElbowRollAngle, WristYawAngle, HandAngle]; 
    LeftLeg  = [HipYawPitchAngle, +HipRollAngle, 
                HipPitchAngle, KneePitchAngle, AnklePitchAngle, +AnkleRollAngle]; 
    RightLeg = [HipYawPitchAngle, -HipRollAngle, 
                HipPitchAngle, KneePitchAngle, AnklePitchAngle, -AnkleRollAngle] 
 
  case "naoRobocup": 
    Head     = [HeadYawAngle, HeadPitchAngle]; 
    LeftArm  = [ShoulderPitchAngle, +ShoulderRollAngle, 
                +ElbowYawAngle, +ElbowRollAngle]; 
    RightArm = [ShoulderPitchAngle, -ShoulderRollAngle, 
                -ElbowYawAngle, -ElbowRollAngle]; 
    LeftLeg  = [HipYawPitchAngle, +HipRollAngle, 
                HipPitchAngle, KneePitchAngle, AnklePitchAngle, +AnkleRollAngle]; 
    RightLeg = [HipYawPitchAngle, -HipRollAngle, 
                HipPitchAngle, KneePitchAngle, AnklePitchAngle, -AnkleRollAngle]; 
 
  case "naoT14": 
    Head     = [HeadYawAngle, HeadPitchAngle]; 
    LeftArm  = [ShoulderPitchAngle, +ShoulderRollAngle, 
                +ElbowYawAngle, +ElbowRollAngle, WristYawAngle, HandAngle]; 
    RightArm = [ShoulderPitchAngle, -ShoulderRollAngle, 
                -ElbowYawAngle, -ElbowRollAngle, WristYawAngle, HandAngle]; 
 
  case "naoT2": 
    Head     = [HeadYawAngle, HeadPitchAngle]; 
}; 
 
// Gather the joints together 
var pTargetAngles = Head + LeftArm + LeftLeg + RightLeg + RightArm; 
// Convert to radians 
pTargetAngles = pTargetAngles.map(function(x) {x*motion.TO_RAD}); 
 
 
// ------------------------------ send the commands --------------------------- 
// We use the "Body" name to denote the collection of all joints. 
var pNames = "Body"; 
// We set the fraction of max speed 
var pMaxSpeedFraction = 0.2; 
 
ALProxy("RobotPose"); 
switch(ALRobotPose.getActualPoseAndTime) 
{ 
  case "crouch": 
    // Ask motion to do this with a blocking call 
    ALMotion.angleInterpolationWithSpeed(pNames, pTargetAngles, pMaxSpeedFraction); 
  case "stand": 
    // Ask motion to do this with a blocking call 
    ALMotion.angleInterpolationWithSpeed(pNames, pTargetAngles, pMaxSpeedFraction); 
  default: 
    voice.setLanguage("english"); 
    voice.say("Please first stand me up"); 
};