Skip to content

Commit 34e3f7f

Browse files
committed
MTC Tutorial: Programmatic Extension
1 parent 83af9ab commit 34e3f7f

4 files changed

Lines changed: 354 additions & 0 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,4 @@ add_subdirectory(doc/collision_environments)
7474
add_subdirectory(doc/visualizing_collisions)
7575
add_subdirectory(doc/bullet_collision_checker)
7676
add_subdirectory(doc/mesh_filter)
77+
add_subdirectory(doc/moveit_task_constructor)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
add_executable(extension src/extension_tutorial.cpp)
2+
target_link_libraries(extension
3+
${catkin_LIBRARIES}
4+
)
5+
6+
# Executables
7+
install(
8+
TARGETS
9+
extension
10+
RUNTIME DESTINATION
11+
${CATKIN_PACKAGE_BIN_DESTINATION}
12+
)

doc/moveit_task_constructor/moveit_task_constructor_tutorial.rst

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ shown in the right-most window. Selecting one of those solutions will start its
6262
.. image:: images/mtc_show_stages.gif
6363
:width: 700px
6464

65+
.. _basic_concepts:
6566
Basic Concepts
6667
--------------
6768

@@ -101,3 +102,182 @@ Examples are running alternative planners for a free-motion plan, picking object
101102
Stages not only support solving motion planning problems.
102103
They can also be used for all kinds of state transitions, as for instance modifying the planning scene.
103104
Combined with the possibility of using class inheritance it is possible to construct very complex behavior while only relying on a well-structured set of primitive stages.
105+
106+
Programmatic Extension
107+
----------------------
108+
109+
You may find that the available stages do not suffice the needs of your application.
110+
Given this situation, you have the option to derive your own classes from core
111+
stage types and implement custom functionality. Your choice of core class determines
112+
the structure, i.e. the `flow path` of incoming and outgoing data with respect to
113+
your new stage.
114+
115+
Overview of core classes
116+
^^^^^^^^^^^^^^^^^^^^^^^^
117+
118+
The section :ref:`basic_concepts` in this tutorial provides an overview of the stage types
119+
together with result propagation directions that are available for programmatic extension.
120+
Remember: Core classes define result flow.
121+
For example, to specify bi-directional result propagation, you might derive from the
122+
``Generator`` class.
123+
You will find this pattern if you take a look at the ``CurrentState`` stage,
124+
which `generates` the current state of the planning scene and forwards it to both interfaces.
125+
126+
When deriving from one of the core classes, you need to implement your new computation in
127+
the provided virtual functions. This ensures that the `MTC` backend can call your code
128+
in the right place when traversing through the task hierarchy.
129+
The following table provides an overview of these functions as well as giving more,
130+
already implemented, examples of programmatic extension stages.
131+
132+
+------------------------+-----------------------+-------------------------+
133+
| Core Class | Virtual Functions | Example Stages |
134+
+========================+=======================+=========================+
135+
| Generator | | ``compute`` | | *CurrentState* |
136+
| | | ``canCompute`` | | *FixedState* |
137+
+------------------------+-----------------------+-------------------------+
138+
| Monitoring Generator | | ``compute`` | | *FixedCartesianPoses* |
139+
| | | ``canCompute`` | | *GeneratePickPose* |
140+
| | | ``onNewSolution`` | | *GeneratePlacePose* |
141+
+------------------------+-----------------------+-------------------------+
142+
| Connecting | | ``compute`` | | *Connect* |
143+
+------------------------+-----------------------+-------------------------+
144+
| Propagating Either Way | | ``computeForward`` | | *MoveRelative* |
145+
| | | ``computeBackward`` | | *MoveTo* |
146+
| | | | *ModifyPlanningScene* |
147+
+------------------------+-----------------------+-------------------------+
148+
149+
.. note::
150+
**compute()**
151+
The functionality of a stage is encapsulated in
152+
the ``compute()`` function.
153+
``Interface`` classes are utilized as input and outputs of a stage.
154+
155+
**canCompute()**
156+
To be able to gain control of the execution of the compute function, ``canCompute()`` acts as a guard.
157+
158+
Implementation
159+
^^^^^^^^^^^^^^
160+
161+
In case you just want to insert the stage and check for the functions later, it is valid
162+
to leave the function body with no contents, i.e.:
163+
164+
.. code-block:: c++
165+
166+
void compute() override {};
167+
168+
Additionally, you may define a custom constructor for your derived stage class and forward the arguments like so:
169+
170+
.. code-block:: c++
171+
172+
class MyGenerator : public Generator{
173+
public:
174+
MyGenerator(const std::string& name) : Generator(name) {};
175+
176+
Solutions of stages are propagated via ``InterfaceStates``.
177+
An interface state can only propagate planning scene instances.
178+
The next section presents code examples for all the core classes that you may use as a
179+
basic implementation reference.
180+
181+
Generator
182+
+++++++++
183+
A template for a ``Generator`` stage is listed below.
184+
As stated above the ``compute()`` function assembles a planning scene and spawns an interface, whilst the
185+
``canCompute()`` function acts as an execution guard.
186+
187+
.. literalinclude:: src/extension_tutorial.cpp
188+
:language: cpp
189+
:lines: 48-84
190+
191+
The example above additionally implements a mechanism to call the ``compute`` function only once.
192+
Create the stage:
193+
194+
.. code-block:: c++
195+
196+
auto g = std::make_unique<MyGenerator>("myGenerator");
197+
198+
Monitoring Generator
199+
++++++++++++++++++++
200+
The ``MonitoringGenerator`` processes a solution that the monitored stage just computed.
201+
202+
.. literalinclude:: src/extension_tutorial.cpp
203+
:language: cpp
204+
:lines: 86-129
205+
206+
Create the stage:
207+
208+
.. code-block:: c++
209+
210+
auto m = std::make_unique<MyMonitoringGenerator>("myMonitoringGenerator");
211+
212+
PropagatingEitherWay
213+
++++++++++++++++++++
214+
215+
The ``PropagatingEitherWay`` stage forwards solutions between interface states of previous and
216+
following stages in a forward and backward manner.
217+
218+
.. literalinclude:: src/extension_tutorial.cpp
219+
:language: cpp
220+
:lines: 9-46
221+
222+
Create the stage:
223+
224+
.. code-block:: c++
225+
226+
auto pr = std::make_unique<MyPropagatingEitherWay>("myPropagatingEitherWay");
227+
228+
Connecting
229+
++++++++++
230+
231+
The Connecting stage computes a solution between interface states
232+
from the previous and following stages.
233+
234+
.. literalinclude:: src/extension_tutorial.cpp
235+
:language: cpp
236+
:lines: 131-155
237+
238+
Create the stage:
239+
240+
.. code-block:: c++
241+
242+
auto c = std::make_unique<MyConnecting>("myConnecting");
243+
244+
Stage Configuration
245+
-------------------
246+
247+
Stages are configured with properties.
248+
A property is a ``boost::any`` value stored inside the `Property` class, which provides the following
249+
wrapper-like functionality:
250+
251+
- Maintain default and current values with get-/set-/reset-to-default functions.
252+
- Provide a description.
253+
- Serialization into strings.
254+
- Get the ``boost::any`` value or the entire `Property` object for a given key.
255+
- Check for type and if-defined status of the property.
256+
- Initialization from an already existing property.
257+
258+
.. tutorial-formatter:: ./src/extension_tutorial.cpp
259+
260+
Notice, that when we initialize our properties from an external domain, we need to provide a property initializer source flag.
261+
You can access these flags through the ``Stage::PropertyInitializerSource::`` scope.
262+
They define a priority hierarchy in which initializations are carried out:
263+
264+
``MANUAL > DEFAULT > INTERFACE > PARENT``
265+
266+
Initialization of properties is carried out during planning of the entire
267+
task hierarchy. You can therefore specify a priority hierarchy on a per-property-basis from where the stage should get
268+
the inforation for its properties.
269+
270+
E.g. use the ``MANUAL`` flag if you want to explicitly configure a (set) of properties
271+
from a property map. ``MANUAL`` takes precedence over all the other flags.
272+
As another example, you can use ``INTERFACE`` and ``PARENT`` flags to let the stage be initialized
273+
by its successor or predecessor.
274+
275+
To summarize, the property map allows you to:
276+
277+
- Declare properties for future use without providing values yet.
278+
- Expose a subset of the properties to another property map.
279+
- Reset all properties.
280+
- Check if a key-value pair is present.
281+
- Initialize still undefined properties using SourceFlags.
282+
- Iterate over the property map.
283+
- Forward property maps through the task hierarchy using established interfaces for planning.
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#include <ros/ros.h>
2+
#include <moveit/task_constructor/stage.h>
3+
4+
using namespace moveit::task_constructor;
5+
6+
class MyPropagatingEitherWay : public PropagatingEitherWay
7+
{
8+
public:
9+
MyPropagatingEitherWay(const std::string& name) : PropagatingEitherWay(name)
10+
{
11+
}
12+
13+
void computeForward(const InterfaceState& from) override
14+
{
15+
// do computation
16+
// ...
17+
// package that into a planning scene pointer
18+
planning_scene::PlanningScenePtr ptr;
19+
20+
// send the solution forward to the next stage
21+
sendForward(from, InterfaceState(ptr), SubTrajectory());
22+
}
23+
24+
void computeBackward(const InterfaceState& to) override
25+
{
26+
// do computation
27+
// ...
28+
// package that into a planning scene pointer
29+
planning_scene::PlanningScenePtr ptr;
30+
31+
// send the solution backward to the previous stage
32+
sendBackward(InterfaceState(ptr), to, SubTrajectory());
33+
}
34+
35+
void init(const moveit::core::RobotModelConstPtr& robot_model) override
36+
{
37+
PropagatingEitherWay::init(robot_model);
38+
robot_model_ = robot_model;
39+
}
40+
41+
private:
42+
moveit::core::RobotModelConstPtr robot_model_;
43+
};
44+
45+
class MyGenerator : public Generator
46+
{
47+
public:
48+
MyGenerator(const std::string& name = "myGenerator") : Generator(name)
49+
{
50+
}
51+
52+
void compute() override
53+
{
54+
compute_count++;
55+
56+
// do computation
57+
// ...
58+
// package that into a planning scene pointer
59+
planning_scene::PlanningScenePtr ptr = std::make_shared<planning_scene::PlanningScene>(robot_model_);
60+
61+
// spawn an interface state with the solution to be provided
62+
// at both ends of the stage
63+
spawn(InterfaceState(ptr), 0.0);
64+
}
65+
66+
bool canCompute() const override
67+
{
68+
return compute_count < 1;
69+
}
70+
71+
void init(const moveit::core::RobotModelConstPtr& robot_model) override
72+
{
73+
Generator::init(robot_model);
74+
robot_model_ = robot_model;
75+
compute_count = 0;
76+
}
77+
78+
private:
79+
unsigned short compute_count;
80+
moveit::core::RobotModelConstPtr robot_model_;
81+
};
82+
83+
class MyMonitoringGenerator : public MonitoringGenerator
84+
{
85+
public:
86+
MyMonitoringGenerator(const std::string& name = "myMonitoringGenerator") : MonitoringGenerator(name)
87+
{
88+
}
89+
90+
void onNewSolution(const SolutionBase& s) override
91+
{
92+
// Perform the following computation with the solution s, that the
93+
// the monitored stage just computed.
94+
// ...
95+
}
96+
97+
void compute() override
98+
{
99+
compute_count++;
100+
101+
// do computation
102+
// ...
103+
// package that into a planning scene pointer
104+
planning_scene::PlanningScenePtr ptr = std::make_shared<planning_scene::PlanningScene>(robot_model_);
105+
106+
// spawn an interface state with the solution to be provided
107+
// at both ends of the stage
108+
spawn(InterfaceState(ptr), 0.0);
109+
}
110+
111+
bool canCompute() const override
112+
{
113+
return compute_count < 1;
114+
}
115+
116+
void init(const moveit::core::RobotModelConstPtr& robot_model) override
117+
{
118+
Generator::init(robot_model);
119+
robot_model_ = robot_model;
120+
compute_count = 0;
121+
}
122+
123+
private:
124+
unsigned short compute_count;
125+
moveit::core::RobotModelConstPtr robot_model_;
126+
};
127+
128+
class MyConnecting : public Connecting
129+
{
130+
public:
131+
MyConnecting(const std::string& name) : Connecting(name){};
132+
133+
void compute(const InterfaceState& from, const InterfaceState& to) override
134+
{
135+
// do computation
136+
// ...
137+
// package that into a Solution to link between the two states
138+
SolutionBasePtr s;
139+
140+
// connect the two states using the solution above
141+
connect(from, to, s);
142+
}
143+
144+
void init(const moveit::core::RobotModelConstPtr& robot_model) override
145+
{
146+
Connecting::init(robot_model);
147+
robot_model_ = robot_model;
148+
}
149+
150+
private:
151+
moveit::core::RobotModelConstPtr robot_model_;
152+
};
153+
154+
int main(int argc, char** argv)
155+
{
156+
const std::string node_name = "extension_tutorial";
157+
ros::init(argc, argv, node_name);
158+
ros::NodeHandle n;
159+
160+
return 0;
161+
}

0 commit comments

Comments
 (0)