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

Add wrapper for OMPL path simplifier #164

Merged
merged 76 commits into from
Apr 26, 2017
Merged

Add wrapper for OMPL path simplifier #164

merged 76 commits into from
Apr 26, 2017

Conversation

aditya-vk
Copy link
Contributor

This introduces a way to simplify and shorten paths obtained from the planners used in AIKIDO using OMPL methods.

AIKIDO Planner -> untimed trajectory -> simplifyOMPL -> simplified and shortened untimed trajectory.

-> The feature has been currently introduced same place as the planning methods although really a post-processing step. Is there a better place for this than here, currently?

@aditya-vk aditya-vk requested a review from Shushman April 11, 2017 16:16
@coveralls
Copy link

Coverage Status

Coverage remained the same at 85.208% when pulling f82bfed on feature/simplifier into a59975f on master.

@jslee02 jslee02 added this to the Aikido 0.1.0 milestone Apr 12, 2017
Copy link
Member

@mkoval mkoval left a comment

Choose a reason for hiding this comment

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

This is a good start. 👍 I left specific comments on the code. In addition to those, this is missing unit tests. The planOMPL unit tests should be a good reference.

/// \param _dmetric A valid distance metric defined on the StateSpace
/// \param _sampler A Sampleable that can sample states from the
/// StateSpace. Warning: Many OMPL planners internally assume this sampler
/// samples uniformly. Care should be taken when using a non-uniform sampler.
Copy link
Member

Choose a reason for hiding this comment

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

Minor Nit: I don't know if this is actually true. As far as I know, most randomized algorithms require that the distribution has non-zero probability of sampling all configurations (i.e. have support over all of configuration space). There is nothing special about the uniform distribution. In fact, what it means to be "uniform" depends on your choice of measure.

@siddhss5 Is what I said above true? 😅

Choose a reason for hiding this comment

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

Yes! The only requirement is that the sampler densely sample configuration space.

/// \param _boundsProjector A Projectable that projects a state back within
/// valid bounds defined on the StateSpace
/// \param _maxPlanTime The maximum time to allow the planner to search for a
/// solution
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Specify units (i.e. seconds).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed this parameter actually. Forgot to take it out of the comment as well. But will add a similar parameter for the timeout as suggested in your other comments.

/// \param _maxPlanTime The maximum time to allow the planner to search for a
/// solution
/// \param _maxDistanceBtwValidityChecks The maximum distance (under dmetric) between
/// validity checking two successive points on a tree extension
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Is "tree extension" meaningful for a shortcutting algorithm?

constraint::TestablePtr _boundsConstraint,
constraint::ProjectablePtr _boundsProjector,
double _maxDistanceBtwValidityChecks,
trajectory::InterpolatedPtr _originalTraj);
Copy link
Member

Choose a reason for hiding this comment

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

I'd prefer to return an std::unique_ptr<Interpolated> rather than a shared_ptr. It's easy to convert from a unique_ptr to a shared_ptr, but the converse is impossible.


// Step 3: Use the OMPL methods to simplify the path
::ompl::geometric::PathSimplifierPtr simplifier;
simplifier.reset(new ::ompl::geometric::PathSimplifier(si));
Copy link
Member

Choose a reason for hiding this comment

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

It is supfluous to use a shared_ptr here. Just declare a local variable with type PathSimplifier.

// Step 3: Use the OMPL methods to simplify the path
::ompl::geometric::PathSimplifierPtr simplifier;
simplifier.reset(new ::ompl::geometric::PathSimplifier(si));
bool const shortened = simplifier->shortcutPath(*path, 1, 1, 1.0, 0.005);
Copy link
Member

Choose a reason for hiding this comment

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

Make maxSteps, maxEmptySteps, rangeRatio, and snapToVertex parameters of this function.

Also, note that the current hard-coded settings are (in my opinion) nonsensical. Setting maxSteps = 1 means that it will only try to shortcut two randomly selected points before returning. The default values in OMPL are a more reasonable choice, possibly with the modification that rangeRatio = 1.0 and snapToVertex = 0.0.

Another option is to leave maxSteps = 1, but call shortcutPath in a loop until a time limit (or other termination condition) expires. This is the approach I used for OMPLSimplifier in or_ompl.

}
else
{
return _originalTraj;
Copy link
Member

Choose a reason for hiding this comment

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

Returning _originalPath here is dangerous because it aliases the input argument. This could very easily cause a bug because the user expects _originalTraj to be unmodified.

This is a bit of a moot point because:

  1. It's not possible to return _originalTraj once we change the return value to an unique_ptr.
  2. It's safe to return returnTraj even if shortcutPath returned false.

auto ompl_state = sspace->allocState(_originalTraj->getWaypoint(idx));
path->append(ompl_state);
sspace->freeState(ompl_state);
}
Copy link
Member

Choose a reason for hiding this comment

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

This code is duplicated from planOMPL. Please move it to a helper function.

path->getState(idx));
// Arbitrary timing
returnTraj->addWaypoint(idx, st->mState);
}
Copy link
Member

@mkoval mkoval Apr 13, 2017

Choose a reason for hiding this comment

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

This code should also be moved to a helper function. See my comment above about what it could be named and where it could live.

Copy link
Member

Choose a reason for hiding this comment

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

This comment has been addressed.

@aditya-vk
Copy link
Contributor Author

Note: Only the termination condition addressed. Other changes shall be addressed in the next commit!

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.8%) to 84.435% when pulling 779d179 on feature/simplifier into a59975f on master.

@coveralls
Copy link

Coverage Status

Coverage remained the same at 80.968% when pulling 46a5120 on feature/simplifier into 1accfe6 on master.

Copy link
Member

@mkoval mkoval left a comment

Choose a reason for hiding this comment

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

This generally looks great. The only requests I have are:

  1. Refactor the the code that converts between ompl::PathGeometric and aikido::path::Interpolated into helper functions.
  2. Write unit tests for simplifyOMPL.
  3. Possibly, if you're feeling generous, write tests for (1).

auto ompl_state = sspace->allocState(_originalTraj->getWaypoint(idx));
path->append(ompl_state);
sspace->freeState(ompl_state);
}
Copy link
Member

Choose a reason for hiding this comment

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

This code should also be moved to a helper function.

std::unique_ptr<trajectory::Interpolated> aikido2ompl(::ompl::geometric::PathGeometric *path,
statespace::StateSpacePtr _stateSpace,
statespace::InterpolatorPtr _interpolator);
} // namespace
Copy link
Member

Choose a reason for hiding this comment

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

  • Anonymous namespaces should almost never be used in header files. This is a fine function that we could promote to a new planning/ompl/Trajectory.hpp file.
  • Rename this function to match our other conversion functions, e.g. added in Function to convert trajectory_msgs::JointTrajectory into aikido::trajectory::Spline #147. I suggesttoInterpolatedTrajectory for this function and toOmplPathGeometric for the inverse. @jslee02 Objections?
  • Take the PathGeometric argument by const & instead of non-const pointer, unless there is a strong reason to do otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops. Did not realize I left this here. Was just playing around with the helper function. Didn't want to commit that yet. Shall make the changes suggested :)

Copy link
Contributor Author

@aditya-vk aditya-vk Apr 16, 2017

Choose a reason for hiding this comment

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

  1. I left the helper functions within the file for now. Do you suggest I move them to a new planner/ompl/Trajectory.hpp file?
  2. When I use a constant pointer and try to static_cast it during the state conversion, it throws an error static_cast...casts away qualifiers. I wasn't sure how to fix it. I'll take another look.

// Set the parameters for termination of simplification process
::ompl::time::point const time_before = ::ompl::time::now();
::ompl::time::point time_current;
::ompl::time::duration const time_limit = ::ompl::time::seconds(_timeout);
Copy link
Member

Choose a reason for hiding this comment

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

Nit: I would slightly prefer to use std::chrono here, instead of the ompl::time wrapper for it.

return returnTraj;
std::pair <std::unique_ptr<trajectory::Interpolated>, bool> returnPair;
returnPair = std::make_pair(std::move(returnTraj),shorten_success);
return returnPair;
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Why not just return std::make_pair(std::move(returnTraj), shorten_success)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Haha yes 😅

si->getStateSpace());

// Step 2: Convert the AIKIDO trajectory to Geometric Path
::ompl::geometric::PathGeometric *path = new ::ompl::geometric::PathGeometric(si);
Copy link
Member

Choose a reason for hiding this comment

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

This leaks memory. I don't think path needs to be heap allocated, so you could replace this with just:

::ompl::geometric::PathGeometric path { si };

//=============================================================================
} // ns aikido
} // ns planner
} // ns ompl
Copy link
Member

Choose a reason for hiding this comment

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

Add a unit tests for simplifyOMPL. The obvious tests are: (1) each error condition, (2) shortcut a two-waypoint trajectory for a large number of iterations and verify that it doesn't change, and (3) shortcut a three waypoint trajectory for a large number of iterations and verify that the trajectory gets shorter.

It would also be nice to add tests for the GeometricPath conversion functions, but I feel too guilty to ask about that since I am the one who asked you to implement them. 😬

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh that's fine! I shall work on 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.

Added tests for trajectory conversion functions. Converted an interpolated trajectory back and forth and ensured it had not changed. That should suffice, yes?

@aditya-vk
Copy link
Contributor Author

ToDo on this PR:

  1. Add unit tests for simplifyOMPL
  2. Move repeating blocks of code to helper functions
  3. Add tests for helper functions

@aditya-vk
Copy link
Contributor Author

ToDo on this PR:

  1. Unit Tests for simplifyOMPL
  2. Tests for helper functions toInterpolatedTrajectory and toOMPLTrajectory

@aditya-vk
Copy link
Contributor Author

For the unit tests to be verified, this needs a fix at #178
@jslee02 is looking at the issue

@jslee02
Copy link
Member

jslee02 commented Apr 24, 2017

Still missing a check that _maxEmptySteps >= 0.

@aditya-vk added it, but I asked to remove it because it's always positive as the type is size_t. Is there any possibility size_t can be negative?

@mkoval
Copy link
Member

mkoval commented Apr 24, 2017

@jslee02 good point, I missed that until @aditya-vk pointed it out.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.2%) to 79.093% when pulling 039e97d on feature/simplifier into dffdf28 on master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.2%) to 79.116% when pulling 039e97d on feature/simplifier into dffdf28 on master.

@aditya-vk
Copy link
Contributor Author

  1. Removed templating.
  2. Replaced static cast with dynamic cast and checked for nullptr

@coveralls
Copy link

Coverage Status

Coverage increased (+0.2%) to 79.103% when pulling 0e84e01 on feature/simplifier into dffdf28 on master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.2%) to 79.079% when pulling d66fe87 on feature/simplifier into dffdf28 on master.

@mkoval mkoval changed the title Feature/simplifier Add wrapper for OMPL path simplifier Apr 26, 2017
Copy link
Member

@mkoval mkoval left a comment

Choose a reason for hiding this comment

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

A few minor bugs in the test. Otherwise, looks great! 💯

auto s0 = stateSpace->createState();
for(size_t idx = 0; idx < traj->getNumWaypoints(); ++idx)
{
traj->evaluate(idx, s0);
Copy link
Member

Choose a reason for hiding this comment

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

This should be getWaypoint, not evaluate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

evaluate works only if unit timing, got it! Thanks!


// Match every point
auto s0 = stateSpace->createState();
for(size_t idx = 0; idx < interpolatedTraj->getDuration(); ++idx)
Copy link
Member

Choose a reason for hiding this comment

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

This should be getNumWaypoints, not getDuration.

auto s0 = stateSpace->createState();
for(size_t idx = 0; idx < interpolatedTraj->getDuration(); ++idx)
{
interpolatedTraj->evaluate(idx, s0);
Copy link
Member

Choose a reason for hiding this comment

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

This should be getWaypoint, not evaluate.

auto interpolatedTraj = aikido::planner::ompl::toInterpolatedTrajectory(
omplTraj, interpolator);

// Match every point
Copy link
Member

Choose a reason for hiding this comment

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

Verify that the two trajectories have the same number of waypoints with an ASSERT_EQUAL. Otherwise, the test may SEGFAULT in the loop below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As stated above.

auto omplTraj = aikido::planner::ompl::toOMPLTrajectory(
traj, si);

// Match every point
Copy link
Member

Choose a reason for hiding this comment

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

Verify that the two trajectories have the same number of waypoints with an ASSERT_EQUAL. Otherwise, the test may SEGFAULT in the loop below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The first test was checking that both trajectories have same number of waypoints. If they don't have equal waypoints, the tests would fail there and never come here. So this should be okay, yes?

Copy link
Member

Choose a reason for hiding this comment

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

Nope:

  1. All tests are independent. GTest will continue to run tests even after one tests fail. This is useful because it often uncovers multiple bugs at once in one Travis run.
  2. Tests aren't guaranteed to be ordered in any particular way. Or even that all of them are run - you can run a subset of tests by passing commandline arguments to GTest.
  3. The EXPECT_ macros intentionally allow testcase to continue and mark it as failed once it is done running. Only ASSERT_ macros cause the test to immediately exit.

In general, all tests should be independent and a test should never crash, only gracefully report failure. If a check is critical to avoid a SEGFAULT (e.g. a length check like this), you should use ASSERT_ instead of EXPECT_.

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 understand. Thanks! Will make the change right away!

@aditya-vk
Copy link
Contributor Author

All suggestions addressed. 😄

@coveralls
Copy link

Coverage Status

Coverage increased (+0.4%) to 79.29% when pulling d69aec4 on feature/simplifier into dffdf28 on master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.4%) to 79.266% when pulling d69aec4 on feature/simplifier into dffdf28 on master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.1%) to 79.266% when pulling 7fe0a65 on feature/simplifier into 1fac8bb on master.

Copy link
Member

@mkoval mkoval left a comment

Choose a reason for hiding this comment

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

Great work! Thanks for making those changes @aditya-vk.

@mkoval mkoval merged commit acd983c into master Apr 26, 2017
@mkoval mkoval deleted the feature/simplifier branch April 26, 2017 13:11
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.

6 participants