Real & Simulated Sensors

The Sensor Framework

Warning

This describes the sensor framework as it is intended to work, and not necessarily how all sensors are working at the moment.

The most abstract concepts of sensors in RobWork are the rw::sensor::Sensor and rwlibs::simulation::SimulatedSensor interfaces:

  • SimulatedSensor: defined with a name, associated frame and an associated rw::sensor::SensorModel. The simulated sensor has an update function for stepping the simulated sensor forward in time. Changes are saved in the State variable. The same SimulatedSensor can be used in multiple simulators simultaneously, as SimualatedSensor is stateless. The SimulatedSensor interface makes is possible to get a separate stateful Sensor handle for each simulator. This makes it possible to use the same Sensor interface for both real and simulated sensors. The simulated sensor does still need to be stepped forward by a rwlibs::simulation::Simulator to produce meaningful output.

  • Sensor: defined with a name, description, properties and an associated rw::sensor::SensorModel. The Sensor interface is to be considered a concrete instance of a sensor, that can be both a physical sensor or a SimulatedSensor instantiated in a specific Simulator instance.

  • SensorModel: is an abstract representation defining one or more simulated or real sensors. The SensorModel is associated to a specific sensor frame in the WorkCell and has a name and description. It also defines data that is part of the RobWork State.

Example:

Consider a physical setup with a sensor. In a virtual environment, we then want to run four simultaneous simulations of the same setup in slightly different conditions.

First, we construct a rw::sensor::SensorModel to model the physical and virtual sensors with a general model of parameters and the data produced by a sensor modelled by this type of sensor. The data is to be stored in the State variable. The rw::sensor::Sensor interface is implemented to acquire data from the physical device, and we attach the SensorModel to the sensor to describe its parameters and output. Then we construct a rwlibs::simulation::SimulatedSensor and four rwlibs::simulation::Simulator instances. Each simulator can now run with the same SimulatedSensor, as the state of the SimulatedSensor is stored in the running State of each simulator. It is possible to wrap the SimulatedSensor under the Sensor interface to hide the distinction between real and physical sensors. Notice that the SimulatedSensor must still be stepped forward in the simulator to be able to retrieve information from it. To get the Sensor abstraction, the getHandle function can be used on SimulatedSensor. One Sensor handle is returned for each separate Simulator. To get the image from a SimulatedSensor for a specific State in the simulation, this can be retrieved from the SensorModel.

The Sensor interface is extended by more specific interfaces, for instance Camera, Scanner2D and Scanner25D. Equivalently the SensorModel is extended by more specific interfaces, such as CameraModel, Scanner2DModel, and Scanner25DModel.

The rwlibs::simulation namespace contains various sensors that can be simulated. This includes cameras, scanners that cary depth information, and a simulated Kinect sensor that can apply a noise model simulating a real Kinect sensor.

WorkCell Definition

A simulated sensor is defined in the WorkCell format by adding a certain property to the sensor frame. The property should have the name Camera, Scanner2D or Scanner25D to be recognised as a sensor frame in RobWorkStudio. An example is shown below. Notice that the property description does not influence the order of the numbers written inside the property tag. The numbers should always be written in the order shown.

<WorkCell name="WCName">
    ...
    <Frame name="Camera" refframe="WORLD" type="Movable">
        <Pos>0 0 2</Pos>
        <RPY />
        <Property name="Camera" desc="[fovy,width,height]">50 640 480</Property>
    </Frame>

    <Frame name="Scanner25D" refframe="WORLD" type="Movable">
        <Pos>0 0 2</Pos>
        <RPY />
        <Property name="Scanner25D" desc="[fovy,width,height]">50 640 480</Property>
    </Frame>

    <Frame name="Scanner2D" refframe="WORLD" type="Movable">
        <Pos>0 0 2</Pos>
        <RPY>90 0 0</RPY>
        <Property name="Scanner2D" desc="[fovy,height]">50 480</Property>
    </Frame>
    ...
</WorkCell>

Important!

  • Multiple cameras are supported but only one camera property per frame!

  • The width and height has no real dimension its the proportion between them that matters

  • The camera looks in the negative Z-axis direction of the frame

  • Field of view is in degree and is defined in the Y-axis

RobWorkStudio & The Sensor Plugin

RobWorkStudio will render the camera frustum for frames that has the Camera property. This is for instance shown in the following scene:

../../_images/SensorTestScene.png

SensorTestScene scene from the Device & Scene Collection (RobWorkData).

You can change views between cameras using Ctrl + the key [1-9], were 1 is the default view.

To see the camera output, open the Sensors sensors plugin in RobWorkStudio.

The sensor plugin in RobWorkStudio will look for frames in the WorkCell with the sensor property tag set. The plugin will use take the picture using OpenGL. This is done with the rwlibs::simulation::GLFrameGrabber. For Scanner2D and Scanner25D, the rwlibs::simulation::GLFrameGrabber25D is used.

Below, the camera view is shown for the SensorTestScene. This camera view is opened by selecting the camera in the dropdown in the sensor plugin, and clicking “Display Sensor”.

../../_images/sensorplugin_camera.png

The sensor plugin to the right and the view after clicing “Display Sensor”

Notice that the camera picture will update automatically if you move the robot in the scene. The update speed can be changed in the plugin.

Code Examples

C++

 1#include <rw/core/PropertyMap.hpp>
 2#include <rw/core/Ptr.hpp>
 3#include <rw/kinematics/State.hpp>
 4#include <rw/loaders/WorkCellLoader.hpp>
 5#include <rwlibs/simulation/GLFrameGrabber.hpp>
 6#include <rwlibs/simulation/SimulatedCamera.hpp>
 7#include <rwslibs/rwstudioapp/RobWorkStudioApp.hpp>
 8
 9using namespace rw::core;
10using namespace rw::common;
11using rw::graphics::SceneViewer;
12using namespace rw::kinematics;
13using rw::loaders::WorkCellLoader;
14using rw::models::WorkCell;
15using rw::sensor::Image;
16using namespace rwlibs::simulation;
17using namespace rws;
18
19int main (int argc, char** argv)
20{
21    if (argc < 2) {
22        std::cout << "Usage: " << argv[0] << "/path/to/robworkdata" << std::endl;
23        exit (1);
24    }
25
26    static const std::string WC_FILE =
27        std::string (argv[1]) + "/scenes/SensorTestScene/SimpleWorkcell.xml";
28    const WorkCell::Ptr wc = WorkCellLoader::Factory::load (WC_FILE);
29    if (wc.isNull ())
30        RW_THROW ("WorkCell could not be loaded.");
31    Frame* const camera = wc->findFrame ("Camera");
32    if (camera == nullptr)
33        RW_THROW ("Camera frame could not be found.");
34    const PropertyMap& properties = camera->getPropertyMap ();
35    if (!properties.has ("Camera"))
36        RW_THROW ("Camera frame does not have Camera property.");
37    const std::string parameters = properties.get< std::string > ("Camera");
38    std::istringstream iss (parameters, std::istringstream::in);
39    double fovy;
40    int width;
41    int height;
42    iss >> fovy >> width >> height;
43    std::cout << "Camera properties: fov " << fovy << " width " << width << " height " << height
44              << std::endl;
45
46    RobWorkStudioApp app ("");
47    RWS_START (app)
48    {
49        RobWorkStudio* const rwstudio = app.getRobWorkStudio ();
50        rwstudio->postOpenWorkCell (WC_FILE);
51        TimerUtil::sleepMs (5000);
52
53        const SceneViewer::Ptr gldrawer = rwstudio->getView ()->getSceneViewer ();
54        const GLFrameGrabber::Ptr framegrabber =
55            ownedPtr (new GLFrameGrabber (width, height, fovy));
56        framegrabber->init (gldrawer);
57        SimulatedCamera::Ptr simcam =
58            ownedPtr (new SimulatedCamera ("SimulatedCamera", fovy, camera, framegrabber));
59        simcam->setFrameRate (100);
60        simcam->initialize ();
61        simcam->start ();
62        simcam->acquire ();
63
64        static const double DT = 0.001;
65        const Simulator::UpdateInfo info (DT);
66        State state = wc->getDefaultState ();
67        int cnt     = 0;
68        const Image* img;
69        while (!simcam->isImageReady ()) {
70            std::cout << "Image is not ready yet. Iteration " << cnt << std::endl;
71            simcam->update (info, state);
72            cnt++;
73        }
74        img = simcam->getImage ();
75        img->saveAsPPM ("Image1.ppm");
76        simcam->acquire ();
77        while (!simcam->isImageReady ()) {
78            std::cout << "Image is not ready yet. Iteration " << cnt << std::endl;
79            simcam->update (info, state);
80            cnt++;
81        }
82        std::cout << "Took " << cnt << " steps" << std::endl;
83        img = simcam->getImage ();
84        std::cout << "Image: " << img->getWidth () << "x" << img->getHeight () << " bits "
85                  << img->getBitsPerPixel () << " channels " << img->getNrOfChannels ()
86                  << std::endl;
87        img->saveAsPPM ("Image2.ppm");
88
89        simcam->stop ();
90        app.close ();
91    }
92    RWS_END ()
93
94    return 0;
95}

Python

 1from sdurw import *
 2from sdurw_simulation import *
 3from sdurw_loaders import *
 4from sdurws import *
 5import sys
 6
 7if __name__ == '__main__':
 8    if len(sys.argv) < 2:
 9        raise Exception("Provide the path to RobWorkData as first argument.")    
10    WC_FILE = str(sys.argv[1]) + "/scenes/SensorTestScene/SimpleWorkcell.xml"
11
12    wc = WorkCellLoaderFactory.load(WC_FILE)
13    if wc.isNull():
14        raise Exception("WorkCell could not be loaded")
15    camera = wc.findFrame("Camera")
16    if camera is None:
17        raise Exception("Camera frame could not be found.")
18    properties = camera.getPropertyMap()
19    if not properties.has("Camera"):
20        raise Exception("Camera frame does not have Camera property.")
21    parameters = properties.getString("Camera").split(" ")
22    fovy = float(parameters[0])
23    width = int(parameters[1])
24    height = int(parameters[2])
25    print("Camera properties: fov " + str(fovy) + " width " + str(width) + " height " + str(height));
26
27    rwstudio = getRobWorkStudioInstance();
28    rwstudio.postOpenWorkCell(WC_FILE);
29    sleep(5);
30    gldrawer = rwstudio.getView().getSceneViewer();
31    framegrabber = sdurw_simulation.ownedPtr( GLFrameGrabber(width,height,fovy) );
32    framegrabber.init(gldrawer);
33    simcam = SimulatedCamera("SimulatedCamera", fovy, camera, framegrabber.asFrameGrabberPtr());
34    simcam.setFrameRate(100);
35    simcam.initialize();
36    simcam.start();
37    simcam.acquire();
38
39    DT = 0.001;
40    info = UpdateInfo(DT);
41    state = wc.getDefaultState();
42    cnt = 0;
43    while not simcam.isImageReady():
44        print("Image is not ready yet. Iteration " + str(cnt));
45        simcam.update(info, state);
46        cnt = cnt+1;
47    img = simcam.getImage();
48    img.saveAsPPM("Image1.ppm");
49    simcam.acquire();
50    while not simcam.isImageReady():
51        print("Image is not ready yet. Iteration " + str(cnt));
52        simcam.update(info, state);
53        cnt = cnt+1;
54    print("Took " + str(cnt) + " steps");
55    img = simcam.getImage();
56    print("Image: " + str(img.getWidth()) + "x" + str(img.getHeight()) + " bits " + str(img.getBitsPerPixel()) + " channels " + str(img.getNrOfChannels()));
57    img.saveAsPPM("Image2.ppm");
58
59    simcam.stop()
60    closeRobWorkStudio()
61    sleep(10)
62
63    print("Example-Done")

Java

 1import java.lang.String;
 2
 3import org.robwork.*;
 4import org.robwork.sdurw.*;
 5import org.robwork.sdurw_simulation.*;
 6import org.robwork.sdurws.RobWorkStudioPtr;
 7import static org.robwork.sdurw_simulation.sdurw_simulation.ownedPtr;
 8import static org.robwork.sdurws.sdurws.getRobWorkStudioInstance;
 9
10public class SimulatedCameraExample {
11    public static void main(String[] args) throws Exception {
12        LoaderRW.load("sdurw");
13        LoaderRW.load("sdurw_simulation");
14        LoaderRWS.load();
15        
16        if (args.length != 1)
17        	throw new Exception("Provide the path to RobWorkData as first argument.");
18        
19        final String WC_FILE =
20                args[0] + "/scenes/SensorTestScene/SimpleWorkcell.xml";
21
22        WorkCellPtr wc = WorkCellLoaderFactory.load(WC_FILE);
23        if (wc.isNull())
24            throw new Exception("WorkCell could not be loaded.");
25        Frame camera = wc.findFrame("Camera");
26        if (camera == null)
27            throw new Exception("Camera frame could not be found.");
28        PropertyMap properties = camera.getPropertyMap();
29        if (!properties.has("Camera"))
30            throw new Exception("Camera frame does not have Camera property.");
31        String[] parameters = properties.getString("Camera").split(" ");
32        Double fovy = Double.parseDouble(parameters[0]);
33        Integer width = Integer.parseInt(parameters[1]);
34        Integer height = Integer.parseInt(parameters[2]);
35        System.out.print("Camera properties: fov " + fovy);
36        System.out.println(" width " + width + " height " + height);
37
38        RobWorkStudioPtr rwstudio = getRobWorkStudioInstance();
39        rwstudio.postOpenWorkCell(WC_FILE);
40        Thread.sleep(5000);
41        SceneViewerPtr gldrawer = rwstudio.getView().getSceneViewer();
42        GLFrameGrabberPtr framegrabber =
43        		ownedPtr( new GLFrameGrabber(width,height,fovy) );
44        framegrabber.init(gldrawer);
45        SimulatedCamera simcam = new SimulatedCamera("SimulatedCamera",
46        		fovy, camera, framegrabber.asFrameGrabberPtr());
47        simcam.setFrameRate(100);
48        simcam.initialize();
49        simcam.start();
50        simcam.acquire();
51
52        final double DT = 0.001;
53        Simulator.UpdateInfo info = new Simulator.UpdateInfo(DT);
54        State state = wc.getDefaultState();
55        int cnt = 0;
56        Image img;
57        while (!simcam.isImageReady()) {
58            System.out.println("Image is not ready yet. Iteration " + cnt);
59            simcam.update(info, state);
60            cnt++;
61        }
62        img = simcam.getImage();
63        img.saveAsPPM("Image1.ppm");
64        simcam.acquire();
65        while (!simcam.isImageReady()) {
66            System.out.println("Image is not ready yet. Iteration " + cnt);
67            simcam.update(info, state);
68            cnt++;
69        }
70        System.out.println("Took " + cnt + " steps");
71        img = simcam.getImage();
72        System.out.print("Image: " + img.getWidth() + "x" + img.getHeight());
73        System.out.print(" bits " + img.getBitsPerPixel());
74        System.out.println(" channels " + img.getNrOfChannels());
75        img.saveAsPPM("Image2.ppm");
76
77        simcam.stop();
78        rwstudio.postExit();
79        Thread.sleep(1000);
80    }
81}

LUA

 1require("sdurw_core")
 2require("sdurw_kinematics")
 3require("sdurw_models")
 4require("sdurw_sensor")
 5require("sdurw")
 6require("sdurw_simulation")
 7require("sdurw_loaders")
 8require("sdurws")
 9
10using("sdurw_core")
11using("sdurw_kinematics")
12using("sdurw")
13using("sdurw_simulation")
14using("sdurw_loaders")
15using("sdurws")
16
17if #arg < 1 then
18    error("Provide the path to RobWorkData as first argument.")
19end
20
21local WC_FILE = arg[1] .. "/scenes/SensorTestScene/SimpleWorkcell.xml"
22print(WC_FILE)
23
24local wc = WorkCellLoaderFactory.load(WC_FILE)
25if wc:isNull() then
26    error("WorkCell could not be loaded")
27end
28local camera = wc:findFrame("Camera")
29if camera == nil then
30    error("Camera frame could not be found.")
31end
32local properties = camera:getPropertyMap();
33if not properties:has("Camera") then
34    error("Camera frame does not have Camera property.")
35end
36local parameters = properties:getString("Camera");
37local parlist={}
38for str in string.gmatch(parameters, "([^%s]+)") do
39    table.insert(parlist, str)
40end
41fovy = tonumber(parlist[1])
42width = tonumber(parlist[2])
43height = tonumber(parlist[3])
44print("Camera properties: fov " .. tostring(fovy) .. " width " .. tostring(width) .. " height " .. tostring(height));
45
46local rwstudio = getRobWorkStudioInstance();
47rwstudio:postOpenWorkCell(WC_FILE);
48sleep(2);
49local gldrawer = rwstudio:getView():getSceneViewer();
50local framegrabber = sdurw_simulation.ownedPtr( GLFrameGrabber(width,height,fovy) );
51framegrabber:init(gldrawer);
52local simcam = SimulatedCamera("SimulatedCamera", fovy, camera, framegrabber:asFrameGrabberPtr());
53simcam:setFrameRate(100);
54simcam:initialize();
55simcam:start();
56simcam:acquire();
57
58local DT = 0.001;
59local info = UpdateInfo(DT);
60local state = wc:getDefaultState();
61local cnt = 0;
62local img;
63while not simcam:isImageReady() do
64    print("Image is not ready yet. Iteration " .. tostring(cnt));
65    simcam:update(info, state);
66    cnt = cnt+1;
67end
68img = simcam:getImage();
69img:saveAsPPM("Image1.ppm");
70simcam:acquire();
71while not simcam:isImageReady() do
72    print("Image is not ready yet. Iteration " .. tostring(cnt));
73    simcam:update(info, state);
74    cnt = cnt+1;
75end
76print("Took " .. tostring(cnt) .. " steps");
77img = simcam:getImage();
78print("Image: " .. tostring(img:getWidth()) .. "x" .. tostring(img:getHeight()) .. " bits " .. tostring(img:getBitsPerPixel()) .. " channels " .. tostring(img:getNrOfChannels()));
79img:saveAsPPM("Image2.ppm");
80
81simcam:stop();
82sleep(20);
83rwstudio:postExit();
84
85while(isRunning()) do
86    sleep(1);
87end
88sleep(10);
89print("Example-Done");