Skip to content

Commit 89c78a9

Browse files
munich35claude
andcommitted
Use the Cartesian waypoint seed when solving IK in the OMPL move profile
OMPLRealVectorMoveProfile solved start/goal states for CartesianWaypoints by calling calcInvKin with a hardcoded zero seed, ignoring any seed state attached to the waypoint (e.g. populated by MinLengthTask's interpolation). For seed-dependent solvers that return a single solution (e.g. the KDL-LMA plugin), the zero-seed solution can land outside the joint limits; calcInvKin's limit filter then empties the goal set and the planner aborts with "All goal states are either in collision or outside limits", even though the waypoint is reachable and its seed would solve cleanly. Honor the waypoint's attached seed (CartesianWaypointPoly::getSeed()) as the IK seed when it is present and sized to the manipulator, falling back to the previous zero seed otherwise. The seed is threaded through the KinGroupIKInput applyStartStates/applyGoalStates overloads via an optional trailing parameter, so the existing API and behavior for callers that pass no seed are unchanged. Fixes #762 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent ca69111 commit 89c78a9

2 files changed

Lines changed: 29 additions & 9 deletions

File tree

motion_planners/ompl/include/tesseract/motion_planners/ompl/profile/ompl_real_vector_move_profile.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,14 @@ class OMPLRealVectorMoveProfile : public OMPLMoveProfile
9898
static void applyGoalStates(ompl::geometric::SimpleSetup& simple_setup,
9999
const tesseract::kinematics::KinGroupIKInput& ik_input,
100100
const tesseract::kinematics::KinematicGroup& manip,
101-
tesseract::collision::DiscreteContactManager& contact_checker);
101+
tesseract::collision::DiscreteContactManager& contact_checker,
102+
const Eigen::VectorXd& seed = Eigen::VectorXd());
102103

103104
static void applyStartStates(ompl::geometric::SimpleSetup& simple_setup,
104105
const tesseract::kinematics::KinGroupIKInput& ik_input,
105106
const tesseract::kinematics::KinematicGroup& manip,
106-
tesseract::collision::DiscreteContactManager& contact_checker);
107+
tesseract::collision::DiscreteContactManager& contact_checker,
108+
const Eigen::VectorXd& seed = Eigen::VectorXd());
107109

108110
static void applyGoalStates(ompl::geometric::SimpleSetup& simple_setup,
109111
const Eigen::VectorXd& joint_waypoint,

motion_planners/ompl/src/profile/ompl_real_vector_move_profile.cpp

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ TESSERACT_COMMON_IGNORE_WARNINGS_POP
6060
#include <tesseract/environment/environment.h>
6161

6262
#include <tesseract/common/profile_plugin_factory.h>
63+
#include <tesseract/common/joint_state.h>
6364

6465
namespace tesseract::motion_planners
6566
{
@@ -193,7 +194,8 @@ std::unique_ptr<ompl::geometric::SimpleSetup> OMPLRealVectorMoveProfile::createS
193194

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

223225
contact_checker->setActiveCollisionObjects(kin_group->getActiveLinkNames());
224226
tesseract::kinematics::KinGroupIKInput ik_input(tcp_frame_cwp, end_mi.working_frame, end_mi.tcp_frame);
225-
applyGoalStates(*simple_setup, ik_input, *kin_group, *contact_checker);
227+
const Eigen::VectorXd seed = cur_wp.hasSeed() ? cur_wp.getSeed().position : Eigen::VectorXd();
228+
applyGoalStates(*simple_setup, ik_input, *kin_group, *contact_checker, seed);
226229
}
227230
else
228231
{
@@ -235,15 +238,23 @@ std::unique_ptr<ompl::geometric::SimpleSetup> OMPLRealVectorMoveProfile::createS
235238
void OMPLRealVectorMoveProfile::applyGoalStates(ompl::geometric::SimpleSetup& simple_setup,
236239
const tesseract::kinematics::KinGroupIKInput& ik_input,
237240
const tesseract::kinematics::KinematicGroup& manip,
238-
tesseract::collision::DiscreteContactManager& contact_checker)
241+
tesseract::collision::DiscreteContactManager& contact_checker,
242+
const Eigen::VectorXd& seed)
239243
{
240244
/** @todo Need to add Descartes pose sample to ompl profile */
241245
const auto dof = manip.numJoints();
242246
tesseract::common::KinematicLimits limits = manip.getLimits();
243247

244248
/** @brief Making this thread_local does not help because it is not called enough during planning */
245249
tesseract::kinematics::IKSolutions joint_solutions;
246-
manip.calcInvKin(joint_solutions, { ik_input }, Eigen::VectorXd::Zero(dof));
250+
// Use the seed attached to the Cartesian waypoint when one is provided, otherwise fall back to the
251+
// zero seed. For seed-dependent solvers that return a single solution (e.g. KDL-LMA), a hardcoded
252+
// zero seed can converge to an out-of-limits branch of the solution manifold; the limit filter then
253+
// discards it and an otherwise-reachable goal ends up with an empty solution set.
254+
Eigen::VectorXd ik_seed = Eigen::VectorXd::Zero(dof);
255+
if (seed.size() == static_cast<Eigen::Index>(dof))
256+
ik_seed = seed;
257+
manip.calcInvKin(joint_solutions, { ik_input }, ik_seed);
247258
auto goal_states = std::make_shared<ompl::base::GoalStates>(simple_setup.getSpaceInformation());
248259
std::vector<tesseract::collision::ContactResultMap> contact_map_vec(static_cast<std::size_t>(joint_solutions.size()));
249260

@@ -343,16 +354,23 @@ void OMPLRealVectorMoveProfile::applyGoalStates(ompl::geometric::SimpleSetup& si
343354
void OMPLRealVectorMoveProfile::applyStartStates(ompl::geometric::SimpleSetup& simple_setup,
344355
const tesseract::kinematics::KinGroupIKInput& ik_input,
345356
const tesseract::kinematics::KinematicGroup& manip,
346-
tesseract::collision::DiscreteContactManager& contact_checker)
357+
tesseract::collision::DiscreteContactManager& contact_checker,
358+
const Eigen::VectorXd& seed)
347359
{
348360
/** @todo Need to add Descartes pose sampler to ompl profile */
349-
/** @todo Need to also provide the seed instruction to use here */
350361
const auto dof = manip.numJoints();
351362
tesseract::common::KinematicLimits limits = manip.getLimits();
352363

353364
/** @brief Making this thread_local does not help because it is not called enough during planning */
354365
tesseract::kinematics::IKSolutions joint_solutions;
355-
manip.calcInvKin(joint_solutions, { ik_input }, Eigen::VectorXd::Zero(dof));
366+
// Use the seed attached to the Cartesian waypoint when one is provided, otherwise fall back to the
367+
// zero seed. For seed-dependent solvers that return a single solution (e.g. KDL-LMA), a hardcoded
368+
// zero seed can converge to an out-of-limits branch of the solution manifold; the limit filter then
369+
// discards it and an otherwise-reachable start ends up with an empty solution set.
370+
Eigen::VectorXd ik_seed = Eigen::VectorXd::Zero(dof);
371+
if (seed.size() == static_cast<Eigen::Index>(dof))
372+
ik_seed = seed;
373+
manip.calcInvKin(joint_solutions, { ik_input }, ik_seed);
356374
bool found_start_state = false;
357375
std::vector<tesseract::collision::ContactResultMap> contact_map_vec(joint_solutions.size());
358376

0 commit comments

Comments
 (0)