Skip to content

Fix pilz planners to consider and plan Cartesian motions w.r.t. subframes#3519

Merged
rhaschke merged 29 commits intomoveit:masterfrom
riv-tnoble:bugfix/fix-lin-plans-using-subframes
Nov 9, 2023
Merged

Fix pilz planners to consider and plan Cartesian motions w.r.t. subframes#3519
rhaschke merged 29 commits intomoveit:masterfrom
riv-tnoble:bugfix/fix-lin-plans-using-subframes

Conversation

@riv-tnoble
Copy link
Contributor

@riv-tnoble riv-tnoble commented Oct 30, 2023

Description

Fixes #3517 by allowing pilz to use subframes in goal constraints.

Updates computePoseIK() to build a RobotState from the PlanningScene rather than the RobotModel, so that subframes are taken into account. Also adds some error handling logic around calls to this function as, currently, if the passed link_name does not exist, the start_pose is set to the identity pose, which leads to a less intuitive IK solver error later on.

This also means the collision checking in the pilz computePoseIK() method is now attached collision object aware.

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

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.

Generally, I think the approach is correct. In order to yield interpolation w.r.t. the subframe, the frame needs to be resolved in the Pilz planner and not only in setFromIK.

Comment on lines +206 to +211
// if (!robot_model_->getJointModelGroup(group_name)->canSetStateFromIK(pos_constraint.link_name))
// {
// std::ostringstream os;
// os << "No IK solver available for link: \"" << pos_constraint.link_name << "\"";
// throw NoIKSolverAvailable(os.str());
// }
Copy link
Contributor

Choose a reason for hiding this comment

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

If not needed anymore: remove completely. Leave a comment here, why it's not needed anymore.
I guess it is redundant to similar checks elsewhere?

But: Instead of doing the error checking and reporting frequently in downstream trajectory generators, why not do it once 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.

Will double check that this is redundant. Do you think it'd make sense to move the transformation into a tip goal here too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you think it'd make sense to move the transformation into a tip goal here too?

Scratch that part given the transformation is no longer necessary (see reply from above comment)

@rhaschke What would be the easiest way of turning this check into one that can detect subframes? In other places, I've been using RobotState::knowsFrameTransform().

The method calls are:

TrajectoryGenerator::generate() -> TrajectoryGenerator::validateRequest() -> TrajectoryGenerator::checkGoalConstraints() -> TrajectoryGenerator::checkCartesianGoalConstraint()

The first of those methods has access to a planning scene, though I'm unsure if there's a cleaner way than passing a state through the method calls (especially since the state itself is arbitrary, it's just the access to the subframes we're interested in)

Copy link
Contributor

Choose a reason for hiding this comment

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

From my understanding, the check (for existence of the IK (sub)frame and it's usability for IK) didn't fail.
In contrast to RobotState::knowsFrameTransform(), canSetStateFromIK() should also verify that the IK frame is rigidly connected to the IK solver's tip frame (so exactly what you need).

Copy link
Contributor Author

@riv-tnoble riv-tnoble Oct 31, 2023

Choose a reason for hiding this comment

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

I do get the same error as before uncommenting this when planning with a subframe:

[10:15:49.016004] [/fake/move_group]: No IK solver available for link: "tb_1_1/disc_tip"
[10:15:49.016126] [/fake/move_group]: Planner threw an exception (error code: -31): Could not solve request

[10:15:49.017372] [/fake/motion_server]: Failed to plan sequence motion foo. Error code: NO_IK_SOLUTION. If problem persists, check toolbit config.

I think the issue the issue is that JointModelGroup has no notion of subframes and only checks for links in canSetStateFromIK, whereas RobotState knows about both and setStateFromIK is designed for that possibility.

Copy link
Contributor Author

@riv-tnoble riv-tnoble Oct 31, 2023

Choose a reason for hiding this comment

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

To summarise my findings:

  • The transformation from subframe goal to tip link goal I added to pilz isn't required provided the RobotState used for setStateFromIK() is built from the scene which knows about the subframes, as setStateFromIK() accounts for this case. I've removed this transformation from pilz now.
  • I'm no longer using the default_planner_request_adapters/ResolveConstraintFrames adapter as this was causing the original issue of planning a linear motion wrt the tip link rather than the subframe.
  • Therefore, anywhere pilz is validating the link_name of constraints, it's the subframe name that's being used (causing the above failure whenever the validation code is subframe unaware). This appears to be the case whenever using JointModelGroup.

@codecov
Copy link

codecov bot commented Oct 30, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (984a0ea) 61.81% compared to head (eda07ab) 61.84%.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3519      +/-   ##
==========================================
+ Coverage   61.81%   61.84%   +0.04%     
==========================================
  Files         385      385              
  Lines       34121    34091      -30     
==========================================
- Hits        21088    21081       -7     
+ Misses      13033    13010      -23     
Files Coverage Δ
..._industrial_motion_planner/planning_context_base.h 100.00% <100.00%> (ø)
...z_industrial_motion_planner/trajectory_generator.h 100.00% <ø> (ø)
...ustrial_motion_planner/trajectory_generator_circ.h 100.00% <ø> (ø)
...dustrial_motion_planner/trajectory_generator_lin.h 100.00% <ø> (ø)
...strial_motion_planner/src/command_list_manager.cpp 100.00% <100.00%> (ø)
...anner/src/trajectory_blender_transition_window.cpp 100.00% <ø> (ø)
...strial_motion_planner/src/trajectory_functions.cpp 100.00% <100.00%> (ø)
...strial_motion_planner/src/trajectory_generator.cpp 100.00% <100.00%> (ø)
...l_motion_planner/src/trajectory_generator_circ.cpp 100.00% <100.00%> (ø)
...al_motion_planner/src/trajectory_generator_lin.cpp 100.00% <100.00%> (ø)
... and 1 more

... and 7 files with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Co-authored-by: Robert Haschke <rhaschke@users.noreply.github.com>
@riv-tnoble
Copy link
Contributor Author

Simple sanity check of the LIN output:
https://github.com/ros-planning/moveit/assets/86778266/4c341090-64c1-4856-96a2-662006716dbf

Will do the same for CIRC as I believe a decent portion of the code is shared

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.

More feedback below.

@riv-tnoble riv-tnoble requested a review from rhaschke October 31, 2023 11:43
@riv-tnoble riv-tnoble changed the title Fixes LIN plans using subframes not being linear with respect to the … WIP Fixes LIN plans using subframes not being linear with respect to the … Nov 1, 2023
@riv-tnoble
Copy link
Contributor Author

riv-tnoble commented Nov 1, 2023

We're currently looking into an issue using this for our use case, which is as follows:

  • We plan to rotate a separate planning group before the sequence plan (the current state of that group at the time of the sequence plan is unrotated)
  • We pass the expected rotation of that other planning group as part of the start state in the plan sequence request
  • The changes to use a robot state from getCurrentState() now takes collision objects into account (desired)
  • However, (I think) this also takes a collision object of the unrotated other planning group into account (rather than the rotated start state version)
  • This causes a collision (which is not an issue, as we expect the other planning group to have already rotated by the time we plan, hence setting the start state as such)

@rr-mark is also looking into this issue

@riv-mjohnson
Copy link
Contributor

riv-mjohnson commented Nov 1, 2023

@rr-mark is also looking into this issue

It looks like

  • constructing the robot state from the robot model has the correct joint states, but no attached collision objects.
  • constructing the robot state from the planning scene has the attached collision objects, but the joint states are the current state, rather than the planning context.

I'm currently trying to rationalise the two; it would be good to get help from someone who know more about what is supposed to happen here.

With a little more digging, what is actually happening is now that computePoseIK has attached collision objects, it is performing collision checks with those objects where it wasn't before.

It looks like info.start_joint_position never contained information about joint groups other than the group currently being planned, and this previously didn't matter because the objects attached to those groups were not being checked.

Now those objects are being checked, it matters that their joints are not properly being updated.

Is computePoseIK supposed to be unaware of attached collision objects, with these checks happening elsewhere? Or is this something which should now be updated? It seems easy enough to fix the info.start_joint_position to contain all joint states, but this might have implications for computation times and for what errors are returned when collisions are detected.

@@ -134,9 +133,7 @@ void TrajectoryGeneratorLIN::extractMotionPlanInfo(const planning_scene::Plannin
info.start_joint_position[joint_name] = req.start_state.joint_state.position[index];
}
Copy link
Contributor

@riv-mjohnson riv-mjohnson Nov 1, 2023

Choose a reason for hiding this comment

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

assert(req.start_state.joint_state.name.size() == req.start_state.joint_state.position.size());
  std::map<std::string, double> start_joint_position;
  for (unsigned i=0; i<req.start_state.joint_state.name.size(); ++i) {
    start_joint_position[req.start_state.joint_state.name[i]] = req.start_state.joint_state.position[i];
  }
  for (const auto& joint_name : robot_model_->getJointModelGroup(req.group_name)->getActiveJointModelNames())
  {
    if (start_joint_position.count(joint_name) == 0)
    {
      std::ostringstream os;
      os << "Could not find joint \"" << joint_name << "\" of group \"" << req.group_name
         << "\" in start state of request";
      throw LinJointMissingInStartState(os.str());
    }
    info.start_joint_position[joint_name] = start_joint_position[joint_name];
  }

  computeLinkFK(robot_state, info.link_name, info.start_joint_position, info.start_pose);

  // check goal pose ik before Cartesian motion plan starts
  std::map<std::string, double> ik_solution;
  if (!computePoseIK(scene, info.group_name, info.link_name, info.goal_pose, frame_id, start_joint_position,
                     ik_solution))
  {
    std::ostringstream os;
    os << "Failed to compute inverse kinematics for link: " << info.link_name << " of goal pose";
    throw LinInverseForGoalIncalculable(os.str());
  }

Copy link
Contributor

@riv-mjohnson riv-mjohnson Nov 1, 2023

Choose a reason for hiding this comment

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

This looks like it correctly updates all the joint states, including those not in the current joint group, so that the IK collision detection is correct.

@rhaschke
Copy link
Contributor

rhaschke commented Nov 2, 2023

I'm not sure what (remaining?) problem you are referring to in #3519 (comment) and #3519 (comment).
In general, a planner should use the current state of the PS (PlanningScene::getCurrentState()) for planning and this state should comprise all joint values. However, this state might differ from the real current robot state.

That computePoseIK() wasn't using that state, but a newly created one, was definitely a design bug.

@rhaschke rhaschke added the awaits 2nd review one maintainer approved this request label Nov 3, 2023
@riv-mjohnson
Copy link
Contributor

Your changes work on my end, thank you for your time.

@riv-mjohnson
Copy link
Contributor

We're seeing a couple of issues with this PR:

Is all the extra collision checking working as intended? Or is this redundant with other collision checking after the path has been generated?

@rhaschke
Copy link
Contributor

rhaschke commented Nov 8, 2023

You are right, collision checking is performed twice: (i) during path generation and (ii) as a final trajectory validation step.
As the Pilz planner is not actually planning around obstacles, but simply generating a trajectory, we can skip the collision checking during planning. I will add a corresponding commit.

@rhaschke
Copy link
Contributor

rhaschke commented Nov 8, 2023

Actually, collision checking in generateJointTrajectory() is optional and disabled by default, particularly for all trajectory generators. The only source that was using collision checking was blend().

Can you please verify that the behavior is as intended now?

@riv-mjohnson
Copy link
Contributor

Yes, it looks good now; collisions along the path are reported as they were before this PR.

Thanks again.

@rhaschke rhaschke merged commit c404ca2 into moveit:master Nov 9, 2023
@rhaschke
Copy link
Contributor

rhaschke commented Nov 9, 2023

Thanks for iterating on this.

@rhaschke
Copy link
Contributor

rhaschke commented Nov 10, 2023

While this PR seems to fix ignored modified start states in unrelated JMGs, the original issue is not yet solved, is it?

When planning a LIN/CIRC motion using a subframe as the link_name in constraints, the motion is linear w.r.t. the end-effector link, rather than the subframe.

I think this can only be resolved by explicitly interpolating between the actual subframe's start and goal pose (i.e. not first back-projecting to the associated robot link) and considering the offset when computing the IK instead. I implemented this for the CartesianInterpolator in #3197. #2890, authored by you guys, should be reverted to a large extent in order to maintain the original goal pose.

Unfortunately, the unit tests don't cover those cases yet - they only consider the final goal pose, but don't check the path in between.

@riv-tnoble
Copy link
Contributor Author

riv-tnoble commented Nov 13, 2023

While this PR seems to fix ignored modified start states in unrelated JMGs, the original issue is not yet solved, is it?

When planning a LIN/CIRC motion using a subframe as the link_name in constraints, the motion is linear w.r.t. the end-effector link, rather than the subframe.

I think this can only be resolved by explicitly interpolating between the actual subframe's start and goal pose (i.e. not first back-projecting to the associated robot link) and considering the offset when computing the IK instead. I implemented this for the CartesianInterpolator in #3197. #2890, authored by you guys, should be reverted to a large extent in order to maintain the original goal pose.

Unfortunately, the unit tests don't cover those cases yet - they only consider the final goal pose, but don't check the path in between.

Hi, and thanks for the feedback and help with this PR.

I think the code does work as intended as passing in the subframe as the link_name and removing the default_planner_request_adapters/ResolveConstraintFrames adapter causes the target point offset to be [0. 0. 0]. The start and goal points are for the subframe, and it's those points we use KDL to build the LIN motion for. It's only when we're sampling points along that path and performing IK that the points are back projected to a robot link (within the robot_state->setFromIK() call I believe).

I do wonder if this makes the point of target_offset_point a little redundant, so I don't mind those changes being reverted. Maybe still useful if you did want to plan using an offset from the subframe itself?

Let me know if you agree with the above

@rhaschke
Copy link
Contributor

Sure, if you remove the ResolveConstraintFrames adapter, the target_point_offset becomes zero. However, I was complaining about the case when this offset is non-zero. See #3523 for the fix. Now it works with both ResolveConstraintFrames enabled or disabled.

riv-tnoble added a commit to riv-tnoble/moveit2 that referenced this pull request Nov 2, 2024
github-merge-queue bot pushed a commit to moveit/moveit2 that referenced this pull request Nov 5, 2024
* Ports moveit/moveit/pull/3519 to ros2

* Applies formatting

* Fixes compilation errors

* Fixes compilation errors

* Fixes compilation errors

* Fixes compilation errors

* Replaces RobotState with pointer in tests

* Fixes tests
mergify bot pushed a commit to moveit/moveit2 that referenced this pull request Nov 5, 2024
* Ports moveit/moveit/pull/3519 to ros2

* Applies formatting

* Fixes compilation errors

* Fixes compilation errors

* Fixes compilation errors

* Fixes compilation errors

* Replaces RobotState with pointer in tests

* Fixes tests

(cherry picked from commit 70e1aae)

# Conflicts:
#	moveit_planners/pilz_industrial_motion_planner/include/pilz_industrial_motion_planner/planning_context_base.h
#	moveit_planners/pilz_industrial_motion_planner/src/trajectory_functions.cpp
#	moveit_planners/pilz_industrial_motion_planner/src/trajectory_generator.cpp
#	moveit_planners/pilz_industrial_motion_planner/test/unit_tests/src/unittest_trajectory_generator_circ.cpp
#	moveit_planners/pilz_industrial_motion_planner/test/unit_tests/src/unittest_trajectory_generator_lin.cpp
mergify bot pushed a commit to moveit/moveit2 that referenced this pull request Nov 5, 2024
* Ports moveit/moveit/pull/3519 to ros2

* Applies formatting

* Fixes compilation errors

* Fixes compilation errors

* Fixes compilation errors

* Fixes compilation errors

* Replaces RobotState with pointer in tests

* Fixes tests

(cherry picked from commit 70e1aae)

# Conflicts:
#	moveit_planners/pilz_industrial_motion_planner/include/pilz_industrial_motion_planner/planning_context_base.h
#	moveit_planners/pilz_industrial_motion_planner/src/trajectory_functions.cpp
#	moveit_planners/pilz_industrial_motion_planner/src/trajectory_generator.cpp
#	moveit_planners/pilz_industrial_motion_planner/test/unit_tests/src/unittest_trajectory_generator_circ.cpp
#	moveit_planners/pilz_industrial_motion_planner/test/unit_tests/src/unittest_trajectory_generator_lin.cpp
sjahr pushed a commit to moveit/moveit2 that referenced this pull request Nov 8, 2024
Co-authored-by: Tom Noble <tom@rivelinrobotics.com>
Markus-Simonsen pushed a commit to Markus-Simonsen/moveit2 that referenced this pull request Aug 12, 2025
* Ports moveit/moveit/pull/3519 to ros2

* Applies formatting

* Fixes compilation errors

* Fixes compilation errors

* Fixes compilation errors

* Fixes compilation errors

* Replaces RobotState with pointer in tests

* Fixes tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaits 2nd review one maintainer approved this request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pilz LIN motions with subframes are not linear in the motion the subframe takes

3 participants