00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 #include <moveit/constraint_samplers/default_constraint_samplers.h>
00038 #include <set>
00039 #include <cassert>
00040 #include <eigen_conversions/eigen_msg.h>
00041 #include <boost/bind.hpp>
00042
00043 bool constraint_samplers::JointConstraintSampler::configure(const moveit_msgs::Constraints &constr)
00044 {
00045
00046 std::vector<kinematic_constraints::JointConstraint> jc;
00047 for (std::size_t i = 0 ; i < constr.joint_constraints.size() ; ++i)
00048 {
00049 kinematic_constraints::JointConstraint j(scene_->getRobotModel());
00050 if (j.configure(constr.joint_constraints[i]))
00051 jc.push_back(j);
00052 }
00053
00054 return jc.empty() ? false : configure(jc);
00055 }
00056
00057 bool constraint_samplers::JointConstraintSampler::configure(const std::vector<kinematic_constraints::JointConstraint> &jc)
00058 {
00059 clear();
00060
00061 if (!jmg_)
00062 {
00063 logError("NULL group specified for constraint sampler");
00064 return false;
00065 }
00066
00067
00068
00069 const std::map<std::string, unsigned int> &vim = jmg_->getJointVariablesIndexMap();
00070 std::map<std::string, JointInfo> bound_data;
00071 bool some_valid_constraint = false;
00072 for (std::size_t i = 0 ; i < jc.size() ; ++i)
00073 {
00074 if (!jc[i].enabled())
00075 continue;
00076
00077 const robot_model::JointModel *jm = jc[i].getJointModel();
00078 if (!jmg_->hasJointModel(jm->getName()))
00079 continue;
00080
00081 some_valid_constraint = true;
00082
00083 std::pair<double, double> joint_bounds;
00084 jm->getVariableBounds(jc[i].getJointVariableName(), joint_bounds);
00085
00086 JointInfo ji;
00087 ji.index_ = vim.find(jc[i].getJointVariableName())->second;
00088 std::map<std::string, JointInfo>::iterator it = bound_data.find(jc[i].getJointVariableName());
00089 if (it != bound_data.end())
00090 ji = it->second;
00091
00092 ji.potentiallyAdjustMinMaxBounds(std::max(joint_bounds.first, jc[i].getDesiredJointPosition() - jc[i].getJointToleranceBelow()),
00093 std::min(joint_bounds.second, jc[i].getDesiredJointPosition() + jc[i].getJointToleranceAbove()));
00094
00095
00096 logDebug("Bounds for %s JointConstraint are %g %g", jc[i].getJointVariableName().c_str(), ji.min_bound_, ji.max_bound_);
00097
00098 if (ji.min_bound_ > ji.max_bound_ + std::numeric_limits<double>::epsilon())
00099 {
00100 std::stringstream cs; jc[i].print(cs);
00101 logError("The constraints for joint '%s' are such that there are no possible values for the joint - min_bound: %g, max_bound: %g. Failing.\n", jm->getName().c_str(), ji.min_bound_, ji.max_bound_);
00102 clear();
00103 return false;
00104 }
00105 bound_data[jc[i].getJointVariableName()] = ji;
00106 }
00107
00108 if (!some_valid_constraint)
00109 {
00110 logWarn("No valid joint constraints");
00111 return false;
00112 }
00113
00114 for (std::map<std::string, JointInfo>::iterator it = bound_data.begin(); it != bound_data.end(); ++it)
00115 bounds_.push_back(it->second);
00116
00117
00118 const std::vector<const robot_model::JointModel*> &joints = jmg_->getJointModels();
00119 for (std::size_t i = 0 ; i < joints.size() ; ++i)
00120 if (bound_data.find(joints[i]->getName()) == bound_data.end())
00121 {
00122
00123 const std::vector<std::string> &vars = joints[i]->getVariableNames();
00124 if (vars.size() > 1)
00125 {
00126 bool all_found = true;
00127 for (std::size_t j = 0 ; j < vars.size() ; ++j)
00128 if (bound_data.find(vars[j]) == bound_data.end())
00129 {
00130 all_found = false;
00131 break;
00132 }
00133 if (all_found)
00134 continue;
00135 }
00136 unbounded_.push_back(joints[i]);
00137 uindex_.push_back(vim.find(joints[i]->getName())->second);
00138 }
00139 values_.resize(jmg_->getVariableCount());
00140 is_valid_ = true;
00141 return true;
00142 }
00143
00144 bool constraint_samplers::JointConstraintSampler::sample(robot_state::JointStateGroup *jsg,
00145 const robot_state::RobotState & ,
00146 unsigned int )
00147 {
00148 if (!is_valid_)
00149 {
00150 logWarn("JointConstraintSampler not configured, won't sample");
00151 return false;
00152 }
00153
00154 if (jsg->getName() != getGroupName())
00155 {
00156 logWarn("JointConstraintSampler sample function called with name %s which is not the group %s for which it was configured",
00157 jsg->getName().c_str(), getGroupName().c_str());
00158 return false;
00159 }
00160
00161
00162 for (std::size_t i = 0 ; i < unbounded_.size() ; ++i)
00163 {
00164 std::vector<double> v;
00165 unbounded_[i]->getVariableRandomValues(random_number_generator_, v);
00166 for (std::size_t j = 0 ; j < v.size() ; ++j)
00167 values_[uindex_[i] + j] = v[j];
00168 }
00169
00170
00171 for (std::size_t i = 0 ; i < bounds_.size() ; ++i)
00172 values_[bounds_[i].index_] = random_number_generator_.uniformReal(bounds_[i].min_bound_, bounds_[i].max_bound_);
00173
00174 jsg->setVariableValues(values_);
00175
00176
00177 return true;
00178 }
00179
00180 bool constraint_samplers::JointConstraintSampler::project(robot_state::JointStateGroup *jsg,
00181 const robot_state::RobotState &reference_state,
00182 unsigned int max_attempts)
00183 {
00184 return sample(jsg, reference_state, max_attempts);
00185 }
00186
00187 void constraint_samplers::JointConstraintSampler::clear()
00188 {
00189 ConstraintSampler::clear();
00190 bounds_.clear();
00191 unbounded_.clear();
00192 uindex_.clear();
00193 values_.clear();
00194 }
00195
00196 constraint_samplers::IKSamplingPose::IKSamplingPose()
00197 {
00198 }
00199
00200 constraint_samplers::IKSamplingPose::IKSamplingPose(const kinematic_constraints::PositionConstraint &pc) : position_constraint_(new kinematic_constraints::PositionConstraint(pc))
00201 {
00202 }
00203
00204 constraint_samplers::IKSamplingPose::IKSamplingPose(const kinematic_constraints::OrientationConstraint &oc) : orientation_constraint_(new kinematic_constraints::OrientationConstraint(oc))
00205 {
00206 }
00207
00208 constraint_samplers::IKSamplingPose::IKSamplingPose(const kinematic_constraints::PositionConstraint &pc, const kinematic_constraints::OrientationConstraint &oc) :
00209 position_constraint_(new kinematic_constraints::PositionConstraint(pc)), orientation_constraint_(new kinematic_constraints::OrientationConstraint(oc))
00210 {
00211 }
00212
00213 constraint_samplers::IKSamplingPose::IKSamplingPose(const boost::shared_ptr<kinematic_constraints::PositionConstraint> &pc) : position_constraint_(pc)
00214 {
00215 }
00216
00217 constraint_samplers::IKSamplingPose::IKSamplingPose(const boost::shared_ptr<kinematic_constraints::OrientationConstraint> &oc) : orientation_constraint_(oc)
00218 {
00219 }
00220
00221 constraint_samplers::IKSamplingPose::IKSamplingPose(const boost::shared_ptr<kinematic_constraints::PositionConstraint> &pc, const boost::shared_ptr<kinematic_constraints::OrientationConstraint> &oc) : position_constraint_(pc), orientation_constraint_(oc)
00222 {
00223 }
00224
00225 void constraint_samplers::IKConstraintSampler::clear()
00226 {
00227 ConstraintSampler::clear();
00228 kb_.reset();
00229 ik_frame_ = "";
00230 transform_ik_ = false;
00231 }
00232
00233 bool constraint_samplers::IKConstraintSampler::configure(const IKSamplingPose &sp)
00234 {
00235 clear();
00236 if(!sp.position_constraint_ && !sp.orientation_constraint_) return false;
00237 if((!sp.orientation_constraint_ && !sp.position_constraint_->enabled()) ||
00238 (!sp.position_constraint_ && !sp.orientation_constraint_->enabled()) ||
00239 (sp.position_constraint_ && sp.orientation_constraint_ &&
00240 !sp.position_constraint_->enabled() && !sp.orientation_constraint_->enabled())) {
00241 logWarn("No enabled constraints in sampling pose");
00242 return false;
00243 }
00244
00245 sampling_pose_ = sp;
00246 ik_timeout_ = jmg_->getDefaultIKTimeout();
00247 if (sampling_pose_.position_constraint_ && sampling_pose_.orientation_constraint_)
00248 if (sampling_pose_.position_constraint_->getLinkModel()->getName() != sampling_pose_.orientation_constraint_->getLinkModel()->getName())
00249 {
00250 logError("Position and orientation constraints need to be specified for the same link in order to use IK-based sampling");
00251 return false;
00252 }
00253
00254 if (sampling_pose_.position_constraint_ && sampling_pose_.position_constraint_->mobileReferenceFrame())
00255 frame_depends_.push_back(sampling_pose_.position_constraint_->getReferenceFrame());
00256 if (sampling_pose_.orientation_constraint_ && sampling_pose_.orientation_constraint_->mobileReferenceFrame())
00257 frame_depends_.push_back(sampling_pose_.orientation_constraint_->getReferenceFrame());
00258 kb_ = jmg_->getSolverInstance();
00259 if (!kb_)
00260 {
00261 logWarn("No solver instance in setup");
00262 is_valid_ = false;
00263 return false;
00264 }
00265 is_valid_ = loadIKSolver();
00266 return is_valid_;
00267 }
00268
00269 bool constraint_samplers::IKConstraintSampler::configure(const moveit_msgs::Constraints &constr)
00270 {
00271 for (std::size_t p = 0 ; p < constr.position_constraints.size() ; ++p)
00272 for (std::size_t o = 0 ; o < constr.orientation_constraints.size() ; ++o)
00273 if (constr.position_constraints[p].link_name == constr.orientation_constraints[o].link_name)
00274 {
00275 boost::shared_ptr<kinematic_constraints::PositionConstraint> pc(new kinematic_constraints::PositionConstraint(scene_->getRobotModel()));
00276 boost::shared_ptr<kinematic_constraints::OrientationConstraint> oc(new kinematic_constraints::OrientationConstraint(scene_->getRobotModel()));
00277 if (pc->configure(constr.position_constraints[p], scene_->getTransforms()) && oc->configure(constr.orientation_constraints[o], scene_->getTransforms()))
00278 return configure(IKSamplingPose(pc, oc));
00279 }
00280
00281 for (std::size_t p = 0 ; p < constr.position_constraints.size() ; ++p)
00282 {
00283 boost::shared_ptr<kinematic_constraints::PositionConstraint> pc(new kinematic_constraints::PositionConstraint(scene_->getRobotModel()));
00284 if (pc->configure(constr.position_constraints[p], scene_->getTransforms()))
00285 return configure(IKSamplingPose(pc));
00286 }
00287
00288 for (std::size_t o = 0 ; o < constr.orientation_constraints.size() ; ++o)
00289 {
00290 boost::shared_ptr<kinematic_constraints::OrientationConstraint> oc(new kinematic_constraints::OrientationConstraint(scene_->getRobotModel()));
00291 if (oc->configure(constr.orientation_constraints[o], scene_->getTransforms()))
00292 return configure(IKSamplingPose(oc));
00293 }
00294 return false;
00295 }
00296
00297 double constraint_samplers::IKConstraintSampler::getSamplingVolume() const
00298 {
00299 double v = 1.0;
00300 if (sampling_pose_.position_constraint_)
00301 {
00302 const std::vector<bodies::BodyPtr> &b = sampling_pose_.position_constraint_->getConstraintRegions();
00303 double vol = 0;
00304 for (std::size_t i = 0 ; i < b.size() ; ++i)
00305 vol += b[i]->computeVolume();
00306 if (!b.empty())
00307 v *= vol;
00308 }
00309
00310 if (sampling_pose_.orientation_constraint_)
00311 v *= sampling_pose_.orientation_constraint_->getXAxisTolerance() * sampling_pose_.orientation_constraint_->getYAxisTolerance() * sampling_pose_.orientation_constraint_->getZAxisTolerance();
00312 return v;
00313 }
00314
00315 const std::string& constraint_samplers::IKConstraintSampler::getLinkName() const
00316 {
00317 if (sampling_pose_.orientation_constraint_)
00318 return sampling_pose_.orientation_constraint_->getLinkModel()->getName();
00319 return sampling_pose_.position_constraint_->getLinkModel()->getName();
00320 }
00321
00322 bool constraint_samplers::IKConstraintSampler::loadIKSolver()
00323 {
00324 if (!kb_)
00325 {
00326 logError("No IK solver");
00327 return false;
00328 }
00329
00330
00331 ik_frame_ = kb_->getBaseFrame();
00332 if (!ik_frame_.empty() && ik_frame_[0] == '/')
00333 ik_frame_.erase(ik_frame_.begin());
00334 transform_ik_ = !robot_state::Transforms::sameFrame(ik_frame_, jmg_->getParentModel()->getModelFrame());
00335 if (transform_ik_)
00336 if (!jmg_->getParentModel()->hasLinkModel(ik_frame_))
00337 {
00338 logError("The IK solver expects requests in frame '%s' but this frame is not known to the sampler. Ignoring transformation (IK may fail)", ik_frame_.c_str());
00339 transform_ik_ = false;
00340 }
00341
00342
00343 bool wrong_link = false;
00344 if (sampling_pose_.position_constraint_)
00345 {
00346 if (!robot_state::Transforms::sameFrame(kb_->getTipFrame(), sampling_pose_.position_constraint_->getLinkModel()->getName()))
00347 wrong_link = true;
00348 }
00349 else
00350 if (!robot_state::Transforms::sameFrame(kb_->getTipFrame(), sampling_pose_.orientation_constraint_->getLinkModel()->getName()))
00351 wrong_link = true;
00352 if (wrong_link)
00353 {
00354 logError("IK cannot be performed for link '%s'. The solver can report IK solutions for link '%s'.",
00355 sampling_pose_.position_constraint_ ? sampling_pose_.position_constraint_->getLinkModel()->getName().c_str() : sampling_pose_.orientation_constraint_->getLinkModel()->getName().c_str(), kb_->getTipFrame().c_str());
00356 return false;
00357 }
00358 return true;
00359 }
00360
00361 bool constraint_samplers::IKConstraintSampler::samplePose(Eigen::Vector3d &pos, Eigen::Quaterniond &quat,
00362 const robot_state::RobotState &ks,
00363 unsigned int max_attempts)
00364 {
00365 if (sampling_pose_.position_constraint_)
00366 {
00367 const std::vector<bodies::BodyPtr> &b = sampling_pose_.position_constraint_->getConstraintRegions();
00368 if (!b.empty())
00369 {
00370 bool found = false;
00371 std::size_t k = random_number_generator_.uniformInteger(0, b.size() - 1);
00372 for (std::size_t i = 0 ; i < b.size() ; ++i)
00373 if (b[(i+k) % b.size()]->samplePointInside(random_number_generator_, max_attempts, pos))
00374 {
00375 found = true;
00376 break;
00377 }
00378 if (!found)
00379 {
00380 logError("Unable to sample a point inside the constraint region");
00381 return false;
00382 }
00383 }
00384 else
00385 {
00386 logError("Unable to sample a point inside the constraint region. Constraint region is empty when it should not be.");
00387 return false;
00388 }
00389
00390
00391 if (sampling_pose_.position_constraint_->mobileReferenceFrame())
00392 pos = ks.getFrameTransform(sampling_pose_.position_constraint_->getReferenceFrame()) * pos;
00393 }
00394 else
00395 {
00396
00397 robot_state::RobotState tempState(ks);
00398 robot_state::JointStateGroup *tmp = tempState.getJointStateGroup(jmg_->getName());
00399 if (tmp)
00400 {
00401 tmp->setToRandomValues();
00402 pos = tempState.getLinkState(sampling_pose_.orientation_constraint_->getLinkModel()->getName())->getGlobalLinkTransform().translation();
00403 }
00404 else
00405 {
00406 logError("Passed a JointStateGroup for a mismatching JointModelGroup");
00407 return false;
00408 }
00409 }
00410
00411 if (sampling_pose_.orientation_constraint_)
00412 {
00413
00414 double angle_x = 2.0 * (random_number_generator_.uniform01() - 0.5) * (sampling_pose_.orientation_constraint_->getXAxisTolerance()-std::numeric_limits<double>::epsilon());
00415 double angle_y = 2.0 * (random_number_generator_.uniform01() - 0.5) * (sampling_pose_.orientation_constraint_->getYAxisTolerance()-std::numeric_limits<double>::epsilon());
00416 double angle_z = 2.0 * (random_number_generator_.uniform01() - 0.5) * (sampling_pose_.orientation_constraint_->getZAxisTolerance()-std::numeric_limits<double>::epsilon());
00417 Eigen::Affine3d diff(Eigen::AngleAxisd(angle_x, Eigen::Vector3d::UnitX())
00418 * Eigen::AngleAxisd(angle_y, Eigen::Vector3d::UnitY())
00419 * Eigen::AngleAxisd(angle_z, Eigen::Vector3d::UnitZ()));
00420 Eigen::Affine3d reqr(sampling_pose_.orientation_constraint_->getDesiredRotationMatrix() * diff.rotation());
00421 quat = Eigen::Quaterniond(reqr.rotation());
00422
00423
00424 if (sampling_pose_.orientation_constraint_->mobileReferenceFrame())
00425 {
00426 const Eigen::Affine3d &t = ks.getFrameTransform(sampling_pose_.orientation_constraint_->getReferenceFrame());
00427 Eigen::Affine3d rt(t.rotation() * quat.toRotationMatrix());
00428 quat = Eigen::Quaterniond(rt.rotation());
00429 }
00430 }
00431 else
00432 {
00433
00434 double q[4];
00435 random_number_generator_.quaternion(q);
00436 quat = Eigen::Quaterniond(q[3], q[0], q[1], q[2]);
00437 }
00438
00439
00440 if (sampling_pose_.position_constraint_ && sampling_pose_.position_constraint_->hasLinkOffset())
00441
00442 pos = pos - quat.toRotationMatrix() * sampling_pose_.position_constraint_->getLinkOffset();
00443
00444
00445
00446 if (transform_ik_)
00447 {
00448
00449
00450 Eigen::Affine3d ikq(Eigen::Translation3d(pos) * quat.toRotationMatrix());
00451 ikq = ks.getFrameTransform(ik_frame_).inverse() * ikq;
00452 pos = ikq.translation();
00453 quat = Eigen::Quaterniond(ikq.rotation());
00454 }
00455
00456 return true;
00457 }
00458
00459 namespace constraint_samplers
00460 {
00461 namespace
00462 {
00463 void samplingIkCallbackFnAdapter(robot_state::JointStateGroup *jsg, const robot_state::StateValidityCallbackFn &constraint,
00464 const geometry_msgs::Pose &, const std::vector<double> &ik_sol, moveit_msgs::MoveItErrorCodes &error_code)
00465 {
00466 const std::vector<unsigned int> &bij = jsg->getJointModelGroup()->getKinematicsSolverJointBijection();
00467 std::vector<double> solution(bij.size());
00468 for (std::size_t i = 0 ; i < bij.size() ; ++i)
00469 solution[i] = ik_sol[bij[i]];
00470 if (constraint(jsg, solution))
00471 error_code.val = moveit_msgs::MoveItErrorCodes::SUCCESS;
00472 else
00473 error_code.val = moveit_msgs::MoveItErrorCodes::NO_IK_SOLUTION;
00474 }
00475 }
00476 }
00477
00478 bool constraint_samplers::IKConstraintSampler::sample(robot_state::JointStateGroup *jsg, const robot_state::RobotState &ks, unsigned int max_attempts)
00479 {
00480 return sampleHelper(jsg, ks, max_attempts, false);
00481 }
00482
00483 bool constraint_samplers::IKConstraintSampler::sampleHelper(robot_state::JointStateGroup *jsg, const robot_state::RobotState &ks, unsigned int max_attempts, bool project)
00484 {
00485 if (!is_valid_)
00486 return false;
00487
00488 if (jsg->getName() != getGroupName())
00489 {
00490 logWarn("IKConstraintSampler sample function called with name %s which is not the group %s for which it was configured",
00491 jsg->getName().c_str(), getGroupName().c_str());
00492 return false;
00493 }
00494
00495 kinematics::KinematicsBase::IKCallbackFn adapted_ik_validity_callback;
00496 if (state_validity_callback_)
00497 adapted_ik_validity_callback = boost::bind(&samplingIkCallbackFnAdapter, jsg, state_validity_callback_, _1, _2, _3);
00498
00499 for (unsigned int a = 0 ; a < max_attempts ; ++a)
00500 {
00501
00502 Eigen::Vector3d point;
00503 Eigen::Quaterniond quat;
00504 if (!samplePose(point, quat, ks, max_attempts))
00505 {
00506 if (verbose_)
00507 logInform("IK constraint sampler was unable to produce a pose to run IK for");
00508 return false;
00509 }
00510
00511 geometry_msgs::Pose ik_query;
00512 ik_query.position.x = point.x();
00513 ik_query.position.y = point.y();
00514 ik_query.position.z = point.z();
00515 ik_query.orientation.x = quat.x();
00516 ik_query.orientation.y = quat.y();
00517 ik_query.orientation.z = quat.z();
00518 ik_query.orientation.w = quat.w();
00519
00520 if (callIK(ik_query, adapted_ik_validity_callback, ik_timeout_, jsg, project && a == 0))
00521 return true;
00522 }
00523 return false;
00524 }
00525
00526 bool constraint_samplers::IKConstraintSampler::project(robot_state::JointStateGroup *jsg,
00527 const robot_state::RobotState &reference_state,
00528 unsigned int max_attempts)
00529 {
00530 return sampleHelper(jsg, reference_state, max_attempts, true);
00531 }
00532
00533 bool constraint_samplers::IKConstraintSampler::callIK(const geometry_msgs::Pose &ik_query, const kinematics::KinematicsBase::IKCallbackFn &adapted_ik_validity_callback,
00534 double timeout, robot_state::JointStateGroup *jsg, bool use_as_seed)
00535 {
00536 const std::vector<unsigned int>& ik_joint_bijection = jmg_->getKinematicsSolverJointBijection();
00537 std::vector<double> seed(ik_joint_bijection.size(), 0.0);
00538 std::vector<double> vals;
00539
00540 if (use_as_seed)
00541 jsg->getVariableValues(vals);
00542 else
00543
00544 jmg_->getVariableRandomValues(random_number_generator_, vals);
00545
00546 assert(vals.size() == ik_joint_bijection.size());
00547 for (std::size_t i = 0 ; i < ik_joint_bijection.size() ; ++i)
00548 seed[ik_joint_bijection[i]] = vals[i];
00549
00550 std::vector<double> ik_sol;
00551 moveit_msgs::MoveItErrorCodes error;
00552
00553 if (adapted_ik_validity_callback ?
00554 kb_->searchPositionIK(ik_query, seed, timeout, ik_sol, adapted_ik_validity_callback, error) :
00555 kb_->searchPositionIK(ik_query, seed, timeout, ik_sol, error))
00556 {
00557 assert(ik_sol.size() == ik_joint_bijection.size());
00558 std::vector<double> solution(ik_joint_bijection.size());
00559 for (std::size_t i = 0 ; i < ik_joint_bijection.size() ; ++i)
00560 solution[i] = ik_sol[ik_joint_bijection[i]];
00561 jsg->setVariableValues(solution);
00562
00563 assert(!sampling_pose_.orientation_constraint_ || sampling_pose_.orientation_constraint_->decide(*jsg->getRobotState(), verbose_).satisfied);
00564 assert(!sampling_pose_.position_constraint_ || sampling_pose_.position_constraint_->decide(*jsg->getRobotState(), verbose_).satisfied);
00565
00566 return true;
00567 }
00568 else
00569 {
00570 if (error.val != moveit_msgs::MoveItErrorCodes::NO_IK_SOLUTION &&
00571 error.val != moveit_msgs::MoveItErrorCodes::INVALID_ROBOT_STATE &&
00572 error.val != moveit_msgs::MoveItErrorCodes::TIMED_OUT)
00573 logError("IK solver failed with error %d", error.val);
00574 else
00575 if (verbose_)
00576 logInform("IK failed");
00577 }
00578 return false;
00579 }