The dynamic-graph package is used to connect computation nodes, "entities" together using a graph system, akin to what Simulink does. Entities are connected through input and output signals. With the building blocks this package provides, you can easily create a full computation graph for a given problem. It is the basis for the stack of tasks operation.
To give a more concrete example, the real-time control used by the Gepetto group for the humanoid robot HRP-2 is detailled.
Real-time control system are usually driven by a cyclic computational node which needs to send a control reference value to each motors of a robot. To compute this control reference values, sensor values need to be provided. In the Stack-Of-Tasks special entities called Device are used to provide an abstract interface to the hardware.
A scheme of the real-time control graph used for the humanoid robot HRP-2 is depicted in the following figure:
The device therefore has a specific input which should contain the control vector. This control vector is the result of a computation solving a control problem. The entity in charge of solving this control problem is called "Solver" in the previous figure. In the SoT framework it is often cast as an optimization problem. This optimization problem is build using a control "Task" (not to be confused with the general word task). A control "Task" regulates the difference with a "Feature" computed on the current robot state and a "Desired Feature". For instance when walking, the regulated feature is the robot's Center-Of-Mass (CoM) position. The "Feature" is computed using a library using the robot model and the sensor value. The entity making this computation is "Dyn". A walking pattern generator using foot-steps position given in advance generates the desired value for the CoM. Note that the "Dyn" entity uses the sensor provided by the entity "Robot".
From a pure computer science viewpoint we wish to avoid recomputing data such as articular Jacobians when this is unnecessary. Therefore the data generated by an entity through signals may have two types of dependencies: one dependency related to time and dependencies on other signals. Internally an entity does not recompute the data if no new information is available, it is simply providing the same information computed before. Please note that this package provides only the computational framework to realize the data dependency and the entities. Solvers, libraries to compute mechanical quantities are provided in different packages.
Finally in order to dynamically create a graph, it is possible on-line to load classes of entities and create instances of entities.
Despite the fact that it looks very similar to a ROS node or a CORBA/OpenRTM server, an entity is simply a C++ object. The main idea is that this entity is providing mostly a data-driven functionnality working at very high rate ( \( 200 Hz\) or \( 1 kHz \)) and should have a minimal computational time foot-print.
For this signals (or ports to use a more classical terminology) are providing a time dependency between data. To implement this, an output signal is linked with a method of the entity. The method calls input signals or use other means to get the needed data. It might be provided by the connection with remote computers through a middleware, or specific protocols, but in general the external data is based upon the sensor values provided by a "Device" entity. For this reason the signal evaluations are realized through the cascade of dependencies and start from the evaluation of an input signal of a periodic node (in general the device). This is realized inside a real-time thread.
To add flexibility to a node, it is possible to add command with arguments to modify the internal behavior of the entity or get information from the entity. As a command is in general asynchronous and rare with respect to the data-flow scheme for the signals the command is in general executed in a none-real-time thread.
Entities are implemented as C++ classes and compiled as dynamic libraries. They can be loaded and instancied dynamically. It is therefore necessary to specify the location of their dynamical libraries. However given the time it might take to load the library, it is not advised to do that during a control-law computation. Entity instanciation also implies memory allocation which is also time consuming and thus not advised inside a real-time thread.
The entities will be placed in ${PREFIX}/lib/plugin (since this may change, it is advised to check the install log or the CMakeLists.txt file to check the installation path).
Since most of the functionality in projects using the dynamic-graph framework is exposed from entities, here is a short description of all the entities contained in this package. Note that most entities are contained in a binary file that closely matches the entities' names in the scripts; loading this file with the plugin loader will enable creation of this entity through the factory.
It is possible to derive classes and apply specific semantic for the entities. In our case we are interested in specific control semantics:
Entities can output different types of signals. All signals are templated by a Time tick type parameter (which is used in the caching of signals) - usually int
. Signals are also templated after the type of data they accept or provide. For example: (example) For a more detailed programmer-oriented description of signals, please see Signals
In this package, the graph considered are directed graphs.
The class dynamicgraph::FactoryStorage is a singleton which register the entity classes and which is allowing the instancation of such classes.
The class dynamicgraph::PoolStorage keeps track of the entities instanciated with the factory. The entities are the graph nodes. Signals are constructed during the class instanciation, they do not live independently from the entities. Signals are the directed edges of the graph. The pool can write a file representing the graph of entities.
Objects, which are derived from Entities (base class dynamicgraph::Entity), can be declared within the code and compiled to shared libraries (.so/.dll files). These libraries can be loaded at run-time using the PluginLoader methods, and at the same time register their class names to the Factory (see the examples in the SOT documentation to learn how).
The Factory can then create instances of these objects and subsequently register them in the Pool, where they can be listed, accessed, and acted upon (see PoolStorage documentation). Basic commands defined by entities include signal connection graph file generation, help and name print, and signals.
The singletons made available by including the corresponding headers in this module are:
For an example of a program creating entities in C++, see the unit test test_pool.cpp (in your package source directory/unitTesting).
A tutorial is available here
"A versatile Generalized Inverted Kinematics implementation for collaborative working humanoid robots: The Stack Of Tasks", N. Mansard, O. Stasse, P. Evrard, A. Kheddar, Int. Conf. on Autonomous Robots, ICAR, 2009
"Task sequencing for sensor-based control", N. Mansard, F. Chaumette, IEEE Trans. on Robotics, 23(1):60-72, February 2007