WorkCells

The WorkCell is one of the primary containers in RobWork. A WorkCell should gather all stateless elements/models of a scene. These are primarily:

  • Kinematic structure of frames

  • Devices: robots, grippers and similar.

  • Objects: physical objects that has geometry and models for visualisation.

  • Sensor models: models of sensors such as cameras or scanners.

  • Controller models: models of controllers that takes some input and can control or manipulate something in the scene.

Building a WorkCell

WorkCells are defined using the RobWork XML format. RobWorkStudio has a WorkCell Editor plugin wceditor that provides some help on building and editing a WorkCell. When editing a WorkCell in this plugin, the changes are reflected in the scene immediately after saving the file. Notice that the plugin can only assist the user with a few element types. The editor will be extended in the future to have assistance for a more complete set of the possible XML elements.

../../_images/workcelleditor.png

The WorkCell editor plugin is shown to the left. Changes to the XML definition here is reflected in the scene immediately after saving the file. The “Add Frame” dialog assists the user in inserting a correct Frame tag in the XML file.

When building a new WorkCell it is a good advice to have a look at the example scenes in RobWorkData. See Device & Scene Collection for an overview. You can base your new scene on one of these examples, or you can reuse existing models of robots and grippers.

The WorkCell Format page gives an overview of the possible XML tags. Use this page as a reference when building your scene.

Loading a WorkCell

The programs shown below loads a workcell from the file given as argument on the command line. If loading of the workcell fails, the load(…) function will print an error message and return a smart pointer that is NULL. Always remember to check if the returned pointer is NULL with the isNull() function.

C++

 1#include <rw/loaders/WorkCellLoader.hpp>
 2#include <rw/models/WorkCell.hpp>
 3
 4using rw::loaders::WorkCellLoader;
 5using rw::models::WorkCell;
 6
 7int main(int argc, char** argv)
 8{
 9    if (argc != 2) {
10        std::cout << "Usage: " << argv[0] << " <workcell>" << std::endl;
11        return 1;
12    }
13
14    const std::string file = argv[1];
15    WorkCell::Ptr workcell = WorkCellLoader::Factory::load(file);
16    if (workcell.isNull()) {
17        std::cout << "WorkCell could not be loaded." << std::endl;
18        return 1;
19    }
20
21    std::cout << "Workcell " << workcell->getName();
22    std::cout << " successfully loaded." << std::endl;
23    return 0;
24}

API Reference: rw::loaders::WorkCellLoader::Factory

See C++ Interface for more information about compilation and execution.

Python

 1from sdurw import *
 2from sdurw_loaders import *
 3import sys
 4
 5if __name__ == '__main__':
 6    if len(sys.argv) != 2:
 7        print("Usage: python3 " + sys.argv[0] + " <workcell>")
 8        sys.exit(1)
 9
10    workcell = WorkCellLoaderFactory.load(sys.argv[1])
11    if workcell.isNull():
12        print("WorkCell could not be loaded.")
13        sys.exit(1)
14
15    print("Workcell " + workcell.getName() + " successfully loaded.")
16    sys.exit(0)

API Reference: rw.WorkCellLoaderFactory

See Python interface for more information about execution.

Java

 1import org.robwork.LoaderRW;
 2import org.robwork.sdurw.*;
 3
 4public class ExLoadWorkCell {
 5
 6    public static void main(String[] args) throws Exception {
 7        LoaderRW.load("sdurw");
 8
 9        if (args.length != 1) {
10            System.out.print("Usage: java " + ExLoadWorkCell.class.getSimpleName());
11            System.out.println(" <workcell>");
12            System.exit(1);
13        }
14
15        WorkCellPtr workcell = WorkCellLoaderFactory.load(args[0]);
16        if (workcell.isNull()) {
17            System.out.println("WorkCell could not be loaded.");
18            System.exit(1);
19        }
20
21        System.out.print("Workcell " + workcell.getName());
22        System.out.println(" successfully loaded.");
23    }
24
25}

API Reference:

See Java Interface for more information about compilation and execution.

LUA

 1require("sdurw_core")
 2require("sdurw_models")
 3require("sdurw_loaders")
 4require("sdurw")
 5using("sdurw")
 6using("sdurw_loaders")
 7
 8if #arg ~= 1 then
 9    print("Usage: lua ex-load-workcell.lua <workcell>")
10    return 1
11end
12
13workcell = WorkCellLoaderFactory.load(arg[1])
14if workcell:isNull() then
15    print("WorkCell could not be loaded")
16    return 1
17end
18
19print("Workcell " .. workcell:getName() .. " successfully loaded.");

See Lua Interface for more information about execution of the script.

Traversing the devices of a WorkCell

A WorkCell contains a number of devices. A device of a specific name can be retrieved from a WorkCell with rw::models::WorkCell::findDevice(). You can add a device type to the search such that only a device of name name and type type will be found: rw::models::WorkCell::findDevice<type>(name)

You can for example traverse the devices stored in a WorkCell and print their names like this:

C++

 1#include <rw/models/Device.hpp>
 2#include <rw/models/WorkCell.hpp>
 3
 4using namespace rw::models;
 5
 6void printDeviceNames(const WorkCell& workcell)
 7{
 8    std::cout << "Workcell " << workcell << " contains devices:" << std::endl;
 9    for(const Device::CPtr device : workcell.getDevices()) {
10        std::cout << "- " << device->getName() << std::endl;
11    }
12}

Python

1from sdurw import *
2
3def printDeviceNames(workcell):
4    print("Workcell " + workcell.getName() + " contains devices:");
5    for device in workcell.getDevices():
6        print("- " + device.getName())

Java

 1import java.util.Iterator;
 2
 3import org.robwork.sdurw.*;
 4
 5public class ExPrintDevices {
 6
 7    public static void printDeviceNames(WorkCellPtr workcell)
 8    {
 9        System.out.println("Workcell " + workcell.getName() + " contains devices:");
10        
11        // Method 1
12        DevicePtrVector devices = workcell.getDevices();
13        DevicePtr[] array = new DevicePtr[devices.size()];
14        array = devices.toArray(array);
15        for(DevicePtr device : array) {
16            System.out.println("- " + device.getName());
17        }
18        
19        // Method 2
20        Iterator<DevicePtr> iterator = devices.iterator();
21        while(iterator.hasNext()) {
22            DevicePtr device = iterator.next();
23            System.out.println("* " + device.getName());
24        }
25        
26        // Method 3
27        for (int i = 0; i < devices.size(); i++) {
28            System.out.println(". " + devices.get(i).getName());
29        }
30    }
31
32}

LUA

1function printDeviceNames(workcell)
2    print("Workcell " .. workcell:getName() .. " contains devices:");
3    devices = workcell:getDevices() 
4    for i = 0,devices:size()-1 do
5        print("- " .. devices[i]:getName())
6    end
7end

Devices, Frames and States

The default State of a WorkCell contains the initial configuration of devices and placement of movable frames. Getting a default State from the WorkCell is a commonly used operation, as well as getting Frames and Devices from the WorkCell. An example is shown below of how this can be done:

C++

 1#include <rw/core/Log.hpp>
 2#include <rw/kinematics/FixedFrame.hpp>
 3#include <rw/kinematics/MovableFrame.hpp>
 4#include <rw/kinematics/State.hpp>
 5#include <rw/models/SerialDevice.hpp>
 6#include <rw/models/WorkCell.hpp>
 7
 8using rw::core::Log;
 9using namespace rw::kinematics;
10using namespace rw::models;
11
12void findFromWorkCell(WorkCell::Ptr wc)
13{
14    // get the default state
15    State state = wc->getDefaultState();
16    Frame* worldFrame = wc->getWorldFrame();
17    // find a frame by name, remember NULL is a valid return
18    Frame* frame = wc->findFrame("FixedFrameName");
19    // find a frame by name, but with a specific frame type
20    FixedFrame* fframe = wc->findFrame<FixedFrame>("FixedFrameName");
21    MovableFrame* mframe = wc->findFrame<MovableFrame>("MovableFrameName");
22    // find a device by name
23    Device::Ptr device = wc->findDevice("SerialDeviceName");
24    SerialDevice::Ptr sdevice = wc->findDevice<SerialDevice>("SerialDeviceName");
25}

Python

 1from sdurw import *
 2
 3def findFromWorkCell(wc):
 4    # get the default state
 5    state = wc.getDefaultState();
 6    worldFrame = wc.getWorldFrame();
 7    # find a frame by name, remember NULL is a valid return
 8    frame = wc.findFrame("FixedFrameName");
 9    # find a frame by name, but with a specific frame type
10    fframe = wc.findFixedFrame("FixedFrameName");
11    mframe = wc.findMovableFrame("MovableFrameName");
12    # find a device by name
13    device = wc.findDevice("SerialDeviceName");
14    sdevice = wc.findSerialDevice("SerialDeviceName");

Java

 1import org.robwork.sdurw.*;
 2
 3public class ExFindFromWorkCell {
 4    public static void findFromWorkCell(WorkCellPtr wc)
 5    {
 6        // get the default state
 7        State state = wc.getDefaultState();
 8        Frame worldFrame = wc.getWorldFrame();
 9        // find a frame by name, remember NULL is a valid return
10        Frame frame = wc.findFrame("FixedFrameName");
11        // find a frame by name, but with a specific frame type
12        FixedFrame fframe = wc.findFixedFrame("FixedFrameName");
13        MovableFrame mframe = wc.findMovableFrame("MovableFrameName");
14        // find a device by name
15        DevicePtr device = wc.findDevice("SerialDeviceName");
16        SerialDevicePtr sdevice = wc.findSerialDevice("SerialDeviceName");
17    }
18}

LUA

 1function findFromWorkCell(wc)
 2    -- get the default state
 3    local state = wc:getDefaultState();
 4    local worldFrame = wc:getWorldFrame();
 5    -- find a frame by name, remember NULL is a valid return
 6    local frame = wc:findFrame("FixedFrameName");
 7    -- find a frame by name, but with a specific frame type
 8    local fframe = wc:findFixedFrame("FixedFrameName");
 9    local mframe = wc:findMovableFrame("MovableFrameName");
10    -- find a device by name
11    local device = wc:findDevice("SerialDeviceName");
12    local sdevice = wc:findSerialDevice("SerialDeviceName");
13end

Notice that the templated versions of findFrame and findDevice makes it possible to get a specific type of Frame of Device directly. Always remember to use the isNull() function on the smart pointer to make sure that the Frame or Device is actually found in the WorkCell. You might encounter segmentation fault errors if you try to use a null pointer.

In Python, Java and Lua the templated functions can not be used. The system for doing this is clear from the examples.

Stateless models

A very important aspect when working with RobWork is the understanding of its use of Stateless models. To illustrate stateful and stateless we give two small code examples:

struct StateFull {
 double getQ(){ return _q; }
 void setQ(double q){ _q=q; }
 double _q;
}

struct StateLess {
 double getQ(State& state){ return state.getDouble(this); }
 void setQ(double q, State& state){ state.setDouble(q, this); }
}

Now in the first struct: StateFull, the Q value is stored local as a member value. In the StateLess struct the Q value is stored in a separate class State. How the state stores this value is currently not important but to see how this is implemented in RobWork you should look into rw::kinematics::State, rw::kinematics::StateData and rw::kinematics::StateStructure.

The benefit of a stateless design is primarily that multiple threads or multiple methods can use the same Device model at the same time. E.g. methods for visualisation can visualize a device in one state, while a user is setting the configuration of a device in another state. This effectively reduce thread related issues and also limits the need to copy the models around.

Only few variables of a stateless Classes in RobWork are actually saved in the state, they are not completely stateless. The variables that are saved in the state are the dynamically changing states such as the configuration of a robot device e.g. joint configurations. The more static variables such as joint boundaries are still saved lokally in the object.

Devices

Algorithms for workcells often do not operate on the level of frames and the values for frames. Instead they operate on devices (rw::models::Device) and configurations (rw::math::Q) for devices.

A device controls a subset of frames of the workcell. Different devices may overlap in the frames that they control and one device may contain one or more other devices (rw::models::CompositeDevice). A workcell for a factory application can for example have one device for a 6-axis industrial robot and another 2-axis device that controls the position of the base of the robot. These two device may be combined into one large 8-axis device (rw::models::CompositeDevice).

A configuration is an vector of values for the frames of a device. Configurations support standard vector operations such as addition, scalar multiplication, inner product, etc. The configuration space of a device is the set of valid configurations of a device. For the rw::models::Device type, the configuration space is always box shaped and described by a tuple containing the lower and upper corner (see rw::models::Device::QBox and rw::models::Device::getBounds()).

Algorithms for devices often assume that only the configuration for the device is changed while the state (rw::kinematics::State) of the rest of the workcell stays fixed. A path-planner may for example return a path in the form of a sequence of configurations together with the common workcell state for which the planning was done. When writing or using such algorithms you will often have translate from a configuration for the device to a state of the workcell. This is accomplished by the methods rw::models::Device::setQ() and rw::models::Device::getQ(). This is example shows to convert a sequence of configurations for a common state into a sequence of states:

 1#include <rw/kinematics/State.hpp>
 2#include <rw/math/Q.hpp>
 3#include <rw/models/Device.hpp>
 4
 5#include <vector>
 6
 7using rw::math::Q;
 8using rw::models::Device;
 9using rw::kinematics::State;
10
11std::vector<State> getStatePath(
12    const Device& device,
13    const std::vector<Q>& path,
14    const State& common_state)
15{
16    State state = common_state;
17
18    std::vector<State> result;
19    for(const Q& q : path) {
20        device.setQ(q, state);
21        result.push_back(state);
22    }
23    return result;
24}

This utility function is also available as rw::models::Models::getStatePath().

Note that rw::models::Device::setQ() and rw::models::Device::getQ() do not store a configuration within the device: The configuration is read from and written to a state value. The device itself is stateless.