354 lines
13 KiB
C++
354 lines
13 KiB
C++
// bsd license blah blah
|
|
#include <cstring>
|
|
#include <ros/ros.h>
|
|
#include <tf/transform_listener.h>
|
|
#include <tf_conversions/tf_kdl.h>
|
|
#include <tf/transform_datatypes.h>
|
|
#include <kdl_parser/kdl_parser.hpp>
|
|
#include <kdl/jntarray.hpp>
|
|
#include <kdl/chainiksolverpos_nr_jl.hpp>
|
|
#include <kdl/chainiksolvervel_pinv.hpp>
|
|
#include <kdl/chainfksolverpos_recursive.hpp>
|
|
#include <kinematics_msgs/GetPositionFK.h>
|
|
#include <kinematics_msgs/GetPositionIK.h>
|
|
#include <kinematics_msgs/GetKinematicSolverInfo.h>
|
|
#include <kinematics_msgs/KinematicSolverInfo.h>
|
|
using std::string;
|
|
|
|
static const std::string IK_SERVICE = "get_ik";
|
|
static const std::string FK_SERVICE = "get_fk";
|
|
static const std::string IK_INFO_SERVICE = "get_ik_solver_info";
|
|
static const std::string FK_INFO_SERVICE = "get_fk_solver_info";
|
|
|
|
class Kinematics {
|
|
public:
|
|
Kinematics();
|
|
bool init();
|
|
|
|
private:
|
|
ros::NodeHandle nh, nh_private;
|
|
std::string root_name, tip_name;
|
|
KDL::JntArray joint_min, joint_max;
|
|
KDL::Chain chain;
|
|
unsigned int num_joints;
|
|
|
|
KDL::ChainFkSolverPos_recursive* fk_solver;
|
|
KDL::ChainIkSolverPos_NR_JL *ik_solver_pos;
|
|
KDL::ChainIkSolverVel_pinv* ik_solver_vel;
|
|
|
|
ros::ServiceServer ik_service,ik_solver_info_service;
|
|
ros::ServiceServer fk_service,fk_solver_info_service;
|
|
|
|
tf::TransformListener tf_listener;
|
|
|
|
kinematics_msgs::KinematicSolverInfo info;
|
|
|
|
bool loadModel(const std::string xml);
|
|
bool readJoints(urdf::Model &robot_model);
|
|
int getJointIndex(const std::string &name);
|
|
int getKDLSegmentIndex(const std::string &name);
|
|
|
|
/**
|
|
* @brief This is the basic IK service method that will compute and return an IK solution.
|
|
* @param A request message. See service definition for GetPositionIK for more information on this message.
|
|
* @param The response message. See service definition for GetPositionIK for more information on this message.
|
|
*/
|
|
bool getPositionIK(kinematics_msgs::GetPositionIK::Request &request,
|
|
kinematics_msgs::GetPositionIK::Response &response);
|
|
|
|
/**
|
|
* @brief This is the basic kinematics info service that will return information about the kinematics node.
|
|
* @param A request message. See service definition for GetKinematicSolverInfo for more information on this message.
|
|
* @param The response message. See service definition for GetKinematicSolverInfo for more information on this message.
|
|
*/
|
|
bool getIKSolverInfo(kinematics_msgs::GetKinematicSolverInfo::Request &request,
|
|
kinematics_msgs::GetKinematicSolverInfo::Response &response);
|
|
|
|
/**
|
|
* @brief This is the basic kinematics info service that will return information about the kinematics node.
|
|
* @param A request message. See service definition for GetKinematicSolverInfo for more information on this message.
|
|
* @param The response message. See service definition for GetKinematicSolverInfo for more information on this message.
|
|
*/
|
|
bool getFKSolverInfo(kinematics_msgs::GetKinematicSolverInfo::Request &request,
|
|
kinematics_msgs::GetKinematicSolverInfo::Response &response);
|
|
|
|
/**
|
|
* @brief This is the basic forward kinematics service that will return information about the kinematics node.
|
|
* @param A request message. See service definition for GetPositionFK for more information on this message.
|
|
* @param The response message. See service definition for GetPositionFK for more information on this message.
|
|
*/
|
|
bool getPositionFK(kinematics_msgs::GetPositionFK::Request &request,
|
|
kinematics_msgs::GetPositionFK::Response &response);
|
|
};
|
|
|
|
|
|
|
|
Kinematics::Kinematics(): nh_private ("~") {
|
|
}
|
|
|
|
bool Kinematics::init() {
|
|
// Get URDF XML
|
|
std::string urdf_xml, full_urdf_xml;
|
|
nh.param("urdf_xml",urdf_xml,std::string("robot_description"));
|
|
nh.searchParam(urdf_xml,full_urdf_xml);
|
|
ROS_DEBUG("Reading xml file from parameter server");
|
|
std::string result;
|
|
if (!nh.getParam(full_urdf_xml, result)) {
|
|
ROS_FATAL("Could not load the xml from parameter server: %s", urdf_xml.c_str());
|
|
return false;
|
|
}
|
|
|
|
// Get Root and Tip From Parameter Service
|
|
if (!nh_private.getParam("root_name", root_name)) {
|
|
ROS_FATAL("GenericIK: No root name found on parameter server");
|
|
return false;
|
|
}
|
|
if (!nh_private.getParam("tip_name", tip_name)) {
|
|
ROS_FATAL("GenericIK: No tip name found on parameter server");
|
|
return false;
|
|
}
|
|
|
|
// Load and Read Models
|
|
if (!loadModel(result)) {
|
|
ROS_FATAL("Could not load models!");
|
|
return false;
|
|
}
|
|
|
|
// Get Solver Parameters
|
|
int maxIterations;
|
|
double epsilon;
|
|
|
|
nh_private.param("maxIterations", maxIterations, 1000);
|
|
nh_private.param("epsilon", epsilon, 1e-2);
|
|
|
|
// Build Solvers
|
|
fk_solver = new KDL::ChainFkSolverPos_recursive(chain);
|
|
ik_solver_vel = new KDL::ChainIkSolverVel_pinv(chain);
|
|
ik_solver_pos = new KDL::ChainIkSolverPos_NR_JL(chain, joint_min, joint_max,
|
|
*fk_solver, *ik_solver_vel, maxIterations, epsilon);
|
|
|
|
ROS_INFO("Advertising services");
|
|
fk_service = nh_private.advertiseService(FK_SERVICE,&Kinematics::getPositionFK,this);
|
|
ik_service = nh_private.advertiseService(IK_SERVICE,&Kinematics::getPositionIK,this);
|
|
ik_solver_info_service = nh_private.advertiseService(IK_INFO_SERVICE,&Kinematics::getIKSolverInfo,this);
|
|
fk_solver_info_service = nh_private.advertiseService(FK_INFO_SERVICE,&Kinematics::getFKSolverInfo,this);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Kinematics::loadModel(const std::string xml) {
|
|
urdf::Model robot_model;
|
|
KDL::Tree tree;
|
|
|
|
if (!robot_model.initString(xml)) {
|
|
ROS_FATAL("Could not initialize robot model");
|
|
return -1;
|
|
}
|
|
if (!kdl_parser::treeFromString(xml, tree)) {
|
|
ROS_ERROR("Could not initialize tree object");
|
|
return false;
|
|
}
|
|
if (!tree.getChain(root_name, tip_name, chain)) {
|
|
ROS_ERROR("Could not initialize chain object");
|
|
return false;
|
|
}
|
|
|
|
if (!readJoints(robot_model)) {
|
|
ROS_FATAL("Could not read information about the joints");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Kinematics::readJoints(urdf::Model &robot_model) {
|
|
num_joints = 0;
|
|
// get joint maxs and mins
|
|
boost::shared_ptr<const urdf::Link> link = robot_model.getLink(tip_name);
|
|
boost::shared_ptr<const urdf::Joint> joint;
|
|
|
|
while (link && link->name != root_name) {
|
|
joint = robot_model.getJoint(link->parent_joint->name);
|
|
if (!joint) {
|
|
ROS_ERROR("Could not find joint: %s",link->parent_joint->name.c_str());
|
|
return false;
|
|
}
|
|
if (joint->type != urdf::Joint::UNKNOWN && joint->type != urdf::Joint::FIXED) {
|
|
ROS_INFO( "adding joint: [%s]", joint->name.c_str() );
|
|
num_joints++;
|
|
}
|
|
link = robot_model.getLink(link->getParent()->name);
|
|
}
|
|
|
|
joint_min.resize(num_joints);
|
|
joint_max.resize(num_joints);
|
|
info.joint_names.resize(num_joints);
|
|
info.limits.resize(num_joints);
|
|
|
|
link = robot_model.getLink(tip_name);
|
|
unsigned int i = 0;
|
|
while (link && i < num_joints) {
|
|
joint = robot_model.getJoint(link->parent_joint->name);
|
|
if (joint->type != urdf::Joint::UNKNOWN && joint->type != urdf::Joint::FIXED) {
|
|
ROS_INFO( "getting bounds for joint: [%s]", joint->name.c_str() );
|
|
|
|
float lower, upper;
|
|
int hasLimits;
|
|
if ( joint->type != urdf::Joint::CONTINUOUS ) {
|
|
lower = joint->limits->lower;
|
|
upper = joint->limits->upper;
|
|
hasLimits = 1;
|
|
} else {
|
|
lower = -M_PI;
|
|
upper = M_PI;
|
|
hasLimits = 0;
|
|
}
|
|
int index = num_joints - i -1;
|
|
|
|
joint_min.data[index] = lower;
|
|
joint_max.data[index] = upper;
|
|
info.joint_names[index] = joint->name;
|
|
info.limits[index].joint_name = joint->name;
|
|
info.limits[index].has_position_limits = hasLimits;
|
|
info.limits[index].min_position = lower;
|
|
info.limits[index].max_position = upper;
|
|
i++;
|
|
}
|
|
link = robot_model.getLink(link->getParent()->name);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
int Kinematics::getJointIndex(const std::string &name) {
|
|
for (unsigned int i=0; i < info.joint_names.size(); i++) {
|
|
if (info.joint_names[i] == name)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int Kinematics::getKDLSegmentIndex(const std::string &name) {
|
|
int i=0;
|
|
while (i < (int)chain.getNrOfSegments()) {
|
|
if (chain.getSegment(i).getName() == name) {
|
|
return i+1;
|
|
}
|
|
i++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool Kinematics::getPositionIK(kinematics_msgs::GetPositionIK::Request &request,
|
|
kinematics_msgs::GetPositionIK::Response &response) {
|
|
|
|
geometry_msgs::PoseStamped pose_msg_in = request.ik_request.pose_stamped;
|
|
tf::Stamped<tf::Pose> transform;
|
|
tf::poseStampedMsgToTF( pose_msg_in, transform );
|
|
|
|
//Do the IK
|
|
KDL::JntArray jnt_pos_in;
|
|
KDL::JntArray jnt_pos_out;
|
|
jnt_pos_in.resize(num_joints);
|
|
for (unsigned int i=0; i < num_joints; i++) {
|
|
int tmp_index = getJointIndex(request.ik_request.ik_seed_state.joint_state.name[i]);
|
|
if (tmp_index >=0) {
|
|
jnt_pos_in(tmp_index) = request.ik_request.ik_seed_state.joint_state.position[i];
|
|
} else {
|
|
ROS_ERROR("i: %d, No joint index for %s",i,request.ik_request.ik_seed_state.joint_state.name[i].c_str());
|
|
}
|
|
}
|
|
|
|
// populate F_dest from tf::Transform parameter
|
|
KDL::Frame F_dest;
|
|
tf::TransformTFToKDL(transform, F_dest);
|
|
|
|
int ik_valid = ik_solver_pos->CartToJnt(jnt_pos_in, F_dest, jnt_pos_out);
|
|
|
|
if (ik_valid >= 0) {
|
|
response.solution.joint_state.name = info.joint_names;
|
|
response.solution.joint_state.position.resize(num_joints);
|
|
for (unsigned int i=0; i < num_joints; i++) {
|
|
response.solution.joint_state.position[i] = jnt_pos_out(i);
|
|
ROS_DEBUG("IK Solution: %s %d: %f",response.solution.joint_state.name[i].c_str(),i,jnt_pos_out(i));
|
|
}
|
|
response.error_code.val = response.error_code.SUCCESS;
|
|
return true;
|
|
} else {
|
|
ROS_DEBUG("An IK solution could not be found");
|
|
response.error_code.val = response.error_code.NO_IK_SOLUTION;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool Kinematics::getIKSolverInfo(kinematics_msgs::GetKinematicSolverInfo::Request &request,
|
|
kinematics_msgs::GetKinematicSolverInfo::Response &response) {
|
|
response.kinematic_solver_info = info;
|
|
return true;
|
|
}
|
|
|
|
bool Kinematics::getFKSolverInfo(kinematics_msgs::GetKinematicSolverInfo::Request &request,
|
|
kinematics_msgs::GetKinematicSolverInfo::Response &response) {
|
|
response.kinematic_solver_info = info;
|
|
return true;
|
|
}
|
|
|
|
bool Kinematics::getPositionFK(kinematics_msgs::GetPositionFK::Request &request,
|
|
kinematics_msgs::GetPositionFK::Response &response) {
|
|
KDL::Frame p_out;
|
|
KDL::JntArray jnt_pos_in;
|
|
geometry_msgs::PoseStamped pose;
|
|
tf::Stamped<tf::Pose> tf_pose;
|
|
|
|
jnt_pos_in.resize(num_joints);
|
|
for (unsigned int i=0; i < num_joints; i++) {
|
|
int tmp_index = getJointIndex(request.robot_state.joint_state.name[i]);
|
|
if (tmp_index >=0)
|
|
jnt_pos_in(tmp_index) = request.robot_state.joint_state.position[i];
|
|
}
|
|
|
|
response.pose_stamped.resize(request.fk_link_names.size());
|
|
response.fk_link_names.resize(request.fk_link_names.size());
|
|
|
|
bool valid = true;
|
|
for (unsigned int i=0; i < request.fk_link_names.size(); i++) {
|
|
int segmentIndex = getKDLSegmentIndex(request.fk_link_names[i]);
|
|
ROS_DEBUG("End effector index: %d",segmentIndex);
|
|
ROS_DEBUG("Chain indices: %d",chain.getNrOfSegments());
|
|
if (fk_solver->JntToCart(jnt_pos_in,p_out,segmentIndex) >=0) {
|
|
tf_pose.frame_id_ = root_name;
|
|
tf_pose.stamp_ = ros::Time();
|
|
tf::PoseKDLToTF(p_out,tf_pose);
|
|
try {
|
|
tf_listener.transformPose(request.header.frame_id,tf_pose,tf_pose);
|
|
} catch (...) {
|
|
ROS_ERROR("Could not transform FK pose to frame: %s",request.header.frame_id.c_str());
|
|
response.error_code.val = response.error_code.FRAME_TRANSFORM_FAILURE;
|
|
return false;
|
|
}
|
|
tf::poseStampedTFToMsg(tf_pose,pose);
|
|
response.pose_stamped[i] = pose;
|
|
response.fk_link_names[i] = request.fk_link_names[i];
|
|
response.error_code.val = response.error_code.SUCCESS;
|
|
} else {
|
|
ROS_ERROR("Could not compute FK for %s",request.fk_link_names[i].c_str());
|
|
response.error_code.val = response.error_code.NO_FK_SOLUTION;
|
|
valid = false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
ros::init(argc, argv, "arm_kinematics");
|
|
Kinematics k;
|
|
if (k.init()<0) {
|
|
ROS_ERROR("Could not initialize kinematics node");
|
|
return -1;
|
|
}
|
|
|
|
ros::spin();
|
|
return 0;
|
|
}
|
|
|