reem_point_frame.cpp
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (Modified BSD License)
00003  *
00004  *  Copyright (c) 2012, PAL Robotics, S.L.
00005  *  Copyright (c) 2008, Willow Garage, Inc.
00006  *  All rights reserved.
00007  *
00008  *  Redistribution and use in source and binary forms, with or without
00009  *  modification, are permitted provided that the following conditions
00010  *  are met:
00011  *
00012  *   * Redistributions of source code must retain the above copyright
00013  *     notice, this list of conditions and the following disclaimer.
00014  *   * Redistributions in binary form must reproduce the above
00015  *     copyright notice, this list of conditions and the following
00016  *     disclaimer in the documentation and/or other materials provided
00017  *     with the distribution.
00018  *   * Neither the name of PAL Robotics, S.L. nor the names of its
00019  *     contributors may be used to endorse or promote products derived
00020  *     from this software without specific prior written permission.
00021  *
00022  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033  *  POSSIBILITY OF SUCH DAMAGE.
00034  */
00035 
00040 #include <ros/ros.h>
00041 
00042 #include <vector>
00043 #include <boost/scoped_ptr.hpp>
00044 #include <boost/thread/condition.hpp>
00045 
00046 #include <actionlib/server/action_server.h>
00047 #include <geometry_msgs/PoseStamped.h>
00048 #include <geometry_msgs/PointStamped.h>
00049 #include <geometry_msgs/Twist.h>
00050 
00051 #include <kdl/chainfksolver.hpp>
00052 #include <kdl/chain.hpp>
00053 #include <kdl/chainjnttojacsolver.hpp>
00054 #include <kdl/frames.hpp>
00055 #include "kdl/chainfksolverpos_recursive.hpp"
00056 #include "kdl_parser/kdl_parser.hpp"
00057 
00058 #include "tf_conversions/tf_kdl.h"
00059 #include <message_filters/subscriber.h>
00060 #include <tf/message_filter.h>
00061 #include <tf/transform_datatypes.h>
00062 #include <tf/transform_listener.h>
00063 
00064 #include <sensor_msgs/JointState.h>
00065 #include <std_msgs/Float32MultiArray.h>
00066 #include <std_msgs/Int32.h>
00067 
00068 #include <urdf/model.h>
00069 
00070 #include <trajectory_msgs/JointTrajectory.h>
00071 #include <pr2_controllers_msgs/PointHeadAction.h>
00072 #include <pr2_controllers_msgs/QueryTrajectoryState.h>
00073 #include <pr2_controllers_msgs/JointTrajectoryControllerState.h>
00074 
00075 
00076 void printVector3(const char * label, tf::Vector3 v)
00077 {
00078   printf("%s % 7.3f % 7.3f % 7.3f\n", label, v.x(), v.y(), v.z() );
00079 
00080 }
00081 
00082 class ControlHead
00083 {
00084 private:
00085   typedef actionlib::ActionServer<pr2_controllers_msgs::PointHeadAction> PHAS;
00086   typedef PHAS::GoalHandle GoalHandle;
00087 
00088   std::string node_name_;
00089   std::string action_name_;
00090   std::string root_;
00091   std::string tip_;
00092   std::string pan_link_;
00093   std::string default_pointing_frame_;
00094   std::string pointing_frame_;
00095   tf::Vector3 pointing_axis_;
00096   std::vector< std::string> joint_names_;
00097 
00098   ros::NodeHandle nh_, pnh_;
00099   ros::Publisher pub_controller_command_;
00100   ros::Subscriber sub_controller_state_;
00101   ros::Subscriber command_sub_;
00102   ros::ServiceClient cli_query_traj_;
00103   ros::Timer watchdog_timer_;
00104 
00105   PHAS action_server_;
00106   bool has_active_goal_;
00107   GoalHandle active_goal_;
00108   tf::Stamped<tf::Point> target_in_pan_;
00109   std::string pan_parent_;
00110   double success_angle_threshold_;
00111 
00112   geometry_msgs::PointStamped target_;
00113   KDL::Tree tree_;
00114   KDL::Chain chain_;
00115   tf::Point target_in_root_;
00116 
00117   boost::scoped_ptr<KDL::ChainFkSolverPos> pose_solver_;
00118   boost::scoped_ptr<KDL::ChainJntToJacSolver> jac_solver_;
00119 
00120   tf::TransformListener tfl_;
00121   urdf::Model urdf_model_;
00122 
00123 public:
00124 
00125   ControlHead(const ros::NodeHandle &node) :
00126       node_name_(ros::this_node::getName())
00127       , action_name_("point_head_action")
00128       , nh_(node)
00129       , pnh_("~")
00130       , action_server_(nh_, action_name_.c_str(),
00131                          boost::bind(&ControlHead::goalCB, this, _1),
00132                          boost::bind(&ControlHead::cancelCB, this, _1), false)
00133       , has_active_goal_(false)
00134   {
00135     pnh_.param("pan_link", pan_link_, std::string("head_pan_link"));
00136     pnh_.param("default_pointing_frame", default_pointing_frame_, std::string("head_tilt_link"));
00137     pnh_.param("success_angle_threshold", success_angle_threshold_, 0.1);
00138 
00139     if(pan_link_[0] == '/') pan_link_.erase(0, 1);
00140     if(default_pointing_frame_[0] == '/') default_pointing_frame_.erase(0, 1);
00141 
00142     // Connects to the controller
00143     pub_controller_command_ =
00144       nh_.advertise<trajectory_msgs::JointTrajectory>("command", 2);
00145     sub_controller_state_ =
00146       nh_.subscribe("state", 1, &ControlHead::controllerStateCB, this);
00147     cli_query_traj_ =
00148         nh_.serviceClient<pr2_controllers_msgs::QueryTrajectoryState>("/head_traj_controller/query_state");
00149 
00150     // Should only ever happen on first call... move to constructor?
00151     if(tree_.getNrOfJoints() == 0)
00152     {
00153       std::string robot_desc_string;
00154       nh_.param("/robot_description", robot_desc_string, std::string());
00155       ROS_DEBUG("Reading tree from robot_description...");
00156       if (!kdl_parser::treeFromString(robot_desc_string, tree_)){
00157          ROS_ERROR("Failed to construct kdl tree");
00158          exit(-1);
00159       }
00160       if (!urdf_model_.initString(robot_desc_string)){
00161         ROS_ERROR("Failed to parse urdf string for urdf::Model.");
00162         exit(-2);
00163       }
00164     }
00165 
00166     ROS_DEBUG("Tree has %d joints and %d segments.", tree_.getNrOfJoints(), tree_.getNrOfSegments());
00167 
00168     action_server_.start();
00169 
00170     watchdog_timer_ = nh_.createTimer(ros::Duration(1.0), &ControlHead::watchdog, this);
00171   }
00172 
00173   void goalCB(GoalHandle gh)
00174   {
00175     // Before we do anything, we need to know that name of the pan_link's parent, which we will treat as the root.
00176     if (root_.empty())
00177     {
00178       for (int i = 0; i < 10; ++i)
00179       {
00180         try {
00181           tfl_.getParent(pan_link_, ros::Time(), root_);
00182           break;
00183         }
00184         catch (const tf::TransformException &ex) {}
00185         ros::Duration(0.5).sleep();
00186       }
00187       if (root_.empty())
00188       {
00189         ROS_ERROR("Could not get parent of %s in the TF tree", pan_link_.c_str());
00190         gh.setRejected();
00191         return;
00192       }
00193     }
00194     if(root_[0] == '/') root_.erase(0, 1);
00195 
00196     ROS_DEBUG("Got point head goal!");
00197 
00198     // Process pointing frame and axis
00199     const geometry_msgs::PointStamped &target = gh.getGoal()->target;
00200     pointing_frame_ = gh.getGoal()->pointing_frame;
00201     if(pointing_frame_.length() == 0)
00202     {
00203       ROS_WARN("Pointing frame not specified, using %s [1, 0, 0] by default.", default_pointing_frame_.c_str());
00204       pointing_frame_ = default_pointing_frame_;
00205       pointing_axis_ = tf::Vector3(1.0, 0.0, 0.0);
00206     }
00207     else
00208     {
00209       if(pointing_frame_[0] == '/') pointing_frame_.erase(0, 1);
00210       bool ret1 = false;
00211       try
00212       {
00213         std::string error_msg;
00214         ret1 = tfl_.waitForTransform(pan_link_, pointing_frame_, target.header.stamp,
00215                                    ros::Duration(5.0), ros::Duration(0.01), &error_msg);
00216 
00217         tf::vector3MsgToTF(gh.getGoal()->pointing_axis, pointing_axis_);
00218         if(pointing_axis_.length() < 0.1)
00219         {
00220           size_t found = pointing_frame_.find("optical_frame");
00221           if (found != std::string::npos)
00222           {
00223             ROS_WARN("Pointing axis appears to be zero-length. Using [0, 0, 1] as default for an optical frame.");
00224             pointing_axis_ = tf::Vector3(0, 0, 1);
00225           }
00226           else
00227           {
00228             ROS_WARN("Pointing axis appears to be zero-length. Using [1, 0, 0] as default for a non-optical frame.");
00229             pointing_axis_ = tf::Vector3(1, 0, 0);
00230           }
00231         }
00232         else
00233         {
00234           pointing_axis_.normalize();
00235         }
00236       }
00237       catch(const tf::TransformException &ex)
00238       {
00239         ROS_ERROR("Transform failure (%d): %s", ret1, ex.what());
00240         gh.setRejected();
00241         return;
00242       }
00243     }
00244 
00245     //Put the target point in the root frame (usually torso_lift_link).
00246     bool ret1 = false;
00247     try
00248     {
00249       std::string error_msg;
00250       ret1 = tfl_.waitForTransform(root_.c_str(), target.header.frame_id, target.header.stamp,
00251                                        ros::Duration(5.0), ros::Duration(0.01), &error_msg);
00252 
00253       geometry_msgs::PointStamped target_in_root_msg;
00254       tfl_.transformPoint(root_.c_str(), target, target_in_root_msg );
00255       tf::pointMsgToTF(target_in_root_msg.point, target_in_root_);
00256       ROS_DEBUG_STREAM("Target point in base frame: (" << target_in_root_[0] << ", " << target_in_root_[1] << ", " << target_in_root_[2] << ")");
00257     }
00258     catch(const tf::TransformException &ex)
00259     {
00260       ROS_ERROR("Transform failure (%d): %s", ret1, ex.what());
00261       gh.setRejected();
00262       return;
00263     }
00264 
00265     if( tip_.compare(pointing_frame_) != 0 )
00266     {
00267       bool success = tree_.getChain(root_.c_str(), pointing_frame_.c_str(), chain_);
00268       if( !success )
00269       {
00270         ROS_ERROR("Couldn't create chain from %s to %s.", root_.c_str(), pointing_frame_.c_str());
00271         gh.setRejected();
00272         return;
00273       }
00274       tip_ = pointing_frame_;
00275 
00276       pose_solver_.reset(new KDL::ChainFkSolverPos_recursive(chain_));
00277       jac_solver_.reset(new KDL::ChainJntToJacSolver(chain_));
00278       joint_names_.resize(chain_.getNrOfJoints());
00279     }
00280 
00281     unsigned int joints = chain_.getNrOfJoints();
00282 
00283 //    int segments = chain_.getNrOfSegments();
00284 //    ROS_INFO("Parsed urdf from %s to %s and found %d joints and %d segments.", root_.c_str(), pointing_frame_.c_str(), joints, segments);
00285 //    for(int i = 0; i < segments; i++)
00286 //    {
00287 //      KDL::Segment segment = chain_.getSegment(i);
00288 //      ROS_INFO("Segment %d, %s: joint %s type %s",
00289 //               i, segment.getName().c_str(), segment.getJoint().getName().c_str(), segment.getJoint().getTypeName().c_str() );
00290 //    }
00291 
00292     KDL::JntArray jnt_pos(joints), jnt_eff(joints);
00293     KDL::Jacobian jacobian(joints);
00294 
00295     pr2_controllers_msgs::QueryTrajectoryState traj_state;
00296     traj_state.request.time = ros::Time::now() + ros::Duration(0.01);
00297     if (!cli_query_traj_.call(traj_state))
00298     {
00299       ROS_ERROR("Service call to query controller trajectory failed.");
00300       gh.setRejected();
00301       return;
00302     }
00303     if(traj_state.response.name.size() != joints)
00304     {
00305       ROS_ERROR("Number of joints mismatch: urdf chain vs. trajectory controller state.");
00306       gh.setRejected();
00307       return;
00308     }
00309     std::vector<urdf::JointLimits> limits_(joints);
00310 
00311     // Get initial joint positions and joint limits.
00312     for(unsigned int i = 0; i < joints; i++)
00313     {
00314       joint_names_[i] = traj_state.response.name[i];
00315       limits_[i] = *(urdf_model_.joints_[joint_names_[i].c_str()]->limits);
00316       ROS_DEBUG("Joint %d %s: %f, limits: %f %f", i, traj_state.response.name[i].c_str(), traj_state.response.position[i], limits_[i].lower, limits_[i].upper);
00317       //jnt_pos(i) = traj_state.response.position[i];
00318       jnt_pos(i) = 0;
00319     }
00320 
00321     int count = 0;
00322     int limit_flips = 0;
00323     float correction_angle = 2*M_PI;
00324     float correction_delta = 2*M_PI;
00325     while( ros::ok() && fabs(correction_delta) > 0.001)
00326     {
00327       //get the pose and jacobian for the current joint positions
00328       KDL::Frame pose;
00329       pose_solver_->JntToCart(jnt_pos, pose);
00330       jac_solver_->JntToJac(jnt_pos, jacobian);
00331 
00332       tf::Transform frame_in_root;
00333       tf::PoseKDLToTF(pose, frame_in_root);
00334 
00335       tf::Vector3 axis_in_frame = pointing_axis_.normalized();
00336       tf::Vector3 target_from_frame = (target_in_root_ - frame_in_root.getOrigin()).normalized();
00337       tf::Vector3 current_in_frame = frame_in_root.getBasis().inverse()*target_from_frame;
00338       float prev_correction = correction_angle;
00339       ROS_DEBUG_STREAM("current_in_frame (target point in stereo_optical_frame): (" << current_in_frame[0] << ", " << current_in_frame[1] << ", " << current_in_frame[2] << ")");
00340       ROS_DEBUG_STREAM("axis_in_frame ( [0,0,1] in stereo_optical_frame): (" << axis_in_frame[0] << ", " << axis_in_frame[1] << ", " << axis_in_frame[2] << ")");
00341       correction_angle = current_in_frame.angle(axis_in_frame);
00342       correction_delta = correction_angle - prev_correction;
00343       ROS_DEBUG("At step %d, joint poses are %.4f and %.4f, angle error is %f", count, jnt_pos(0), jnt_pos(1), correction_angle);
00344       if(correction_angle < 0.5*success_angle_threshold_) break;
00345       tf::Vector3 correction_axis = frame_in_root.getBasis()*(axis_in_frame.cross(current_in_frame).normalized());
00346       //printVector3("correction_axis in root:", correction_axis);
00347       tf::Transform correction_tf(tf::Quaternion(correction_axis, 0.5*correction_angle), tf::Vector3(0,0,0));
00348       KDL::Frame correction_kdl;
00349       tf::TransformTFToKDL(correction_tf, correction_kdl);
00350 
00351       // We apply a "wrench" proportional to the desired correction
00352       KDL::Frame identity_kdl;
00353       KDL::Twist twist = diff(correction_kdl, identity_kdl);
00354       KDL::Wrench wrench_desi;
00355       for (unsigned int i=0; i<6; i++)
00356         wrench_desi(i) = -1.0*twist(i);
00357 
00358       // Converts the "wrench" into "joint corrections" with a jacbobian-transpose
00359       for (unsigned int i = 0; i < joints; i++)
00360       {
00361         jnt_eff(i) = 0;
00362         for (unsigned int j=0; j<6; j++)
00363           jnt_eff(i) += (jacobian(j,i) * wrench_desi(j));
00364         jnt_pos(i) += jnt_eff(i);
00365       }
00366 
00367       // account for pan_link joint limit in back.
00368      // if(jnt_pos(0) < limits_[0].lower && limit_flips++ == 0){ jnt_pos(0) += 1.5*M_PI; }
00369      // if(jnt_pos(0) > limits_[0].upper && limit_flips++ == 0){ jnt_pos(0) -= 1.5*M_PI; }
00370 
00371 
00372       jnt_pos(0) = std::max(limits_[0].lower, jnt_pos(0));
00373       jnt_pos(0) = std::min(limits_[0].upper, jnt_pos(0));
00374 
00375       jnt_pos(1) = std::max(limits_[1].lower, jnt_pos(1));
00376       jnt_pos(1) = std::min(limits_[1].upper, jnt_pos(1));
00377 
00378       count++;
00379 
00380       if(limit_flips > 1){
00381         ROS_ERROR("Goal is out of joint limits, trying to point there anyway... \n");
00382         break;
00383       }
00384     }
00385     ROS_DEBUG("Iterative solver took %d steps", count);
00386     
00387     std::vector<double> q_goal(joints);
00388 
00389     for(unsigned int i = 0; i < joints; i++)
00390     {
00391       jnt_pos(i) = std::max(limits_[i].lower, jnt_pos(i));
00392       jnt_pos(i) = std::min(limits_[i].upper, jnt_pos(i));
00393       q_goal[i] = jnt_pos(i);
00394       ROS_DEBUG("Joint %d %s: %f", i, joint_names_[i].c_str(), jnt_pos(i));
00395     }
00396 
00397     if (has_active_goal_)
00398     {
00399       active_goal_.setCanceled();
00400       has_active_goal_ = false;
00401     }
00402 
00403     gh.setAccepted();
00404     active_goal_ = gh;
00405     has_active_goal_ = true;
00406 
00407 
00408     // Computes the duration of the movement.
00409     ros::Duration min_duration(0.01);
00410 
00411     if (gh.getGoal()->min_duration > min_duration)
00412         min_duration = gh.getGoal()->min_duration;
00413 
00414     // Determines if we need to increase the duration of the movement in order to enforce a maximum velocity.
00415     if (gh.getGoal()->max_velocity > 0)
00416     {
00417       // Very approximate velocity limiting.
00418       double dist = sqrt(pow(q_goal[0] - traj_state.response.position[0], 2) +
00419                          pow(q_goal[1] - traj_state.response.position[1], 2));
00420       ros::Duration limit_from_velocity(dist / gh.getGoal()->max_velocity);
00421       if (limit_from_velocity > min_duration)
00422         min_duration = limit_from_velocity;
00423     }
00424 
00425 
00426     // Computes the command to send to the trajectory controller.
00427     trajectory_msgs::JointTrajectory traj;
00428     traj.header.stamp = traj_state.request.time;
00429 
00430     traj.joint_names.push_back(traj_state.response.name[0]);
00431     traj.joint_names.push_back(traj_state.response.name[1]);
00432 
00433     traj.points.resize(2);
00434     traj.points[0].positions = traj_state.response.position;
00435     traj.points[0].velocities = traj_state.response.velocity;
00436     traj.points[0].time_from_start = ros::Duration(0.0);
00437     traj.points[1].positions = q_goal;
00438     traj.points[1].velocities.push_back(0);
00439     traj.points[1].velocities.push_back(0);
00440     traj.points[1].time_from_start = ros::Duration(min_duration);
00441 
00442     pub_controller_command_.publish(traj);
00443   }
00444 
00445   
00446   void watchdog(const ros::TimerEvent &e)
00447   {
00448     ros::Time now = ros::Time::now();
00449 
00450     // Aborts the active goal if the controller does not appear to be active.
00451     if (has_active_goal_)
00452     {
00453       bool should_abort = false;
00454       if (!last_controller_state_)
00455       {
00456         should_abort = true;
00457         ROS_WARN("Aborting goal because we have never heard a controller state message.");
00458       }
00459       else if ((now - last_controller_state_->header.stamp) > ros::Duration(5.0))
00460       {
00461         should_abort = true;
00462         ROS_WARN("Aborting goal because we haven't heard from the controller in %.3lf seconds",
00463                  (now - last_controller_state_->header.stamp).toSec());
00464       }
00465 
00466       if (should_abort)
00467       {
00468         // Stops the controller.
00469         trajectory_msgs::JointTrajectory empty;
00470         empty.joint_names = joint_names_;
00471         pub_controller_command_.publish(empty);
00472 
00473         // Marks the current goal as aborted.
00474         active_goal_.setAborted();
00475         has_active_goal_ = false;
00476       }
00477     }
00478   }
00479 
00480 
00481   void cancelCB(GoalHandle gh)
00482   {
00483     if (active_goal_ == gh)
00484     {
00485       // Stops the controller.
00486       trajectory_msgs::JointTrajectory empty;
00487       empty.joint_names = joint_names_;
00488       pub_controller_command_.publish(empty);
00489 
00490       // Marks the current goal as canceled.
00491       active_goal_.setCanceled();
00492       has_active_goal_ = false;
00493     }
00494   }
00495 
00496   pr2_controllers_msgs::JointTrajectoryControllerStateConstPtr last_controller_state_;
00497   void controllerStateCB(const pr2_controllers_msgs::JointTrajectoryControllerStateConstPtr &msg)
00498   {
00499     last_controller_state_ = msg;
00500     ros::Time now = ros::Time::now();
00501 
00502     if (!has_active_goal_)
00503       return;
00504 
00506     try
00507     {
00508       KDL::JntArray jnt_pos(msg->joint_names.size());
00509       for(unsigned int i = 0; i < msg->joint_names.size(); i++)
00510         jnt_pos(i) = msg->actual.positions[i];
00511 
00512       KDL::Frame pose;
00513       pose_solver_->JntToCart(jnt_pos, pose);
00514 
00515       tf::Transform frame_in_root;
00516       tf::PoseKDLToTF(pose, frame_in_root);
00517 
00518       tf::Vector3 axis_in_frame = pointing_axis_.normalized();
00519       tf::Vector3 target_from_frame = target_in_root_ - frame_in_root.getOrigin();
00520 
00521       target_from_frame.normalize();
00522       tf::Vector3 current_in_frame = frame_in_root.getBasis().inverse()*target_from_frame;
00523 
00524       pr2_controllers_msgs::PointHeadFeedback feedback;
00525       feedback.pointing_angle_error = current_in_frame.angle(axis_in_frame);
00526       active_goal_.publishFeedback(feedback);
00527 
00528       if (feedback.pointing_angle_error < success_angle_threshold_)
00529       {
00530         active_goal_.setSucceeded();
00531         has_active_goal_ = false;
00532       }
00533     }
00534     catch(const tf::TransformException &ex)
00535     {
00536       ROS_ERROR("Could not transform: %s", ex.what());
00537     }
00538   }
00539 
00540 
00541 };
00542 
00543 int main(int argc, char** argv)
00544 {
00545   ros::init(argc, argv, "point_head_action");
00546   ros::NodeHandle node;
00547   ControlHead ch(node);
00548   ros::spin();
00549   return 0;
00550 }
00551 


reem_head_action
Author(s): Stuart Glaser. Adapted by Hilario Tome.
autogenerated on Thu Jan 2 2014 11:41:57