00001
00026 #ifndef INDUSTRIAL_MOVEIT_STOMP_MOVEIT_INCLUDE_STOMP_MOVEIT_UTILS_KINEMATICS_H_
00027 #define INDUSTRIAL_MOVEIT_STOMP_MOVEIT_INCLUDE_STOMP_MOVEIT_UTILS_KINEMATICS_H_
00028
00029 #include <ros/console.h>
00030 #include <Eigen/Geometry>
00031 #include <eigen_conversions/eigen_msg.h>
00032 #include <moveit/robot_state/robot_state.h>
00033 #include <moveit/robot_state/conversions.h>
00034 #include <pluginlib/class_list_macros.h>
00035 #include <XmlRpcException.h>
00036 #include <moveit_msgs/PositionConstraint.h>
00037 #include <moveit_msgs/OrientationConstraint.h>
00038
00039
00040 namespace stomp_moveit
00041 {
00042 namespace utils
00043 {
00044
00049 namespace kinematics
00050 {
00051
00052 const static double EPSILON = 0.011;
00053 const static double LAMBDA = 0.01;
00059 struct KinematicConfig
00060 {
00064 Eigen::Array<int,6,1> constrained_dofs = Eigen::Array<int,6,1>::Ones();
00066 Eigen::Affine3d tool_goal_pose = Eigen::Affine3d::Identity();
00071 Eigen::ArrayXd joint_update_rates = Eigen::ArrayXd::Zero(1,1);
00072 Eigen::Array<double,6,1> cartesian_convergence_thresholds = Eigen::Array<double,6,1>::Zero();
00073 Eigen::VectorXd init_joint_pose = Eigen::ArrayXd::Zero(1,1);
00074 int max_iterations = 100;
00079 Eigen::ArrayXd null_proj_weights = Eigen::ArrayXd::Zero(1,1);
00080 Eigen::VectorXd null_space_vector = Eigen::VectorXd::Zero(1);
00082 };
00083
00093 static bool createKinematicConfig(const moveit::core::JointModelGroup* group,
00094 const moveit_msgs::PositionConstraint& pc,const moveit_msgs::OrientationConstraint& oc,
00095 const Eigen::VectorXd& init_joint_pose,KinematicConfig& kc)
00096 {
00097 int num_joints = group->getActiveJointModelNames().size();
00098 if(num_joints != init_joint_pose.size())
00099 {
00100 ROS_ERROR("Initial joint pose has an incorrect number of joints");
00101 return false;
00102 }
00103
00104
00105 kc.tool_goal_pose.setIdentity();
00106 auto& v = pc.constraint_region.primitive_poses[0].position;
00107 kc.tool_goal_pose.translation() = Eigen::Vector3d(v.x,v.y,v.z);
00108
00109
00110 Eigen::Quaterniond q;
00111 tf::quaternionMsgToEigen(oc.orientation,q);
00112 kc.tool_goal_pose.rotate(q);
00113
00114
00115 kc.constrained_dofs << 1, 1, 1, 1, 1, 1;
00116
00117
00118 const shape_msgs::SolidPrimitive& bv = pc.constraint_region.primitives[0];
00119 if(bv.type != shape_msgs::SolidPrimitive::BOX || bv.dimensions.size() != 3)
00120 {
00121 ROS_ERROR("Position constraint incorrectly defined, a BOX region is expected");
00122 return false;
00123 }
00124
00125 using SP = shape_msgs::SolidPrimitive;
00126 kc.cartesian_convergence_thresholds[0] = bv.dimensions[SP::BOX_X];
00127 kc.cartesian_convergence_thresholds[1] = bv.dimensions[SP::BOX_Y];
00128 kc.cartesian_convergence_thresholds[2] = bv.dimensions[SP::BOX_Z];
00129
00130
00131 kc.cartesian_convergence_thresholds[3] = oc.absolute_x_axis_tolerance;
00132 kc.cartesian_convergence_thresholds[4] = oc.absolute_y_axis_tolerance;
00133 kc.cartesian_convergence_thresholds[5] = oc.absolute_z_axis_tolerance;
00134
00135
00136 for(std::size_t i = 3; i < kc.cartesian_convergence_thresholds.size(); i++)
00137 {
00138 auto v = kc.cartesian_convergence_thresholds[i];
00139 kc.constrained_dofs[i] = v >= 2*M_PI ? 0 : 1;
00140 }
00141
00142
00143 kc.joint_update_rates = Eigen::ArrayXd::Constant(num_joints,0.5f);
00144 kc.init_joint_pose = init_joint_pose;
00145 kc.max_iterations = 100;
00146
00147 return true;
00148 }
00149
00159 static bool createKinematicConfig(const moveit::core::JointModelGroup* group,
00160 const moveit_msgs::PositionConstraint& pc,const moveit_msgs::OrientationConstraint& oc,
00161 const moveit_msgs::RobotState& start_state,KinematicConfig& kc)
00162 {
00163 const auto& joint_names= group->getActiveJointModelNames();
00164 const auto& jstate = start_state.joint_state;
00165 std::size_t ind = 0;
00166 Eigen::VectorXd joint_vals = Eigen::VectorXd::Zero(joint_names.size());
00167 for(std::size_t i = 0; i < joint_names.size();i ++)
00168 {
00169 auto pos = std::find(jstate.name.begin(),jstate.name.end(),joint_names[i]);
00170 if(pos == jstate.name.end())
00171 {
00172 return false;
00173 }
00174
00175 ind = std::distance(jstate.name.begin(),pos);
00176 joint_vals(i) = jstate.position[ind];
00177 }
00178
00179 return createKinematicConfig(group,pc,oc,joint_vals,kc);
00180 }
00181
00191 static void computeTwist(const Eigen::Affine3d& p0,
00192 const Eigen::Affine3d& pf,
00193 const Eigen::ArrayXi& nullity,Eigen::VectorXd& twist)
00194 {
00195 twist.resize(nullity.size());
00196 twist.setConstant(0);
00197
00198
00199 auto p0_inv = p0.inverse();
00200 Eigen::Affine3d t = (p0_inv) * pf;
00201
00202 Eigen::Vector3d twist_pos = p0_inv.rotation()*(pf.translation() - p0.translation());
00203
00204
00205 Eigen::AngleAxisd relative_rot(t.rotation());
00206 double angle = relative_rot.angle();
00207 Eigen::Vector3d axis = relative_rot.axis();
00208
00209
00210 while( (angle > M_PI) || (angle < -M_PI))
00211 {
00212 angle = (angle > M_PI) ? (angle - 2*M_PI) : angle;
00213 angle = (angle < -M_PI )? (angle + 2*M_PI) : angle;
00214 }
00215
00216
00217 Eigen::Vector3d twist_rot = axis.normalized() * angle;
00218
00219
00220 twist.head(3) = twist_pos;
00221 twist.tail(3) = twist_rot;
00222
00223
00224 twist = (nullity == 0).select(0,twist);
00225 }
00226
00233 static void reduceJacobian(const Eigen::MatrixXd& jacb,
00234 const std::vector<int>& indices,Eigen::MatrixXd& jacb_reduced)
00235 {
00236 jacb_reduced.resize(indices.size(),jacb.cols());
00237 for(auto i = 0u; i < indices.size(); i++)
00238 {
00239 jacb_reduced.row(i) = jacb.row(indices[i]);
00240 }
00241 }
00242
00250 static void calculateDampedPseudoInverse(const Eigen::MatrixXd &jacb, Eigen::MatrixXd &jacb_pseudo_inv,
00251 double eps = 0.011, double lambda = 0.01)
00252 {
00253 using namespace Eigen;
00254
00255
00256
00257
00258 Eigen::JacobiSVD<MatrixXd> svd(jacb, Eigen::ComputeThinU | Eigen::ComputeThinV);
00259 const MatrixXd &U = svd.matrixU();
00260 const VectorXd &Sv = svd.singularValues();
00261 const MatrixXd &V = svd.matrixV();
00262
00263
00264
00265 size_t nSv = Sv.size();
00266 VectorXd inv_Sv(nSv);
00267 for(size_t i=0; i< nSv; ++i)
00268 {
00269 if (fabs(Sv(i)) > eps)
00270 {
00271 inv_Sv(i) = 1/Sv(i);
00272 }
00273 else
00274 {
00275 inv_Sv(i) = Sv(i) / (Sv(i)*Sv(i) + lambda*lambda);
00276 }
00277 }
00278
00279 jacb_pseudo_inv = V * inv_Sv.asDiagonal() * U.transpose();
00280 }
00281
00299 static bool solveIK(moveit::core::RobotStatePtr robot_state, const std::string& group_name,
00300 const Eigen::Array<int,6,1>& constrained_dofs,
00301 const Eigen::ArrayXd& joint_update_rates,
00302 const Eigen::Array<double,6,1>& cartesian_convergence_thresholds,
00303 const Eigen::ArrayXd& null_proj_weights,
00304 const Eigen::VectorXd& null_space_vector,
00305 int max_iterations,
00306 const Eigen::Affine3d& tool_goal_pose,
00307 const Eigen::VectorXd& init_joint_pose,
00308 Eigen::VectorXd& joint_pose)
00309 {
00310
00311 using namespace Eigen;
00312 using namespace moveit::core;
00313
00314
00315 VectorXd delta_j = VectorXd::Zero(init_joint_pose.size());
00316 joint_pose = init_joint_pose;
00317 const JointModelGroup* joint_group = robot_state->getJointModelGroup(group_name);
00318 robot_state->setJointGroupPositions(joint_group,joint_pose);
00319 const auto& joint_names = joint_group->getActiveJointModelNames();
00320 std::string tool_link = joint_group->getLinkModelNames().back();
00321 Affine3d tool_current_pose = robot_state->getGlobalLinkTransform(tool_link);
00322
00323 auto harmonize_joints = [&joint_group,&joint_names](Eigen::VectorXd& joint_vals) -> bool
00324 {
00325 if(joint_names.size() != joint_vals.size())
00326 {
00327 return false;
00328 }
00329
00330 const double incr = 2*M_PI;
00331 for(std::size_t i = 0; i < joint_names.size();i++)
00332 {
00333 if( joint_group->getJointModel(joint_names[i])->getType() != JointModel::REVOLUTE )
00334 {
00335 continue;
00336 }
00337
00338 const JointModel::Bounds& bounds = joint_group->getJointModel(joint_names[i])->getVariableBounds();
00339
00340 double j = joint_vals(i);
00341 for(const VariableBounds& b: bounds)
00342 {
00343
00344 while(j > b.max_position_)
00345 {
00346 j-= incr;
00347 }
00348
00349 while(j < b.min_position_)
00350 {
00351 j += incr;
00352 }
00353 }
00354
00355 joint_vals(i) = j;
00356 }
00357
00358 return true;
00359 };
00360
00361
00362 VectorXd tool_twist, tool_twist_reduced;
00363 std::vector<int> indices;
00364 for(auto i = 0u; i < constrained_dofs.size(); i++)
00365 {
00366 if(constrained_dofs(i) != 0)
00367 {
00368 indices.push_back(i);
00369 }
00370 }
00371 tool_twist_reduced = VectorXd::Zero(indices.size());
00372
00373
00374 static MatrixXd jacb_transform(6,6);
00375 MatrixXd jacb, jacb_reduced, jacb_pseudo_inv;
00376 MatrixXd identity = MatrixXd::Identity(init_joint_pose.size(),init_joint_pose.size());
00377 VectorXd null_space_proj;
00378 bool project_into_nullspace = (null_proj_weights.size()> 0) && (null_proj_weights >1e-8).any();
00379
00380 unsigned int iteration_count = 0;
00381 bool converged = false;
00382 while(iteration_count < max_iterations)
00383 {
00384
00385 computeTwist(tool_current_pose,tool_goal_pose,constrained_dofs,tool_twist);
00386
00387
00388 if((tool_twist.cwiseAbs().array() <= cartesian_convergence_thresholds).all())
00389 {
00390 if(robot_state->satisfiesBounds(joint_group))
00391 {
00392 converged = true;
00393 }
00394 else
00395 {
00396 ROS_DEBUG("IK joint solution violates bounds");
00397 }
00398 break;
00399 }
00400
00401
00402 for(auto i = 0u; i < indices.size(); i++)
00403 {
00404 tool_twist_reduced(i) = tool_twist(indices[i]);
00405 }
00406
00407
00408 if(!robot_state->getJacobian(joint_group,robot_state->getLinkModel(tool_link),Vector3d::Zero(),jacb))
00409 {
00410 ROS_ERROR("Failed to get Jacobian for link %s",tool_link.c_str());
00411 return false;
00412 }
00413
00414
00415 auto rot = tool_current_pose.inverse().rotation();
00416 jacb_transform.setZero();
00417 jacb_transform.block(0,0,3,3) = rot;
00418 jacb_transform.block(3,3,3,3) = rot;
00419 jacb = jacb_transform*jacb;
00420
00421
00422 reduceJacobian(jacb,indices,jacb_reduced);
00423 calculateDampedPseudoInverse(jacb_reduced,jacb_pseudo_inv,EPSILON,LAMBDA);
00424
00425 if(project_into_nullspace)
00426 {
00427 null_space_proj = (identity - jacb_pseudo_inv*jacb_reduced)*null_space_vector;
00428 null_space_proj = (null_space_proj.array() * null_proj_weights).matrix();
00429
00430
00431 delta_j = (jacb_pseudo_inv*tool_twist_reduced) + null_space_proj;
00432 }
00433 else
00434 {
00435
00436 delta_j = (jacb_pseudo_inv*tool_twist_reduced);
00437 }
00438
00439
00440 joint_pose += (joint_update_rates* delta_j.array()).matrix();
00441 harmonize_joints(joint_pose);
00442
00443
00444 robot_state->setJointGroupPositions(joint_group,joint_pose);
00445 robot_state->updateLinkTransforms();
00446 tool_current_pose = robot_state->getGlobalLinkTransform(tool_link);
00447
00448 iteration_count++;
00449 }
00450
00451 ROS_DEBUG_STREAM_COND(!converged,"Error tool twist "<<tool_twist.transpose());
00452
00453 return converged;
00454 }
00455
00466 static bool solveIK(moveit::core::RobotStatePtr robot_state, const std::string& group_name,const KinematicConfig& config, Eigen::VectorXd& joint_pose)
00467 {
00468
00469 return solveIK(robot_state,
00470 group_name,
00471 config.constrained_dofs,
00472 config.joint_update_rates,
00473 config.cartesian_convergence_thresholds,
00474 config.null_proj_weights,
00475 config.null_space_vector,
00476 config.max_iterations,
00477 config.tool_goal_pose,
00478 config.init_joint_pose,
00479 joint_pose);
00480 }
00481
00492 static bool computeJacobianNullSpace(moveit::core::RobotStatePtr state,std::string group,std::string tool_link,
00493 const Eigen::ArrayXi& constrained_dofs,const Eigen::VectorXd& joint_pose,
00494 Eigen::MatrixXd& jacb_nullspace)
00495 {
00496 using namespace Eigen;
00497 using namespace moveit::core;
00498
00499
00500 const JointModelGroup* joint_group = state->getJointModelGroup(group);
00501 state->setJointGroupPositions(joint_group,joint_pose);
00502 Affine3d tool_pose = state->getGlobalLinkTransform(tool_link);
00503
00504
00505 static MatrixXd jacb_transform(6,6);
00506 MatrixXd jacb, jacb_reduced, jacb_pseudo_inv;
00507 jacb_transform.setZero();
00508
00509 if(!state->getJacobian(joint_group,state->getLinkModel(tool_link),Vector3d::Zero(),jacb))
00510 {
00511 ROS_ERROR("Failed to get Jacobian for link %s",tool_link.c_str());
00512 return false;
00513 }
00514
00515
00516 auto rot = tool_pose.inverse().rotation();
00517 jacb_transform.setZero();
00518 jacb_transform.block(0,0,3,3) = rot;
00519 jacb_transform.block(3,3,3,3) = rot;
00520 jacb = jacb_transform*jacb;
00521
00522
00523 std::vector<int> indices;
00524 for(auto i = 0u; i < constrained_dofs.size(); i++)
00525 {
00526 if(constrained_dofs(i) != 0)
00527 {
00528 indices.push_back(i);
00529 }
00530 }
00531 reduceJacobian(jacb,indices,jacb_reduced);
00532 calculateDampedPseudoInverse(jacb_reduced,jacb_pseudo_inv,EPSILON,LAMBDA);
00533
00534 int num_joints = joint_pose.size();
00535 jacb_nullspace = MatrixXd::Identity(num_joints,num_joints) - jacb_pseudo_inv*jacb_reduced;
00536
00537 return true;
00538 }
00539
00555 static bool solveIK(moveit::core::RobotStatePtr robot_state, const std::string& group_name, const Eigen::ArrayXi& constrained_dofs,
00556 const Eigen::ArrayXd& joint_update_rates,const Eigen::ArrayXd& cartesian_convergence_thresholds, int max_iterations,
00557 const Eigen::Affine3d& tool_goal_pose,const Eigen::VectorXd& init_joint_pose,
00558 Eigen::VectorXd& joint_pose)
00559 {
00560 using namespace Eigen;
00561 using namespace moveit::core;
00562
00563 return solveIK(robot_state,group_name,constrained_dofs,joint_update_rates,cartesian_convergence_thresholds,
00564 ArrayXd::Zero(joint_update_rates.size()),VectorXd::Zero(joint_update_rates.size()),max_iterations,
00565 tool_goal_pose,init_joint_pose,joint_pose);
00566 }
00567
00568
00569 }
00570 }
00571 }
00572
00573
00574
00575 #endif