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...
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;
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.
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.
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).
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.
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).
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.
JPEG mode gives the smallest images and uses less bandwidth to access the images remotely, but uses more CPU power.
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.
The following sensors are available through a urbiscript variable. Reading the variable will give the latest available sensor value.
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:
All standard NaoQi modules (ALMemory, ALLogger, ALMotion, ALFrameManager, ALTextToSpeech, ALAudioPlayer...) are available in Urbi. You can call all their methods directly.
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, it’s 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’.
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 there’s a new method, and what it’s doing
urbiHelloWorld.bindUrbiMethod("sayHello", "Nao will say ’Hello’", [], []);
// to test it outside of Urbi, you can for example type in a browser:
// http://<Nao’s IP>:9559/?eval=urbiHelloWorld.version(); or
// http://<Nao’s IP>:9559/?eval=urbiHelloWorld.ping(); and more naturally
// http://<Nao’s 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://<Nao’s IP>:9559/?eval=mathematics.add(2,3)
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");
};