Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allowing users to load and change dynamically new optimization objective for OMPL's planners #1436

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

AntoineDevop
Copy link
Contributor

These modification allow user to create new optimizer for OMPL and load it into MoveIt trough the parameter optimization_objective.

After creating his own custom optimizer, with a simple call to the macro defined in the new header, the user can load it in MoveIt.

Checklist

  • [ x] Required by CI: Code is auto formatted using clang-format
  • [ x] Extend the tutorials / documentation reference
  • Document API changes relevant to the user in the MIGRATION.md notes
  • Create tests, which fail without this PR reference
  • Include a screenshot if changing a GUI
  • While waiting for someone to review your request, please help review another open pull request to support the maintainers

AntoineDevop added a commit to AntoineDevop/moveit2_tutorials that referenced this pull request Jul 20, 2022
Update the document accordingly the pull request #1436 (moveit/moveit2#1436)
Copy link
Contributor

@v4hn v4hn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great feature, I'm unsure about the concrete implementation.

@@ -350,8 +354,26 @@ void ompl_interface::ModelBasedPlanningContext::useConfig()
}
else
{
objective =
std::make_shared<ompl::base::PathLengthOptimizationObjective>(ompl_simple_setup_->getSpaceInformation());
pluginlib::ClassLoader<ompl_optimization_loader::OptimizationObjectiveLoader> poly_loader("moveit_core", "ompl_optimization_loader::OptimizationObjectiveLoader");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did they address this issue in ROS2 or shouldn't you keep the ClassLoader instance alive?
Technically the OptimizationObjectiveLoader is correctly destroyed before the plugin, but I guess the usual assumption is that all the methods in the instantiated objective returned by getOptimizationObjective live in the same shared object as the loader and technically pluginlib is allowed to dlclose the object once the plugin is destroyed.

@@ -34,7 +34,7 @@ install(TARGETS moveit_ompl_interface moveit_ompl_planner_plugin
INCLUDES DESTINATION include
)
ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET)
ament_export_dependencies(moveit_core ompl)
ament_export_dependencies(moveit_core ompl moveit_ros_planning)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moveit/planning_interface/ lives in moveit_core, so there is no need for this new dependency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes my bad, the include in top of the file was false. See new commit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't see the need to depend on moveit_ros_planning unless that dependency was missing here before your patch?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reasons, the header inside moveit/ompl_interface are not found unless we add moveit_ros_planning to the export_dependendencies.

try
{
RCLCPP_DEBUG(LOGGER, "Using optimization objective : %s", optimizer.c_str());
std::shared_ptr<ompl_optimization_loader::OptimizationObjectiveLoader> obj = poly_loader.createSharedInstance(optimizer);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this way the name for the objective you need to define in your config is

optimization_objective: your_package/YourObjectiveLoader

right? It would be nicer to leave the Loader part out of the config name to keep them in line with the other objective names.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depend on how the user creates his plugin_description.xml because the name tag allows the user to define the name he wants to use to load and therefor he can give a name consistent with the other objective, without any Loader.
If he doesn't give any name, yes he has to give the fully qualified type to the optimization_objective parameter. But because we have no choice to use a loader because of the parameter given to the constructor of the class OtpimizationObjective, I could not think to another solution.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the name tag allows the user to define the name he wants to use to load

Indeed. I guess I assumed everyone always uses the same name for C++ class name and plugin name.
I guess it is ok to deviate from that norm here.

@codecov
Copy link

codecov bot commented Jul 20, 2022

Codecov Report

Patch coverage: 33.34% and project coverage change: -0.01 ⚠️

Comparison is base (72dbc00) 50.47% compared to head (37c1128) 50.46%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1436      +/-   ##
==========================================
- Coverage   50.47%   50.46%   -0.01%     
==========================================
  Files         387      387              
  Lines       31819    31840      +21     
==========================================
+ Hits        16059    16066       +7     
- Misses      15760    15774      +14     
Impacted Files Coverage Δ
...veit/ompl_interface/model_based_planning_context.h 91.12% <ø> (ø)
...mpl_interface/src/model_based_planning_context.cpp 57.72% <33.34%> (-0.96%) ⬇️

... and 2 files with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Do you have feedback about the report comment? Let us know in this issue.

@v4hn
Copy link
Contributor

v4hn commented Jul 20, 2022

It would really help to have an example for such a plugin in a tutorial because otherwise we have another undocumented feature with additional aspects to consider.

I guess my main open question is #1436 (comment)

Aside from that I'm wondering whether we could get around having to load the plugin for each planning request instead of once.
In theory the user could request a different plugin in each low-level call, but in practice these parameters are set in the ompl_planning.yaml, right?

@ejalaa12
Copy link
Contributor

It would really help to have an example for such a plugin in a tutorial because otherwise we have another undocumented feature with additional aspects to consider.

We updated the ompl_interface tutorial in one of the commit here
Is there anywhere else we could add this documentation ?

I guess my main open question is #1436 (comment)

I think we could add the pluginlib::ClassLoader<ompl_optimization_loader::OptimizationObjectiveLoader> as a member of the class. That way the ClassLoader is initialized only once and destroyed only when the context is destroyed.
Also, if

Aside from that I'm wondering whether we could get around having to load the plugin for each planning request instead of once. In theory the user could request a different plugin in each low-level call, but in practice these parameters are set in the ompl_planning.yaml, right?

The objective was re-created at each request https://github.com/ros-planning/moveit2/blob/a2a8b48d990bc840820d7c608b8b502c00094c84/moveit_planners/ompl/ompl_interface/src/model_based_planning_context.cpp#L325-L354

Or Is it because loading the plugin takes time compared to instantiating a class normally? In that case we might have to store a shared instance somewhere in the context to be "re-used" later.

@mamoll
Copy link
Contributor

mamoll commented Aug 4, 2022

I like the idea behind this PR a lot! I share @v4hn's concern, though, that creating an optimization objective for every planning request seems redundant. It sounds like @ejalaa12 is saying that this is, in fact, what is currently done for all the predefined optimization objectives (so this PR would behave no differently). Is that right?

@ejalaa12
Copy link
Contributor

ejalaa12 commented Aug 4, 2022

@mamoll yes exactly, the lines I've cited in my previous response shows that all the predefined objective are created at the moment of the request.
This PR would not behave differently.
However, I'm no expert in pluginlib, so my question was if the cost of instantiating an object using pluginlib is higher than just creating it "manually".
In any case, one improvement I see with this PR, is that we can at least move the creation of the pluginlib::ClassLoader to the constructor. There is no need to re-create a new ClassLoader which would then create a new OptimizationObjective for each request.

@v4hn
Copy link
Contributor

v4hn commented Aug 4, 2022 via email

@ejalaa12
Copy link
Contributor

ejalaa12 commented Aug 9, 2022

One solution for this problem is the following:

  • we make the ClassLoader a member of model_based_planning_context
  • at construction we load a list of optimizers into a std::map<string, OptimizationObjective>. This list can be pre-defined (hard-coded or from ros-parameters or a combination of both
  • for each planning request we select the OptimizationObjective from the std::map, if it does not exist, we can try loading it and appending to the map.

That way, we minimize the amount of call to pluginlib.

We can provide a PR for that if this is ok for you.

@v4hn
Copy link
Contributor

v4hn commented Aug 9, 2022 via email

@AntoineDevop
Copy link
Contributor Author

In fact, OptimizationObjectives has indeed need to be created each time a planning is requested because it needs the updated version of the SpaceInformation and the only way to give it to it is by instantiated again.
So what we did is :

  • Create a map of std::map<string, OptimizationObjectiveLoader>at the creation of the planning context where is registered all the declared optimization objective loader plugin
  • When a planning request is set, if it's not one of the predefined objective, it search it in the map and created the corresponding OptimizationObjectives.

This method ensure that the call to PluginLib occurs only once at the creation of the planning context. One of the drawbacks is that it will not search for newly, during runtime, added plugin but this should not be a frequent case.

@mergify
Copy link

mergify bot commented Sep 7, 2022

This pull request is in conflict. Could you fix it @AntoineDevop?

1 similar comment
@mergify
Copy link

mergify bot commented Sep 27, 2022

This pull request is in conflict. Could you fix it @AntoineDevop?

@ejalaa12 ejalaa12 force-pushed the custom_ompl_optimization_objective branch from 8426df7 to 52fa9b7 Compare November 7, 2022 15:11
ejalaa12 pushed a commit to forssea-robotics/moveit2_tutorials that referenced this pull request Nov 7, 2022
Update the document accordingly the pull request #1436 (moveit/moveit2#1436)
@ejalaa12
Copy link
Contributor

ejalaa12 commented Nov 7, 2022

@mamoll @v4hn
Sorry for the late update, we just created a Pull Request with the instructions in the moveit2_tutorials repo: moveit/moveit2_tutorials#542.

We also went ahead and rebased our PR on main.

Thanks for reviewing it.

@sjahr sjahr requested review from mamoll and v4hn November 7, 2022 17:01
@ejalaa12
Copy link
Contributor

ejalaa12 commented May 5, 2023

@mamoll Any update on this PR ? I've just rebased the changes on main again.

@mamoll
Copy link
Contributor

mamoll commented May 5, 2023

@mamoll Any update on this PR ? I've just rebased the changes on main again.

@sjahr @henningkayser can you have a look at this? This seems relevant to our recent discussion.

@mergify
Copy link

mergify bot commented Jun 2, 2023

This pull request is in conflict. Could you fix it @AntoineDevop?

@sjahr sjahr self-assigned this Jun 12, 2023
@mergify
Copy link

mergify bot commented Aug 11, 2023

This pull request is in conflict. Could you fix it @AntoineDevop?

@tylerjw
Copy link
Member

tylerjw commented Aug 23, 2023

I created an issue about this. I'll try to get someone to review this and provide more feedback. I'm sorry it looks like this has gone without reviews since you last updated it.

@ejalaa12
Copy link
Contributor

@tylerjw Hi, I can update this branch with what's necessary. I was waiting for a review and forgot about it ^^.

@tylerjw tylerjw added the discuss Highlight for discussion in Standup or Maintainer Meeting label Aug 29, 2023
@henningkayser henningkayser removed the discuss Highlight for discussion in Standup or Maintainer Meeting label Sep 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants