YARP
Yet Another Robot Platform
Getting Started with YARP Devices

For robotics, we need access to all sorts of strange devices.

+ Collaboration diagram for Getting Started with YARP Devices:

For robotics, we need access to all sorts of strange devices.

This section contains a tutorial on the YARP view of what devices are. If you just want to get coding immediately, see the main page for YARP Devices, including information on interfaces, implementation, configuration and commandline usage.

YARP Devices

There are three separate concerns related to devices in YARP:

The first step, creating drivers for particular devices, is obvious; every robotics project needs to interface with hardware somehow.

The second step, defining interfaces for families of devices, is important in the longer term. If you change your camera or your motor control board, how much of your code needs to change too? If you view your devices through well thought out interfaces, the impact of device change can be minimized.

The third step, network wrappers, is important to give flexibility. You can scale up your computing cluster, or isolate hardware devices that don't play well together, or have specific OS dependencies etc.

A driver in YARP is just a C++ class. Interfaces are simply abstract base classes. And network wrappers are just special cases of devices that happen to use network resources to satisfy their interfaces. Let's look at some examples to make this clear.

Framegrabber interfaces

We call devices that produce a stream of images "framegrabbers" for historical reasons. There are a number of interfaces associated with framegrabbers. Here's one, IFrameGrabberImage:

public:
virtual int height() const = 0;
virtual int width() const = 0;
};
virtual int width() const =0
Return the width of each frame.
virtual int height() const =0
Return the height of each frame.
virtual bool getImage(yarp::sig::ImageOf< yarp::sig::PixelRgb > &image)=0
Get an image from the frame grabber.

Notice that IFrameGrabberImage doesn't do anything itself – it just declares methods to get an image and image dimensions. There are several classes in YARP that implement this interface (in other words, they inherit from it and define the methods). One example is DragonflyDeviceDriver. This interfaces with a Point Grey digital camera, using different libraries on different operating systems. It implements several interfaces; here are just two of them:

dot_inline_dotgraph_9.png

Since you may not have any hardware devices available to you right now, let's make a "fake" framegrabber that implements the IFrameGrabberImage interface:

private:
int w, int h;
public:
FakeFrameGrabber(int w, int h) : w(w), h(h) {
}
Time::delay(0.5); // simulate waiting for hardware to report
image.resize(w,h);
image.zero();
return true;
}
virtual int height() const {
return h;
}
virtual int width() const {
return w;
}
};
fakeFrameGrabber: A fake camera for testing.
int width() const override
Return the width of each frame.
int height() const override
Return the height of each frame.
FakeFrameGrabber()=default
bool getImage(yarp::sig::ImageOf< yarp::sig::PixelRgb > &image) override
Get an image from the frame grabber.
void resize(size_t imgWidth, size_t imgHeight)
Reallocate an image to be of a desired size, throwing away its current contents.
Definition: Image.cpp:453
void zero()
Set all pixels to 0.
Definition: Image.cpp:446
IFrameGrabberOf< yarp::sig::ImageOf< yarp::sig::PixelRgb > > IFrameGrabberImage
void delay(double seconds)
Wait for a certain number of seconds.
Definition: Time.cpp:111

All this does is return blank images roughly every half second, but it does indeed implement the IFrameGrabberImage interface.

We very nearly have a YARP device driver. Sometimes we want to be able to create devices flexibly, based on a configuration file, or without having to worry about where their header file is. For these purposes, YARP requires that:

This is quite straightforward to do for our fake framegrabber:

private:
int w, int h;
public:
h = w = 0;
}
bool open(int w, int h) {
this->w = w;
this->h = h;
return w>0 && h>0;
}
virtual bool open(yarp::os::Searchable& config) {
// extract width and height configuration, if present
// otherwise use 128x128
int desiredWidth = config.check("w",Value(128)).asInt32();
int desiredHeight = config.check("h",Value(128)).asInt32();
return open(desiredWidth,desiredHeight);
}
virtual bool close() {
return true; // easy
}
Time::delay(0.5); // simulate waiting for hardware to report
image.resize(w,h);
image.zero();
return true;
}
virtual int height() const {
return h;
}
virtual int width() const {
return w;
}
};
bool open(yarp::os::Searchable &config) override
Configure with a set of options.
bool close() override
Close the DeviceDriver.
Interface implemented by all device drivers.
Definition: DeviceDriver.h:30
A base class for nested structures that can be searched.
Definition: Searchable.h:56
virtual bool check(const std::string &key) const =0
Check if there exists a property of the given name.

Now we have a good YARP device driver (even if it is fake). We've chosen to require that the width and height of images be greater than zero, which seems reasonable.

Creating and configuring the new device

We can create, configure, and use our device directly, without any bureaucracy:

fakey.open(640,480);
ImageOf<PixelRgb> img;
fakey.getImage(img);
...

If we're smart, we'd make as much of our code as possible depend just on the interface IFrameGrabberImage, so that we can reuse it or substitute in a different framegrabber later:

// creation and configuration -- depends on specific device type
fakey.open(640,480);
IFrameGrabberImage& genericGrabber = fakey;
// from here on, we only care that our device implements IFrameGrabberImage
ImageOf<PixelRgb> img;
genericGrabber.getImage(img);
...

This is a standard software engineering technique for minimizing unnecessary coupling between modules.

Suppose we want to go further, and let the framegrabber we use be controlled by a command line option. So it could either be our FakeFrameGrabber, or a real device like DragonflyDeviceDriver or PicoloDeviceDriver (another framegrabber), or RemoteFrameGrabber (a proxy for a framegrabber device on another machine).

YARP comes with a helper for doing this. It maintains a simple database of the drivers you have compiled and available. You can see this database by running "yarpdev", which tells you something like:

...
Here are devices listed for your system:

Device <dragonfly>
   documented by the C++ class DragonflyDeviceDriver
   Wrapped for the network by <grabber>

Device <fakeFrameGrabber>
   documented by the C++ class FakeFrameGrabber
   Wrapped for the network by <grabber>

Device <grabber>
   documented by the C++ class ServerFrameGrabber
   Does not need a network wrapper.
...

We see a "dragonfly" listing associated with the DragonflyDeviceDriver class – "dragonfly" is this devices common name to which it is referred to in configuration files, command line options, etc.. There is also a "fakeFrameGrabber" listing associated with a FakeFrameGrabber class – this is in fact a more elaborate version of the fake framegrabber we've been working on, for testing purposes. We can ignore it for now. There is also a "grabber" device listed, with is a network wrapper that works for all framegrabber devices.

You can instantiate any device listed here from your code as follows:

#include <yarp/sig/Image.h>
...
PolyDriver dd("dragonfly");
if (!dd.isValid()) {
printf("Dragonfly not available\n");
exit(1);
}
dd.view(grabber);
if (grabber!=NULL) {
printf("*** Device can supply images\n");
ImageOf<PixelRgb> img;
if (grabber->getImage(img)) {
printf("*** Got a %zux%zu image\n", img.width(), img.height());
}
}
A container for a device driver.
Definition: PolyDriver.h:23
Packed RGB pixel type.
Definition: Image.h:460

Just replace the name "dragonfly" with the device you want.

If we want to be able to create a FakeFrameGrabber through the same mechanism, we need to add a factory for it to the device driver database. Here's how:

DriverCreator *fakey_factory =
new DriverCreatorOf<FakeFrameGrabber>("fakey","grabber","FakeFrameGrabber");
Drivers::factory().add(fakey_factory); // hand factory over to YARP

The name "fakey" is an arbitrary common name we pick. The name "grabber" gives a network wrapper for this device ("" if there is none). The class name "FakeFrameGrabber" is recorded to let the user know where to look for documentation on this device.

Now we can do:

PolyDriver dd("fakey");
dd.view(grabber);
if (grabber == nullptr) { exit(1); } // failed
// from here on, we only care that our device implements IFrameGrabberImage
ImageOf<PixelRgb> img;
grabber->getImage(img);
...

This form is calling FakeFrameGrabber::open without any configuration information set up. To pass in configuration options, we do:

Property config("(device fakey) (w 640) (h 480)");
PolyDriver dd(config);
...

Once we've reached this point, some fun things become possible. For example:

Property config("(device grabberDual) (subdevice fakey) (w 640) (h 480)");
PolyDriver dd(config);
...

This will create and configure a FakeFrameGrabber, then wrap it up in a ServerFrameGrabber so that it can be accessed on the network.

Find out available devices and their parameters

To have a full lost of devices and check their parameters follow the instruction at the page yarpdev: the standard YARP device utility

More advanced topics: true nesting of device properties; calibration objects.