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

Backport: Planning with multiple pipelines in parallel with moveit_cpp #3244

Merged
merged 26 commits into from
Nov 11, 2022

Conversation

sjahr
Copy link
Contributor

@sjahr sjahr commented Oct 17, 2022

Description

This is a backport of the parallel planning API for moveit_cpp from moveit2#1420.

This PR adds:

  • A new interface for moveit_cpp to plan with multiple pipelines in parallel with a user-defined stopping criterion and solution selection function
  • A free function to the robot trajectory package to calculate the length of a given trajectory
  • Replace PlanSolution with extended MotionPlanResponse to reduce duplicate code
  • Add PlannerConfigurations for CHOMP, PILZ and trajopt (detailed motivation for this can be found in original moveit2 PR

Usage example:

config.yaml

one:
  plan_request_params:
    planning_attempts: 1
    planning_pipeline: ompl
    planner_id: "RRTConnectkConfigDefault"
    max_velocity_scaling_factor: 1.0
    max_acceleration_scaling_factor: 1.0

two:
  plan_request_params:
    planning_attempts: 1
    planning_pipeline: ompl
    planner_id: "PRMstarkConfigDefault"
    max_velocity_scaling_factor: 1.0
    max_acceleration_scaling_factor: 1.0

three:
  plan_request_params:
    planning_attempts: 1
    planning_pipeline: pilz_industrial_motion_planner
    planner_id: "LIN"
    max_velocity_scaling_factor: 1.0
    max_acceleration_scaling_factor: 1.0

demo_node.cpp

    planning_component->setGoal(some_goal_state);
    planning_component->setStartStateToCurrentState();

    moveit_cpp::PlanningComponent::MultiPipelinePlanRequestParameters multi_pipeline_plan_request;
    multi_pipeline_plan_request.load(node, {"one", "two", "three"});

    auto plan_solution = planning_components->plan(multi_pipeline_plan_request);

Checklist

  • Required by CI: Code is auto formatted using clang-format
  • 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

@welcome
Copy link

welcome bot commented Oct 17, 2022

Thanks for helping in improving MoveIt and open source robotics!

@codecov
Copy link

codecov bot commented Oct 17, 2022

Codecov Report

Base: 62.03% // Head: 61.82% // Decreases project coverage by -0.22% ⚠️

Coverage data is based on head (6aa4ca5) compared to base (4c10a9e).
Patch coverage: 29.37% of modified lines in pull request are covered.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3244      +/-   ##
==========================================
- Coverage   62.03%   61.82%   -0.21%     
==========================================
  Files         377      378       +1     
  Lines       33268    33373     +105     
==========================================
- Hits        20636    20630       -6     
- Misses      12632    12743     +111     
Impacted Files Coverage Δ
...include/moveit/robot_trajectory/robot_trajectory.h 98.31% <ø> (ø)
...eit_core/robot_trajectory/src/robot_trajectory.cpp 67.42% <0.00%> (-1.81%) ⬇️
.../moveit_cpp/include/moveit/moveit_cpp/moveit_cpp.h 100.00% <ø> (ø)
moveit_ros/planning/moveit_cpp/src/moveit_cpp.cpp 62.30% <0.00%> (-6.20%) ⬇️
...clude/moveit/planning_pipeline/planning_pipeline.h 75.00% <0.00%> (-25.00%) ⬇️
...cpp/include/moveit/moveit_cpp/planning_component.h 47.83% <14.29%> (-52.17%) ⬇️
...ros/planning/moveit_cpp/src/planning_component.cpp 37.63% <21.63%> (-11.04%) ⬇️
...anning/planning_pipeline/src/planning_pipeline.cpp 81.53% <60.00%> (-0.70%) ⬇️
...lude/moveit/planning_interface/planning_response.h 100.00% <100.00%> (ø)
...ude/moveit/planning_interface/planning_interface.h 69.24% <0.00%> (-15.38%) ⬇️
... and 16 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

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

@AndyZe
Copy link
Member

AndyZe commented Oct 31, 2022

We've been using this a lot and I can attest that it's AWESOME! I'm going to review it over at MoveIt2, though, so I don't review twice.

Copy link
Contributor

@rhaschke rhaschke left a comment

Choose a reason for hiding this comment

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

I have a handful of remarks. The most important one is the API incompatibility with existing PlannerManager plugins. While you might break API in ROS2, we shouldn't do so in ROS1.

@@ -53,7 +53,15 @@ struct MotionPlanResponse

robot_trajectory::RobotTrajectoryPtr trajectory_;
double planning_time_;
moveit_msgs::MoveItErrorCodes error_code_;
moveit::core::MoveItErrorCode error_code_;
moveit_msgs::RobotState start_state_;
Copy link
Contributor

Choose a reason for hiding this comment

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

As far as I could see, both start_state_ and planner_id_ are not actually used. What's the purpose of these fields?

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 is possible to pass an optional solution_selection_callback and/or a stopping_criterion_function both use the produced MotionPlanResponses to make a decision and these to additional fields might be useful to make that decision. For example

bool stoppingCriterionCallBack(
    moveit_cpp::PlanningComponent::PlanSolutions const& plan_solutions,
    moveit_cpp::PlanningComponent::MultiPipelinePlanRequestParameters const& plan_request_parameters)
  for (auto const& solution : solutions)
  {
      // Stop parallel planning if PILZ found a solution
      if (solution.planner_id_ == "LIN")
      {
        return true;
      }
  }
 return false;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree for the planner_id_, but all plans share the very same start state, don't they?
Please add a comment, e.g. "planner this solution originates from".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's true, it does not matter for the solution selection. I added it since the MotionPlanResponse message has a similar field and it can be useful when the MotionPlanResponse is processed further independent from moveit_cpp/ the planning_component. Do you think we should remove it or is a comment sufficient?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure yet. If the start state is identical for all solutions, saving it here would be a waste of memory.
However, due to different pipeline configs, modifying the start state (or not), the actual start state might differ between solutions (but I don't know by heart). That would be an argument to keep it here...

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd remove the underscore from the field names but you're right, the API will be broken by this. I can open a PR for moveit_tutorials to fix this. Would MTC be affected?

I'm not only worried about moveit_tutorials or MTC. The problem is that any user code out there accessing those members will be affected by this change! At least we should provide a deprecation period for transition.
My suggestion is to:

  • remove the underscores
  • provide additional, deprecated members with the underscore as const references to the new members:
    moveit_msgs::RobotState start_state;
    [[deprecated("Use start_state instead.")]] const moveit_msgs::RobotState& start_state_;
    Obviously, those references need to be initialized in the constructor. This will render the struct read-only. So you will need to provide a custom copy constructor and assignment operator as well.

@@ -63,7 +71,15 @@ struct MotionPlanDetailedResponse
std::vector<robot_trajectory::RobotTrajectoryPtr> trajectory_;
std::vector<std::string> description_;
std::vector<double> processing_time_;
moveit_msgs::MoveItErrorCodes error_code_;
moveit::core::MoveItErrorCode error_code_;
moveit_msgs::RobotState start_state_;
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here.

Comment on lines 64 to 69
const planning_interface::PlannerConfigurationSettings planner_config_settings{
group, group, std::map<std::string, std::string>()
};
pconfig[planner_config_settings.name] = planner_config_settings;
}
setPlannerConfigurations(pconfig);
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the purpose of setting these empty planner configs?

Copy link
Contributor Author

@sjahr sjahr Nov 7, 2022

Choose a reason for hiding this comment

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

Isn't it only empty when the robot model does not have any JointModelGroups? The line above adds for each joint model group settings

Copy link
Contributor

Choose a reason for hiding this comment

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

But those settings are simply empty: <std::string, std::string>()

Comment on lines 123 to 126
void setPlannerConfigurations(const planning_interface::PlannerConfigurationMap& pcs) override
{
config_settings_ = pcs;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe introduce this as the default implementation of PlannerManager?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Personally, I'd like to remind the person that implements a new planner plugin to think about the planner config and forcing them to implement this function would be a good way. But I can make this a default implementation if you don't like that

Copy link
Contributor

Choose a reason for hiding this comment

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

I understand your reasoning and for MoveIt2 it is probably the right way to go.
For MoveIt1, I personally think we should maintain API stability. Other maintainers might have a different opinion.

Copy link
Contributor

Choose a reason for hiding this comment

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

Reverting this will be required for CI to pass...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, let's do it that way then 👍

@sjahr sjahr requested a review from rhaschke November 7, 2022 10:16
@rhaschke
Copy link
Contributor

rhaschke commented Nov 8, 2022

This PR had several clang-tidy warnings. Don't you have clang-tidy checks enabled anymore for MoveIt2?

@rhaschke
Copy link
Contributor

rhaschke commented Nov 8, 2022

How did you handle the API-incompatible member renamings in MoveIt2??
PlanSolution's trajectory and start_state members didn't have a trailing underscore, but MotionPlanResponse's members have!
This results in a failure in downstream packages accessing those public members.

@sjahr
Copy link
Contributor Author

sjahr commented Nov 9, 2022

How did you handle the API-incompatible member renamings in MoveIt2?? PlanSolution's trajectory and start_state members didn't have a trailing underscore, but MotionPlanResponse's members have! This results in a failure in downstream packages accessing those public members.

Since MotionPlanResponse is a struct and all member variables are public the _ underscore naming convention for private member variables is not correctly used here anyway's. In moveit2 we opened an additional issue to fix this but I can included into this pr for simplicity's sake

@sjahr
Copy link
Contributor Author

sjahr commented Nov 9, 2022

Yes, moveit2 uses clang-tidy but the related clang-tidy check is disabled moveit/moveit2#214

@rhaschke
Copy link
Contributor

rhaschke commented Nov 9, 2022

In moveit2 we opened an additional issue to fix this

I still don't know how you solved it in MoveIt2. Please add a reference.

but I can included into this pr for simplicity's sake

Depends on the solution approach you took. I don't yet see how this can be fixed in a fashion not breaking API for somebody.

@sjahr sjahr force-pushed the pr-add_parallel_planning branch 2 times, most recently from 279038b to c72ca11 Compare November 10, 2022 09:36
Copy link
Contributor

@rhaschke rhaschke left a comment

Choose a reason for hiding this comment

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

There is still a lot of redundancy. Can you handle the missing config settings in moveitcpp instead?

Comment on lines 120 to 131

// Construct default configurations for planning groups that don't have configs already passed in. This is necessary
// since moveit_cpp can only use planners with a config for a given joint group
for (const moveit::core::JointModelGroup* group : robot_model_->getJointModelGroups())
{
if (config_settings_.find(group->getName()) == config_settings_.end())
{
planning_interface::PlannerConfigurationSettings empty_config;
empty_config.name = empty_config.group = group->getName();
config_settings_[empty.name] = empty;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why did you decide to initialize settings for "missing" groups here instead of in PlannerManager::initialize as suggested here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, you reviewed to fast, that change did not work. The problem is that initialize() function can easily be overwritten and thus compatibility with moveit_cpp be destroyed. The way I implemented it would have added a default empty config for all planning plugins with setPlannerConfigurations. But that did not work because the robot model is not available in this function.

I think the planner plugins should be modified because the flaw right now is that pconfig is not used correctly (as expected by moveit_cpp) by the plugins. If we hide this implementation detail away in the base class, I fear future plugin implementations will repeat the same error,

Copy link
Contributor

Choose a reason for hiding this comment

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

pconfig is not used correctly (as expected by moveit_cpp) by the plugins.

Can you please detail this? As far as I can see, moveit_cpp just generates a "shortcut" map from these configs.
If there are no config settings, why should a plugin declare them as empty? I think we should first agree on the semantics, before implementing anything further...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My preference would be to use the current solution and open an issue to re-evaluate the PlannerConfigurationSettings, since it is only properly used by OMPL at the moment. Maybe it would be better to remove/refactor it and the pipeline detection in moveit_cpp but I think this is out of the scope of this PR.

Copy link
Contributor Author

@sjahr sjahr Nov 10, 2022

Choose a reason for hiding this comment

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

From: moveit/moveit2#1522

When the plan() method of the planning component gets called, all available planning pipelines are searched based on their name [1]. These names are provided by moveit_cpp [2]. The moveit_cpp function that provides the planning pipeline names reads them from an internal map [3]. A planning pipelines needs to have a planner plugin that has a PlannerConfiguration in order to be added to this map [4].

moveit_planners/chomp/chomp_interface/src/chomp_plugin.cpp Outdated Show resolved Hide resolved
@@ -279,7 +261,7 @@ void ompl_interface::OMPLInterface::loadPlannerConfigurations()
ROS_DEBUG_STREAM_NAMED(LOGNAME, " - " << parameters.first << " = " << parameters.second);
}

setPlannerConfigurations(pconfig);
context_manager_.setPlannerConfigurations(pconfig);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why does the OMPL planner use a different approach to configure settings?
I would expect a similar approach to other planners, i.e. calling setPlannerConfigurations(pconfig).

Copy link
Contributor Author

@sjahr sjahr Nov 10, 2022

Choose a reason for hiding this comment

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

I like the way, ompl defined a default setPlannerConfigurations implementation to add empty configs for joint model groups and moved that implementation into the base class. But that did not work

moveit_msgs::RobotState start_state;
std::string planner_id;

[[deprecated("Use trajectory instead.")]] robot_trajectory::RobotTrajectoryPtr& trajectory_;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@rhaschke Probably I am doing something wrong, but if I make these references const, the API breaks again. Do I miss something here?

This reverts commit 5fd520f.
- reverse naming:
  - underscore_ members stay the default
  - (deprecated) non_underscore members are added for API compatibility
  - add deprecated start_state member
- avoid code duplication between copy constructor and copy assignment
This is a backport of moveit/moveit2#1710,
dropping function getPlanningPipelineNames().
This member is not needed anymore.
Copy link
Contributor

@rhaschke rhaschke left a comment

Choose a reason for hiding this comment

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

If the latest version passes CI, I'm fine with the current state.

@sjahr
Copy link
Contributor Author

sjahr commented Nov 11, 2022

👍 Thanks a lot for your help to get this merged!

@rhaschke rhaschke merged commit 5e6f817 into moveit:master Nov 11, 2022
@welcome
Copy link

welcome bot commented Nov 11, 2022

Congrats on getting your first MoveIt pull request merged and improving open source robotics!

@jspricke
Copy link
Contributor

Document API changes relevant to the user in the MIGRATION.md notes

I think it would be a good idea to add some notes on the API change there. @sjahr

@v4hn
Copy link
Contributor

v4hn commented Jan 20, 2023

@rhaschke @sjahr I might misunderstand something just perusing this thread, but I just ran into these deprecations and they are the inverted logic of what was discussed throughout this thread. It recommends the historical trailing_ notation over clean public field names. Was that intended?

@rhaschke
Copy link
Contributor

This PR removed the rather new data structure PlanningComponent::PlanSolution using member names w/o an underscore and replaced it with PlanSolutions, which is essentially a vector of MotionPlanResponse. The latter in turn used very similar members, but with a trailing underscore.
Due to this incompatibility of member names between PlanSolution and MotionPlanResponse we had to break API for one of them. I suggested not modifying this older data structure and sticking to the more widely used member names with the trailing underscore. All MoveItCpp code already using the new member names thus exhibits the deprecation warnings now.

@v4hn
Copy link
Contributor

v4hn commented Jan 23, 2023

Thanks for the explanation. Not great to keep the legacy annotation _ when code is reworked anyway, but fair enough.

@rhaschke
Copy link
Contributor

rhaschke commented Jan 24, 2023

Our naming rules enforce the underscore:
https://github.com/ros-planning/moveit/blob/e4ede1f2a88a763554ee5e62b39ef0bd70bbc061/.clang-tidy#L45-L46

Why do you claim this notation legacy?

@v4hn
Copy link
Contributor

v4hn commented Jan 25, 2023

Why do you claim this notation legacy?

Somewhat a mix of personal opinion, conventions I know and the class/struct dichotomy I guess. (MotionPlanResponse is a struct)
From my perspective the suffix should only be added to protected and private members (public members in classes should be shunned anyway).
E.g. google's code conventions splits structs and classes based on data access & functionality and only class members are expected to have the suffix.
I'm surprised clang-tidy does not seem to support StructMemberSuffix separately.

@rhaschke
Copy link
Contributor

rhaschke commented Jan 25, 2023

There are Public, Protected, and Private variants for all rules. So we could remove the suffix for public members...
https://clang.llvm.org/extra/clang-tidy/checks/readability/identifier-naming.html#cmdoption-arg-publicmembersuffix

I have triggered a corresponding build: https://github.com/ros-planning/moveit/actions/runs/4005320877
Let's see how many API changes this will trigger...

rhaschke added a commit to rhaschke/moveit_tutorials that referenced this pull request Jan 31, 2023
rhaschke added a commit to moveit/moveit_tutorials that referenced this pull request Jan 31, 2023
* Fix old-style-cast
* Fix deprecated member names (due to moveit/moveit#3244)
* Enable -Werror
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.

None yet

6 participants