Skip to content

Commit 21380ca

Browse files
committed
MTC Tutorial: Programmatic Extension
1 parent 049f999 commit 21380ca

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
@@ -57,6 +57,7 @@ shown in the right-most window. Selecting one of those solutions will start its
5757
.. image:: images/mtc_show_stages.gif
5858
:width: 700px
5959

60+
.. _basic_concepts:
6061
Basic Concepts
6162
--------------
6263

@@ -96,3 +97,182 @@ Examples are running alternative planners for a free-motion plan, picking object
9697
Stages not only support solving motion planning problems.
9798
They can also be used for all kinds of state transitions, as for instance modifying the planning scene.
9899
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.
100+
101+
Programmatic Extension
102+
----------------------
103+
104+
You may find that the available stages do not suffice the needs of your application.
105+
Given this situation, you have the option to derive your own classes from core
106+
stage types and implement custom functionality. Your choice of core class determines
107+
the structure, i.e. the `flow path` of incoming and outgoing data with respect to
108+
your new stage.
109+
110+
Overview of core classes
111+
^^^^^^^^^^^^^^^^^^^^^^^^
112+
113+
The section :ref:`basic_concepts` in this tutorial provides an overview of the stage types
114+
together with result propagation directions that are available for programmatic extension.
115+
Remember: Core classes define result flow.
116+
For example, to specify bi-directional result propagation, you might derive from the
117+
``Generator`` class.
118+
You will find this pattern if you take a look at the ``CurrentState`` stage,
119+
which `generates` the current state of the planning scene and forwards it to both interfaces.
120+
121+
When deriving from one of the core classes, you need to implement your new computation in
122+
the provided virtual functions. This ensures that the `MTC` backend can call your code
123+
in the right place when traversing through the task hierarchy.
124+
The following table provides an overview of these functions as well as giving more,
125+
already implemented, examples of programmatic extension stages.
126+
127+
+------------------------+-----------------------+-------------------------+
128+
| Core Class | Virtual Functions | Example Stages |
129+
+========================+=======================+=========================+
130+
| Generator | | ``compute`` | | *CurrentState* |
131+
| | | ``canCompute`` | | *FixedState* |
132+
+------------------------+-----------------------+-------------------------+
133+
| Monitoring Generator | | ``compute`` | | *FixedCartesianPoses* |
134+
| | | ``canCompute`` | | *GeneratePickPose* |
135+
| | | ``onNewSolution`` | | *GeneratePlacePose* |
136+
+------------------------+-----------------------+-------------------------+
137+
| Connecting | | ``compute`` | | *Connect* |
138+
+------------------------+-----------------------+-------------------------+
139+
| Propagating Either Way | | ``computeForward`` | | *MoveRelative* |
140+
| | | ``computeBackward`` | | *MoveTo* |
141+
| | | | *ModifyPlanningScene* |
142+
+------------------------+-----------------------+-------------------------+
143+
144+
.. note::
145+
**compute()**
146+
The functionality of a stage is encapsulated in
147+
the ``compute()`` function.
148+
``Interface`` classes are utilized as input and outputs of a stage.
149+
150+
**canCompute()**
151+
To be able to gain control of the execution of the compute function, ``canCompute()`` acts as a guard.
152+
153+
Implementation
154+
^^^^^^^^^^^^^^
155+
156+
In case you just want to insert the stage and check for the functions later, it is valid
157+
to leave the function body with no contents, i.e.:
158+
159+
.. code-block:: c++
160+
161+
void compute() override {};
162+
163+
Additionally, you may define a custom constructor for your derived stage class and forward the arguments like so:
164+
165+
.. code-block:: c++
166+
167+
class MyGenerator : public Generator{
168+
public:
169+
MyGenerator(const std::string& name) : Generator(name) {};
170+
171+
Solutions of stages are propagated via ``InterfaceStates``.
172+
An interface state can only propagate planning scene instances.
173+
The next section presents code examples for all the core classes that you may use as a
174+
basic implementation reference.
175+
176+
Generator
177+
+++++++++
178+
A template for a ``Generator`` stage is listed below.
179+
As stated above the ``compute()`` function assembles a planning scene and spawns an interface, whilst the
180+
``canCompute()`` function acts as an execution guard.
181+
182+
.. literalinclude:: src/extension_tutorial.cpp
183+
:language: cpp
184+
:lines: 48-84
185+
186+
The example above additionally implements a mechanism to call the ``compute`` function only once.
187+
Create the stage:
188+
189+
.. code-block:: c++
190+
191+
auto g = std::make_unique<MyGenerator>("myGenerator");
192+
193+
Monitoring Generator
194+
++++++++++++++++++++
195+
The ``MonitoringGenerator`` processes a solution that the monitored stage just computed.
196+
197+
.. literalinclude:: src/extension_tutorial.cpp
198+
:language: cpp
199+
:lines: 86-129
200+
201+
Create the stage:
202+
203+
.. code-block:: c++
204+
205+
auto m = std::make_unique<MyMonitoringGenerator>("myMonitoringGenerator");
206+
207+
PropagatingEitherWay
208+
++++++++++++++++++++
209+
210+
The ``PropagatingEitherWay`` stage forwards solutions between interface states of previous and
211+
following stages in a forward and backward manner.
212+
213+
.. literalinclude:: src/extension_tutorial.cpp
214+
:language: cpp
215+
:lines: 9-46
216+
217+
Create the stage:
218+
219+
.. code-block:: c++
220+
221+
auto pr = std::make_unique<MyPropagatingEitherWay>("myPropagatingEitherWay");
222+
223+
Connecting
224+
++++++++++
225+
226+
The Connecting stage computes a solution between interface states
227+
from the previous and following stages.
228+
229+
.. literalinclude:: src/extension_tutorial.cpp
230+
:language: cpp
231+
:lines: 131-155
232+
233+
Create the stage:
234+
235+
.. code-block:: c++
236+
237+
auto c = std::make_unique<MyConnecting>("myConnecting");
238+
239+
Stage Configuration
240+
-------------------
241+
242+
Stages are configured with properties.
243+
A property is a ``boost::any`` value stored inside the `Property` class, which provides the following
244+
wrapper-like functionality:
245+
246+
- Maintain default and current values with get-/set-/reset-to-default functions.
247+
- Provide a description.
248+
- Serialization into strings.
249+
- Get the ``boost::any`` value or the entire `Property` object for a given key.
250+
- Check for type and if-defined status of the property.
251+
- Initialization from an already existing property.
252+
253+
.. tutorial-formatter:: ./src/extension_tutorial.cpp
254+
255+
Notice, that when we initialize our properties from an external domain, we need to provide a property initializer source flag.
256+
You can access these flags through the ``Stage::PropertyInitializerSource::`` scope.
257+
They define a priority hierarchy in which initializations are carried out:
258+
259+
``MANUAL > DEFAULT > INTERFACE > PARENT``
260+
261+
Initialization of properties is carried out during planning of the entire
262+
task hierarchy. You can therefore specify a priority hierarchy on a per-property-basis from where the stage should get
263+
the inforation for its properties.
264+
265+
E.g. use the ``MANUAL`` flag if you want to explicitly configure a (set) of properties
266+
from a property map. ``MANUAL`` takes precedence over all the other flags.
267+
As another example, you can use ``INTERFACE`` and ``PARENT`` flags to let the stage be initialized
268+
by its successor or predecessor.
269+
270+
To summarize, the property map allows you to:
271+
272+
- Declare properties for future use without providing values yet.
273+
- Expose a subset of the properties to another property map.
274+
- Reset all properties.
275+
- Check if a key-value pair is present.
276+
- Initialize still undefined properties using SourceFlags.
277+
- Iterate over the property map.
278+
- 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)