YARP  2.3.70
Yet Another Robot Platform
ROS Types in YARP: sending data to ROS
Author
Paul Fitzpatrick

This tutorial assumes that you have completed the steps in ROS Types in YARP: writing a portable. That tutorial showed how to use types defined in a ROS-style .msg file within YARP. Now, we will work on sending data from YARP to ROS.

Make sure you have the latest version of YARP from github. Make sure you run the latest version of the yarp name server, there have been important fixes.

At the beginning of this tutorial, we assume you have a YARP server running, without any special configuration. During the tutorial, we will reconfigure the name server to communicate with ROS.

Making the sender program ROS-compatible

The sender program from ROS Types in YARP: writing a portable looks like this:

#include "SharedData.h"
#include <iostream>
#include <yarp/os/Time.h>
using namespace std;
int main()
{
if (!port.open("/sender"))
{
cerr<<"Error opening port, check your yarp network\n";
return -1;
}
cout<<"Starting sender\n";
double count=0.0;
while(true)
{
SharedData d;
// d.text is a string
d.text="Hello from sender";
//d.content is a vector, let's push some data
d.content.push_back(count++);
d.content.push_back(count++);
port.write(d);
}
return 0;
}

This program was written using a yarp::os::Port, a very flexible creature. It can communicate with ROS, if we tame it a little bit to fit in with ROS's expectations that promise to never change the type of data sent, and promise to always send data in a particular direction. We can make those promises by calls to yarp::os::Port::setWriteOnly and yarp::os::Port::promiseType, or we can switch to using the helper class yarp::os::Publisher that is already correctly configured. We can also add a yarp::os::Node to make ROS even happier.

#include "SharedData.h"
#include <iostream>
#include <yarp/os/Node.h>
#include <yarp/os/Time.h>
using namespace std;
int main()
{
yarp::os::Node node("/sender/node"); // added a Node
yarp::os::Publisher<SharedData> port; // changed Port to Publisher
if (!port.topic("/data")) // replaced open() with topic()
{
cerr<<"Error opening port, check your yarp network\n";
return -1;
}
cout<<"Starting sender\n";
double count=0.0;
while(true)
{
SharedData d;
// d.text is a string
d.text="Hello from sender";
//d.content is a vector, let's push some data
d.content.push_back(count++);
d.content.push_back(count++);
port.write(d);
}
return 0;
}

Great, at this point our port is nice and tame by ROS standards. And the code will still work just fine without ROS. For example, we can run our existing receiver program from ROS Types in YARP: writing a portable. We're now using a topic called /data so after running the receiver you'll need to subscribe it to the topic:

yarp connect topic://data /receiver

We can also update the receiver code to use the yarp::os::Subscriber helper class:

#include <SharedData.h>
#include <iostream>
#include <yarp/os/Node.h>
using namespace std;
int main()
{
cout<<"Starting receiver\n";
yarp::os::Node node("/receiver/node"); // added a Node
yarp::os::Subscriber<SharedData> port; // changed Port to Subscriber
if (!port.topic("/data")) // replaced open() with topic()
{
cerr<<"Error opening port, check your yarp network\n";
return -1;
}
// network.connect("/sender", "/receiver"); // don't need this anymore
int count=0;
while(true)
{
SharedData d;
port.read(d);
//access d
cout << count << " Received SharedData:\n";
cout << d.text << "\n";
for (int i=0; i<d.content.size(); i++)
{
cout<<d.content[i]<<" ";
}
cout<<"\n";
count++;
}
return 0;
}

Registering with ROS

Now let's make our programs visible with ROS. Stop running them, and then also stop the yarp name server. Make sure you have a ROS name server running ("roscore"/"rosmaster"), and that the ROS_MASTER_URI environment variable is correctly set. Then run the yarp name server with the –ros flag:

$  yarpserver --ros
... start-up messages omitted ..
Using ROS with ROS_MASTER_URI=http://127.0.0.1:11311
... start-up messages omitted ..
Ok.  Ready!

Done! Now, if we rerun the sender and receiver programs, they should work as before. The (invisible) difference is that they are being connected by ROS rather than YARP. A visible difference is that the programs are now visible within ROS. Try:

$  rostopic list
...
/data
...
$  rosnode list
...
/receiver/node
/sender/node
...
$  rostopic info /data
Type: yarp/SharedData

Publishers:
 * /sender/node (http://192.168.1.3:10005)

Subscribers:
 * /receiver/node (http://192.168.1.3:10003)
$  rosnode info /receiver/node
Node [/receiver/node]
Publications: None

Subscriptions:
 * /data [yarp/SharedData]

Services: None

contacting node http://192.168.1.3:10003 ...
Pid: [12432]

Notice that ROS is reporting the topic /data as having the type yarp/SharedData. ROS needs types to include a "package" name, and we haven't specified one yet, so YARP has added a fake one. To get full interoperation, we now need to add our SharedData type in ROS (not just YARP), and as part of that process we'll end up making a real ROS package.

Making the .msg file available to ROS

In ROS Types in YARP: writing a portable we defined a SharedData structure in a SharedData.msg file and used it to communicate between sender and receiver programs written using YARP. Now we need to make that file visible to ROS. ROS expects .msg files to be in a directory called msg within a ROS "package". If you already have a package set up for your work, you can just use that. For completeness, we give a very minimal ROS package here, just enough to experiment with, but you can also just follow ROS tutorials, there's nothing YARP-specific here. Do the following:

  • Create an empty directory, called say yarpros_tutorial.
  • Within that directory, create another empty directory called src.
  • Within that directory, create another empty directory that will be our package, called say yarp.
  • Within yarp, create an empty directory called msg.
  • Copy SharedData.msg into yarpros_tutorial/src/yarp/msg/.
  • Create a file called yarpros_tutorial/src/yarp/package.xml and place the following in it:
<?xml version="1.0"?>
<package>
<name>yarp</name>
<version>0.0.0</version>
<description>A test yarp package</description>
<maintainer email="paul@robotrebuilt.com">Paul Fitzpatrick</maintainer>
<license>BSD</license>
<build_depend>message_generation</build_depend>
<run_depend>message_runtime</run_depend>
<buildtool_depend>catkin</buildtool_depend>
</package>
  • Create a file called yarpros_tutorial/src/CMakeLists.txt and place the following in it:
cmake_minimum_required(VERSION 3.0)
project(yarp)
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation)
add_message_files(FILES SharedData.msg)
generate_messages(DEPENDENCIES std_msgs)
catkin_package(CATKIN_DEPENDS message_runtime)
  • While in the yarpros_tutorial directory, run catkin_make. You should see something like:
...
-- +++ processing catkin package: 'yarp'
...
Generating C++ code from yarp/SharedData.msg
[ 50%] Generating Python from MSG yarp/SharedData
...
[100%] Built target yarp_generate_messages

Now we need to tell ROS about the existence of yarpros_tutorial. There are many ways to do that. In a bash shell, the simplest is to do (from the yarpros_tutorial directory):

$  source devel/setup.bash

At this point, the rosmsg command should be able to find our type:

$  rosmsg show yarp/SharedData
string text
float64[] content

And if we run the sender program, we can print it from ROS:

$  rostopic echo /data
text: Hello from sender
content: [4794.0, 4795.0]
---
text: Hello from sender
content: [4796.0, 4797.0]
---
text: Hello from sender
content: [4798.0, 4799.0]
---
text: Hello from sender
content: [4800.0, 4801.0]
---

If we stop the sender program and run the receiver, we can publish to if from ROS. If we do this:

rostopic pub /data yarp/SharedData "hello from ROS" "[1.1,2.2,3.3]"

The receiver program prints this:

0 Received SharedData:
hello from ROS
1.1 2.2 3.3