Skip to content

A library that provides methods to create 3d drawings and plot data from inside any code for debugging purpose using vizkit3d

License

Notifications You must be signed in to change notification settings

rock-gui/gui-vizkit3d_debug_drawings

Repository files navigation

V3DD (vizkit3d_debug_drawings)

About

A ROCK (http://rock-robotics.org) library that provides methods to create 3d drawings and plot data from inside any code for debugging purpose.

Logo

Build and Test Status

Build Status

Features

  • Create and manipulate simple 3D drawings from inside any code

  • Create and manipulate 2D plots from inside any code

  • Minimal configuration needed

  • Drawings can be enabled/disabled using compile switch

  • No runtime overhead if disabled

  • Three different operation modes:

  • Standalone mode: A standalone window pops up automatically to visualize the drawings

  • Embedded mode: Drawings can be visualized using an existing Vizkit3DWidget and QApplication context.

  • Port mode: Drawings are published using rock ports and can be visualized using rock-display.

  • Available 3d primitives include:

  • Box, Arrow, Ring, Sphere, Cylinder, Line, Polyline, Text, AABB, Axes, etc.

  • New primitives can be added by implementing a simple interface (command pattern)

Compiling

Compiling inside ROCK

The package is part of the rock-core packageset (https://github.com/rock-core/rock-package_set). Nothing special has to be done to compile inside ROCK. Just call amake and wait.

Compiling standalone

Install all system dependencies
apt update && apt -y install build-essential gcc g++ cmake git wget  libboost-filesystem-dev libboost-serialization-dev libboost-system-dev pkg-config libeigen3-dev libopenscenegraph-dev  doxygen libqt4-dev ruby-dev libboost-thread-dev libboost-test-dev
Install local dependencies
./install_dependencies.sh <path_to_prefix>

This will create an env.sh. Source it before continuing.

Build V3DD

Port support doesn’t make much sense when building outside rock. It can be disabled with WITH_PORTS=OFF. If ports are disabled you may skip the rtt dependency (remove it from the install_dependencies.sh script).

Set DROCK_TEST_ENABLED=ON to build the tests.

mkdir build
cmake -DCMAKE_INSTALL_PREFIX=<path_to_prefix> -DWITH_PORTS=ON -DROCK_TEST_ENABLED=ON ..
make -j install

Run draw_test_standalone in build/test to check if everything works.

Contribution

If you want to contribute code please do so using pull requests. Requests should contain appropriate test cases.

Usage

Configuration at Compile Time

Every component that contains drawing code needs to link against libvizkit3d_debug_drawings.

DEPS_PKGCONFIG
    vizkit3d_debug_drawings

If the component only needs to work with the command submission side and wants to avoid actually linking against Qt libraries, they can link against libvizkit3d_debug_drawings-commands instead. (This is generally the case for rock orogen components.)

DEPS_PKGCONFIG
    vizkit3d_debug_drawings-commands

By default all drawing code is disabled and will be removed by the compiler. To enable it ENABLE_DEBUG_DRAWINGS needs to be defined for every component containing debug drawing commands.

add_definitions(-DENABLE_DEBUG_DRAWINGS)

If you do not use pkg-config and want to use the port features you have to define USE_PORTS aswell. pkg-config knows about this flag and sets it automatically if the library has been built with port support.

add_definitions(-DUSE_PORTS)

Configuration at Runtime

At runtime you need to choose which operation mode should be used to visualize the drawings. This is done by executing one of the following methods at startup:

void CONFIGURE_DEBUG_DRAWINGS_STANDALONE();
void CONFIGURE_DEBUG_DRAWINGS_USE_EXISTING_WIDGET(vizkit3d::Vizkit3DWidget* widget);
void CONFIGURE_DEBUG_DRAWINGS_USE_PORT(RTT::TaskContext* taskContext, const std::vector<std::string>&);

The STANDALONE and EXISTING_WIDGET configurations cannot be changed once configured. The PORT configuration should be configured exactly once per task (although it can be configured several times without causing problems).

STANDALONE Configuration

In standalone mode a new QThread will be started containing a new QApplication context. This thread is used to display a Vizkit3DWidget which is used for visualization.

EXISTING_WIDGET Configuration

In this mode the application expects that there already is a QApplication context and a Vizkit3DWidget already exists. The existing widget will be used for visualization.

PORT Configuration

In port mode the application expects to be running inside a rock task. The context of that task has to be provided. For each drawing channel a new port will be added to the task and the corresponding drawing commands will be sent through that port. The drawings can be visualized using rock-display. Additional configuration is needed for this to work. See example below.

Declaring Drawings

For some use cases the drawing channels need to be known at static initialization time. Therefore all drawing channels need to be declared using the V3DD_DECLARE_DEBUG_DRAWING_CHANNEL macro.

V3DD_DECLARE_DEBUG_DRAWING_CHANNEL("channel_name");

The macro can be placed anywhere inside a cpp file outside of functions. Once a channel has been declared it can be used anywhere inside your code. If you have a lot of drawings it makes sense to create a dedicated drawing_declarations.cpp and link it.

At runtime a list of all declared channel names is available using GET_DECLARED_CHANNELS.

std::vector<std::string> channels = V3DD::GET_DECLARED_CHANNELS();

Note that GET_DECLARED_CHANNELS will return all declared channels known to the process. I.e. also the once that might have been defined in different libraries. It is adviced to use some sort of prefix for your channel names to be able to identify them later on.

Drawing

Once configured you can start adding drawing commands anywhere inside your code. The commands will be executed when the corresponding code path is executed. Take a look at vizkit3d_debug_drawings/DebugDrawing.hpp for an overview of all available commands.

All drawing commands are part of the V3DD namespace.

#include <vizkit3d_debug_drawings/DebugDrawing.hpp>
#include <vizkit3d_debug_drawings/DebugDrawingColors.hpp> //only needed for named colors

Example:

Eigen::Vector3d pos(-3, -3, -3);
V3DD::DRAW_SPHERE("some_pos", pos, 1, V3DD::Color::red);

All drawing commands follow the same structure. The first parameter is always the name of the drawing channel, the last parameter is always the color. A list of named colors can be found in vizkit3d_debug_drawings/DebugDrawingColors.hpp. If none of the named colors suits you, you can always define your own. A color is just an Eigen::Vector4d containing RGBA values.

The drawing channel has special relevance. All drawings that belong to a channel will be visualized by the same instance of a visualizer or send through the same port. Thus a user can enable or disable the visualizations on a per channel basis. Channels are not limited to a certain type of drawing. They can contain any mix of drawing types (even plots).

Complex Drawings

Sometimes a lot of extra instructions (e.g. coordinate transformations) are needed before a drawing command can be issued. While the drawing command itself would be removed when debug drawings are disabled, the extra instructions would remain. TO avoid this the COMPLEX_DRAWING method can be used. This method takes a lambda that should contain the drawing code. When debug drawings are disabled the lambda is never executed.

V3DD::COMPLEX_DRAWING([]()
{
    Eigen::Vector3d min, max;
    min << -1, -1, -1;
    max << 1, 1, 1;
    Eigen::AlignedBox3d boundingBox(min, max);
    V3DD::DRAW_AABB("Complex", boundingBox, V3DD::Color::alloy_orange);
    V3DD::DRAW_SPHERE("Complex", -7, 1, 1, 1, V3DD::Color::magenta);
});

Clearing and Removing Drawings

With a lot of drawings the visualization might get cluttered and laggy. To avoid that the user can clear drawings or remove them altogether. This is done by calling one of the following methods:

void V3DD::REMOVE_DRAWING(const std::string& drawingChannel);
void V3DD::CLEAR_DRAWING(const std::string& drawingChannel);

REMOVE_DRAWING will remove all drawings belonging to the specified channel. It will also unload the corresponding Vizkit3DPlugin. Thus REMOVE_DRAWING should be called when you want to permanently remove a channel.

CLEAR_DRAWING will also remove all drawings from the specified channel. But it will not remove the plugin. It should be used when you intended to use the same channelagain (e.g. during a later iteration) but want a clean canvas to draw on.

Plotting 2D data

In addition to 3D debug drawings, it is also possible to create simple 2D plots.

void V3DD::PLOT_2D(const std::string& plotName, const Eigen::Vector2d& dataPoint);
void V3DD::CLEAR_PLOT(const std::string& plotName);

PLOT_2D will add a data point to an existing plot or create a new plot if the plot doesn’t exist. Plots show up as docked widgets in the Vizkit3DWidget.

At the time of writing plots can be cleared but not completely removed.

double x = 0.0;
while(true)
{
    x += 0.1;
    V3DD::PLOT_2D("sin", Eigen::Vector2d{x,std::sin(x)});
}
Logo

FLUSHING

When sending drawing commands through rock ports the user needs to flush the send queue regularly. This should be done in the update loop of the corresponding task. If you do not flush manually the library will flush for you every 1.5 seconds.

This is only relevant for port mode. In other modes there is no need to flush!

When several tasks use debug drawings they will ultimatly all use the same internal drawing dispatcher. Thus flushing in one task will also flush the drawings of other tasks. This is in itself not a problem but could become a performance bottleneck if a lot of tasks are running.

See example below.

Examples

A minimal standalone Example

A minimal standlone example can be found in test/draw_test_standalone.cpp. Take a look at test/CMakeLists.txt to learn about the neccessary flags to build the example.

A minimal embedded Example

An example attaching to an existing Vizkit3DWidget can be found in test/draw_test_attach.cpp.

A minimal ROCK Task

If you want to output debug drawings through the ports of a ROCK task the following needs to be done:

Build V3DD with port support

For the port output to work you need to enable port support. Compile the V3DD library with

add_definitions(-DUSE_PORTS)

Without this flag the commands for port output will not be available.

Add dependencies

A minimal manifest.xml of your tasks should look like this:

<package>
  <depend package="base/cmake" />
  <depend package="gui/orogen/vizkit3d_debug_drawings" />
  <depend package="gui/vizkit3d_debug_drawings" />
</package>
Modify CMakeLists

Modify the src/CMakeLists.txt and add the following:

# enable debug drawings
add_definitions(-DENABLE_DEBUG_DRAWINGS)

# find v3dd
find_package(PkgConfig REQUIRED)
pkg_check_modules(V3DD REQUIRED vizkit3d_debug_drawings)

# link v3dd
TARGET_LINK_LIBRARIES(${YOUR_TASKNAME_HERE_TASKLIB_NAME}
    #other libs here
    ${V3DD_LIBRARIES})

# add include directories and linker flags:
target_include_directories(${YOUR_TASKNAME_HERE_TASKLIB_NAME} PUBLIC ${V3DD_INCLUDE_DIRS})
target_compile_options(${YOUR_TASKNAME_HERE_TASKLIB_NAME} PUBLIC ${V3DD_CFLAGS_OTHER})
Modify orogen file

To be able to output data through ports you need to tell orogen to load the typekit. If you do not do this, rock-display will not be able to deserialize the debug messages. It will shown an error instead.

Add the following to the orogen file:

using_library "vizkit3d_debug_drawings"
import_types_from "vizkit3d_debug_drawings"

And add a dynamic port to every Task that outputs debug data:

dynamic_output_port /^debug_/, "/boost/shared_ptr</vizkit3dDebugDrawings/CommandBuffer>"
Modify Task

You have to tell the library the drawing channels that should be associated with the current tasks. For each drawing a debug_XXX port will be added to your task. The port will be added at configuration time.

bool Task::configureHook()
{
    std::vector<std::string> channels = V3DD::GET_DECLARED_CHANNELS();
    // ...
    //filter channels somehow to decided which channels you care about in this task
    // ...
    V3DD::CONFIGURE_DEBUG_DRAWINGS_USE_PORT(this, channels);

    if (! TaskBase::configureHook())
        return false;
    return true;
}

void Task::updateHook()
{
    TaskBase::updateHook();
    //your code here
    V3DD::FLUSH_DRAWINGS();
}

Under the Hood

Architecture

Class Diagram

Serialization

Commands are serialized using boost to send them through rock ports as opaque type containing a binary blob with the serialized data. The Opaque conversion can be found [in this repository](https://github.com/rock-gui/gui-orogen-vizkit3d_debug_drawings).

Boost serialization was chosen over typekit serialization because typekit cannot handle virtual inheritance.

Namespaces

There are two namespaces within V3DD. The user facing namespace is V3DD. All methods that should be used by the user are in this namespace. Internal stuff is in vizkit3dDebugDrawings.

Why is the whole drawing state sent through ports every time?

The way rock-display connects ports allows for message loss. I.e. when too may messages are sent, they are dropped. This happens regularly. Thus we have to send the whole drawing state every time. Sending only incremental updates might lead to a corrupt state due to message loss.

Inspiration

This project was heavily inspired by the inline drawing macros that can be found in the [B-Human](https://b-human.de) framework. See: https://github.com/bhuman/BHumanCodeRelease/blob/master/Src/Tools/Debugging/DebugDrawings3D.hpp

Maintenance and development

DFKI GmbH - Robotics Innovation Center

DFKI Logo

About

A library that provides methods to create 3d drawings and plot data from inside any code for debugging purpose using vizkit3d

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages