Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,14 @@ class OMPLRealVectorMoveProfile : public OMPLMoveProfile
static void applyGoalStates(ompl::geometric::SimpleSetup& simple_setup,
const tesseract::kinematics::KinGroupIKInput& ik_input,
const tesseract::kinematics::KinematicGroup& manip,
tesseract::collision::DiscreteContactManager& contact_checker);
tesseract::collision::DiscreteContactManager& contact_checker,
const Eigen::VectorXd& seed = Eigen::VectorXd());

static void applyStartStates(ompl::geometric::SimpleSetup& simple_setup,
const tesseract::kinematics::KinGroupIKInput& ik_input,
const tesseract::kinematics::KinematicGroup& manip,
tesseract::collision::DiscreteContactManager& contact_checker);
tesseract::collision::DiscreteContactManager& contact_checker,
const Eigen::VectorXd& seed = Eigen::VectorXd());

static void applyGoalStates(ompl::geometric::SimpleSetup& simple_setup,
const Eigen::VectorXd& joint_waypoint,
Expand Down
87 changes: 63 additions & 24 deletions motion_planners/ompl/src/profile/ompl_real_vector_move_profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ TESSERACT_COMMON_IGNORE_WARNINGS_POP
#include <tesseract/environment/environment.h>

#include <tesseract/common/profile_plugin_factory.h>
#include <tesseract/common/joint_state.h>

namespace tesseract::motion_planners
{
Expand Down Expand Up @@ -193,7 +194,8 @@ std::unique_ptr<ompl::geometric::SimpleSetup> OMPLRealVectorMoveProfile::createS

contact_checker->setActiveCollisionObjects(kin_group->getActiveLinkNames());
tesseract::kinematics::KinGroupIKInput ik_input(tcp_frame_cwp, start_mi.working_frame, start_mi.tcp_frame);
applyStartStates(*simple_setup, ik_input, *kin_group, *contact_checker);
const Eigen::VectorXd seed = cur_wp.hasSeed() ? cur_wp.getSeed().position : Eigen::VectorXd();
applyStartStates(*simple_setup, ik_input, *kin_group, *contact_checker, seed);
}
else
{
Expand Down Expand Up @@ -222,7 +224,8 @@ std::unique_ptr<ompl::geometric::SimpleSetup> OMPLRealVectorMoveProfile::createS

contact_checker->setActiveCollisionObjects(kin_group->getActiveLinkNames());
tesseract::kinematics::KinGroupIKInput ik_input(tcp_frame_cwp, end_mi.working_frame, end_mi.tcp_frame);
applyGoalStates(*simple_setup, ik_input, *kin_group, *contact_checker);
const Eigen::VectorXd seed = cur_wp.hasSeed() ? cur_wp.getSeed().position : Eigen::VectorXd();
applyGoalStates(*simple_setup, ik_input, *kin_group, *contact_checker, seed);
}
else
{
Expand All @@ -235,21 +238,38 @@ std::unique_ptr<ompl::geometric::SimpleSetup> OMPLRealVectorMoveProfile::createS
void OMPLRealVectorMoveProfile::applyGoalStates(ompl::geometric::SimpleSetup& simple_setup,
const tesseract::kinematics::KinGroupIKInput& ik_input,
const tesseract::kinematics::KinematicGroup& manip,
tesseract::collision::DiscreteContactManager& contact_checker)
tesseract::collision::DiscreteContactManager& contact_checker,
const Eigen::VectorXd& seed)
{
/** @todo Need to add Descartes pose sample to ompl profile */
const auto dof = manip.numJoints();
tesseract::common::KinematicLimits limits = manip.getLimits();

/** @brief Making this thread_local does not help because it is not called enough during planning */
tesseract::kinematics::IKSolutions joint_solutions;
manip.calcInvKin(joint_solutions, { ik_input }, Eigen::VectorXd::Zero(dof));
auto goal_states = std::make_shared<ompl::base::GoalStates>(simple_setup.getSpaceInformation());
std::vector<tesseract::collision::ContactResultMap> contact_map_vec(static_cast<std::size_t>(joint_solutions.size()));

for (std::size_t i = 0; i < joint_solutions.size(); ++i)
{
Eigen::VectorXd& solution = joint_solutions[i];
std::vector<tesseract::collision::ContactResultMap> contact_map_vec;

// Use the seed attached to the Cartesian waypoint when one is provided, otherwise fall back to the
// zero seed. For seed-dependent solvers that return a single solution (e.g. KDL-LMA), a hardcoded
// zero seed can converge to an out-of-limits branch of the solution manifold; the limit filter then
// discards it and an otherwise-reachable goal ends up with an empty solution set.
//
// The waypoint seed is used PREFERENTIALLY, not exclusively: a seed produced upstream without
// collision awareness (e.g. the simple planner's interpolation states) can equally steer a
// single-solution solver onto a branch that is in collision. If the seeded pass contributes no
// valid goal state, a second pass with the legacy zero seed recovers the previous behavior so a
// reachable goal is never lost to an unlucky seed.
const bool has_wp_seed = (seed.size() == static_cast<Eigen::Index>(dof));

auto addStatesForSeed = [&](const Eigen::VectorXd& ik_seed) {
/** @brief Making this thread_local does not help because it is not called enough during planning */
tesseract::kinematics::IKSolutions joint_solutions;
manip.calcInvKin(joint_solutions, { ik_input }, ik_seed);
contact_map_vec.reserve(contact_map_vec.size() + joint_solutions.size());

for (std::size_t i = 0; i < joint_solutions.size(); ++i)
{
Eigen::VectorXd& solution = joint_solutions[i];
contact_map_vec.emplace_back();

// Check limits
if (tesseract::common::satisfiesLimits<double>(solution, limits.joint_limits))
Expand All @@ -264,7 +284,7 @@ void OMPLRealVectorMoveProfile::applyGoalStates(ompl::geometric::SimpleSetup& si
// Get discrete contact manager for testing provided start and end position
// This is required because collision checking happens in motion validators now
// instead of the isValid function to avoid unnecessary collision checks.
if (!checkStateInCollision(contact_map_vec[i], contact_checker, manip, solution))
if (!checkStateInCollision(contact_map_vec.back(), contact_checker, manip, solution))
{
{
ompl::base::ScopedState<> goal_state(simple_setup.getStateSpace());
Expand All @@ -285,7 +305,12 @@ void OMPLRealVectorMoveProfile::applyGoalStates(ompl::geometric::SimpleSetup& si
goal_states->addState(goal_state);
}
}
}
}
};

addStatesForSeed(has_wp_seed ? seed : Eigen::VectorXd::Zero(dof));
if (has_wp_seed && !goal_states->hasStates() && !seed.isZero(0.0))
addStatesForSeed(Eigen::VectorXd::Zero(dof));

if (!goal_states->hasStates())
{
Expand Down Expand Up @@ -343,22 +368,31 @@ void OMPLRealVectorMoveProfile::applyGoalStates(ompl::geometric::SimpleSetup& si
void OMPLRealVectorMoveProfile::applyStartStates(ompl::geometric::SimpleSetup& simple_setup,
const tesseract::kinematics::KinGroupIKInput& ik_input,
const tesseract::kinematics::KinematicGroup& manip,
tesseract::collision::DiscreteContactManager& contact_checker)
tesseract::collision::DiscreteContactManager& contact_checker,
const Eigen::VectorXd& seed)
{
/** @todo Need to add Descartes pose sampler to ompl profile */
/** @todo Need to also provide the seed instruction to use here */
const auto dof = manip.numJoints();
tesseract::common::KinematicLimits limits = manip.getLimits();

/** @brief Making this thread_local does not help because it is not called enough during planning */
tesseract::kinematics::IKSolutions joint_solutions;
manip.calcInvKin(joint_solutions, { ik_input }, Eigen::VectorXd::Zero(dof));
bool found_start_state = false;
std::vector<tesseract::collision::ContactResultMap> contact_map_vec(joint_solutions.size());
std::vector<tesseract::collision::ContactResultMap> contact_map_vec;

for (std::size_t i = 0; i < joint_solutions.size(); ++i)
{
Eigen::VectorXd& solution = joint_solutions[i];
// Seed handling mirrors applyGoalStates: waypoint seed preferentially, zero-seed second pass as
// the fallback when the seeded pass contributes no valid start state (see the goal-side comment
// for the full rationale).
const bool has_wp_seed = (seed.size() == static_cast<Eigen::Index>(dof));

auto addStatesForSeed = [&](const Eigen::VectorXd& ik_seed) {
/** @brief Making this thread_local does not help because it is not called enough during planning */
tesseract::kinematics::IKSolutions joint_solutions;
manip.calcInvKin(joint_solutions, { ik_input }, ik_seed);
contact_map_vec.reserve(contact_map_vec.size() + joint_solutions.size());

for (std::size_t i = 0; i < joint_solutions.size(); ++i)
{
Eigen::VectorXd& solution = joint_solutions[i];
contact_map_vec.emplace_back();

// Check limits
if (tesseract::common::satisfiesLimits<double>(solution, limits.joint_limits))
Expand All @@ -373,7 +407,7 @@ void OMPLRealVectorMoveProfile::applyStartStates(ompl::geometric::SimpleSetup& s
// Get discrete contact manager for testing provided start and end position
// This is required because collision checking happens in motion validators now
// instead of the isValid function to avoid unnecessary collision checks.
if (!checkStateInCollision(contact_map_vec[i], contact_checker, manip, solution))
if (!checkStateInCollision(contact_map_vec.back(), contact_checker, manip, solution))
{
found_start_state = true;
{
Expand All @@ -395,7 +429,12 @@ void OMPLRealVectorMoveProfile::applyStartStates(ompl::geometric::SimpleSetup& s
simple_setup.addStartState(start_state);
}
}
}
}
};

addStatesForSeed(has_wp_seed ? seed : Eigen::VectorXd::Zero(dof));
if (has_wp_seed && !found_start_state && !seed.isZero(0.0))
addStatesForSeed(Eigen::VectorXd::Zero(dof));

if (!found_start_state)
{
Expand Down