Urbi SDK is a fully-featured environment to orchestrate complex organizations of components. It is an open source framework for robotics and other complex systems. It relies on a middleware architecture that coordinates components named UObjects. It also features urbiscript, a scripting language tailored to write orchestration programs.
Urbi makes the orchestration of independent and concurrent components easier. It was first designed for robotics: it provides all the needed features to coordinate the execution of various components (actuators, sensors, software devices that provide features such as text-to-speech, face recognition and so forth). Languages such as C++ are well suited to program the local, low-level, handling of these hardware or software devices; indeed one needs efficiency, small memory footprint, and access to low-level hardware details. Yet, when it comes to component orchestration and coordination, in a word, when it comes to addressing concurrency, it can be tedious to use such languages.
Middleware infrastructures make possible to use remote components as if they were local, to allow concurrent execution, to make synchronous or asynchronous requests and so forth. The UObject C++ architecture provides exactly this: a common API that allows conforming components to be used seamlessly in highly concurrent settings. Components need not be designed with UObjects in mind, rather, UObjects are typically “shells” around “regular” components.
Components with an UObject interface are naturally supported by the urbiscript programming language. This provides a tremendous help: one can interact with these components (making queries, changing them, observing their state, monitoring various kinds of events and so forth), which provides a huge speed-up during development.
Although made with robots in mind, the UObject architecture is well suited to tame any heavily concurrent environment, such as video games or complex systems in general.
The Section 1.1 shows the architecture of Urbi. Let’s browse it bottom up.
At the lowest level, Urbi requires a (possibly very limited) embedded computer. This is the case for most robots today, but on occasion, some device cannot even run reasonably small pieces of code. In that case, Urbi can still be used, but then the robot is actually remote-controlled from a computer running Urbi.
Right on top of the hardware, is running the Operating System. Urbi supports the major OSes; it was also ported on top of real-time OSes such as Xenomai, and on specific OSes such as Aperios, Sony’s proprietary system running its Aibo robotic dog.
The Urbi Runtime, which is the actual core of the system, also known as the engine or the kernel, is interfacing the OS with the rest of the Urbi world, urbiscript and UObjects.
UObjects are used to bind hardware or software components, such as actuators and sensors on the one hand, and voice synthesis or face recognition on the other hand. They can be run locally on the robot, or on a remote, more powerful, computer.
To orchestrate all the components, urbiscript is a programming language of choice (see below).
Finally, applications are available for the Urbi environment. For instance, Gostai Studio provides high-level tools to develop complex robotic behaviors.
urbiscript is a programming language primarily designed for robotics. It’s a dynamic, prototype-based, object-oriented scripting language. It supports and emphasizes parallel and event-based programming, which are very popular paradigms in robotics, by providing core primitives and language constructs.
Its main features are:
Urbi what first designed and implemented by Jean-Christophe Baillie, together with Matthieu Nottale. Because its users wildly acclaimed it, Jean-Christophe founded Gostai, a France-based Company that develops software for robotics with a strong emphasis on personal robotics.
Authors Urbi SDK 1 was further developed by Akim Demaille, Guillaume Deslandes, Quentin Hocquet, and Benoît Sigoure.
The Urbi SDK 2 project was started and developed by Akim Demaille, Quentin Hocquet, Matthieu Nottale, and Benoît Sigoure. Samuel Tardieu provided an immense help during the year 2008, in particular for the concurrency and event support.
The maintenance is currently carried out by Akim Demaille, Quentin Hocquet, and Matthieu Nottale. Jean-Christophe Baillie is still deeply involved in the development of urbiscript, he regularly submits ideas, and occasionally even code!
Contributors Many people contributed significantly to Urbi, including Alexandre Morgand, Romain Bezut, Thomas Moulard, Clément Moussu, Nicolas Pierron.
This multi-part document provides a complete guide to Urbi. See Chapter 34 for the various notations that are used in the document.
No knowledge of the urbiscript language is needed. As a matter of fact, Urbi can be used as a standalone middleware architecture to orchestrate the execution of existing components.
Yet urbiscript is a feature that “comes for free”: it is easy using it to experiment, prototype, and even program fully-featured applications that orchestrate native components. The interested reader should read either the urbiscript user manual (Part II), or the reference manual (Chapter 22).
This part is not an urbiscript tutorial; it is not structured in a progressive manner and is too detailed. Think of it as a dictionary: one does not learn a foreign language by reading a dictionary. For an urbiscript Tutorial, see Part II.
This part does not aim at giving advanced programming techniques. Its only goal is to define the language and its libraries.
You may want to look at the documentation of the latest version, and visit http://urbiforge.org, the Urbi community web site, and its forum.
This document and others are updated regularly on the Gostai Web site:
urbiscript comes with a set of tools, two of which being of particular importance:
Please, first make sure that these tools are properly installed. If you encounter problems, please see the frequently asked questions (Chapter 17), and the detailed installation instructions (Chapter 16).
# Make sure urbi is properly installed.
$ urbi --version
Urbi version 2.7.4 rev. 268868e
Copyright (C) 2004-2012 Gostai S.A.S..
Libport version urbi-sdk-2.7.4 rev. f870ce6
Copyright (C) 2005-2012 Gostai S.A.S..
There are several means to interact with a server spawned by urbi, see Section 21.3 for details. First of all, you may use the options ‘-e’/‘--expression code ’ and ‘-f’/‘--file file ’ to send some code or the contents of some file to the newly run server. The option ‘q’/‘--quiet’ discards the banner.
You may combine any number of these options, but beware that being event-driven, the server does not “know” when a program ends. Therefore, batch programs should end by calling shutdown. Using a Unix shell:
# A classical program.
$ urbi -q -e ’echo("Hello, World!");’ -e ’shutdown;’
[00000004] *** Hello, World!
If you are running Windows, then, since the quotation rules differ, run:
# A classical program.
$ urbi -q -e "echo(""Hello, World!"");" -e "shutdown;"
[00000004] *** Hello, World!
To run an interactive session, use option ‘-i’/‘--interactive’. Like most interactive interpreters, Urbi will evaluate the given commands and print out the results.
$ urbi -i
[00000825] *** ********************************************************
[00000825] *** Urbi version 2.7.4 rev. 268868e
[00000825] *** Copyright (C) 2004-2012 Gostai S.A.S.
[00000825] ***
[00000825] *** This program comes with ABSOLUTELY NO WARRANTY. It can
[00000825] *** be used under certain conditions. Type ‘license;’,
[00000825] *** ‘authors;’, or ‘copyright;’ for more information.
[00000825] ***
[00000825] *** Check our community site: http://www.urbiforge.org.
[00000825] *** ********************************************************
1+2;
[00001200] 3
shutdown;
The output from the server is prefixed by a number surrounded by square brackets: this is the date (in milliseconds since the server was launched) at which that line was sent by the server. This is useful at occasions, since Urbi is meant to run many parallel commands. But since these timestamps are irrelevant in most examples, they will often be filled with zeroes through this documentation.
Under Unix, the program rlwrap provides additional services (history of commands, advanced command line edition etc.); run ‘rlwrap urbi -i’.
In either case the server can also be made available for network-based interactions using option ‘--port port ’. Note that while shutdown asks the server to quit, quit only quits one interactive session. In the following example (under Unix) the server is still available for other, possibly concurrent, sessions.
$ urbi --port 54000 &
[1] 77024
$ telnet localhost 54000
Trying 127.0.0.1...
Connected to localhost.
Escape character is ’^]’.
[00004816] *** ********************************************************
[00004816] *** Urbi version 2.7.4 rev. 268868e
[00004816] *** Copyright (C) 2004-2012 Gostai S.A.S.
[00004816] ***
[00004816] *** This program comes with ABSOLUTELY NO WARRANTY. It can
[00004816] *** be used under certain conditions. Type ‘license;’,
[00004816] *** ‘authors;’, or ‘copyright;’ for more information.
[00004816] ***
[00004816] *** Check our community site: http://www.urbiforge.org.
[00004816] *** ********************************************************
12345679*8;
[00018032] 98765432
quit;
Connection closed by foreign host.
Under Windows, instead of using telnet, you may use Gostai Console (part of the package), which provides a Graphical User Interface to a network-connection to an Urbi server. To launch the server, run:
and to launch the client, click on Gostai Console which is installed by the installer.
Then, the interaction proceeds in the Gostai Console windows. Specify the host name and port to use (‘127.0.0.1:54000’) in the text field in the top of the window and click on the right to start the connection.

The program urbi-send (see Section 21.8) provides a nice interface to send batches of instructions (and/or files) to a running server.
$ urbi-send -P 54000 -e "1+2*3;" -Q
[00018032] 7
# Have the server shutdown;
$ urbi-send -P 54000 -e "shutdown;"
You can now send commands to your Urbi server. If at any point you get lost, or want a fresh start, you can simply close and reopen your connection to the server to get a clean environment. In some cases, particularly if you made global changes in the environment, it is simpler to start anew: shut your current server down using the command shutdown, and spawn a new one. In interactive mode you can also use the shortcut sequence Ctrl-D, like in many other interpreters.
In case of a foreground task preventing you to execute other commands, you can use Ctrl-C to kill the foreground task, clear queued commands and restore interactive mode.
You are now ready to proceed to the urbiscript tutorial: see Part II.
Enjoy!
No knowledge of the urbiscript language is needed. As a matter of fact, Urbi can be used as a standalone middleware architecture to orchestrate the execution of existing components.
Yet urbiscript is a feature that “comes for free”: it is easy using it to experiment, prototype, and even program fully-featured applications that orchestrate native components. The interested reader should read either the urbiscript user manual (Part II), or the reference manual (Chapter 22).
This chapter presents Urbi SDK with a specific focus on its middleware features. It is self-contained in order to help readers quickly grasp the potential of Urbi used as a middleware. References to other sections of this document are liberally provided to point the reader to the more complete documentation; they should be ignored during the first reading.
As a simple running example, consider a (very) basic factory. Raw material delivered to the factory is pushed into some assembly machines, which takes some time.
As a firth component of this factory, the core machine of the factory is implemented as follows. This class is pure regular C++, it uses no Urbi feature at all.
The header ‘machine.hh’, which declares Machine, is traditional. The documentation uses the Doxygen syntax.
The implementation file, ‘machine.cc’, is equally straightforward.
By binding a UObject, we mean using the UObject API to declare objects to the Urbi world. These objects have member variables (also known as attributes) and/or member functions (also known as methods) all of them or some of them being declared to Urbi.
One could modify the Machine class to make it a UObject, yet we recommend wrapping pure C++ classes into a different, wrapping, UObject. It is strongly suggested to aggregate the native C++ objects in the UObject — rather than trying to derive from it. By convention, we prepend a “U” to the name of the base class, hence the UMachine class. This class provides a simplified interface, basically restricted to what will be exposed to Urbi. It must derive from urbi::UObject.
The implementation of UMachine is simple. It uses some of the primitives used in the binding process (Section 4.2):
Urbi relies on the prototype model for object-oriented programming, which is somewhat different from the traditional C++ class-based model (Chapter 8). This is reflected by the presence of two different constructors:
Functions and variables that do not make sense for the initial prototype (which might not be fully functional) should be bound here, rather than in the C++ constructor.
The following listing is abundantly commented, and is easy to grasp.
As a first benefit from using the Urbi environment, this source code is already runnable! No main function is needed, the Urbi system provides one.
Of course, beforehand, we need to compile this UObject into some loadable module. The Urbi modules are shared objects, i.e., libraries that can be loaded on demand (and unloaded) during the execution of the program. Their typical file names depend on the architecture: ‘machine.so’ on most Unix platforms (including Mac OS X), and ‘machine.dll’ on Windows. To abstract away from these differences, we will simply use the base name, ‘machine’ with the Urbi tool chain.
There are several options to compile our machine as a UObject. If you are using Microsoft Visual Studio, the Urbi SDK installer created a “UObject” project template accessible through the “New project” wizard. Otherwise, you can use directly your regular compiler tool chain. You may also use umake-shared from the ‘umake-*’ family of programs (Section 21.10.2):
$ ls machine.uob
machine.cc machine.hh umachine.cc umachine.hh
$ umake-shared machine.uob -o machine
# ... Lots of compilation log messages ...
$ ls
_ubuild-machine.so machine.la machine.so machine.uob
The various files are:
umake-shared traversed ‘machine.uob’ to gather and process relevant files (source files, headers, libraries and object files) in order to produce this output file.
There are several means to toy with this simple UObject. You can use urbi-launch (Section 21.5) to plug the UMachine in an Urbi server and enter an interactive session.
# Launch an Urbi server with UMachine plugged in.
$ urbi-launch --start machine -- --interactive
[00000000] *** ********************************************************
[00000000] *** Urbi SDK version 2.7 rev. a6a1ec5
[00000000] *** Copyright (C) 2004-2011 Gostai S.A.S.
[00000000] ***
[00000000] *** This program comes with ABSOLUTELY NO WARRANTY. It can
[00000000] *** be used under certain conditions. Type ‘license;’,
[00000000] *** ‘authors;’, or ‘copyright;’ for more information.
[00000000] ***
[00000000] *** Check our community site: http://www.urbiforge.org.
[00000000] *** ********************************************************
var f = UMachine.new(1s);
[00020853] UMachine_0x1899c90
f.assemble(["Hello, ", "World!"]);
[00038705] "Hello, World!"
shutdown;
You may also launch the machine UObject in the background, as a network component:
and interact with it using your favorite client (telnet, netcat, socat, …), or using the urbi-send tool (Section 21.8).
$ urbi-send --port 54000 \
-e ’UMachine.assemble([12, 34]);’ \
--quit
[00126148] "1234"
[00000000:client_error] End of file
$ urbi-send --port 54000 \
-e ’var f = UMachine.new(1s)|’ \
-e ’f.assemble(["ab", "cd"]);’ \
--quit
[00146159] "abcd"
[00000000:client_error] End of file
urbiscript is a programming language primarily designed for robotics. Its syntax is inspired by that of C++: if you know C, C++, Java or C#, writing urbiscript programs is easy. It’s a dynamic object-oriented (Chapter 11) scripting language, which makes it well suited for high-level application. It supports and emphasizes parallel (Chapter 13) and event-based programming (Chapter 14), which are very popular paradigms in robotics, by providing core primitives and language constructs.
Thanks to its client/server approach, one can easily interact with a robot, to monitor it, or to experiment live changes in the urbiscript programs.
Courtesy of the UObject architecture, urbiscript is fully integrated with C++. As already seen in the above examples (Section 3.1.3), you can bind C++ classes in urbiscript seamlessly. urbiscript is also integrated with many other languages such as Java (Chapter 5), MatLab or Python. The UObject framework also naturally provides urbiscript with support for distributed architectures: objects can run in different processes, possibly on remote computers.
The following example shows how one can easily interface UObjects into the urbiscript language. The following simple class (actually, a genuine object, in urbiscript “classes are objects”, see Chapter 11) aggregates two assembly machines, a fast one, and a slow one. This class demonstrates usual object-oriented, sequential, features.
class TwoMachineFactory
{
// A shorthand common to all the Two Machine factories.
var UMachine = uobjects.UMachine;
// Default machines.
var fastMachine = UMachine.new(10ms);
var slowMachine = UMachine.new(100ms);
// The urbiscript constructor.
// Build two machines, a fast one, and a slow one.
function init(fast = 10ms, slow = 100ms)
{
// Make sure fast <= slow.
if (slow < fast)
[fast, slow] = [slow, fast];
// Two machines for each instance of TwoMachineFactory.
fastMachine = UMachine.new(fast);
slowMachine = UMachine.new(slow);
};
// Wrappers to make invocation of the machine simpler.
function fast(input) { fastMachine.assemble(input) };
function slow(input) { slowMachine.assemble(input) };
// Use the slow machine for large jobs.
function assemble(input)
{
var res|
var machine|
if (5 < input.size)
{ machine = "slow" | res = slow(input); }
else
{ machine = "fast" | res = fast(input); } |
echo ("Used the %s machine (%s => %s)" % [machine, input, res]) |
res
};
};
[00000001] TwoMachineFactory
Using this class is straightforward.
var f = TwoMachineFactory.new|;
f.assemble([1, 2, 3, 4, 5, 6]);
[00000002] *** Used the slow machine ([1, 2, 3, 4, 5, 6] => 123456)
[00000003] "123456"
f.assemble([1]);
[00000004] *** Used the fast machine ([1] => 1)
[00000005] "1"
The genuine power of urbiscript is when concurrency comes into play.
Why should we wait for the slow job to finish if we have a fast machine available? To do so, we must stop requesting a sequential composition between both calls. We did that by using the sequential operator, ‘;’. In urbiscript, there exists its concurrent counter-part: ‘,’. Indeed, running a, b means “launch the program a and then launch the program b”. The urbiscript Manual contains a whole section devoted to explaining these operators (Chapter 13).
Let’s try it:
f.assemble([1, 2, 3, 4, 5, 6]),
f.assemble([1]),
[00000002] *** Used the slow machine ([1, 2, 3, 4, 5, 6] => 123456)
[00000004] *** Used the fast machine ([1] => 1)
This is a complete failure.
Why?
Since Urbi cannot expect that your code is thread-safe, by default all calls to UObject features are synchronous, or blocking: the whole Urbi system is suspended until the function returns. There is a single thread of execution for Urbi, and when calling a function from a (plugged) UObject, that thread of execution is devoted to evaluated the code.
See for instance below that, in even though f.assemble is slow and launched in background, the almost instantaneous display of ping is not performed immediately.
There are several means to address this unintended behavior. If the base library provides a threaded API (in our example, the Machine class, not the UMachine UObject wrapper), then you could use it. Yet we don’t recommend it, as it takes away from Urbi the possibility to really control concurrency issues (for instance it cannot turn non-blocking functions into blocking functions).
A better option is to ask Urbi to turn blocking function calls into non-blocking ones.
If you read carefully the body of the UMachine::init function, you will find the following piece of code:
UBindFunction(UMachine, assemble);
UBindThreadedFunctionRename
(UMachine, assemble, "threadedAssemble", urbi::LOCK_FUNCTION);
Both calls bind the function UMachine::assemble, but the second one will run the call in a separate thread. Since multiple calls to a single function (or different functions of a single object etc.) are likely to fail, a locking model must be provided. Here, urbi::LOCK_FUNCTION means that concurrent calls to UMachine::assemble must be serialized: one at a time.
To use this threaded version of assemble, we can simply patch our TwoMachineFactory class:
do (TwoMachineFactory)
{
fast = function (input) { fastMachine.threadedAssemble(input) };
slow = function (input) { slowMachine.threadedAssemble(input) };
}|;
Let’s try again where we failed previously (Section 3.2.2.1):
f.assemble([1, 2, 3, 4, 5, 6]),
f.assemble([1]),
[00000004] *** Used the fast machine ([1] => 1)
[00000002] *** Used the slow machine ([1, 2, 3, 4, 5, 6] => 123456)
sleep(200ms);
Victory! The fast machine answered first.
You may have noticed that the result is no longer reported. Indeed, the urbiscript interactive shell only displays the result of synchronous expressions (i.e., those ending with a ‘;’): asynchronous answers are confusing (see the inversion here).
Channels are useful to “send” asynchronous answers.
var c1 = Channel.new("c1")|;
var c2 = Channel.new("c2")|;
c1 << f.assemble([10, 20, 30, 40, 50, 60]),
c2 << f.assemble([100]),
sleep(200ms);
[00000535] *** Used the fast machine ([100] => 100)
[00000535:c2] "100"
[00000625] *** Used the slow machine ([10, 20, 30, 40, 50, 60] => 102030405060)
[00000625:c1] "102030405060"
This section gave only a quick glance to all the power that Urbi provides to support concurrency. Actually, it makes plenty of sense to embed an Urbi engine in a native C++ program, and to delegate the concurrency issues to it. Thanks to its middleware and client/server architecture, it is then possible to connect it to remote components of different kinds, such as using ROS for instance.
Then, a host of features are at hand, ready to be used when you need them: event-driven programming, automatic monitoring of expressions, interactive sessions, …and, last but not least, the urbiscript programming language.
The UObject API can be used to add new objects written in C++ to the urbiscript language, and to interact from C++ with the objects that are already defined. We cover the use cases of controlling a physical device (servomotor, speaker, camera…), and interfacing higher-lever components (voice recognition, object detection…) with Urbi.
The C++ API defines the UObject class. To each instance of a C++ class deriving from UObject will correspond an urbiscript object sharing some of its methods and attributes. The API provides methods to declare which elements of your object are to be shared. To share a variable with Urbi, you have to give it the type UVar. This type is a container that provides conversion and assignment operators for all types known to Urbi: double, std::string and char*, and the binary-holding structures UBinary, USound and UImage. This type can also read from and write to the liburbi UValue class. The API provides methods to set up callbacks functions that will be notified when a variable is modified or read from Urbi code. Instance methods of any prototype can be rendered accessible from urbiscript, providing all the parameters types and the return type can be converted to/from UValue.
UObjects can be compiled easily directly with any regular compiler. Nevertheless, Urbi SDK provides two tools to compile UObject seamlessly.
In the following sections, we will try to compile a shared library named ‘factory.so’ (or ‘factory.dll’ on Windows platforms) from a set of four files (‘factory.hh’, ‘factory.cc’, ‘ufactory.hh’, ‘ufactory.cc’). These files are stored in a ‘factory.uob’ directory; its name bares no importance, yet the ‘*.uob’ extension makes clear that it is a UObject.
In what follows, urbi-root denotes the top-level directory of your Urbi SDK package, see Section 16.2.
On Unix platforms, compiling by hand into a shared library is straightforward:
$ g++ -I urbi-root/include \
-fPIC -shared \
factory.uob/*cc -o factory.so
$ file factory.so
factory.so: ELF 32-bit LSB shared object, Intel 80386, \
version 1 (SYSV), dynamically linked, not stripped
On Mac OS X the flags ‘-Wl,-undefined,dynamic_lookup’ are needed:
$ g++ -I urbi-root/include \
-shared -Wl,-undefined,dynamic_lookup \
factory.uob/*.cc -o factory.so
$ file factory.so
factory.so: Mach-O 64-bit dynamically linked shared library x86_64
umake can be used to compile UObjects. See Section 21.10 for its documentation.
You can give it a list of files to compile:
or directories in which C++ sources are looked for:
or finally, if you give no argument at all, the sources in the current directory:
If you installed Urbi SDK using its installer, and if you had Visual C++ installed, then the UObject wizard was installed. Use it to create your UObject code:

Then, compile your UObject.

And run it.

Let’s illustrate those concepts by defining a simple object: adder. This object has one variable v, and a method add that returns the sum of this variable and its argument.
To summarize:
When you start an Urbi server, an object of each class registered with UStart is created with the same name as the class. New instances can be created from Urbi using the new method. For each instance created in Urbi, a corresponding instance of the C++ object is created. You can get the arguments passed to the constructor by defining and binding a method named init with the appropriate number of arguments.
You can register any member function of your UObject using the macro
UBindFunction(class-name, function-name).
Once done, the function can be called from urbiscript.
The following types for arguments and return value are supported:
The procedure to register new types to this system is explained in Section 4.18.
If you have multiple functions to bind, you can use the UBindFunctions macro to bind multiple functions at once:
UBindFunctions(class-name, function1, function2...).
Functions bound using UBindFunction are called synchronously, and thus block everything until they return.
If you wish to bind a function that requires a non-negligible amount of time to execute, you can have it execute in a separate thread by calling
UBindThreadedFunction(class-name, function-name, lock-mode).
The function code will be executed in a separate thread without breaking the urbiscript execution semantics.
The lock-mode argument can be used to prevent parallel execution of multiple bound functions if your code is not thread-safe. It can be any of the following values.
Other queue sizes can be used by passing LockSpec(LOCK_FUNCTION, my-queue-size) as lock-mode.
There is a restriction to the locking mechanism: you cannot mix multiple locking modes. For instance a function bound with LOCK_FUNCTION mode will not prevent another function bound with LOCK_INSTANCE from executing in parallel.
You can perform your own locking using semaphores if your code needs a more complex locking model.
You can limit the maximum number of threads that can run in parallel by using the setThreadLimit function.
You can register a function that will be called each time a variable is modified by calling UNotifyChange(var, func).
The function can take 0 or 1 argument. If the argument is of type UVar&, then the function will receive the UVar that was passed to UNotifyChange. If it is of any other type, then the new value in the UVar will be converted to this type and passed to the function.
In plugin mode, there is a similar mechanism to create a getter function that will be called each time an UVar is accessed: the UNotifyAccess function. It has the same signature as UNotifyChange, and calls the given function each time someone tries to access the UVar. The function can update the value in the UVar before the access takes place. Usage of UNotifyAccess should be reserved to infrequently used UVar that take a long time to update, as it disrupts the data flow between UObject.
You can remove all notifies associated to any given UVar by calling its unnotify function.
In a manner similar to UBindThreadedFunction, you can request your callback function to be called in a separate thread by using UNotifyThreadedChange(var, func, lock-mode).
The lock-mode argument has the same semantic as for bound functions.
There is one restriction: the callback function must not take a UVar& as argument. This restriction is here to ensure that each invocation of your callback will receive the correct value that the source UVar had at call time.
The UNotifyChange and UNotifyAccess features can be used to link multiple UObjects together, and perform data-flow based programming: the UNotifyChange can be called to monitor UVars from other UObjects. Those UVars can be transmitted through bound function calls.
One possible pattern is to have each data-processing UObject take its input from monitored UVars, given in its constructor, and output the result of its processing in other UVars. Consider the following example of an object-tracker:
class ObjectTracker: public urbi::UObject
{
public:
ObjectTracker(const std::string& n)
: urbi::UObject(n)
{
// Bind our constructor.
UBindFunction(ObjectTracker, init);
}
// Take our data source in our constructor.
void init(UVar& image)
{
UNotifyChange(image, &ObjectTracker::onImage);
// Bind our output variable.
UBindVar(ObjectTracker, val);
}
void onImage(UVar& src)
{
UBinary b = src;
// Processing here.
val = processing_result;
}
UVar val;
};
UStart(ObjectTracker);
The following urbiscript code would be used to initialize an ObjectTracker given a camera:
An other component could then take the tracker output as its input.
Using this model, chains of processing elements can be created. Each time the UObject at the start of the chain updates, all the notifyChange will be called synchronously in cascade to update the state of the intermediate components.
Urbi provides a second and more standard way to perform data-flow programming. In this approach, inputs of a component are declared as local InputPort, and the binding between this InputPort and the output of another component is done in urbiscript using the >> operator between two UVar:
class ObjectTracker: public urbi::UObject
{
ObjectTracker(const std::string& n)
: urbi::UObject(n)
{
// Bind our constructor.
UBindFunction(ObjectTracker, init);
// Bind our input port.
UBindVar(ObjectTracker, input);
// NotifyChange on our own input port
UNotifyChange(input, &ObjectTracker::onImage);
}
// Init is empty.
void init()
{
}
// onImage is unchanged.
void onImage(UVar& src)
{
UBinary b = src;
// Processing here.
val = processing_result;
}
UVar val;
// Declare our input port.
InputPort input;
};
UStart(ObjectTracker);
In this model, linking the components is done in urbiscript:
The >> operator to establish a data-flow link between two UVar returns an object of type UConnection that can be used to customize the link.
This object is also present in the slot changeConnections of the source UVar.
Here is the list of UConnection slots:
The function uobjects.connectionStats() can be used to display the statistics of all the connections, and uobjects.resetConnectionStats() can be called to reset all statistics.
The API provides two methods to have a function called periodically:
In Urbi, a variable can have a different meaning depending on whether you are reading or writing it: you can use the same variable to represent the target value of an actuator and the current value measured by an associated sensor. This special mode is activated by the UObject defining the variable by calling UOwned after calling UBindVar. This call has the following effects:
The C++ class UVar is used to represent any Urbi slot in C++. To bind the UVar to a specific slot, pass its name to the UVar constructor, or its init method. Once the UVar is bound, you can write any compatible type to it, and the new value will be visible in urbiscript. Similarly, you can cast the UVar (or use the as() method) to convert the current urbiscript value held to any compatible type.
Compatible types are the same as for bound functions (see Section 4.18).
Some care must be taken in remote mode: changes on the variable coming from Urbi code or an other module can take time to propagate to the UVar. By default, all changes to the value will be sent to the remote UObject. To have more control on the bandwidth used, you can disable the automatic update by calling unnotify(). Then you can get the value on demand by calling UVar::syncValue().
UVar v("Global", "x");
send("every|(100ms) Global.x = time,");
// At this point, v is updated approximately every 100 milliseconds.
v.unnotify();
// At this point v is no longer updated. If v was the only UVar pointing to
// ’Global.x’, the value is no longer transmitted.
v.syncValue();
// The previous call will ask for the value of Global.x once, and block until
// the value is written to v.
You can read and write all the Urbi properties of an UVar by reading and writing the appropriate UProp object in the UVar.
The UEvent class can be used to create and emit urbiscript events. Instances are created and initialized exactly as UVar: either by using the UBindEvent macro, or by calling one of its constructors or the init function.
Once initialized, the emit function will trigger the emission of the associated urbiscript event. It can be called with any number of arguments, of any compatible type.
The UObject API is thread-safe in both plugin and remote mode: All API calls including operations on UVar can be performed from any thread.
Urbi can store binary objects of any type in a generic container, and provides specific structures for sound and images. The generic containers is called UBinary and is defined in the ‘urbi/ubinary.hh’ header. It contains an enum field type giving the type of the binary (UNKNOWN, SOUND or IMAGE), and an union of a USound and UImage struct containing a pointer to the data, the size of the data and type-specific meta-information.
The UBinary manages its memory: when destroyed (or going out-of-scope), it frees all its allocated data. The USound and UImage do not.
Reading an UBinary from a UVar, and writing a UBinary, USound or UImage to an UVar performs a deep-copy of the data (by default, see below).
Reading a USound or UImage from an UVar directly will perform a shallow copy from the internal data. The structure content is only guaranteed to be valid until the function returns, and should not be modified.
To convert between various sound and image formats, two functions are provided in the header ‘urbi/uconversion.hh’:
void urbi::convert(UImage& source, UImage& destination);
void urbi::convert(USound& source, USound& destination);
For those functions to work, destination must be filled correctly:
Consider this example of a sound algorithm requiring 8-bit mono input:
class SoundAlgorithm: public UObject
{
public:
<...>
void init();
void onData(UVar& v);
// We reuse the same USound for converted data to avoid reallocation.
USound convertedData;
}
void SoundAlgorithm::init(UVar& dataSource)
{
// initialize convertedData
convertedData.data = 0;
convertedData.size = 0; // Let convert allocate for us
convertedData.soundFormat = SOUND_RAW;
convertedData.channels = 1;
convertedData.rate = 0; // Use sample rate of the source
convertedData.sampleSize = 8;
convertedData.sampleFormat = SAMPLE_UNSIGNED;
UNotifyChange(dataSource, &SoundAlgorithm::onData);
}
void SoundAlgorithm::onData(UVar& v)
{
USound src = v;
convert(src, convertedData);
// Work on convertedData.
}
In plugin mode, you can setup any UVar in 0-copy mode by calling setBypass(true). In this mode, binary data written to the UVar is not copied, but a reference is kept. As a consequence, the data is only available from within registered notifyChange callbacks. Those callbacks can use UVar::val() or cast the UVar to a UBinary & to retrieve the reference. Attempts to read the UVar from outside notifyChange will block until the UVar is updated again, and copy the value at this time.
An example will certainly clarify: Let us first declare an UObject that will generate binary data using 0-copy mode.
// Declare an UObject producing images in 0-copy optimized mode.
class OptimizedImageSource: public UObject
{
<...>
public:
UVar val;
UBinary imageData;
};
void OptimizedImageSource::init()
{
// Bind val
UBindVar(OptimizedImageSource, val);
// Mark it as bypass mode
val.setBypass(true);
// Start a timer.
USetUpdate(10);
}
int OptimizedImageSource::update()
{
<Update imageData here>
// Notify all notifyChange callbacks without copying the data.
val = imageData;
}
Let us then declare an other component that will access this binary data without any copy:
class BinaryProcessor: public UObject
{
public:
void init();
void onData(UVar& v);
InputPort binaryIn;
};
void BinaryProcessor::init()
{
UBindVar(BinaryProcessor, binaryIn);
UNotifyChange(binaryIn, &BinaryProcessor::onData);
}
void BinaryProcessor::onData(UVar& v)
{
const UBinary& b = v;
// If in urbiscript you connect the two components using:
// OptimizedImageSource.&val >> BinaryProcessor.&binaryIn
// then b will be OptimizedImageSource.binaryData, not a copy.
}
Typing OptimizedImageSource.val in urbiscript will wait for the next update from OptimizedImageSource::update and copy the data at this point.
For modularity reasons, all interactions between UObjects should go through the various middleware communication mechanisms, mainly InputPort and UNotifyChange. But it is possible to access directly the C++ instance of an UObject:
Sometimes, you need to perform actions for a group of UObjects, for instance devices that need to be updated together. The API provides the UObjectHub class for this purpose. To create a hub, simply declare a subclass of UObjectHub, and register it by calling once the macro UStartHub (class-name). A single instance of this class will then be created upon server start-up. UObject instances can then register to this hub by calling URegister (hub-class-name). Timers can be attached to UObjectHub the same way as to UObject (see Section 4.8). A hub instance can be retrieved by calling getUObjectHub (string class-name). The hub also holds the list of registered UObject in its members attribute.
If you need to send urbiscript code to the server, the URBI() macro is available, as well as the send() function. You can either pass it a string, or directly Urbi code inside a double pair of parentheses:
You can also use the call method to make an urbiscript function call:
// C++ equivalent of urbiscript ’System.someFunc(12, "foo");’
call("System", "someFunc", 12, "foo");
By default, Urbi uses TCP connections for all communications between the engine and remote UObjects. Urbi also supports the UDP-based RTP protocol for more efficient transmission of updated variable values. RTP will provide a lower latency at the cost of possible packet loss, especially in bad wireless network conditions.
To enable RTP connections, both the engine and the remote-mode urbi-launch containing your remote UObject must load the RTP UObject. This can be achieved by passing urbi/rtp as an extra argument to both urbi-launch command lines (one for the engine, the other for your remote UObject).
Once done, all binary data transfer (like sound and image) in both directions will by default use a RTP connection.
You can control whether a specific UVar uses RTP mode by calling its useRTP(bool) function. Each binary-type UVar will have its own RTP connection, and all non-binary UVar will share one.
From urbiscript, you can also write to the rtp slot of each UVar. Existing notifies will be modified to use rtp if you set it to true.
The same cast system is used both for bound function’s arguments and return values, and for reading/writing UVar.
Should you want to add new type MyType to the system you must define two functions:
namespace urbi
{
void operator, (UValue& v, const MyType& t)
{
// Here you must fill v with the serialized representation of t.
}
template<> struct uvalue_caster<MyType>
{
MyType operator()(UValue& v)
{
// Here you must return a MyType made with the information in v.
}
}
}
Once done, you will be able without any other change to
The system provides facilities to serialize simple structures by value between C++ and urbiscript. This system uses two declarations of each structure, one in C++ and the other in Urbi, and maps between the two.
Here is a complete commented example to map a simple Point structure between urbiscript and C++.
struct Point
{
// Your struct must have a default constructor.
Point()
: x(0), y(0)
{}
double x, y;
};
// Declare the structure to the cast system. First argument is the struct,
// following arguments are the field names.
URBI_REGISTER_STRUCT(Point, x, y);
// Declare the urbiscript structure. It must be globally accessible, and
// inheriting from UValueSerializable.
class Global.Point: UValueSerializable
{
var x = 0;
var y = 0;
function init(var xx = 0, var yy = 0)
{
x = xx|
y = yy
};
function asString()
{
"<%s, %s>" % [x, y]
};
};
// Add the class to Serializables to register it.
var Serializables.Point = Point;
Once done, you can call bound functions taking a C++ Point by passing them an urbiscript Point and exchange Point between both worlds through UVar read/write:
// This function can be bound using UBindFunction.
Point MyObject::opposite(Point p)
{
return Point(-p.x, -p.y);
}
// Writing a Point to an UVar is OK.
void MyObject::writePoint(Point p)
{
UVar v(this, "val");
v = p;
}
// Converting an UVar to a Point is easy.
ufloat MyObject::xCoord()
{
UVar v(this, "val");
Point p;
// Fill p with content of v.
v.fill(p);
// Alternate for the above.
p = v.as<Point>();
return v.x;
}
The UObject Java API can be used to add new remote objects written in Java to the urbiscript language, and to interact from Java with the objects that are already defined. We cover the use cases of interfacing higher-level components (voice recognition, object detection…) with Urbi using Java.
The Java API defines the UObject class. To each instance of a Java class deriving from UObject will correspond an urbiscript object sharing some of its methods and attributes. The API provides methods to declare which elements of your object are to be shared. To share a variable with Urbi, you have to give it the type UVar. This type is a container that provides conversion and setter member functions for all types known to Urbi: double, java.lang.String, the binary-holding structures urbi.UBinary, urbi.USound and urbi.UImage, list types urbi.UList and dictionaries urbi.Dictionary. This type can also read from and write to the urbi.UValue class. The API provides methods to set up callbacks functions that will be notified when a variable is modified or read from urbiscript code. Instance methods of any prototype can be made accessible from urbiscript, providing all the parameter types and the return type can be converted to/from urbi.UValue.
The UObject Java API has the following limitations:
UObjects can be compiled easily directly with the javac compiler, then you can create JAR archives using the jar tool.
In the following sections, we will try to create an uobject jar archive named ‘machine.jar’ from a set of two files (‘Machine.java’, ‘UMachine.java’.
In what follows, urbi-root denotes the top-level directory of your Urbi SDK package, see Section 16.2.
To compile your UObject you need to include in the classpath liburbijava.jar:
$ javac -cp urbi-root/share/sdk-remote/java/lib/liburbijava.jar:. \
Machine.java UMachine.java
$ jar -cvf machine.jar UMachine.class Machine.class
added manifest
adding: UMachine.class
adding: Machine.class
Then to run your uobject, you need to call java. We provide a main class called urbi.UMain in the liburbijava.jar archive. You can use this class to start your UObjects. This class takes the names of your uobjects jar files as argument. You also need to specify the lib folder of the urbi SDK into java.library.path:
$ java -Djava.library.path=urbi-root/lib \
-cp urbi-root/share/sdk-remote/java/lib/liburbijava.jar \
urbi.UMain ./machine.jar
urbi-launch: obeying to URBI_ROOT = /usr/local/gostai
UObject: Urbi SDK version 2.3 rev. 3e93ec1
Copyright (C) 2004-2010 Gostai S.A.S.
Libport version urbi-sdk-2.3 rev. 66cb0ec
Copyright (C) 2004-2010 Gostai S.A.S.
UObject: Remote Component Running on 127.0.0.1 54000
Kernel Version: 0
[LibUObject] Registering function UMachine.init 1 into UMachine.init from UMachine
[LibUObject] Pushing UMachine.init in function
umake-java can be used to compile Java UObjects. It will produce a JAR archive that you can use with urbi-launch-java.
You can give it a list of files to compile:
or directories in which C++ sources are looked for:
or finally, if you give no argument at all, the sources in the current directory:
To run your UObject then use urbi-launch-java (see Section 21.6):
$ urbi-launch-java machine.jar
urbi-launch: obeying to URBI_ROOT = /usr/local/gostai
UObject: Urbi SDK version 2.3 rev. 3e93ec1
Copyright (C) 2004-2010 Gostai S.A.S.
Libport version urbi-sdk-2.3 rev. 66cb0ec
Copyright (C) 2004-2010 Gostai S.A.S.
UObject: Remote Component Running on 127.0.0.1 54000
Kernel Version: 0
[LibUObject] Registering function UMachine.init 1 into UMachine.init from UMachine
[LibUObject] Pushing UMachine.init in function
Let’s illustrate those concepts by defining a simple object: adder. This object has one variable v, and a method add that returns the sum of this variable and its argument.
public class Adder extends UObject // must extends UObject
{
/// Register the class within urbi
static { UStart(Adder.class); }
/// Declare a variable v that will be accessible in Urbi
private UVar v = new UVar ();
/// the class must have a single constructor taking a string
public Adder (String s)
{
super (s);
/// Bind the variable v to Urbi
UBindVar (v, "v");
/// Initialize our UVar v to some value
/// (we choose 42 :)
v.setValue(42);
/// Bind the function add to Urbi
UBindFunction ("add");
}
/// Our method.
public double add (double rhs)
{
/// Return the value of our UVar v (converted to double)
/// plus the value of the argument of the function.
return v.doubleValue () + rhs;
}
}
To bind the variables to Urbi, we use the function:
This function take as parameter the UVar variables, and the name of the UVar (because Urbi need to know what is the name of your variable). Once your variable is bound with UBindVar it will be accessible in Urbi.
If you run this UObject and test it from Urbi it gives:
[00000102] *** ********************************************************
[00000102] *** Urbi SDK version 2.0 rev. 96a4b2f
[00000102] *** Copyright (C) 2004-2010 Gostai S.A.S.
[00000102] ***
[00000102] *** This program comes with ABSOLUTELY NO WARRANTY.
[00000102] *** It can be used under certain conditions.
[00000102] *** Type ‘license;’ or ‘copyright;’ for more information.
[00000102] ***
[00000102] *** Check our community site: http://www.urbiforge.org.
[00000102] *** ********************************************************
Adder;
[00006783] Adder
Adder.v;
[00010871] 42
Adder.add(-26);
[00025795] 16
Adder.add(-2.6);
[00035411] 39.4
To summarize:
When you start an Urbi server, an object of each class registered with UStart is created with the same name as the class. New instances can be created from Urbi using the new method. For each instance created in Urbi, a corresponding instance of the Java object is created. You can get the arguments passed to the constructor by defining and binding a method named init with the appropriate number of arguments.
For example let’s add an Urbi constructor to our Adder class. We rewrite it as follow:
public class Adder extends UObject // must extends UObject
{
/// Register the class within urbi
static { UStart(Adder.class); }
/// Declare a variable v that will be accessible in Urbi
private UVar v = new UVar ();
/// Constructor
public Adder (String s)
{
super (s);
UBindFunction ("init");
}
/// The init function is the constructor in Urbi. Here it takes
/// one argument that we use to initialize the ’v’ variable.
/// The init function must return an int of value 0
/// if all went OK.
public int init (double v_init)
{
/// Bind the variable v to Urbi
UBindVar (v, "v");
/// Initialize our UVar v to the value given in the
/// constructor
v.setValue(v_init);
/// Bind the function add to Urbi
UBindFunction ("add");
return 0;
}
public double add (double rhs)
{
/// Return the value of our UVar v (converted to double)
/// plus the value of the argument of the function.
return v.doubleValue () + rhs;
}
}
Now ’v’ and ’add’ are bound only when instance of the Adder object are constructed. We have added an ’init’ constructor with one parameter that we use to initialize the value of v. You can run this UObject and test it in Urbi to see the difference with the previous example. Here is what it gives:
[00000097] *** ********************************************************
[00000097] *** Urbi SDK version 2.0 rev. 96a4b2f
[00000097] *** Copyright (C) 2004-2010 Gostai S.A.S.
[00000097] ***
[00000097] *** This program comes with ABSOLUTELY NO WARRANTY.
[00000097] *** It can be used under certain conditions.
[00000097] *** Type ‘license;’ or ‘copyright;’ for more information.
[00000097] ***
[00000097] *** Check our community site: http://www.urbiforge.org.
[00000097] *** ********************************************************
Adder;
[00010592] Adder
Adder.v;
[00013094:error] !!! 2.1-7: lookup failed: v
var a = Adder.new(51);
[00041405] object_13
a.v;
[00044742] 51
a.add(10);
[00054783] 61
To bind the functions to Urbi, you can use:
or one of the convenient version:
void UBindFunction (String method_name)
void UBindFunctions(String ... method_names)
void UBindFunction (Object obj, String method_name)
void UBindFunctions (Object obj, String ... method_names)
The first function takes as parameter the object containing the function (for now it is only possible to bind instance method, we do not handle static methods). The second parameter is the name of the function you want to bind. The third parameter is a list of the names if the types of the arguments. For example for the function add, in the previous Adder example, we could have used:
If in your UObject you have different names for each of your methods, then you can use the shorter versions of UBindFunction.
The functions you can bind must follow these rules:
You can register a function that will be called each time a variable is modified by calling UNotifyChange, passing either an UVar or a variable name as first argument, and a member function of your UObject as second argument (and optionally a String array containing the name of the types of the parameters). The prototype for UNotifyChange is:
void UNotifyChange(String var_name, String method_name, String[] args_name);
void UNotifyChange(String var_name, String method_name);
void UNotifyChange(UVar v, String method_name, String[] args_name);
void UNotifyChange(UVar v, String method_name);
The callback function can take zero or one argument: an UVar pointing to the UVar being modified. And the callback function must return an int (the value returned is currently ignored in the actual implementation) or nothing at all (void). The notifyChange callback function is always called after the variable value is changed.
Notify functions can be unregistered by calling the unnotify function of the UVar class.
The API provides two methods to have a function called periodically:
You can read or write any Urbi variable by creating an UVar passing the variable name to the constructor. Change the value by writing any compatible type to the UVar, and access the value by casting the UVar to any compatible type.
Note however that changes on the variable coming from Urbi code or an other module can take time to propagate to the UVar. You can read and write all the Urbi properties of an UVar by reading and writing the appropriate UProp object in the UVar.
If you need to send Urbi code to the server, the send() function is available. You can pass it a string containing Urbi code:
You can also use the call method to make an urbiscript function call:
// Java equivalent of urbiscript ’System.someFunc(12, "foo");’
call("System", "someFunc", new UValue(12), new UValue("foo"));
These functions are member functions of the UObject class.
We provide a main class, containing a main function, embedded in the liburbijava.jar file. This main class, called urbi.UMain is responsible for the loading of the liburbijava native library, and also for the registering of your uobjects.
import liburbi.main.*;
public class Main
{
/// load urbijava library
static
{
System.loadLibrary("urbijava");
}
public static void main(String argv[])
{
UObject.UStart(MyUObject1.class);
UObject.UStart(MyUObject2.class);
// ...
UObject.main(argv);
}
}
Call System.loadLibrary("urbijava"); to load the liburbijava native library.
Note: when you call System.loadLibrary, java search for the library in the locations given in java.library.path. This special Java variable must be correctly set or you will get a loading error when you run your Java program. You can set this options giving: ‘-Djava.library.path=path to dir containing urbijava lib ’ to the Java VM running your program.
We provide two UObjects examples under eclipse. One use urbi.UMain, the other provide it’s own main class.
We provide a sample Eclipse project configuration that you can import in Eclipse and use to create your own UObject Java.
We illustrate here how you can do this:




The Java project is loaded. You can see the jar containing the liburbi (liburbijava.jar, storing the UObject Java API) which contains the urbi package, and also see the sources of the example we provide. We put them in the packages examples. You can inspire yourself from these examples to make your own UObjects. Here, we will see how to compile and run them in eclipse
NB: If Eclipse complains about errors in the source code, it can be that your compiler compliance level is two low. You have to set the compiler compliance level to Java 5 at least (Windows/Preferences/Java/Compiler).

We provide a sample uobjectjava.launch files that you can load in eclipse to run the projects.


Let’s write a UObject for a servomotor device whose underlying API is:
First our header. Our servo device provides an attribute named val, the standard Urbi name, and two ways to set PID gain: a method, and three variables.
class servo : public urbi::UObject // must inherit UObject
{
public:
// the class must have a single constructor taking a string
servo(const std::string&);
// Urbi constructor
void init(int id);
// main attribute
urbi::UVar val;
// position variables:
// P gain
urbi::UVar P;
// I gain
urbi::UVar I;
// D gain
urbi::UVar D;
// callback for val change
void valueChanged(UVar& v);
//callback for val access
void valueAccessed(UVar& v);
// callback for PID change
void pidChanged(UVar& v);
// method to change all values
void setPID(int p, int i, int d);
// motor ID
int id_;
};
The constructor only registers init, so that our default instance servo does nothing, and can only be used to create new instances.
servo::servo (const std::string& s)
: urbi::UObject (s)
{
// register init
UBindFunction (servo, init);
}
The init function, called in a new instance each time a new Urbi instance is created, registers the four variables (val, P, I and D), and sets up callback functions.
// Urbi constructor.
void
servo::init (int id)
{
id_ = id;
if (!initialize (id))
return 1;
UBindVar (servo, val);
// val is both a sensor and an actuator.
Uowned (val);
// Set blend mode to mix.
val.blend = urbi::UMIX;
// Register variables.
UBindVar (servo, P);
UBindVar (servo, I);
UBindVar (servo, D);
// Register functions.
UBindFunction (servo, setPID);
// Register callbacks on functions.
UNotifyChange (val, &servo::valueChanged);
UNotifyAccess (val, &servo::valueAccessed);
UNotifyChange (P, &servo::pidChanged);
UNotifyChange (I, &servo::pidChanged);
UNotifyChange (D, &servo::pidChanged);
}
Then we define our callback methods. servo::valueChanged will be called each time the val variable is modified, just after the value is changed: we use this method to send our servo commands. servo::valueAccessed is called just before the value is going to be read. In this function we request the current value from the servo, and set val accordingly.
// Called each time val is written to.
void
servo::valueChanged (urbi::UVar& v)
{
// v is a reference to our class member val: you can use both
// indifferently.
setPosition (id, (double)val);
}
// Called each time val is read.
void
servo::valueAccessed (urbi::UVar& v)
{
// v is a reference to val.
val = getPosition (id);
}
servo::pidChanged is called each time one of the PID variables is written to. The function servo::setPID can be called directly from Urbi.
void
servo::pidChanged (urbi::UVar& v)
{
setPID(id, (int)P, (int)I, (int)D);
}
void
servo::setPID (int p, int i, int d)
{
setPID (id, p, i, d);
P = p;
I = i;
D = d;
}
// Register servo class to the Urbi kernel.
UStart (servo);
That’s it, compile this module, and you can use it within urbiscript:
// Create a new instance. Calls init (1).
headPan = new servo (1);
// Calls setPID ().
headPan.setPID (8,2,1);
// Calls valueChanged ().
headPan.val = 13;
// Calls valueAccessed ().
headPan.val * 12;
// Periodically calls valueChanged ().
headPan.val = 0 sin:1s ampli:20,
// Periodically calls valueAccessed ().
at (headPan.val < 0)
echo ("left");
The sample code above has one problem: valueAccessed and valueChanged are called each time the value is read or written from Urbi, which can happen quite often. This is a problem if sending the actual command (setPosition in our example) takes time to execute. There are two solutions to this issue.
One solution is to remember the last time the value was read/written, and not apply the new command before a fixed time. Note that the kernel is doing this automatically for UOwned’d variables that are in a blend mode different than normal. So the easiest solution to the above problem is likely to set the variable to the mix blending mode. The unavoidable drawback is that commands are not applied immediately, but only after a small delay.
Instead of updating/fetching the value on demand, you can chose to do it periodically based on a timer. A small difference between the two API methods comes in handy for this case: the update() virtual method called periodically after being set up by USetUpdate(interval) is called just after one pass of Urbi code execution, whereas the timers set up by USetTimer are called just before one pass of Urbi code execution. So the ideal solution is to read your sensors in the second callback, and write to your actuators in the first. Our previous example (omitting PID handling for clarity) can be rewritten. The header becomes:
// Inherit from UObject.
class servo : public urbi::UObject
{
public:
// The class must have a single constructor taking a string.
servo (const std::string&)
// Urbi constructor.
void init (int id);
// Called periodically.
virtual int update ();
// Called periodically.
void getVal ();
// Our position variable.
urbi::UVar val;
// Motor ID.
int id_;
};
Constructor is unchanged, init becomes:
// Urbi constructor.
void
servo::init (int id)
{
id_ = id;
if (!initialize (id))
return 0;
UBindVar (servo,val);
// Val is both a sensor and an actuator.
UOwned(val);
// Will call update () periodically.
USetUpdate(1);
// Idem for getVal ().
USetTimer (1, &servo::getVal);
}
valueChanged becomes update and valueAccessed becomes getVal. Instead of being called on demand, they are now called periodically. The period of the call cannot be lower than the value returned by Object.getPeriod; so you can set it to 0 to mean “as fast as is useful”.
Now, suppose that, for our previous example, we can speed things up by sending all the servomotor commands at the same time, using the following method that takes two arrays of ids and positions.
A hub is the perfect way to handle this task. The UObject header stays the same. We add a hub declaration:
class servohub : public urbi::UObjectHub
{
public:
// The class must have a single constructor taking a string.
servohub (const std::string&);
// Called periodically.
virtual int update ();
// Called by servo.
void addValue (int id, double val);
int* ids;
double* vals;
int size;
int count;
};
servo::update becomes a call to the addValue method of the hub:
The following line can be added to the servo init method, although it has no use in our specific example:
Finally, the implementation of our hub methods is:
servohub::servohub (const std::string& s)
: UObjectHub (s)
, ids (0)
, vals (0)
, size (0)
, count (0)
{
// setup our timer
USetUpdate (1);
}
int
servohub::update ()
{
// Called periodically.
setPositions (count, ids, vals);
// Reset position counter.
count = 0;
return 0;
}
void
servohub::addValue (int id, double val)
{
if (count + 1 < size)
{
// Allocate more memory.
ids = (int*) realloc (ids, (count + 1) * sizeof (int));
vals = (double*) realloc (vals, (count + 1) * sizeof (double));
size = count + 1;
}
ids[count] = id;
vals[count++] = val;
}
UStartHub (servohub);
Periodically, the update method is called on each servo instance, which adds commands to the hub arrays, then the update method of the hub is called, actually sending the command and resetting the array.
Alternatively, to demonstrate the use of the members hub variable, we can entirely remove the update method in the servo class (and the USetUpdate() call in init), and rewrite the hub update method the following way:
int servohub::update()
{
//called periodically
for (UObjectList::iterator i = members.begin ();
i != members.end ();
++i)
addValue (((servo*)*i)->id, (double)((servo*)*i)->val);
setPositions(count, ids, vals);
// reset position counter
count = 0;
return 0;
}
A camera device is an UObject whose val field is a binary object. The Urbi kernel itself doesn’t make any difference between all the possible binary formats and data type, but the API provides image-specific structures for convenience. You must be careful about memory management. The UBinary structure handles its own memory: copies are deep, and the destructor frees the associated buffer. The UImage and USound structures do not.
Let’s suppose we have an underlying camera API with the following functions:
Our device code can be written as follows:
// Inherit from UObject.
class Camera : public urbi::UObject
{
public:
// The class must have a single constructor taking a string.
Camera(const std::string&);
// Urbi constructor. Throw in case of error.
void init (int id);
// Our image variable and dimensions.
urbi::UVar val;
urbi::UVar width;
urbi::UVar height;
// Called on access.
void getVal (UVar&);
// Called periodically.
virtual int update ();
// Frame counter for caching.
int frame;
// Frame number of last access.
int accessFrame;
// Camera id.
int id_;
// Storage for last captured image.
UBinary bin;
};
The constructor only registers init:
Camera::Camera (const std::string& s)
: urbi::UObject (s)
, frame (0)
{
UBindFunction (Camera, init);
}
The init function binds the variable, a function called on access, and sets a timer up on update. It also initializes the UBinary structure.
void
Camera::init (int id)
{
//urbi constructor
id_ = id;
frame = 0;
accessFrame = 0;
if (!initialize (id))
throw std::runtime_error("Failed to initialize camera");
UBindVar (Camera, val);
UBindVar (Camera, width);
UBindVar (Camera, height);
width = getWidth (id);
height = getHeight (id);
UNotifyAccess (val, &Camera::getVal);
bin.type = BINARY_IMAGE;
bin.image.width = width;
bin.image.height = height;
bin.image.imageFormat = IMAGE_RGB;
bin.image.size = width * height * 3;
// Call update () periodically.
USetUpdate (1);
}
The update function simply updates the frame counter:
The getVal updates the camera value, only if it hasn’t already been called this frame, which provides a simple caching mechanism to avoid performing the potentially long operation of acquiring an image too often.
void
Camera::getVal(urbi::UVar&)
{
if (frame == accessFrame)
return;
bin.image.data = getImage (id);
// Assign image to bin.
val = bin;
}
UStart(Camera);
The image data is copied inside the kernel when proceeding this way.
Be careful, suppose that we had created the UBinary structure inside the getVal method, our buffer would have been freed at the end of the function. To avoid this, set it to 0 after assigning the UBinary to the UVar.
In plugin mode, it is possible to access the buffer used by the kernel by casting the UVar to a UImage. You can modify the content of the kernel buffer but no other argument.
Sound handling works similarly to image manipulation, the USound structure is provided for this purpose. The recommended way to implement a microphone is to fill the UObject val variable with the sound data corresponding to one kernel period. If you do so, the Urbi code loop tag:micro.val, will produce the expected result.
Algorithms that require intense computation can be written in C++ but still be usable within Urbi: they acquire their data using UVar referencing other modules’ variables, and output their results to other UVar. Let’s consider the case of a ball detector device that takes an image as input, and outputs the coordinates of a ball if one is found.
The header is defined like:
class BallTracker : public urbi::UObject
{
public:
BallTracker (const std::string&);
void init (const std::string& varname);
// Is the ball visible?
urbi::UVar visible;
// Ball coordinates.
urbi::UVar x;
urbi::UVar y;
};
The constructor only registers init:
// The constructor registers init only.
BallTracker::BallTracker (const::string& s)
: urbi::UObject (s)
{
UBindFunction (BallTracker, init);
}
The init function binds the variables and a callback on update of the image variable passed as a argument.
void
BallTracker::init (const std::string& cameraval)
{
UBindVar (BallTracker, visible);
UBindVar (BallTracker, x);
UBindVar (BallTracker, y);
UNotifyChange (cameraval, &BallTracker::newImage);
visible = 0;
}
The newImage function runs the detection algorithm on the image in its argument, and updates the variables.
void
BallTracker::newImage (urbi::UVar& v)
{
// Cast to UImage.
urbi::UImage i = v;
int px,py;
bool found = detectBall (i.data, i.width, i.height, &px, &py);
if (found)
{
visible = 1;
x = px / i.width;
y = py / i.height;
}
else
visible = 0;
}
This section expects that you already know how to run urbi. If not, please first see Chapter 2.
This section introduces the most basic notions to write urbiscript code. Some aspects are presented only minimally. The goal of this section is to bootstrap yourself with the urbiscript language, to be able to study more in-depth examples afterward.
Commenting your code is crucial, so let’s start by learning how to do this in urbiscript. Comments are ignored by the interpreter, and can be left as documentation, reminder, …urbiscript supports C and C++ style comments:
1; // This is a C++ style comment.
[00000000] 1
2 + /* This is a C-style comment. */ 2;
[00000000] 4
"foo" /* You /* can /* nest */ */ comments. */ "bar";
[00000000] "foobar"
Chapter 2 introduced some of the conventions used in this document: frames such as the previous one denote “urbiscript sessions”, i.e., dialogs between Urbi and you. The output is prefixed by a number between square brackets: this is the date (in milliseconds since the server was launched) at which that line was sent by the server. This is useful at occasions, since Urbi is meant to run many parallel commands. Since these timestamps are irrelevant in documentation, they will often be filled with zeroes. More details about the typesetting of this document (and the other kinds of frames) can be found in Chapter 34.
Several special kinds of “values” can be entered directly with a specific syntax. They are called literals, or sometimes manifest values. We just met a first kind of literals: integers. There are several others, such as:
These examples highlight some points:
You can call functions with the classical, mathematical notation.
cos(0); // Compute cosine
[00000000] 1
max(1, 3); // Get the maximum of the arguments.
[00000000] 3
max(1, 3, 4, 2);
[00000000] 4
Again, the result of the evaluation are printed out. You can see here that function in urbiscript can be variadic, that is, take different number of arguments, such as the max function. Let’s now try the echo function, that prints out its argument.
The server prints out Hello world!, as expected. Note that this output is still prepended with the time stamp. Since echo returns void, no evaluation result is printed.
Variables can be introduced with the var keyword, given a name and an initial value. They can be assigned new values with the = operator.
Note that, just as in C++, assignments return the (right-hand side) value, so you can write code like “x = y = 0”. The rule for valid identifiers is also the same as in C++: they may contain alphanumeric characters and underscores, but they may not start with a digit.
You may omit the initialization value, in which case it defaults to void.
var y;
y;
// Remember, the interpreter remains silent because void is printed out
// as nothing. You can convince yourself that y is actually void with
// the following methods.
y.asString;
[00000000] "void"
y.isVoid;
[00000000] true
Scopes are introduced with curly brackets ({}). They can contain any number of statements. Variables declared in a scope only exist within this scope.
{
var x = "test";
echo(x);
};
[00000000] *** test
// x is no longer defined here
x;
[00000073:error] !!! lookup failed: x
Note that the interpreter waits for the whole scope to be input to evaluate it. Also note the mandatory terminating semicolon after the closing curly bracket.
Methods are called on objects with the dot (.) notation as in C++. Method calls can be chained. Methods with no arguments don’t require the parentheses.
0.cos();
[00000000] 1
"a-b-c".split("-");
[00000000] ["a", "b", "c"]
// Empty parentheses are optional
"foo".length();
[00000000] 3
"foo".length;
[00000000] 3
// Method call can be chained
"".length.cos;
[00000000] 1
In obj.method, we say that obj is the target, and that we are sending him the method message.
You know how to call routines, let’s learn how to write some. Functions can be declared thanks to the function keyword, followed by the comma separated, parentheses surrounded list of formal arguments, and the body between curly brackets.
// Define myFunction
function myFunction()
{
echo("Hello world");
echo("from my function!");
};
[00000000] function () {
echo("Hello world");
echo("from my function!");
}
// Invoke it
myFunction();
[00000000] *** Hello world
[00000000] *** from my function!
Note the strange output after you defined the function. urbiscript seems to be printing the function you just typed in again. This is because a function definition evaluates to the freshly created function.
Functions are first class citizen: they are values, just as 0 or "foobar". The evaluation of a function definition yields the new function, and as always, the interpreter prints out the evaluation result, thus showing you the function again:
// Work in a scope.
{
// Define f
function f()
{
echo("f")
};
// This does not invoke f, it returns its value.
f;
};
[00000000] function () { echo("f") }
{
// Define f
function f()
{
echo("Hello World");
};
// This actually calls f
f();
};
[00000000] *** Hello World
Here you can see that f is actually a simple value. You can just evaluate it to see its value, that is, its body. By adding the parentheses, you can actually call the function. This is a difference with methods calling, where empty parentheses are optional: method are always evaluated, you cannot retrieve their functional value — of course, you can with a different construct, but that’s not the point here.
Since this output is often irrelevant, most of the time it is hidden in this documentation using the |; trick. When a statement is “missing”, an empty statement ({}) is inserted. So code|; is actually equivalent to code | {};, which means “run code, then run {} and return its value”. Since the value of {} is void, which is not displayed, this is a means to discard the result of a computation, and avoid that something is printed. Contrast the two following function definitions.
function sum(a, b, c)
{
return a + b + c;
};
[00003553] function (var a, var b, var c) { return a.’+’(b).’+’(c) }
function sum2(a, b, c)
{
return a + b + c;
}|;
sum(20, 2, 20);
[00003556] 42
The return keyword breaks the control flow of a function (similarly to the way break interrupts a loop) and returns the control flow to the caller. It accepts an optional argument, the value to return to the caller.
In urbiscript, if no return statement is executed, the value of the last expression is returned. Actually, refrained from using return when you don’t need it, it is both less readable (once you get used to this programming style), and less efficient (Listing 18.1.2).
You’re now up and running with basic urbiscript code, and we can dive in details into advanced urbiscript code.
In this section, we focus on urbiscript values as objects, and study urbiscript by-reference values model. We won’t study classes and actual objective programming yet, these points will be presented in Chapter 11.
An object in urbiscript is a rather simple concept: a list of slots. A slot is a value associated to a name. So an object is a list of slot names, each of which indexes a value — just like a dictionary.
The localSlotNames method lists the names of the slots of an object (Object).
You can get an object’s slot value by using the dot (.) operator on this object, followed by the name of the slot.
// We now know the name of its slots. Let’s see their value.
Foo.a;
[00000000] 42
Foo.b;
[00000000] "foo"
It’s as simple as this. The inspect method provides a convenient short-hand to discover an object (Object).
Foo.inspect;
[00000000] *** Inspecting Foo
[00000000] *** ** Prototypes:
[00000000] *** Object
[00000000] *** ** Local Slots:
[00000000] *** a : Float
[00000000] *** asFoo : Code
[00000000] *** b : String
[00000000] *** type : String
Let’s now try to build such an object. First, we want a fresh object to work on. In urbiscript, Object is the parent type of every object (in fact, since urbiscript is prototype-based, Object is the uppermost prototype of every object, but we’ll talk about prototypes later). An instance of object, is an empty, neutral object, so let’s start by instantiating one with the clone method of Object.
// Create the o variable as a fresh object.
var o = Object.clone;
[00000000] Object_0x00000000
// Check its content
o.inspect;
[00006725] *** Inspecting Object_0x00000000
[00006725] *** ** Prototypes:
[00006726] *** Object
[00006726] *** ** Local Slots:
As you can see, we obtain an empty fresh object. Note that it still inherits from Object features that all objects share, such as the localSlotNames method.
Also note how o is printed out: Object_, followed by an hexadecimal number. Since this object is empty, its printing is quite generic: its type (Object), and its unique identifier (every urbiscript object has one). Since these identifiers are often irrelevant and might differ between two executions, they are often filled with zeroes in this document.
We’re now getting back to our empty object. We want to give it two slots, a and b, with values 42 and "foo" respectively. We can do this with the setSlot method, which takes the slot name and its value.
o.setSlot("a", 42);
[00000000] 42
o.inspect;
[00009837] *** Inspecting Object_0x00000000
[00009837] *** ** Prototypes:
[00009837] *** Object
[00009838] *** ** Local Slots:
[00009838] *** a : Float
Here we successfully created our first slot, a. A good shorthand for setting slot is using the var keyword.
// This is equivalent to o.setSlot("b", "foo").
var o.b = "foo";
[00000000] "foo"
o.inspect;
[00072678] *** Inspecting Object_0x00000000
[00072678] *** ** Prototypes:
[00072679] *** Object
[00072679] *** ** Local Slots:
[00072679] *** a : Float
[00072680] *** b : String
The latter form with var is preferred, but you need to know the name of the slot at the time of writing the code. With the former one, you can compute the slot name at execution time. Likewise, you can read a slot with a run-time determined name with the getSlot method, which takes the slot name as argument. The following listing illustrates the use of getSlot and setSlot to read and write slots whose names are unknown at code-writing time.
function set(object, name, value)
{
// We have to use setSlot here, since we don’t
// know the actual name of the slot.
return object.setSlot("x_" + name, value);
}|;
function get(object, name)
{
// We have to use getSlot here, since we don’t
// know the actual name of the slot.
return object.getSlot("x_" + name);
}|;
var x = Object.clone;
[00000000] Object_0x42342448
set(x, "foo", 0);
[00000000] 0
set(x, "bar", 1);
[00000000] 1
x.localSlotNames;
[00000000] ["x_bar", "x_foo"]
get(x, "foo");
[00000000] 0
get(x, "bar");
[00000000] 1
Right, now we can create fresh objects, create slots in them and read them afterward, even if their name is dynamically computed, with getSlot and setSlot. Now, you might wonder if there’s a method to update the value of the slot. Guess what, there’s one, and it’s named…updateSlot (originality award). Getting back to our o object, let’s try to update one of its slots.
Again, there’s a shorthand for updateSlot: operator =.
o.b;
[00000000] "foo"
// Equivalent to o.updateSlot("b", "bar")
o.b = "bar";
[00000000] "bar"
o.b;
[00000000] "bar"
Likewise, prefer the ’=’ notation whenever possible, but you’ll need updateSlot to update a slot whose name you don’t know at code-writing time.
Note that defining the same slot twice, be it with setSlot or var, is an error. The slot must be defined once with setSlot, and subsequent writes must be done with updateSlot.
var o.c = 0;
[00000000] 0
// Can’t redefine a slot like this
var o.c = 1;
[00000000:error] !!! slot redefinition: c
// Okay.
o.c = 1;
[00000000] 1
Finally, use removeLocalSlot to delete a slot from an object.
o.localSlotNames;
[00000000] ["a", "b", "c"]
o.removeLocalSlot("c");
[00000000] Object_0x00000000
o.localSlotNames;
[00000000] ["a", "b"]
Here we are, now you can inspect and modify objects at will. Don’t hesitate to explore urbiscript objects you’ll encounter through this documentation like this. Last point: reading, updating or removing a slot which does not exist is, of course, an error.
Methods in urbiscript are simply object slots containing functions. We made a little simplification earlier by saying that obj.slot is equivalent to obj.getSlot("slot"): if the fetched value is executable code such as a function, the dot form evaluates it, as illustrated below. Inside a method, this gives access to the target — as in C++. It can be omitted if there is no ambiguity with local variables.
var o = Object.clone;
[00000000] Object_0x0
// This syntax stores the function in the ’f’ slot of ’o’.
function o.f ()
{
echo("This is f with target " + this);
return 42;
} |;
// The slot value is the function.
o.getSlot("f");
[00000001] function () {
echo("This is f with target ".’+’(this));
return 42;
}
// Huho, the function is invoked!
o.f;
[00000000] *** This is f with target Object_0x0
[00000000] 42
// The parentheses are in fact optional.
o.f();
[00000000] *** This is f with target Object_0x0
[00000000] 42
This was designed this way so as one can replace an attribute, such as an integer, with a function that computes the value. This enables to replace an attribute with a method without changing the object interface, since the parentheses are optional.
This implies that getSlot can be a better tool for object inspection to avoid invoking slots, as shown below.
// The ’empty’ method of strings returns whether the string is empty.
"foo".empty;
[00000000] false
"".empty;
[00000000] true
// Using getSlot, we can fetch the function without calling it.
"".getSlot("asList");
[00000000] function () { split("") }
The asList function simply bounces the task to split. Let’s try getSlot’ing another method:
The size method of String is another type of object: a Primitive. These objects are executable, like functions, but they are actually opaque primitives implemented in C++.
If you’re wondering what is an object and what is not, the answer is simple: every single bit of value you manipulate in urbiscript is an object, including primitive types, types themselves, functions, …
var x = 0;
[00000000] 0
x.localSlotNames;
[00000000] []
var x.slot = 1;
[00000000] 1
x.localSlotNames;
[00000000] ["slot"]
x.slot;
[00000000] 1
x;
[00000000] 0
As you can see, integers are objects just like any other value.
We are now going to focus on the urbiscript value model, that is how values are stored and passed around. The whole point is to understand when variables point to the same object. For this, we introduce uid, a method that returns the target’s unique identifier — the same one that was printed when we evaluated Object.clone. Since uids might vary from an execution to another, their values in this documentation are dummy, yet not null to be able to differentiate them.
var o = Object.clone;
[00000000] Object_0x100000
o.uid;
[00000000] "0x100000"
42.uid;
[00000000] "0x200000"
42.uid;
[00000000] "0x300000"
Our objects have different uids, reflecting the fact that they are different objects. Note that entering the same integer twice (42 here) yields different objects. Let’s introduce new operators before diving in this concept. First the equality operator: ==. This operator is the exact same as C or C++’s one, it simply returns whether its two operands are semantically equal. The second operator is ===, which is the physical equality operator. It returns whether its two operands are the same object, which is equivalent to having the same uid. This can seem a bit confusing; let’s have an example.
var a = 42;
[00000000] 42
var b = 42;
[00000000] 42
a == b;
[00000000] true
a === b;
[00000000] false
Here, the == operator reports that a and b are equal — indeed, they both evaluate to 42. Yet, the === operator shows that they are not the same object: they are two different instances of integer objects, both equal 42.
Thanks to this operator, we can point out the fact that slots and local variables in urbiscript have a reference semantic. That is, when you defining a local variable or a slot, you’re not copying any value (as you would be in C or C++), you’re only making it refer to an already existing value (as you would in Ruby or Java).
var a = 42;
[00000000] 42
var b = 42;
[00000000] 42
var c = a; // c refers to the same object as a.
[00000000] 42
// a, b and c are equal: they have the same value.
a == b && a == c;
[00000000] true
// Yet only a and c are actually the same object.
a === b;
[00000000] false
a === c;
[00000000] true
So here we see that a and c point to the same integer, while b points to a second one. This a non-trivial fact: any modification on a will affect c as well, as shown below.
a.localSlotNames;
[00000000] []
b.localSlotNames;
[00000000] []
c.localSlotNames;
[00000000] []
var a.flag; // Create a slot in a.
a.localSlotNames;
[00000000] ["flag"]
b.localSlotNames;
[00000000] []
c.localSlotNames;
[00000000] ["flag"]
Updating slots or local variables does not update the referenced value. It simply redirects the variable to the new given value.
var a = 42;
[00000000] 42
var b = a;
[00000000] 42
// b and a point to the same integer.
a === b;
[00000000] true
// Updating b won’t change the referred value, 42,
// it makes it reference a fresh integer with value 51.
b = 51;
[00000000] 51
// Thus, a is left unchanged:
a;
[00000000] 42
Understanding the two latter examples is really important, to be aware of what your variable are referring to.
Finally, function and method arguments are also passed by reference: they can be modified by the function.
function test(arg)
{
var arg.flag; // add a slot in arg
echo(arg.uid); // print its uid
} |;
var x = Object.clone;
[00000000] Object_0x1
x.uid;
[00000000] "0x1"
test(x);
[00000000] *** 0x1
x.localSlotNames;
[00000000] ["flag"]
Beware however that arguments are passed by reference, and the behavior might not be what you may expected.
function test(arg)
{
// Updates the local variable arg to refer 1.
// Does not affect the referred value, nor the actual external argument.
arg = 1;
} |;
var x = 0;
[00000000] 0
test(x);
[00000000] 1
// x wasn’t modified
x;
[00000000] 0
You should now understand the reference semantic of local variables, slots and arguments. It’s very important to keep them in mind, otherwise you will end up modifying variables you didn’t want, or change a copy of reference, failing to update the desired one.
In this section, we’ll introduce some flow control structures that will prove handy later. Most of them are inspired by C/C++.
The if construct is the same has C/C++’s one. The if keyword is followed by a condition between parentheses and an expression, and optionally the else keyword and another expression. If the condition evaluates to true, the first expression is evaluated. Otherwise, the second expression is evaluated if present.
The if construct is an expression: it has a value.
The while construct is, again, the same as in C/C++. The while keyword is followed by a condition between parentheses and an expression. If the condition evaluation is false, the execution jumps after the while block; otherwise, the expression is evaluated and control jumps before the while block.
var x = 2;
[00000000] 2
while (x < 40)
{
x += 10;
echo(x);
};
[00000000] *** 12
[00000000] *** 22
[00000000] *** 32
[00000000] *** 42
The for keyword supports different constructs, as in languages such as Java, C#, or even the forthcoming C++ revision.
The first construct is hardly more than syntactic sugar for a while loop.
for (var x = 2; x < 40; x += 10)
echo(x);
[00000000] *** 2
[00000000] *** 12
[00000000] *** 22
[00000000] *** 32
The second construct allows to iterate over members of a collection, such as a list. The for keyword, followed by var, an identifier, a colon (or in), an expression and a scope, executes the scope for every element in the collection resulting of the evaluation of the expression, with the variable named with the identifier referring to the list members.
The syntax of the switch construct is similar to C/C++’s one, except it works on any kind of object, not only integral ones. Comparison is done by semantic equality (operator ==). Execution will jump out of the switch-block after a case has been executed (no need to break). Also, contrary to C++, the whole construct has a value: that of the matching case.
A do scope is a shorthand to perform several actions on an object.
var o1 = Object.clone;
[00000000] Object_0x423a0708
var o1.one = 1;
[00000000] 1
var o1.two = 2;
[00000000] 2
echo(o1.uid);
[00000000] *** 0x423a0708
The same result can be obtained with a short do scope, that redirect method calls to their target, as in the listing below. This is similar to the Pascal “with” construct. The value of the do-block is the target itself.
var o2 = Object.clone;
[00000000] Object_0x42339e08
// All the message in this scope are destined to o.
do (o2)
{
var one = 1; // var is a shortcut for the setSlot
var two = 2; // message, so it applies on obj too.
echo(uid);
};
[00000000] *** 0x42339e08
[00000000] Object_0x42339e08
This section presents advanced uses of functions and scoping, as well as their combo: lexical closures, which prove to be a very powerful tool.
Contrary to other languages from the C family, scopes are expressions: they can be used where values are expected, just as 1 + 1 or "foo". They evaluate to the value of their last expression, or void if they are empty. The following listing illustrates the use of scopes as expressions. The last semicolon inside a scope is optional.
// Scopes evaluate to the value of their last expression.
{ 1; 2; 3; };
[00000000] 3
// They are expressions.
echo({1; 2; 3});
[00000000] *** 3
Scopes can be nested. Variables can be redefined in nested scopes. In this case, the inner variables hide the outer ones, as illustrated below.
var x = 0; // Define the outer x.
[00000000] 0
{
var x = 1; // Define an inner x.
x = 2; // These refer to
echo(x); // the inner x
};
[00000000] *** 2
x; // This is the outer x again.
[00000000] 0
{
x = 3; // This is still the outer x.
echo(x);
};
[00000000] *** 3
x;
[00000000] 3
Functions can be defined anywhere local variables can — that is, about anywhere. These functions’ visibility are limited to the scope they’re defined in, like variables. This enables for instance to write local helper functions like max2 in the example below.
function max3(a, b, c) // Max of three values
{
function max2(a, b)
{
if (a > b)
a
else
b
};
max2(a, max2(b, c));
}|;
A closure is the capture by a function of a variable external to this function. urbiscript supports lexical closure: functions can refer to outer local variables, as long as they are visible (in scope) from where the function is defined.
function printSalaries(var rate)
{
var charges = 100;
function computeSalary(var hours)
{
// rate and charges are captured from the environment by closure.
rate * hours - charges
};
echo("Alice’s salary is " + computeSalary(35));
echo("Bob’s salary is " + computeSalary(30));
}|;
printSalaries(15);
[00000000] *** Alice’s salary is 425
[00000000] *** Bob’s salary is 350
Closures can also change captured variables, as shown below.
var a = 0;
[00000000] 0
var b = 0;
[00000000] 0
function add(n)
{
// a and b are updated by closure.
a += n;
b += n;
{}
}|;
add(25);
add(25);
add(1);
a;
[00000000] 51
b;
[00000000] 51
Closure can be really powerful tools in some situations; they are even more useful when combined with functional programming, as described in Chapter 12.
This section presents object programming in urbiscript: the prototype-based object model of urbiscript, and how to define and use classes.
You’re probably already familiar with class-based object programming, since this is the C++, Java, C# model. Classes and objects are very different entities. Classes and types are static entities that do not exist at run-time, while objects are dynamic entities that do not exist at compile time.
Prototype-based object programming is different: the difference between classes and objects, between types and values, is blurred. Instead, you have an object, that is already an instance, and that you might clone to obtain a new one that you can modify afterward. Prototype-based programming was introduced by the Self language, and is used in several popular script languages such as Io or JavaScript.
Class-based programming can be considered with an industrial metaphor: classes are molds, from which objects are generated. Prototype-based programming is more biological: a prototype object is cloned into another object which can be modified during its lifetime.
Consider pairs for instance (see Pair). Pairs hold two values, first and second, like an std::pair in C++. Since urbiscript is prototype-based, there is no pair class. Instead, Pair is really a pair (object).
We can see here that Pair is a pair whose two values are equal to nil — which is a reasonable default value. To get a pair of our own, we simply clone Pair. We can then use it as a regular pair.
var p = Pair.clone;
[00000000] (nil, nil)
p.first = "101010";
[00000000] "101010"
p.second = true;
[00000000] true
p;
[00000000] ("101010", true)
Pair;
[00000000] (nil, nil)
Since Pair is a regular pair object, you can modify and use it at will. Yet this is not a good idea, since you will alter your base prototype, which alters any derivative, future and even past.
var before = Pair.clone;
[00000000] (nil, nil)
Pair.first = false;
[00000000] false
var after = Pair.clone;
[00000000] (false, nil)
before;
[00000000] (false, nil)
// before and after share the same first: that of Pair.
assert(Pair.first === before.first);
assert(Pair.first === after.first);
In prototype-based language, is-a relations (being an instance of some type) and inheritance relations (extending another type) are simplified in a single relation: prototyping. You can inspect an object prototypes with the protos method.
As expected, our fresh pair has one prototype, (nil, nil), which is how Pair displays itself. We can check this as presented below.
// List.head returns the first element.
p.protos.head;
[00000000] (nil, nil)
// Check that the prototype is really Pair.
p.protos.head === Pair;
[00000000] true
Prototypes are the base of the slot lookup mechanism. Slot lookup is the action of finding an object slot when the dot notation is used. So far, when we typed obj.slot, slot was always a slot of obj. Yet, this call can be valid even if obj has no slot slot, because slots are also looked up in prototypes. For instance, p, our clone of Pair, has no first or second slots. Yet, p.first and p.second work, because these slots are present in Pair, which is p’s prototype. This is illustrated below.
var p = Pair.clone;
[00000000] (nil, nil)
// p has no slots of its own.
p.localSlotNames;
[00000000] []
// Yet this works.
p.first;
// This is because p has Pair for prototype, and Pair has a ’first’ slot.
p.protos.head === Pair;
[00000000] true
"first" in Pair.localSlotNames && "second" in Pair.localSlotNames;
[00000000] true
As shown here, the clone method simply creates an empty object, with its target as prototype. The new object has the exact same behavior as the cloned on thanks to slot lookup.
Let’s experience slot lookup by ourselves. In urbiscript, you can add and remove prototypes from an object thanks to addProto and removeProto.
// We create a fresh object.
var c = Object.clone;
[00000000] Object_0x1
// As expected, it has no ’slot’ slot.
c.slot;
[00000000:error] !!! lookup failed: slot
var p = Object.clone;
[00000000] Object_0x2
var p.slot = 0;
[00000000] 0
c.addProto(p);
[00000000] Object_0x1
// Now, ’slot’ is found in c, because it is inherited from p.
c.slot;
[00000000] 0
c.removeProto(p);
[00000000] Object_0x1
// Back to our good old lookup error.
c.slot;
[00000000:error] !!! lookup failed: slot
The slot lookup algorithm in urbiscript in a depth-first traversal of the object prototypes tree. Formally, when the s slot is requested from x:
Thus, slots from the last prototype added take precedence over other prototype’s slots.
var proto1 = Object.clone;
[00000000] Object_0x10000000
var proto2 = Object.clone;
[00000000] Object_0x20000000
var o = Object.clone;
[00000000] Object_0x30000000
o.addProto(proto1);
[00000000] Object_0x30000000
o.addProto(proto2);
[00000000] Object_0x30000000
// We give o an x slot through proto1.
var proto1.x = 0;
[00000000] 0
o.x;
[00000000] 0
// proto2 is visited first during lookup.
// Thus its "x" slot takes precedence over proto1’s.
var proto2.x = 1;
[00000000] 1
o.x;
[00000000] 1
// Of course, o’s own slots have the highest precedence.
var o.x = 2;
[00000000] 2
o.x;
[00000000] 2
You can check where in the prototype hierarchy a slot is found with the locateSlot method. This is a very handful tool when inspecting an object.
var p = Pair.clone;
[00000000] (nil, nil)
// Check that the ’first’ slot is found in Pair
p.locateSlot("first") === Pair;
[00000000] true
// Where does locateSlot itself come from? Object itself!
p.locateSlot("locateSlot");
[00000000] Object
The prototype model is rather simple: creating a fresh object simply consists in cloning a model object, a prototype, that was provided to you. Moreover, you can add behavior to an object at any time with a simple addProto: you can make any object a fully functional Pair with a simple myObj.addProto(Pair).
One point might be bothering you though: what if you want to update a slot value in a clone of your prototype?
Say we implement a simple prototype, with an x slot equal to 0, and clone it twice. We have three objects with an x slot, yet only one actual 0 integer. Will modifying x in one of the clone change the prototype’s x, thus altering the prototype and the other clone as well?
The answer is, of course, no, as illustrated below.
var proto = Object.clone;
[00000000] Object_0x1
var proto.x = 0;
[00000000] 0
var o1 = proto.clone;
[00000000] Object_0x2
var o2 = proto.clone;
[00000000] Object_0x3
// Are we modifying proto’s x slot here?
o1.x = 1;
[00000000] 1
// Obviously not
o2.x;
[00000000] 0
proto.x;
[00000000] 0
o1.x;
[00000000] 1
This work thanks to copy-on-write: slots are first duplicated to the local object when they’re updated, as we can check below.
// This is the continuation of previous example.
// As expected, o2 finds "x" in proto
o2.locateSlot("x") === proto;
[00000000] true
// Yet o1 doesn’t anymore
o1.locateSlot("x") === proto;
[00000000] false
// Because the slot was duplicated locally
o1.locateSlot("x") === o1;
[00000000] true
This is why, when we cloned Pair earlier, and modified the “first” slot of our fresh Pair, we didn’t alter Pair one all its other clones.
Now that we know the internals of urbiscript’s object model, we can start defining our own classes.
But wait, we just said there are no classes in prototype-based object-oriented languages! That is true: there are no classes in the sense of C++, i.e., compile-time entities that are not objects. Instead, prototype-based languages rely on the existence of a canonical object (the prototype) from which (pseudo) instances are derived. Yet, since the syntactic inspiration for urbiscript comes from languages such as Java, C++ and so forth, it is nevertheless the class keyword that is used to define the pseudo-classes, i.e., prototypes.
As an example, we define our own Pair class. We just have to create a pair, with its first and second slots. For this we use the do scope described in Section 9.5. The listing below defines a new Pair class. The asString function is simply used to customize pairs printing — don’t give it too much attention for now.
var MyPair = Object.clone;
[00000000] Object_0x1
do (MyPair)
{
var first = nil;
var second = nil;
function asString ()
{
"MyPair: " + first + ", " + second
};
}|;
// We just defined a pair
MyPair;
[00000000] MyPair: nil, nil
// Let’s try it out
var p = MyPair.clone;
[00000000] MyPair: nil, nil
p.first = 0;
[00000000] 0
p;
[00000000] MyPair: 0, nil
MyPair;
[00000000] MyPair: nil, nil
That’s it, we defined a pair that can be cloned at will! urbiscript provides a shorthand to define classes as we did above: the class keyword.
class MyPair
{
var first = nil;
var second = nil;
function asString() { "(" + first + ", " + second + ")"; };
};
[00000000] (nil, nil)
The class keyword simply creates MyPair with Object.clone, and provides you with a do (MyPair) scope. It actually also pre-defines a few slots, but this is not the point here.
It is also possible to specify a proto for the newly created “class”, using the same syntax as Java and C++:
class Top
{
var top = "top";
};
[00000000] Top
class Bottom : Top
{
var bottom = "bottom";
};
[00000000] Bottom
Bottom.new.top;
[00000000] "top"
For more details, see Section 22.1.6.8.
As we’ve seen, we can use the clone method on any object to obtain an identical object. Yet, some classes provide more elaborate constructors, accessible by calling new instead of clone, potentially passing arguments.
While clone guarantees you obtain an empty fresh object inheriting from the prototype, new behavior is left to the discretion of the cloned prototype — although its behavior is the same as clone by default.
To define such constructors, prototypes only need to provide an init method, that will be called with the arguments given to new. For instance, we can improve our previous Pair class with a constructor.
class MyPair
{
var first = nil;
var second = nil;
function init(f, s) { first = f; second = s; };
function asString() { "(" + first + ", " + second + ")"; };
};
[00000000] (nil, nil)
MyPair.new(0, 1);
[00000000] (0, 1)
In urbiscript, operators such as +, && and others, are regular functions that benefit from a bit of syntactic sugar. To be more precise, a+b is exactly the same as a.’+’(b). The rules to resolve slot names apply too, i.e., the ’+’ slot is looked for in a, then in its prototypes.
The following example provides arithmetic between pairs.
class ArithPair
{
var first = nil;
var second = nil;
function init(f, s) { first = f; second = s; };
function asString() { "(" + first + ", " + second + ")"; };
function ’+’(rhs) { new(first + rhs.first, second + rhs.second); };
function ’-’(rhs) { new(first - rhs.first, second - rhs.second); };
function ’*’(rhs) { new(first * rhs.first, second * rhs.second); };
function ’/’(rhs) { new(first / rhs.first, second / rhs.second); };
};
[00000000] (nil, nil)
ArithPair.new(1, 10) + ArithPair.new(2, 20) * ArithPair.new(3, 30);
[00000000] (7, 610)
Slots should be understood as names to values. Several names may point to the same value (a phenomenon called aliasing). Enriching values is easy: provide them with new slots. Yet it is sometimes needed to define the behavior of the slot rather than the property of the value. This is the purpose of properties in urbiscript.
In following example, we attach some random property foo to the value pointed to by the slot x.
If y is another slot to the value of x, then it provides the same foo feature:
If x is bound to a new object (e.g., 456), then the feature foo is no longer present, since it’s a feature of the value (i.e., 123), and not one of the slot (i.e., x).
Of course, y, which is still linked to the original value (123), answers to queries to foo.
If, on the contrary you want to attach a feature to the slot-as-a-name, rather than to the value it contains, use the properties. The syntax is slotName->propertyName.
Copying the value contained by a slot does not propagate the properties of the slot:
And if you assign a new value to a slot, the properties of the slot are preserved:
urbiscript support functional programming through first class functions and lambda expressions.
urbiscript has first class functions, i.e., functions are regular values, just like integers or strings. They can be stored in variables, passed as arguments to other functions, and so forth. For instance, you don’t need to write function object.f(){/* ... */} to insert a function in an object, you can simply use setSlot.
var o = Object.clone|;
// Here we can use f as any regular value.
o.setSlot("m1", function () { echo("Hello") })|;
// This is strictly equivalent.
var o.m2 = function () { echo("Hello") }|;
o.m1;
[00000000] *** Hello
o.m2;
[00000000] *** Hello
This enables to write powerful pieces of code, like functions that take function as argument. For instance, consider the all function: given a list and a function, it applies the function to each element of the list, and returns whether all calls returned true. This enables to check very simply if all elements in a list verify a predicate.
function all(list, predicate)
{
for (var elt : list)
if (!predicate(elt))
return false;
return true;
}|;
// Check if all elements in a list are positive.
function positive(x) { x >= 0 }|;
all([1, 2, 3], getSlot("positive"));
[00000000] true
all([1, 2, -3], getSlot("positive"));
[00000000] false
It turns out that all already exists: instead of all(list, predicate), use list.all(predicate), see RangeIterable.all.
Another nice feature is the ability to write lambda functions, which are anonymous functions. You can create a functional value as an expression, without naming it, with the syntax shown below.
// Create an anonymous function
function (x) {x + 1}|;
// This enable to easily pass function
// to our "all" function:
[1, 2, 3].all(function (x) { x > 0});
[00000000] true
In fact, the function construct we saw earlier is only a shorthand for a variable assignment.
// This ...
function obj.f (/*...*/) {/*...*/};
// ... is actually a shorthand for:
const var obj.f = function (/*...*/) {/* ... */};
Most popular programming languages use strict arguments evaluation: arguments are evaluated before functions are called. Other languages use lazy evaluation: argument are evaluated by the function only when needed. In urbiscript, evaluation is strict by default, but you can ask a function not to evaluate its arguments, and do it by hand. This works by not specifying formal arguments. The function is provided with a call object that enables you to evaluate arguments.
// Note the lack of formal arguments specification
function first
{
// Evaluate only the first argument.
call.evalArgAt(0);
}|;
first(echo("first"), echo("second"));
[00000000] *** first
function reverse
{
call.evalArgAt(1);
call.evalArgAt(0);
}|;
reverse(echo("first"), echo("second"));
[00000000] *** second
[00000000] *** first
A good example are logic operators. Although C++ is a strict language, it uses a few logic operators. For instance, the logical and (&&) does not evaluate its right operand if the left operand is false (the result will be false anyway).
urbiscript logic operator mimic this behavior. The listing below shows how one can implement such a behavior.
function myAnd
{
if (call.evalArgAt(0))
call.evalArgAt(1)
else
false;
}|;
function f()
{
echo("f executed");
return true;
}|;
myAnd(false, f());
[00000000] false
myAnd(true, f());
[00000000] *** f executed
[00000000] true
Parallelism is a major feature of urbiscript. So far, all we’ve seen already existed in other languages — although we tried to pick, mix and adapt features and paradigms to create a nice scripting language. Parallelism is one of the corner stones of its paradigm, and what makes it so well suited to high-level scripting of interactive agents, in fields such as robotics or AI.
For now, we’ve separated our different commands with a semicolon (;). There are actually four statement separators in urbiscript:
The example below demonstrates the use of & to launch two functions in parallel.
function test(name)
{
echo(name + ": 1");
echo(name + ": 2");
echo(name + ": 3");
} |;
// Serialized executions
test("left") ; test ("middle"); test ("right");
[00000000] *** left: 1
[00000000] *** left: 2
[00000000] *** left: 3
[00000000] *** middle: 1
[00000000] *** middle: 2
[00000000] *** middle: 3
[00000000] *** right: 1
[00000000] *** right: 2
[00000000] *** right: 3
// Parallel execution
test("left") & test("middle") & test ("right");
[00000000] *** left: 1
[00000000] *** middle: 1
[00000000] *** right: 1
[00000000] *** left: 2
[00000000] *** middle: 2
[00000000] *** right: 2
[00000000] *** left: 3
[00000000] *** middle: 3
[00000000] *** right: 3
In this test, we see that the & runs its operands simultaneously.
The difference between “&” and “,” is rather subtle:
function test(name)
{
echo(name + ": 1");
echo(name + ": 2");
echo(name + ": 3");
}|;
// Run test and echo("right") in parallel,
// and wait until both are done before continuing
test("left") & echo("right"); echo("done");
[00000000] *** left: 1
[00000000] *** right
[00000000] *** left: 2
[00000000] *** left: 3
[00000000] *** done
// Run test in background, then both echos without waiting.
test("left") , echo("right"); echo("done");
[00000000] *** left: 1
[00000000] *** right
[00000000] *** left: 2
[00000000] *** done
[00000000] *** left: 3
That’s about all there is to say about these operators. Although they’re rather simple, they are really powerful and enables you to include parallelism anywhere at no syntactical cost.
The Control.detach function backgrounds the execution of its argument. Its behavior is the same as the comma (,) operator, except that the execution is completely detached, and not waited for at the end of the scope.
function test()
{
// Wait for one second, and echo "foo".
detach({sleep(1s); echo("foo")});
}|;
test();
echo("Not blocked");
[00000000] Job<shell_4>
[00000000] *** Not blocked
sleep(2s);
echo("End of sleep");
[00001000] *** foo
[00002000] *** End of sleep
A Tag is a multipurpose code execution control and instrumentation feature. Any chunk of code can be tagged, by preceding it with a tag and a colon (:). Tag can be created with Tag.new(name). Naming tags is optional, yet it’s a good idea since it will be used for many features. The example below illustrates how to tag chunks of code.
// Create a new tag
var mytag = Tag.new("name");
[00000000] Tag<name>
// Tag the evaluation of 42
mytag: 42;
[00000000] 42
// Tag the evaluation of a block.
mytag: { "foo"; 51 };
[00000000] 51
// Tag a function call.
mytag: echo("tagged");
[00000000] *** tagged
You can use tags that were not declared previously, they will be created implicitly (see below). However, this is not recommended since tags will be created in a global scope, the Tag object. This feature can be used when inputting test code in the top level to avoid bothering to declare each tag, yet it is considered poor practice in regular code.
// Since mytag is not declared, this will first do:
// var Tag.mytag = Tag.new("mytag");
mytag : 42;
[00000000] 42
So you can tag code, yet what’s the use? One of the primary purpose of tags is to be able to control the execution of code running in parallel. Tags have a few control methods (see Tag):
You can think of block like a permanent stop.
The three following examples illustrate these features.
// Launch in background (using the comma) code that prints "ping"
// every second. Tag it to keep control over it.
mytag:
every (1s)
echo("ping"),
sleep(2.5s);
[00000000] *** ping
[00001000] *** ping
[00002000] *** ping
// Suspend execution
mytag.freeze;
// No printing anymore
sleep(1s);
// Resume execution
mytag.unfreeze;
sleep(1s);
[00007000] *** ping
// Now, we print out a message when we get out of the tag.
{
mytag:
every (1s)
echo("ping");
// Execution flow jumps here if mytag is stopped.
echo("Background job stopped")|
},
sleep(2.5s);
[00000000] *** ping
[00001000] *** ping
[00002000] *** ping
// Stop the tag
mytag.stop;
[00002500] *** Background job stopped
// Our background job finished.
// Unfreezing the tag has no effect.
mytag.unfreeze;
// Now, print out a message when we get out of the tag.
loop
{
echo("ping"); sleep(1s);
mytag: { echo("pong"); sleep(1s); };
},
sleep(3.5s);
[00000000] *** ping
[00001000] *** pong
[00002000] *** ping
[00003000] *** pong
// Block printing of pong.
mytag.block;
sleep(3s);
// The second half of the while isn’t executed anymore.
[00004000] *** ping
[00005000] *** ping
[00006000] *** ping
// Reactivate pong
mytag.unblock;
sleep(3.5s);
[00008000] *** pong
[00009000] *** ping
[00010000] *** pong
[00011000] *** ping
In this section, we implement a more advanced example with parallelism.
The listing below presents how to implement a timeOut function, that takes code to execute and a timeout as arguments. It executes the code, and returns its value. However, if the code execution takes longer than the given timeout, it aborts it, prints "Timeout!" and returns void. In this example, we use:
// timeout (Code, Duration).
function timeOut
{
// In background, launch a timeout job that waits
// for the given duration before aborting the function.
// call.evalArgAt(1) is the second argument, the duration.
{
sleep(call.evalArgAt(1));
echo("Timeout!");
return;
},
// Run the Code and return its value.
return call.evalArgAt(0);
} |;
timeOut({sleep(1s); echo("On time"); 42}, 2s);
[00000000] *** On time
[00000000] 42
timeOut({sleep(2s); echo("On time"); 42}, 1s);
[00000000] *** Timeout!
When dealing with highly interactive agent programming, sequential programming is inconvenient. We want to react to external, random events, not execute code linearly with a predefined flow. urbiscript has a strong support for event-based programming.
The first construct we will study uses the at keyword. Given a condition and a statement, at will evaluate the statement each time the condition becomes true. That is, when a rising edge occurs on the condition.
var x = 0;
[00000000] 0
at (x > 5)
echo("ping");
x = 5;
[00000000] 5
// This triggers the event.
x = 6;
[00000000] 6
[00000000] *** ping
// Does not trigger, since the condition is already true.
x = 7;
[00000000] 7
// The condition becomes false here.
x = 3;
[00000000] 3
x = 10;
[00000000] 10
[00000000] *** ping
An onleave block can be appended to execute an expression when the expression becomes false — that is, on falling edges.
var x = false;
[00000000] false
at (x)
echo("x")
onleave
echo("!x");
x = true;
[00000000] true
[00000000] *** x
x = false;
[00000000] false
[00000000] *** !x
See Listing 22.10.1 for more details on at statements.
The whenever construct is similar to at, except the expression evaluation is systematically restarted when it finishes as long as the condition stands true.
var x = false;
[00000000] false
whenever (x)
{
echo("ping");
sleep(1s);
};
x = true;
[00000000] true
sleep(3s);
// Whenever keeps triggering
[00000000] *** ping
[00001000] *** ping
[00002000] *** ping
x = false;
[00002000] false
// Whenever stops triggering
Just like at has onleave, whenever has else: the given expression is evaluated as long as the condition is false.
var x = false;
[00002000] false
whenever (x)
{
echo("ping");
sleep(1s);
}
else
{
echo("pong");
sleep(1s);
};
sleep (3s);
[00000000] *** pong
[00001000] *** pong
[00002000] *** pong
x = true;
[00003000] true
sleep (3s);
[00003000] *** ping
[00004000] *** ping
[00005000] *** ping
x = false;
[00006000] false
sleep (2s);
[00006000] *** pong
[00007000] *** pong
In addition to monitoring an expression with a watchdog, urbiscript enables you to define events that can be caught with the at and whenever constructs we saw earlier. You can create events by instantiating the Event prototype. They can then be emitted with the ! keyword.
var myEvent = Event.new;
[00000000] Event_0xb5579008
at (myEvent?)
echo("ping");
myEvent!;
[00000000] *** ping
// events work well with parallelism
myEvent! & myEvent!;
[00000000] *** ping
[00000000] *** ping
Both at and whenever have the same behavior on punctual events. However, if you emit an event for a given duration, whenever will keep triggering for this duration, contrary to at.
var myEvent = Event.new;
[00000000] Event_0xb558a588
whenever (myEvent?)
{
echo("ping (whenever)")|
sleep(200ms)
};
at (myEvent?)
{
echo("ping (at)")|
sleep(200ms)
};
// Emit myEvent for .3 second.
myEvent! ∼ 300ms;
[00000000] *** ping (whenever)
[00000100] *** ping (whenever)
[00000000] *** ping (at)
Events behave very much like “channels”: listeners use at or whenever, and producers use !. Fortunately, the messages can include a payload, i.e., something sent in the “message”. The Event then behaves very much like an identifier of the message type. To send/catch the payload, just pass arguments to ! and ?:
var event = Event.new;
[00000000] Event_0x0
at (event?(var payload))
echo("received: " + payload)
onleave
echo("had received: " + payload);
event!(1);
[00000008] *** received: 1
[00000009] *** had received: 1
event!(["string", 124]);
[00000010] *** received: ["string", 124]
[00000011] *** had received: ["string", 124]
Like functions, events have an arity, i.e., they depend on the number of arguments: at (event?(arg)) will only match emissions whose payload contain exactly one argument, i.e., event!(arg).
Event handlers that do not specify their arity (i.e., without parentheses) match event emissions of any arity.
at (event?)
echo("received an event")
onleave
echo("had received an event");
event!;
[00000014] *** received an event
[00000015] *** had received an event
event!(1);
[00000016] *** received: 1
[00000017] *** had received: 1
[00000018] *** received an event
[00000019] *** had received an event
event!(1, 2);
[00000020] *** received an event
[00000021] *** had received an event
Actually, the feature is much more powerful than this: full pattern matching applies, as with the switch/case construct.
var e = Event.new|;
at (e?)
echo("e");
at (e?(var x))
echo("e(x)");
at (e?(1))
echo("e(1)");
at (e?(var x) if x.isA(Float) && x % 2)
echo("e(odd)");
// Payload must be a list of three members, the first two being 1 and 2, and
// the third one being greater than 2, when converted as a Float.
at (e?([1, 2, var x]) if 2 < x.asFloat)
echo("e([1, 2, x = %s])" % x);
e!;
[00000845] *** e
e!(0);
[00011902] *** e
[00011902] *** e(x)
e!(1);
[00023327] *** e
[00023327] *** e(x)
[00023327] *** e(1)
[00023327] *** e(odd)
e!([1, 2, 1]);
[00024327] *** e
[00024327] *** e(x)
e!([1, 2, 3]);
[00025327] *** e
[00025327] *** e(x)
[00025327] *** e([1, 2, x = 3])
e!([1, 2, "4"]);
[00026327] *** e
[00026327] *** e(x)
[00026327] *** e([1, 2, x = 4])
This chapter extends the ROS official tutorials 1 . Be sure to complete this tutorial before reading this document.
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:
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.
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).
First you need to make sure that roscore is running, and the ROS module is loaded correctly:
Then we can get the list of launched nodes:
This returns a Dictionary 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:
To stop temporarily the Global.echo, we take advantages of tags (Section 13.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.
To advertise a topic, this is roughly the same procedure.
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.
Now we are going to move the turtle with Urbi. First let’s launch the turtle node:
Ros.topics shows 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 binding 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.
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 command is stopped. |
|
Alternatively, Tags could have been used to get more control over the trajectory:
// 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;
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 for more information. After 20 seconds the tag is frozen, pausing the trajectory generation and the at.
Services work the same way topics do, with minor differences.
Let’s take back the turtle simulation example (Section 15.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 |;
for (var s in services)
if (s not in logger)
echo(s);
[00000001] *** "/clear"
[00000001] *** "/kill"
[00000001] *** "/turtle1/teleport_absolute"
[00000001] *** "/turtle1/teleport_relative"
[00000001] *** "/turtle1/set_pen"
[00000001] *** "/reset"
[00000001] *** "/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:
The new function takes the service name as first argument, and as second argument whether the connection should be kept alive.
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 with spawn.reqStruct, and the structure of the response with 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"]
This section will use topics manipulation with advertising and subscription. Be sure to understand these topics before doing this tutorial.
Requirements You have to finish the image Publisher/Subscriber tutorial (http://www.ros.org/wiki/image_transport/Tutorials) before doing this tutorial.
First, we will make a ROS Publisher and subscribe to it with Urbi. Make sure that Publisher ‘learning_image_transport’ package is compiled:
We will also run urbi with a network connection opened (e.g., on port 54000) to allow urbi-image (Section 21.4) to connect to it.
Also, you have to run roscore to communicate with ROS.
Run the Publisher The Publisher is a process that will send a image and wait for a Subscriber to get it.
// In this example the image is in the current directory.
var publisher =
Process.new("rosrun",
["learning_image_transport", "my_publisher", "test.jpg"]);
[0000002] Process rosrun
publisher.run;
Using a camera to display By default, urbi-image displays the images that are available via the camera device (see Section 21.4). To simplify the setup, let’s define a pseudo camera which will store the data received:
class Global.camera: Loadable
{
// A variable to store image data.
UVar.new(this, "val");
val = 0;
}|;
Subscribe to the topic Now, our Publisher is running and we have a camera waiting for data. All we need to do is connecting to the Publisher with a topic, the Subscriber.
Have a look at the different topics created by the Publisher, for instance by running rxgraph, which generates the graph in paragraph 15.1. As you can see, seven topics are available for the camera. We will use the ‘/camera/image/compressed’ topic for this example. For further information about the image format in ROS see http://www.ros.org/doc/api/sensor_msgs/html/msg/CompressedImage.html.
var cameraTopic = Ros.Topic.new("/camera/image/compressed")|;
at (cameraTopic.onMessage?(var imgMsg))
{
// Converting the ROS image to Urbi format.
imgMsg["data"].keywords = imgMsg["format"]|
// We can now store the data into camera.
if (!camera.val)
echo("Image well received. Store the image into the camera") |
camera.val = imgMsg["data"];
},
// Waiting for the "publisher" Process to be set up.
sleep(2s);
cameraTopic.subscribe;
We are now connected and ready to display.
In a new terminal run urbi-image:
You have now your image displayed in a window.
Now, we want to send images to ROS using a Urbi Publisher. Make sure roscore is running and ‘learning_image_transport’ package is compiled.
Run the Subscriber The basic Subscriber in the ‘learning_image_transport’ package is expecting a ‘/camera/image’ topic. To avoid modifying the Subscriber code in ROS, we will simply ask to the Subscriber topic to accept ‘/camera/image/compressed’ topics.
var subscriber =
Process.new("rosrun",
["learning_image_transport", "my_subscriber",
"_image_transport:=compressed"]);
[00037651] Process rosrun
subscriber.run;
Publishing images with Urbi The ‘sensor_msgs/CompressedImage’ message format provides a structure that requires a few changes.
// File.new("...").content returns a Binary.
var urbiImage = File.new("test.jpg").content|;
urbiImage.keywords = "jpeg"|;
var publisher = Ros.Topic.new("/camera/image/compressed")|;
// Advertising the type of message used.
publisher.advertise("sensor_msgs/CompressedImage");
var rosImg = publisher.structure.new|;
// The rosImg is a dictionary containing a Binary and a String.
rosImg["data"] = urbiImage|;
rosImg["format"] = "jpeg"|;
This message contains more fields but you need only these two to send an image.
Now, you just have to publish the image.
Communication is done, the image should be displayed.
We have worked with a roscore running on the machine as the ROS processes but the purpose of using ROS with Urbi is to communicate with a remote machine. All you need is to setup your network configuration to avoid unexpected behaviors (see NetworkSetup2 ).
Make sure the ROS environment variables are well set, especially ROS_URI, ROS_HOSTNAME, ROS_IP.
See Tutorials/MultipleMachines3 for additional information.
Try our tutorials remotely to check if the connection is set correctly.
To go further… Please see the Urbi/ROS Reference Manual, Chapter 24.
Various pre-compiled packages are provided. They are named
‘urbi-sdk-version-arch-os-compiler.ext ’
where
The package is relocatable, i.e., it does not need to be put at a specific location, nor does it need special environment variables to be set. It is not necessary to be a super-user to install it. The root of the package, denoted by urbi-root hereafter, is the absolute name of the directory which contains the package.
After the install, the quickest way to test your installation is to run the various programs.
Decompress the package where you want to install it. If urbi-sdk-2.x denotes the version of Urbi SDK you downloaded (say, urbi-sdk-2.x is ‘urbi-sdk-2.3-linux-x86-gcc4’), run something like:
This directory, urbi-root, should contain ‘bin’, ‘FAQ.txt’ and so forth. Do not move things around inside this directory. In order to have an easy access to the Urbi programs, set up your PATH:
# Check that urbi is properly set up.
$ urbi --version
# Check that urbi-launch is properly installed.
$ urbi-launch --version
# Check that urbi-launch can find its dependencies.
$ urbi-launch -- --version
# Check that Urbi can compute.
$ urbi -e ’1+2*3; shutdown;’
[00000175] 7
Decompress the zip file wherever you want or execute the installer.
Execute the script ‘urbi.bat’, located at the root of the uncompressed package. It should open a terminal with an interactive Urbi session.
Inputs and outputs of windows native application are buffered under Cygwin. Thus, either running the interactive mode of Urbi or watching the output of the server under Cygwin is not recommended.
Although we tried to avoid it, there might still be shell scripts where we use ‘+=’, which Ash (aka, dash and sash) does not support. Please, use bash or zsh instead of Ash as /bin/sh.
If you encounter this error:
cc1plus: warnings being treated as errors
parser/ugrammar.hh: In member function \
‘void yy::parser::yypush_(const char*, int, yy::parser::symbol_type&)’:
parser/ugrammar.hh:1240: error: ‘<anonymous>’ is used uninitialized \
in this function
parser/ugrammar.cc:1305: note: ‘<anonymous>’ was declared here
parser/ugrammar.hh: In member function \
‘void yy::parser::yypush_(const char*, yy::parser::stack_symbol_type&)’:
parser/ugrammar.hh:1240: error: ‘<anonymous>’ is used uninitialized \
in this function
parser/ugrammar.cc:1475: note: ‘<anonymous>’ was declared here
then you found a problem that we don’t know how to resolved currently. Downgrade from GCC-4.4 to GCC-4.3.
If at bootstrap you have something like:
configure:12176: error: possibly undefined macro: AM_LANGINFO_CODESET
If this token and others are legitimate, please use m4_pattern_allow.
See the Autoconf documentation.
configure:12246: error: possibly undefined macro: gl_GLIBC21
it probably means your Automake installation is incomplete. See the Automake item in Section 20.1.
If you experience the following failure:
checking if java works...
configure: error: The Java VM java failed
(see config.log, check the CLASSPATH?)
and if you looked at ‘config.log’, you should find something like:
Exception in thread "main" java.lang.NoClassDefFoundError: Test
Caused by: java.lang.ClassNotFoundException: Test
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
Could not find the main class: Test. Program will exit.
You might be trying to compile Urbi SDK from a directory with non-ASCII characters in its full name (for instance ‘/home/jessy/Téléchargements/Sources’). In that case the JVM fails to decode properly the path, and configure fails with the above message.
Move you sources elsewhere, with only plain ASCII characters, such as ‘/home/jessy/Sources’.
Be sure to read Section 20.9. In particular, run ‘make check’ several times (see Section 20.9 to know why). If the failures remain, please submit the ‘test-suite.log’ file(s) (see Section 17.5.3).
If on GNU/Linux you get an error such as:
urbi-sdk/2.7.1 $ ./bin/urbi
./bin/urbi: error while loading shared libraries: libport.so: \
cannot open shared object file: No such file or directory
then check that ‘/proc’ is properly mounted. To make Urbi SDK relocatable, executables and libraries use a relative path to their peers. To resolve these paths into absolute paths, the loader needs to know where the program is located, a feature provided by ‘/proc’. If for instance you run Urbi SDK in a chrooted environment, then it is possible that you forgot to mount ‘/proc’. The traditional ps utility also needs ‘/proc’ to be mounted, so running it would also help checking if the setup is complete.
This error is raised when you try to install a program like vcredist-x86.exe. This program use the “Windows Installer” which is probably outdated on your system.
To fix this problem, update the “Windows Installer” and re-start the installation of vcredist which should no longer fail.
This library is necessary to start running any application. Run ‘vcredist-x86.exe’ to install the missing libraries.
If you have used the Urbi SDK installer, it is ‘vcredist-x86.exe’ in your install directory. Otherwise download it from the Microsoft web site. Be sure to get the one corresponding to the right Visual C++ version.
Same answer as Section 17.2.3.
Your program might be deeply recursive, or use large temporary objects. Use ‘--stack-size’ to augment the stack size, see Section 21.3.
Note that one stack is allocated per “light thread”. This can explain why programs that heavily rely on concurrency might succeed where sequential programs can fail. For instance the following program is very likely to quickly exhaust the (single) stack.
But if you use & instead of |, then each recursive call to consume will be spawn with a fresh stack, and therefore none will run out of stack space:
However your machine will run out of resources: this heavily concurrent program aims at creating no less than 2513 threads, about 2.68 × 10156 (a 156-digit long number, by far larger than the number of atoms in the observable universe, estimated to 1080).
If urbi-launch (or urbi) fails to load an UObject (a shared library or DLL) although the file exists, then the most probable cause is an undefined symbol in your shared library.
It is also useful to use ldd to check that the dependencies of your object are correct. See the documentation of ldd on your machine (‘man ldd’). The following run is successful: every request (left-hand side of =>) is satisfied (by the file shown on the right-hand side).
$ all.so
linux-gate.so.1 => (0xb7fe8000)
libstdc++.so.6 => \
/usr/lib/gcc/i686-pc-linux-gnu/4.4.1/libstdc++.so.6 (0xb7eba000)
libm.so.6 => /lib/libm.so.6 (0xb7e94000)
libc.so.6 => /lib/libc.so.6 (0xb7d51000)
libgcc_s.so.1 => \
/usr/lib/gcc/i686-pc-linux-gnu/4.4.1/libgcc_s.so.1 (0xb7d35000)
/lib/ld-linux.so.2 (0xb7fe9000)
The following run shows a broken dependency.
# A simple C++ program.
$ echo ’int main() {}’ >foo.cc
# Compile it, and depend on the libport shared library.
$ g++ foo.cc -Lurbi-root/gostai/lib -lport -o foo
# Run it.
$ ./foo
./foo: error while loading shared libraries: \
libport.so: cannot open shared object file: No such file or directory
# See that ldd is unhappy.
$ ldd foo
linux-gate.so.1 => (0xb7fa4000)
libport.so => not found
libstdc++.so.6 => \
/usr/lib/gcc/i686-pc-linux-gnu/4.4.1/libstdc++.so.6 (0xb7eae000)
libm.so.6 => /lib/libm.so.6 (0xb7e88000)
libgcc_s.so.1 => \
/usr/lib/gcc/i686-pc-linux-gnu/4.4.1/libgcc_s.so.1 (0xb7e6c000)
libc.so.6 => /lib/libc.so.6 (0xb7d29000)
/lib/ld-linux.so.2 (0xb7fa5000)
Notice the ‘not found’ message. The shared object could not be loaded because it is not found in the runtime path, which is the list of directories where the system looks for shared objects to be loaded when running a program.
You may extend your LD_LIBRARY_PATH to include the missing directory.
Use otool to check whether a shared object “finds” all its dependencies.