YARP  2.3.68+283-20170530.4+git10f9853
Yet Another Robot Platform
Use ROS with existing YARP programs

Decide what to do about types

ROS has a type system that YARP knows nothing about. You can tell YARP about ROS types either at compile-time or at run-time. In either case, you'll need access to the .msg/.srv files stored in ROS packages. You have at least three options:

  • Install ROS. This can be easier said than done, if you're not used to it. Tip: don't try to work against the grain - install exactly the recommended version of Ubuntu, or use a virtual machine or chroot.
  • Or: ask YARP to read type definitions from a website. This is a bit of a hack, but can be good for getting up and running quickly.
  • Or: track down the .msg/.srv files for the types you care about by any means available, and copy them. This can be good for embedded scenarios.

We'll focus on the first two options, but bear in mind that the third is possible if ever you need to strip your system down to the essentials.

You can make ROS types available to YARP via reading from a website by running the following additional type server:

yarpidl_rosmsg --web true --name /typ@/yarpidl

This server will lurk around waiting for YARP clients baffled by a ROS type, and will swoop in to their rescue. For example if you were running the ROS "add two ints" tutorial:

rosrun rospy_tutorials add_two_ints_server

You could now talk to it from a YARP program in the way you might to intuitively expect:

$ yarp rpc /add_two_ints
22 20
Response: 42

This looks straightforward, but it relies critically on YARP being able to determine ROS types. If you were to shut down the yarpidl_rosmsg server, here is what you would see:

$ yarp rpc /add_two_ints
Do not know anything about type 'rospy_tutorials/AddTwoInts'
Could not connect to a type server to look up type 'rospy_tutorials/AddTwoInts'

YARP is successfully connecting to the ROS integer-adding service, which lets it know the type of data it expects, but is baffled at how to supply it. That's where the type server comes in.

Having the type server do type lookups via the web is handy but subject to the random flakiness of the internet. If you can, you'll be much better off running yarpidl_rosmsg on a machine with ROS installed. Then you can leave off the –web option:

yarpidl_rosmsg --name /typ@/yarpidl

That's the only YARP program you'll need on that machine.

Here we've used yarpidl_rosmsg as a server, to pass around type information at run-time. It can also be used to do the translation at compile-time, see yarp_with_ros_roscore_ros_types. Translation at compile-time works for arbitrary ROS types, whereas run-time translation currently does not support ROS types with nesting via arrays (coming soon though!).

Reading ROS topics

Let's start with a simple test reader:

yarp read /msg@/test_node

For someone familiar with ROS, the "/msg@/test_node" syntax can be read as "use a topic name of /msg, and a node name of /test_node". We can check that everything looks familiar with rosnode and rostopic:

$ rosnode list
...
/test_node
...

$ rostopic info /msg
Type: unknown type
Publishers: None
Subscribers:
* /test_node (... address ...)

As invoked, we didn't assign any particular type to the port/topic - this can be picked up from the sender. Try any of:

rostopic pub /msg std_msgs/String "hello yarp"
rostopic pub /msg turtlesim/Pose 0 5 10 15 20

And you should see the messages print out on the console of yarp read.

Publishing to ROS topics

Let's start a ROS program to print out strings:

$ rosrun roscpp_tutorials listener

This program subscribes to a topic called "/chatter". Let's publish something there from YARP:

$ yarp write /chatter@/yarp_writer
yarp: Port /yarp_writer active at tcp://192.168.1.2:54473
yarp: Port /chatter+@/yarp_writer active at tcp://192.168.1.2:50896
yarp: Sending output from /chatter+@/yarp_writer to /listener using tcpros
hello?

Once we type a message (here "hello?") and hit return, we should see it echoed on the listener's console:

[ INFO] [1386605949.838711935]: I heard: [hello?]

Under the hood, the yarp port has found the type of data expected (from the listener) and is matching what you enter with that type. If you try to send a number, you'll get a message like this:

42
yarp: Structure of message is unexpected (expected std_msgs/String)

(If you actually want to send a string that looks like an integer, just put it in quotes). In this case, the listener is expecting a message of type 'std_msgs/String'.

Let's start a ROS service that adds two integers:

$ rosrun rospy_tutorials add_two_ints_server

This creates a service named /add_two_ints that expects two integers and gives back an integer. We can use it from 'yarp rpc', for example:

$ yarp rpc /add_two_ints
22 20
Response: 42
1 -10
Response: -9

As explained before, this relies critically on YARP being able to determine ROS types. If you were to shut down the yarpidl_rosmsg server, here is what you would see:

$ yarp rpc /add_two_ints
Do not know anything about type 'rospy_tutorials/AddTwoInts'
Could not connect to a type server to look up type 'rospy_tutorials/AddTwoInts'

With the type server running, YARP can make the needed translations. Let's try the opposite direction, setting up a ROS-style rpc server, for example this:

$ yarp rpcserver /add_two_ints@/my_int_server --type rospy_tutorials/AddTwoInts

Notice that we specify a node name for the server here, and also we give the type of data it expects and returns (ROS-style clients expect us to know that). Now from ROS we can do:

$ rosrun roscpp_tutorials add_two_ints_client 3 4

On the "yarp rpcserver" terminal we will now see:

Waiting for a message...
yarp: Receiving input from /add_two_ints_client to /add_two_ints-1@/my_int_server using tcpros
Message: 3 4
Reply:

It is waiting for us to reply. We can type in whatever number we like ("7" if we are feeling constructive), and it will be reported on the "roscpp_tutorials" terminal:

[ INFO] [1386793386.003997149]: Sum: 7

An important note on making connections

In YARP we usually make connections using "yarp connect". This may lead to thinking that we can connect a YARP port to a topic using the following sequence of commands:

$ yarpview --name /view 
$ yarp connect /topic@/node /view

ROS however requires to manage the connections through roscore. Therefore YARP ports need to be registered as topic with "roscore" as described above. In our example this means running yarpview as follows:

$ yarpview --name /topic@/viewer 

and let ROS (roscore)establish the connection with /topic.

Reading existing YARP ports from ROS

To read existing YARP ports from ROS, we need to define their type for ROS's benefit. There is a tool to help with this.

For testing purposes, let's start a fake motor control board:

yarpdev --device test_motor --name /motor

One of the ports this creates is /motor/state:o. ROS forbids port names with ":" in them, and YARP versions 2.3.15 and earlier didn't have a work-around for this, so make sure you are using YARP trunk or YARP version > 2.3.15 for this part.

To see how to access /motor/state:o from ROS, do:

yarpros sniff out /motor/state:o

This gives us the following rather arcane lines:

int32 v_tag # set to 266 (BOTTLE_TAG_LIST+code)
float64[] v # suggested length: 4

This is a valid .msg file, and can be used directly in ROS.

See some more examples in the yarp source code, in the example/yarpros_examples directory.

How to configure Ports to talk to ROS

In a previous example (Use YARP to talk to ROS services) we have seen how to use yarp::os::RpcClient to talk to a ROS service. In fact we could have used a normal port configured appropriately:

Port client;
client.setRpcClient();

In Writing code to talk to ROS topics we have used special classes (yarp::os::Node, yarp::os::Publisher, yarp::os::Subscriber) to crate ROS nodes, publishers and subscribers. The same result can be achieved by configuring a yarp::os::Port. However if we try to use a regular port without telling YARP how we'll be using it (by calling one of setRpcClient(), setRpcServer(), setWriteOnly(), setReadOnly()), YARP will complain because it won't know how to describe it to ROS. However, if you have a YARP-using program that you can't easily modify to add such a call, you can sneak the needed information into the port name using the special syntax described above.

ROS ideaCall needed to specialize a Port/BufferedPortOr add this decoration before "@" in port nameSpecialized class
Topic Subscriberport.setWriteOnly()-yarp::os::Subscriber
Topic Publisherport.setReadOnly()+yarp::os::Publisher
Serviceport.setRpcServer()-1yarp::os::RpcServer
Clientport.setRpcClient()+1yarp::os::RpcClient

For new code, however, it may be convenient to create ROS-like nodes and topics explicitly rather than having names that bundles node and topic/service names together.

One thing to watch out for is that if you stop a program using ^C or if it crashes, YARP will not yet unregister your ports with ROS. If this bothers you, either add a signal handler to close the Port, or run "rosnode cleanup" from time to time. Will fix this soon.