= EnviRe Core Documentation :toc: macro :toclevels: 5
toc::[]
== Build and Test Status
[link=https://circleci.com/gh/envire/envire-envire_core] image::https://circleci.com/gh/envire/envire-envire_core.svg?style=svg[Build Status]
== About EnviRe Core
This is the core library for Environment Representation. The goal of this core part is to deal with the representation commonalities among plugins.
Environment Representation (EnviRe) technologies are meant to close the gap and provide techniques to store, operate and interchange information within a robotic system. The application of EnviRe mainly focus to support navigation, simulation and operations and simplify the interchange of algorithms among software components.
== Installation === Using Rock's build system The easiest way to build and install this package is to use Rock's build system. See http://rock-robotics.org/documentation/installation.html[this page] on how to install Rock.
The envire package set(https://github.com/envire/envire-package_set) is required to fulfil the
dependencies to base/boost_serialisation
and external/ogdf
. All other dependencies should be available in the default rock package sets.
=== Standalone Installation
Some dependencies need to be build from source. A script is provided to install those:
==== Install Dependencies Automatically when building envire core
Defining -DINSTALL_DEPS=ON for cmake builds and isntalls the source dependencies automatically.
When -DCMAKE_INSTALL_PREFIX is used, the dependencies are also installed there.
The install script generates an env.sh
file in the CMAKE_INSTALL_PREFIX folder. If you did not install system wide, source this file before building and running code. It exports all neccessary environment variables.
==== Install Dependencies Manually
=== Test Coverage and API Documentation
Run make doc
to generate the API documentation.
Set the COVERAGE
flag during configuration to generate a code coverage make target.
Run make test && make coverage
to generate the coverage report. You can find the
generated report in build/cov
. Please note that coverage reporting only works
when code optimization is disabled. I.e. when CMAKE_BUILD_TYPE=Debug
.
The option ROCK_TEST_ENABLED
can be used to enabled/disabled the tests. It is on by default.
== Rock CMake Macros This package uses a set of CMake helper shipped as the Rock CMake macros. Documentations is available on http://rock-robotics.org/documentation/packages/cmake_macros.html[this page].
== Rock Standard Layout This directory structure follows some simple rules, to allow for generic build processes and simplify reuse of this project. Following these rules ensures that the Rock CMake macros automatically handle the project's build process and install setup properly.
STRUCTURE -- src/ Contains all header (.h/.hpp) and source files -- build/ The target directory for the build process, temporary content -- bindings/ Language bindings for this package, e.g. put into subfolders such as |-- ruby/ Ruby language bindings -- viz/ Source files for a vizkit plugin / widget related to this library -- resources/ General resources such as images that are needed by the program -- configuration/ Configuration files for running the program -- external/ When including software that needs a non standard installation process, or one that can be easily embedded include the external software directly here -- doc/ should contain the existing doxygen file: doxygen.conf
== Developement and Contribution Contributions are very welcome. Please use the pull-request mechanism of github. The maintainers will give feedback and merge when they are satisfied. Please make sure that your contribution is covered by unit tests.
== Documentation Envire Core is the main component of the envire library. It consists of:
=== The Graph The envire graph is the backbone of the whole library. It stores arbitrary data and time & space transformations between the data.
==== Structure The graph itself is implemented as inheritance chain. Each class in the chain adds some of the functionality.
envire::core::Graph<E,V>
is the root class of the graph structure. It extends
a boost::labeled_graph
. The template parameters E
and V
are edge and
vertex properties, i.e. they define the type of the data that can be stored
in the edges and vertices of the graph. Edge properties need to implement the
envire::core::EdgePropertyConcept
while vertex properties need to implement
envire::core::FramePropertyConcept
.
The following features are provided by the Graph
:
The TransformGraph<V>
extends Graph<Transformation, V>
. It adds functionality
to calculate and set transformations (including covariance) between frames.
Transformation chains are calculated automatically.
The EnvireGraph
extends TransformGraph<Frame>
. It adds functionality to
add, remove and manipulate items. Items can be used to store arbitrary data in
the graph.
==== Edge & Vertex Property Concepts
Edge and vertex properties (E
and V
) need to follow special concepts to be compatible with
the Graph
. All edge properties need to implement envire::core::EdgePropertyConcept
while all vertex properties have to implement envire::core::FramePropertyConcept
.
Both concepts ensure, that the property is serializable using boost serialization
(boost::SerializableConcept
) and that a string representation of the
vertex/edge can be generated. The string representation is used when
visualizing the graph.
Furthermore edge properties need to implement an inverse()
method, that
inverts the meaning of the edge.
Vertex properties need to implement const FrameId& getId()
and
void setId(const FrameId&)
. Those methods are used to store a unique
vertex identifier inside each vertex. This identifier is used as index when
storing a frame inside the graph.
==== Frames
Frames are vertices in the structure of the EnvireGraph
and implement the
FramePropertyConcept
. Each Frame
stores a set of items indexed by type.
==== Transformations
Transformations (envire::core::Transformation
) are edges in the EnvireGraph
.
They implement the EdgePropertyConcept
and describe the spatial and temporal
displacement between frames.
==== Items
The data elements that are stored in the Frames of the graph are called Items.
Every item must inherit from envire::core::ItemBase
. getTypeInfo()
and getEmbeddedTypeInfo()
need to be overridden to provide correct type information
about the item. getTypeInfo()
should return the type_info
of the item itself
while getEmbeddedTypeInfo()
should return the type of the encapsulated data (i.e.
the type of the data that is returned in getRawData()
).
A template (envire::core::Item<T>
) that inherits from ItemBase
and carries
arbitrary data T
is provided for convenience. Thus manually inheriting from ItemBase
should not be necessary.
==== Tree Views
TreeViews
are lightweight structures that view a portion of the graph as tree.
Views are generated by bfs-visiting the graph starting at a given frame.
All frames that are reachable from that frame will be part of the view. The structure
does not contain any loops (it is a tree, not a graph). Edges that would create
loops in the tree are called cross-edges and are stored in a special list inside
the TreeView
.
A TreeView
contains pointers to the actual data, thus if the underlying graph
is destroyed or manipulated, the view becomes invalid.
A TreeView
can either be static or dynamic. A static view is a snapshot of the
graph at the time it was taken. I.e. it will not update or change. If the graph changes,
parts of the tree might become invalid. Accessing the graph trough a static view
after the underlying graph has changed may result in memory corruption and should
be used with care.
A dynamic TreeView
is updated automatically whenever the underlying graph changes.
The view provides signals that will be emitted when that happens. Dynamic views
significantly increase the computational cost of all manipulative graph operations.
Especially the removal of edges is expensive.
=== Graph Events
The event-system is used by the Graph
to inform the user about changes to the
graph structure.
==== Publishing Events
The GraphEventPublisher
manages the subscribers and provides methods to
notify subscribers about events. Every class that wants to publish events
needs to extend GraphEventPublisher
. Graph
and its subclasses extend
this class.
==== Receiving Events
In order to receive events a class needs to extend GraphEventSubscriber
and override the notifyGraphEvent()
method.
Three convenience classes already exist, that do this and simplify
the usage of the event-system. Thus there is usually no need to derive from
GraphEventSubscriber
directly:
The GraphEventDispatcher
handles all events and provides virtual methods
for each event. Thus a subscriber can simply extend the dispatcher and
override the methods that it cares about.
The GraphEventQueue
buffers all events in a queue. If flush()
is called,
all events are processed at once. The user needs to override the process()
method to process the events. The queue detects contradicting events and
removes them from the queue. E.g. if a frame is added and removed before
flush()
is called, neither the added- nor the removed-event is processed.
The GraphItemEventDispatcher<T>
is a special dispatcher that is used to
receive typed item events. To receive only item events for a certain item
type, the user should derive from GraphItemEventDispatcher<T>
where
T
is the item type that he cares about.
==== TreeView Events
The TreeView
does not use the event system. Instead it provides
simple events using boost signals.
=== Plugins
EnviRe is designed on a modular plug-in mechanism in order to facilitate maintainability and integrability of 3rd party libraries as PCL and OctoMap.
EnviRe provides tooling to easily define and load plug-in classes. As plugin-in back-end EnviRe relies on the http://wiki.ros.org/class_loader[class_loader] library. To gather and provide meta informations about all available plug-ins the plugin_manager library is used.
For more details see the chapter on plugin <
==== Providing a user-data plug-in
In order to handle user data types in EnviRe they have to be embedded into a envire::core::Item<T>
class.
The Item
class augments the embedded type by a time-stamp, a reference frame and an unique ID.
To register a new plug-in of the type envire::core::Item<namespace::UserType>
for it's use with EnviRe, the macro
ENVIRE_REGISTER_ITEM ( namespace::UserType )
has to be placed in a source file (*.cpp).
It adds the class loader registration macro CLASS_LOADER_REGISTER_CLASS
and also registers the
class to the serialization (See the serialization section for further details).
Note that the class UserType
must be serializeable by
http://www.boost.org/libs/serialization/doc/[boost serialization] at that point.
In order to make the plug-in available to your system a XML file containing meta informations about the plug-in class needs to be exported.
==== Example
boost::shared_ptr<::octomap::AbstractOcTree>
, is defined in a *.cpp file:
[source, c++]It is strongly recommended to use this macros when a new item is defined, since the plug-in mechanism and the serialization relay on it. Nonetheless it's possible to define item classes without using this macro, in this case the class won't be available as plug-in and it won't be possible to serialize the class.
Since the embedded type must be serializeable by http://www.boost.org/libs/serialization/doc/[boost serialization], it might be necessary to implement the necessary methods in a header file.
This minimal layout can be extended by a class description, associations to other types and a singleton flag. If this optional fields are not defined, the description will be empty, there won't be any associations and the plug-in won't be a singleton instance.
To install the XML file there is a cmake macro install_plugin_info
available, which is
exported by the plugin_manager library.
rock_library(envire_octomap SOURCES OcTree.cpp HEADERS OcTree.hpp DEPS_CMAKE Boost octomap DEPS_PKGCONFIG class_loader envire_core)
The macro install_plugin_info
installs a file named envire_octomap.xml
to the folder
lib/plugin_manager
relative to the currently defined CMAKE install path.
==== Using a plug-in
To create an instance of a plug-in the envire::core::ClassLoader
singleton class can be used.
Since EnviRe plug-ins are pure class_loader plug-ins it's also possible to load them by using
only the class_loader library or the PluginLoader
class of the plugin_manager library.
For more details read the design section of this page.
==== Example
In this case at least the embedded type has to be known at compile time.
It is also possible to get an Item for a given embedded type by calling
the method createEnvireItemFor("boost::shared_ptr<octomap::AbstractOcTree>", item)
.
==== Design image::https://github.com/envire/envire.github.io/raw/master/images/docs/plugins/plugin_manager_design.png[plugin_manager_design]
The EnviRe envire_core::ClassLoader
relies on the plugin_manager library which relies on the
class_loader library.
The class_loader library handles the export of classes, loading of shared libraries
and the creation of new instances. More informations about the class_loader can be
found http://wiki.ros.org/class_loader[here].
The plugin_manager library handles XML files to provide a-priori meta informations
about the available plug-ins. In contrast to the ROS http://wiki.ros.org/pluginlib[plugin_lib],
the plugin_manager supports singleton instances, associations and is framework
independent.
Advantages of the plugin_manager library:
The plugin_manager::PluginManager
class parses all XML files and preprocesses the informations.
It can be queried about available plug-in classes, relations, associations or properties of classes.
An example of a XML file can be found in the previous section.
The plugin_manager::PluginLoader
is a singleton class which on demand creates a new
class_loader::ClassLoader
instance for each new library that is required. It also holds and
returns the same instance of a plug-in class if it is marked as singleton.
The envire_core::ClassLoader
extends the PluginLoader
by knowledge about the EnviRe
base classes.
=== Serialization
EnviRe supports serialization and de-serialization based on the http://www.boost.org/libs/serialization/doc/[boost serialization] library.
EnviRe relays on boost serialization to be able to save and load it's internal state.
By making use of the plugin architecture, it is possible to serialize and de-serialize
Item
's when knowing only their base class ItemBase
.
However in this case the following methods need to be used:
envire::core::ItemBase::Ptr plugin;
// instantiate item base pointer
if (envire::core::Serialization::save(stream, plugin))
{
// plugin was successfully serialized
}
envire::core::ItemBase::Ptr plugin;
if (envire::core::Serialization::load(stream, plugin))
{
// plugin was successfully de-serialized
}
Also the complete graph with all it's items can be serialized.
envire::core::EnvireGraph graph;
// fill envire graph
boost::archive::binary_oarchive oa(stream);
oa << graph;
envire::core::EnvireGraph graph;
boost::archive::binary_iarchive ia(stream);
ia >> graph;
==== Providing a serializable EnviRe Item
In order to create a new EnviRe item and support it's serialization the item and it's embedded type must be serializable.
To register a new Item of type envire::core::Item<namespace::UserType>
for it's use with EnviRe, the macro
ENVIRE_REGISTER_ITEM ( namespace::UserType )
has to be placed in a source file (*.cpp).
It registers the class to the serialization by exporting the class to boost using BOOST_CLASS_EXPORT
and creates a helper class which is statically instantiated as soon as the library is loaded. This allows to serialize base classes correctly even if the concrete class is not included (unknown to the implementation at runtime). However the shared library needs to be linked or dynamically loaded of course.
The serialization will try to load the necessary plugin libraries on it's own, i.e. they have to be available on your system.
The macro will also export the class as class_loader plugin (See the plugins section for further details).
The embedded type must be serializable by boost serialization as well. This can be done by defining a intrusive or non-intrusive function. More information can be found in the boost serialization documentation.
==== Example
// Include the actual type definition (can also be in the same header)
// write non-intrusive boost serialization for DummyType (if the type is already serializable by boost the header file might not be necessary) namespace boost { namespace serialization {
template<class Archive>
void serialize(Archive & ar, ::example::DummyType & dummy_type, const unsigned int version)
{
ar & dummy_type.member1;
ar & dummy_type.member2;
}
How to create and install the plugin meta-informations on your system is
described in the <
==== Framework connection
In the ROCK framework types are exported using the typelib library. Typelib is able to automatically parse types, but has some limitations: e.g. pointer, virtual functions, private members, std library container (besides of std::vector and std::string). For those more complex classes it is possible to define so called opaque types and write methods to convert the data structure from the origin type to the opaque type and vise versa. The opaque type must be typelib compatible and does hold the same data that the origin type does.
Since EnviRe items (envire::core::Item<T>
) are not typelib compatible due to it's use of virtual functions, only the inner data container is exported to typelib.
The inner data holding container of every Item
is a envire::core::SpatioTemporal<T>
class. Since it is also templated with the user data type the concrete type has to
be exported to typelib. This can be achieved using the following commands in an .orogen file:
Note that at this point the embedded type example::DummyType
must already be known to typelib.
It can either be typelib compatible (the header of the type can be parsed), the user can write it's own opaque type or the boost serialization based opaque auto-generation can be used.
If the embedded type isn't directly typelib compatible the easiest way of exporting it is to make use of the fact that it is serializable by boost. To auto-generate opaque (transport) types for classes supporting boost serialization the following commands in an .orogen file can be used:
typekit do opaque_autogen '/example/DummyType', :includes => 'example/DummyType.hpp', :type => :boost_serialization end
This makes the type example::DummyType
known to typelib.
== Code Examples This section contains code examples showcasing most of the envire core features. Additional there exist over 100 unit tests that show how every aspect of the framework can be used. Make sure to take a look at the tests!
addFrame()
[source,c++]addTransform()
.
[source,c++]Frames cannot be added twice. If a frame with the given name already exists, an exception will be thrown.
The above examples will create the frame property using the default constructor.
Another constructor can be used by calling emplaceFrame()
. Calling
emplaceFrame()
does only make sense, if the frame property has non-default
constructors.
removeFrame()
:
[source,c++]disconnectFrame()
removes all transforms that are connected to the given frame.
Frames can only be removed, if they are not connected to the graph. I.e. if no
edges are connected to the frame. An exception will be thrown, if the frame is
still connected. This is an artificial restriction, technically it would be
possible to remove frames while they are still connected. The intention of this
restriction is, to make the user aware of the consequences that removing a frame
might have for the graph structure as a whole.
=== Plugins TODO
=== Items
ClassLoader
.
[source,c++]It is also possible to instantiate items directly, however this is only
recommended for testing because visualization and serialization only work if
the ClassLoader
was used to load the item.
addItemToFrame()
:
[source,c++]The item will remember the frame that it was added to. I.e. an item cannot be part of two frames at the same time.
addItem()
.
[source,c++]boost::shared_ptr
to any subclass of ItemBase
.
Item contains a typedef Ptr
to make working with the pointer more convenient.
[source,c++]==== Accessing Items
When working with items, the user needs to know the item type. The type can
either be provided at compile time using template parameters or at runtime using
std::type_index
.
containsItems()
is used to check for the existence of items of a given type
in a given frame.
[source,c++]std::type_index
. You can get the type index by calling
getTypeIndex()
on any Item
.
[source,c++]==== Accessing Items with Iterators
ItemIterator
can be used to iterate over all items of a specific type
in a frame. The iterator internally takes care of the necessary type casting
and type checks.
[source,c++]ItemIterator
of the i'th item:
[source,c++]getItems()
can also
be used with std::type_index
:
[source,c++]However without compile time type information automatic type casting is not
available, thus in this case getItems
returns a list of ItemBase::Ptr
.
The list is returned as reference and points to graph internal memory.
removeItemFromFrame()
. Removing items invalidates
all iterators of the same type. To be able to iteratively remove items, the
method returns a new pair of iterators.
[source,c++]clearFrame()
.
[source,c++]If a transformation is added, the inverse will be added automatically. If one or both of the frames are not part of the graph, they will be added.
The inverse will be removed as well.
updateTransform
.
The inverse will be updated automatically.
[source,c++]getTransform()
can be used to calculate the transformation between two
frames if a path connecting the two exists in the graph. Breadth first search is
used to find the path connecting the two frames.
[source,c++]TreeView
can be used to speed
up the calculation:
[source,c++]Since creating the TreeView
walks the whole graph once, using this methods
only makes sense when multiple transformations need to be calculated.
getPath()
to retrieve a list of all frames that need to be traversed
to calculate the transformation. The path can be used to speed up the calculation
of the transform even further.
[source,c++]==== Disconnecting a Frame from the Graph
disconnectFrame()
can be used to remove all transformations coming from
or leading to a certain frame.
=== TreeViews
TreeViews
provide a tree view of the graph structure. I.e. when viewed
through a TreeView
the graph turns into a tree with a specific root node.
TreeViews use vertex_descriptors instead of FrameIds to reference frames because vertex_descriptors can be hashed in constant time (they are just pointers).
getTree()
and providing a root node.
[source,c++]==== Updating Tree Views
The view has three signals crossEdgeAdded
, edgeAdded
and edgeRemoved
that will be emitted whenever the tree view changes.
== Maintenance and development DFKI GmbH - Robotics Innovation Center [link=https://robotik.dfki-bremen.de/en/startpage.html] image::https://github.com/envire/envire.github.io/raw/master/images/dfki_logo.jpg[DFKI Logo]