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

adding absolute jump threshold capability in parallel #821

Closed
wants to merge 11 commits into from
Closed

adding absolute jump threshold capability in parallel #821

wants to merge 11 commits into from

Conversation

mlautman
Copy link
Contributor

@mlautman mlautman commented Apr 5, 2018

Description

The git history in my pr #798 got very messy with multiple upstream merges followed by local commits. I cleaned up the git history so I could re-open the PR. I'm not sure if that was the best way and am open to feedback on better ways to cleanup my PR's in the future.

I took a stab at implementing a absolute jump threshold for the computeCartesianPath functionality. (#773) This implementation required separate jump thresholds for prismatic and revolute joints and does not support planar, floating, fixed or multi-DOF joints. This implementation does not affect or replace the old computeCartesianPath method which is used by the move_group computeCartesianPath capability since that would involve changing messages and would have a much larger scope.

The goal of this PR is to address the issue where using the existing jump_threshold method, paths with few waypoints can have huge jumps in joint space. To demonstrate this problem, I generated hundreds of thousands of trajectories with varying goals, eef_steps and jump_thresholds, filtered out anything with less than 100% completion, sorted them by the number of interpolated waypoints in the returned trajectory, and then searched through for the largest absolute jump.

image

eef_steps = [0.01, 0.05, 0.1]
jump_thresholds = [1.1, 1.5, 1.75, 2.0]

After testing this new implementation and comparing it to the existing computeCartesianPath, I was able to filter out jumps from 100,000 trajectories with minimal parameter tuning. For what it's worth, IMHO it is a lot easier to tune the absolute jump thresholds than the jump threshold factor.

Checklist

  • Required: Code is auto formatted using clang-format
  • Extended the tutorials / documentation, if necessary reference
  • Include a screenshot if changing a GUI
  • Optional: Created tests, which fail without this PR reference
  • Optional: Decide if this should be cherry-picked to other current ROS branches (Indigo, Jade, Kinetic)

for (std::size_t i = 0; i < dist_vector.size(); ++i)
if (dist_vector[i] > thres)
{
CONSOLE_BRIDGE_logDebug("Truncating Cartesian path due to detected jump in joint-space distance");
percentage = (double)i / (double)dist_vector.size();
traj.resize(i);
percentage = (double)(i + 1) / (double)(traj.size());
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like you identified another bug. Well done!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks

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 had various style improvements and thus pushed them directly. In general I approve this.
However, I suggest to not throw an exception from within testJointSpaceJump(). What do you think, @mlautman and @davetcoleman?

else if (jt.prismatic_jump_threshold > 0.0 || jt.revolute_jump_threshold > 0.0)
CONSOLE_BRIDGE_logWarn("Attempting to test for absolute joint-space jumps, but zero threshold was provided");

return 1.0;
}

double RobotState::testJointSpaceJump(const JointModelGroup* group, std::vector<RobotStatePtr>& traj,
Copy link
Contributor

Choose a reason for hiding this comment

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

What about renaming these worker functions to testJointSpaceJumpRelative and testJointSpaceJumpAbsolute?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think that's a good idea

EXPECT_EQ(traj.size(), 3); // traj should be cut
EXPECT_NEAR(fraction, 3. / 4., 0.01);
}

Copy link
Contributor

Choose a reason for hiding this comment

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

What about adding tests that ensure that testJointSpaceJump correctly relays to testJointSpaceJumpRelative and testJointSpaceJumpAbsolute?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll add that

traj.insert(traj.end(), start, waypoint_traj.end());
if (!joint->getType() == JointModel::PRISMATIC && !joint->getType() == JointModel::REVOLUTE)
{
CONSOLE_BRIDGE_logError("Unsupported joint type %zu in JointModelGroup %s testJointSpaceJump can only support "
Copy link
Contributor

Choose a reason for hiding this comment

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

Throwing an exception here, seems rather hard. I think it should be perfectly valid to test a trajectory involving multi-dof joints and just ignore them. Probably there should be a warning once to emphasize the fact that we ignore them.

Copy link
Member

Choose a reason for hiding this comment

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

I think Cartesian planning with multi-dof joints is an unusual circumstance that has not yet been addressed in moveit and probably should be in the future. However allowing it to return a solution that is wrong is very misleading and perhaps even potentially dangerous. I don't see why this is perfectly valid. Please give an example of why you would allow a multi-dof joint to not be checked for jumps.

Copy link
Contributor

Choose a reason for hiding this comment

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

The joint-space jump test is only supported for revolute or prismatic joints. If the trajectory involves multi-dof joints, why shouldn't it be possible to test all the remaining, normal joints?
I'm not proposing to silently ignore multi-dof joints.

Copy link
Member

Choose a reason for hiding this comment

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

My point is that multi-dof joints should also be checked for jumps for potential safety reasons. Until we support this, we should disallow someone to be mislead into thinking it has been checked by throwing an error.

Another option would be to just implement this now, but I'm not convinced this use-case is very common or necessary and it would delay getting "MVP" features out.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think that testing for joint-space jumps for Cartesian-pose-based joints is necessary at all. If we do Cartesian interpolation for those poses with a given max_step, they will have the given max_step distance anyway...

const GroupStateValidityCallbackFn& validCallback = GroupStateValidityCallbackFn(),
const kinematics::KinematicsQueryOptions& options = kinematics::KinematicsQueryOptions())
{
return computeCartesianPath(group, traj, link, direction, global_reference_frame, distance, max_step,
Copy link
Contributor

Choose a reason for hiding this comment

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

I moved the implementation of these functions into the header, as this clearly states that they simply relay to the actual worker function. Having nice constructors for the the JumpThreshold struct facilitates readability.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is great

@@ -2007,6 +2007,13 @@ double RobotState::testJointSpaceJump(const JointModelGroup* group, std::vector<
double RobotState::testJointSpaceJump(const JointModelGroup* group, std::vector<RobotStatePtr>& traj,
double jump_threshold_factor)
{
if (traj.size() < MIN_STEPS_FOR_JUMP_THRESH)
{
CONSOLE_BRIDGE_logWarn("The computed trajectory is too short to detect jumps in joint-space "
Copy link
Contributor

Choose a reason for hiding this comment

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

This warning should stay here, shouldn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think so

if (jump_threshold.prismatic_jump_threshold > 0.0 || jump_threshold.revolute_jump_threshold > 0.0)
percentage *= testJointSpaceJump(group, traj, jump_threshold.revolute_jump_threshold,
jump_threshold.prismatic_jump_threshold);
else if (jt.prismatic_jump_threshold > 0.0 && jt.revolute_jump_threshold > 0.0)
Copy link
Contributor

Choose a reason for hiding this comment

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

Switched to else if here. For me, it doesn't make sense to test for both, relative and absolute jumps.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I generally agree


return percentage;
else if (jt.prismatic_jump_threshold > 0.0 || jt.revolute_jump_threshold > 0.0)
Copy link
Contributor

Choose a reason for hiding this comment

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

For me it doesn't make sense to test only revolute or only prismatic joints. Thus, both parameters need to be positive.

Copy link
Contributor Author

@mlautman mlautman Apr 5, 2018

Choose a reason for hiding this comment

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

I'm not sure if I agree with this. If I have a robot with only revolute joints, I don't want to have to pass in a made up prismatic_jump_threshold and vice versa

Copy link
Contributor

Choose a reason for hiding this comment

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

I think one should provide reasonable thresholds for both cases. If you are very sure that you have no joints of either type, you can still pass any value > 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I still don't like the idea of asking the user to make up prismatic joint cutoff values for their fully revolute robot. I just pushed a commit that deals with this issue in a way that I think is clean.

I also think that else if should be if to cover the use case when someone wants to be super safe and run both checks

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 like the idea of asking the user to make up prismatic joint cutoff values for their fully revolute robot.

I guess we keep to have different opinions on that. What about the following: The thresholds are kept zero by default. If there is a joint of "wrong" type in the trajectory (although not expected), the zero threshold will reject the trajectory if this joint moves at all.
Then the absolute testing can be triggered if (jt.prismatic > 0 || jt.revolute > 0)

I just pushed a commit that deals with this issue in a way that I think is clean.

In my point of view, your last commit 8566096 is only bloating the code without any benefit. Please revert this.

I also think that else if should be if to cover the use case when someone wants to be super safe and run both checks.

If you like. Maybe that's the expected behaviour when all params are set. However, you should update the comments to match this behaviour. I actually adapted the behaviour to the behaviour originally promised in the comments...

else if (jt.prismatic_jump_threshold > 0.0 || jt.revolute_jump_threshold > 0.0)
CONSOLE_BRIDGE_logWarn("Attempting to test for absolute joint-space jumps, but zero threshold was provided");

return 1.0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably, there should be a warning about illegal ( < 0) parameters here.

unsigned int steps = floor(distance / max_step) + 1;
if (test_joint_space_jump && steps < MIN_STEPS_FOR_JUMP_THRESH)
if (jump_threshold.jump_threshold_factor > 0 && steps < MIN_STEPS_FOR_JUMP_THRESH)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch!

if (jump_threshold.prismatic_jump_threshold > 0.0 || jump_threshold.revolute_jump_threshold > 0.0)
percentage *= testJointSpaceJump(group, traj, jump_threshold.revolute_jump_threshold,
jump_threshold.prismatic_jump_threshold);
else if (jt.prismatic_jump_threshold > 0.0 && jt.revolute_jump_threshold > 0.0)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I generally agree


return percentage;
else if (jt.prismatic_jump_threshold > 0.0 || jt.revolute_jump_threshold > 0.0)
Copy link
Contributor Author

@mlautman mlautman Apr 5, 2018

Choose a reason for hiding this comment

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

I'm not sure if I agree with this. If I have a robot with only revolute joints, I don't want to have to pass in a made up prismatic_jump_threshold and vice versa

else if (jt.prismatic_jump_threshold > 0.0 || jt.revolute_jump_threshold > 0.0)
CONSOLE_BRIDGE_logWarn("Attempting to test for absolute joint-space jumps, but zero threshold was provided");

return 1.0;
}

double RobotState::testJointSpaceJump(const JointModelGroup* group, std::vector<RobotStatePtr>& traj,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think that's a good idea

{
}

explicit JumpThreshold(double factor) : JumpThreshold()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

love it

@@ -1087,9 +1102,13 @@ as the new values that correspond to the group */

double computeCartesianPath(const JointModelGroup* group, std::vector<RobotStatePtr>& traj, const LinkModel* link,
const Eigen::Vector3d& direction, bool global_reference_frame, double distance,
double max_step, double jump_threshold,
double max_step, double jump_threshold_factor,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yessss :)

const GroupStateValidityCallbackFn& validCallback = GroupStateValidityCallbackFn(),
const kinematics::KinematicsQueryOptions& options = kinematics::KinematicsQueryOptions())
{
return computeCartesianPath(group, traj, link, direction, global_reference_frame, distance, max_step,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is great

@@ -2007,6 +2007,13 @@ double RobotState::testJointSpaceJump(const JointModelGroup* group, std::vector<
double RobotState::testJointSpaceJump(const JointModelGroup* group, std::vector<RobotStatePtr>& traj,
double jump_threshold_factor)
{
if (traj.size() < MIN_STEPS_FOR_JUMP_THRESH)
{
CONSOLE_BRIDGE_logWarn("The computed trajectory is too short to detect jumps in joint-space "
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think so

EXPECT_EQ(traj.size(), 3); // traj should be cut
EXPECT_NEAR(fraction, 3. / 4., 0.01);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll add that

@v4hn
Copy link
Contributor

v4hn commented Apr 5, 2018 via email

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 like the source code changes.
However, I'm puzzled about the test changes.

@@ -70,23 +70,23 @@ typedef boost::function<bool(RobotState* robot_state, const JointModelGroup* joi
thresholds for detecting joint space jumps. */
struct JumpThreshold
Copy link
Contributor

Choose a reason for hiding this comment

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

+1 to the name cleanup

"prismatic and revolute joints.",
joint->getType(), group->getName().c_str());
throw Exception("Unsupported joint type");
CONSOLE_BRIDGE_logWarn("Unsupported joint type %s in JointModelGroup %s testAbsoluteJointSpaceJump can only "
Copy link
Contributor

Choose a reason for hiding this comment

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

Please split the message into two separate sentences. Maybe even separated by a newline char \n.

}

double distance = traj[traj_ix]->distance(*traj[traj_ix + 1], joint);
if ((joint->getType() == JointModel::PRISMATIC && distance > prismatic_jump_threshold) ||
(joint->getType() == JointModel::REVOLUTE && distance > revolute_jump_threshold))
{
CONSOLE_BRIDGE_logDebug("Truncating Cartesian path due to detected jump of %.4f > %.4f in joint-space distance",
CONSOLE_BRIDGE_logError("Truncating Cartesian path due to detected jump of %.4f > %.4f in joint-space distance",
Copy link
Contributor

Choose a reason for hiding this comment

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

Also name the joint:
"Cartesian path due to jump of %.4f > %.4f in joint %s"

void generateTestTraj(std::vector<std::shared_ptr<robot_state::RobotState>>& traj,
const moveit::core::RobotModelConstPtr& robot_model,
const robot_model::JointModelGroup* joint_model_group)
std::size_t generateTestTraj(std::vector<std::shared_ptr<robot_state::RobotState>>& traj,
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand why you made these tests so much more complicated. This doesn't add any functionality.
Please revert this part or explain.

Copy link
Contributor Author

@mlautman mlautman Apr 6, 2018

Choose a reason for hiding this comment

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

@rhaschke I did a few things here. I changed the tests to use the testJointSpaceJump with the struct parameter rather than calling the absolute and relative tests directly. To make it clear which test is being used by testJointSpaceJump under the hood I changed the test trajectory so that the absolute jump threshold and relative jump threshold trip at different waypoints. I also now use the PR2's default values as a base instead of a vector of all zeros since the zero vector is out of the joint limits for the PR2. This isn't that important for the absolute and relative jump tests but it becomes important for the cartesian tests that I am going to open a PR for after this get merged.

Copy link
Contributor

Choose a reason for hiding this comment

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

I noticed these changes. But I think they are not necessary to perform the testing. Particularly adding many more waypoints make the whole test less comprehensible (for a short glance). Having exactly a single jump is much clearer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I reverted the test to 4 waypoints with a single obvious jump at the end

@rhaschke
Copy link
Contributor

rhaschke commented Apr 7, 2018

@mlautman, as I still didn't like some of your recent changes, I assembled an alternative proposal.
Please have a look at this using the following git commands:

git fetch https://github.com/ubi-agni/moveit.git jump-threshold
git checkout -b rhaschke-abs-jt FETCH_HEAD

This should fetch my changes and checkout locally as branch rhaschke-abs-jt.
If you like them, you can reset your branch to the alternative and push as follows:

git checkout kinetic-abs-jt
git reset --hard rhaschke-abs-jt
git push --force <your picknick origin>

@rhaschke
Copy link
Contributor

Closing this in favour of #843.

@rhaschke rhaschke closed this Apr 16, 2018
@mlautman
Copy link
Contributor Author

@rhaschke why did you close this? I don't think we had resolved whether the changes you wanted to make are an improvement

@mlautman
Copy link
Contributor Author

I finally had a chance to look through your changes a minute ago. I like the changes to robot_state but I think the tests are better in this one.

@rhaschke
Copy link
Contributor

Why did you close this?

@mlautman, as I didn't get any feedback from you for a while, I thought to close this in favour of #843, which is a PR comprising my changes applied to your original branch. This way, I was hoping to facilitate reviewing my alternative branch. As we shouldn't have two similar PRs open at a time, I closed this one.

@mlautman
Copy link
Contributor Author

@rhaschke In the future, do not do that again. You could have ping'd me, you could have emailed me but instead you decided to go ahead and overwrite my changes with yours which I found to be pretty disrespectful

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

4 participants