Prev

Prev-tail

Tail

Up

Chapter 13
Urbi for ROS Users

This chapter extends the ROS official tutorials (http://www.ros.org/wiki/ROS/Tutorials). Be sure to complete this tutorial before reading this document.

 13.1 Communication on topics
  13.1.1 Starting a process from Urbi
  13.1.2 Listening to Topics
  13.1.3 Advertising on Topics
 13.2 Using Services

13.1 Communication on topics

First we will take back examples about topics; make sure that talker and listener in the ‘beginner_tutorial’ package are compiled. You can recompile it with the following command:

 
$ rosmake beginner_tutorial  

13.1.1 Starting a process from Urbi

To communicate with ROS components, you need to launch them. You can do it by hand, or ask Urbi to do it for you. To launch new processes through Urbi, we will use the class Process (Section 20.45).

Let’s say we want to start roscore, and the talker of the beginner tutorial. Open an Urbi shell by typing the command ‘rlwrap urbi -i’. Here rlwrap makes ‘urbi -i’ acts like a shell prompt, with features like line editing, history, …

 
var core = Process.new("roscore", []); 
[00000001] Process roscore 
var talker = Process.new("rosrun", ["beginner_tutorial", "talker"]); 
[00000002] Process rosrun 
core.run; 
talker.run;  

At this point, the processes are launched. The first argument of Process.new is the name of the command to launch, the second is a list of arguments.

Then you can check the status of the processes, get their stdout/stderr buffers, kill them in urbiscript (see Process (Section 20.45)).

13.1.2 Listening to Topics

First you need to make sure that roscore is running, and the Ros module is loaded correctly:

 
Global.hasLocalSlot("Ros"); 
[00016931] true  

Then we can get the list of launched nodes:

 
Ros.nodes;  

This returns a Dictionary (Section 20.11) with the name of the node as key, and a dictionary with topics subscribed, topics advertised, topics advertised as value.

We can check that our talker is registered, and on which channel it advertises:

 
// Get the structure. 
// "|;" is an idiom to discard the display of the return value. 
var nodes = Ros.nodes|; 
 
// List of nodes (keys). 
nodes.keys; 
[00000002] ["/rosout", "/urbi_1273060422295250703", "/talker"] 
 
// Details of the node "talker". 
nodes["talker"]["publish"]; 
[00000003] ["/rosout", "/chatter"]  

Here we see that this node advertises ‘/rosout’ and ‘/chatter’. Let’s subscribe to ‘/chatter’:

 
// Initialize the subscription object. 
var chatter = Ros.Topic.new("/chatter")|; 
// Subscribe. 
chatter.subscribe; 
// This is the way we are called on new message. 
var chatTag = Tag.new|; 
chatTag: at (chatter.onMessage?(var e)) 
  // This will be executed on each message. 
  echo(e);  

In this code, e is a Dictionary that follows the structure of the ROS message. Here is an example of what this code produces:

 
[00000004] *** ["data" => "Hello there! This is message [4]"] 
[00000005] *** ["data" => "Hello there! This is message [5]"] 
[00000006] *** ["data" => "Hello there! This is message [6]"]  

We can also get a template for the message structure on this channel with:

 
chatter.structure; 
[00000007] ["data" => ""]  

To stop temporarily the Global.echo, we take advantages of tags (Section 11.3), by doing chatTag.freeze. Same thing goes with unfreeze. Of course you could also call chatter.unsubscribe, which unsubscribes you completely from this channel.

13.1.3 Advertising on Topics

To advertise a topic, this is roughly the same procedure.

13.1.3.1 Simple Talker

Here is a quick example:

 
// Initialize our object. 
var talker = Ros.Topic.new("/chatter")|; 
// Advertise (providing the ROS Type of this topic). 
talker.advertise("std_msgs/String"); 
 
// Get a template of our structure. 
var msg = talker.structure.new; 
msg["data"] = "Hello ROS world"|; 
talker << msg;  

We have just sent our first message to ROS, here if you launch the chatter, you will be able to get the message we have just sent.

The << operator is an convenient alias for Ros.Topic.publish.

13.1.3.2 Turtle Simulation

Now we are going to move the turtle with Urbi. First let’s launch the turtle node:

 
var turtle = Process.new("rosrun", ["turtlesim", "turtlesim_node"])|; 
turtle.run;  

With the help of Ros.topics, we can see that this turtle subscribes to a topic ‘/turtle1/command_velocity’. Let’s advertise on it:

 
var velocity = Ros.Topic.new("/turtle1/command_velocity")|; 
velocity.advertise("turtlesim/Velocity"); 
velocity.structure; 
[00000001] ["linear" => 0, "angular" => 0]  

Now we want to have it moving in circle with a small sinusoid wave. This goes in two step. First, we set up the infrastructure so that changes in Urbi are seamlessly published in ROS.

 
// Get our template structure. 
var m = velocity.structure.new |; 
m["linear"] = 0.8 |; 
var angular = 0 |; 
// Every time angular is changed, we send a message. 
at (angular->changed?) 
{ 
  m["angular"] = angular; 
  velocity << m 
};  

In the future Urbi will provide helping functions to spare the user from the need to perform this “binding”. But once this is done, all the features of urbiscript can be used transparently. For instance we can assign a sinusoidal trajectory to ‘angular’, which results in the screen-shot on the right-hand side.

 
// A Tag to control the following endless 
// statement. 
var angTag = Tag.new|; 
 
angTag: 
  // Bind "angular" to a trajectory. 
  // Put in background thanks to ",", 
  // since this statement is never ending. 
  angular = 0.3 sin: 2s ampli: 2, 
 
// Leave 20 seconds to the turtle... 
sleep(20s); 
 
// before freezing it. 
angTag.freeze;  

PIC

We won’t cover this code in details, but the general principle is that angular is updated every 20ms with the values of a sinusoid wave trajectory with 0.3 as average value, 2 seconds for the period and 2 for the amplitude. See TrajectoryGenerator (Section 20.64) for more information.

Every time angular is changed, a new message is sent on the Topic ‘/turtle1/command_velocity’, thus updating the position of the turtle. After 20 seconds the tag is frozen, pausing the trajectory generation and the at.

13.2 Using Services

Services work the same way topics do, with minor differences.

Let’s take back the turtle simulation example (Section 13.1.3.2). Then we can list the available services, and filter out loggers:

 
var logger = Regexp.new("(get|set)_logger") |; 
var services = Ros.services.keys |; 
services.filter(closure (var item) { item not in logger }); 
[00000001] ["/clear", "/kill", "/turtle1/teleport_absolute", "/turtle1/teleport_relative", "/turtle1/set_pen", "/reset", "/spawn"]  

The closure construct allows us to keep access to the local variables, here logger.

Now there is a service called ‘/spawn’; to initialize it:

 
var spawn = Ros.Service.new("/spawn", false) |; 
waituntil(spawn.initialized);  

The new function takes the service name as first argument, and whether or not the connection should be kept alive as second argument.

Since the creation of this object checks the service name, you should wait until initialized is true to use this service. You can also see the structure of the request through spawn.reqStruct, and the structure of the response by doing spawn.resStruct.

Now let’s spawn a turtle called Jenny, at position (4, 4).

 
var req = spawn.reqStruct.new |; 
req["x"] = 4 | 
req["y"] = 4 | 
req["name"] = "Jenny" |; 
spawn.request(req); 
[00000001] ["name" => "Jenny"]  

Now, if you want to go further, please see the reference manual section about ROS, Section 21.