jog_arm_server.cpp
Go to the documentation of this file.
00001 
00002 //      Title     : jog_arm_server.cpp
00003 //      Project   : jog_arm
00004 //      Created   : 3/9/2017
00005 //      Author    : Brian O'Neil, Blake Anderson, Andy Zelenak
00006 //      Platforms : Ubuntu 64-bit
00007 //      Copyright : Copyright© The University of Texas at Austin, 2014-2017. All rights reserved.
00008 //                 
00009 //          All files within this directory are subject to the following, unless an alternative
00010 //          license is explicitly included within the text of each file.
00011 //
00012 //          This software and documentation constitute an unpublished work
00013 //          and contain valuable trade secrets and proprietary information
00014 //          belonging to the University. None of the foregoing material may be
00015 //          copied or duplicated or disclosed without the express, written
00016 //          permission of the University. THE UNIVERSITY EXPRESSLY DISCLAIMS ANY
00017 //          AND ALL WARRANTIES CONCERNING THIS SOFTWARE AND DOCUMENTATION,
00018 //          INCLUDING ANY WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
00019 //          PARTICULAR PURPOSE, AND WARRANTIES OF PERFORMANCE, AND ANY WARRANTY
00020 //          THAT MIGHT OTHERWISE ARISE FROM COURSE OF DEALING OR USAGE OF TRADE.
00021 //          NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH RESPECT TO THE USE OF
00022 //          THE SOFTWARE OR DOCUMENTATION. Under no circumstances shall the
00023 //          University be liable for incidental, special, indirect, direct or
00024 //          consequential damages or loss of profits, interruption of business,
00025 //          or related expenses which may arise from use of software or documentation,
00026 //          including but not limited to those resulting from defects in software
00027 //          and/or documentation, or loss or inaccuracy of data of any kind.
00028 //
00030 
00031 #include <jog_arm_server.h>
00032 
00033 namespace jog_arm {
00034 
00035 JogArmServer::JogArmServer(std::string move_group_name, std::string cmd_topic_name) :
00036   nh_("~"),
00037   arm_(move_group_name),
00038   spinner_(1) // There's no noticeable improvement from >1 thread. Tested by AJZ, 3/16/2017
00039 {
00041   joint_sub_ = nh_.subscribe("/joint_states", 1, &JogArmServer::jointStateCB, this);
00042 
00043   cmd_sub_ = nh_.subscribe(cmd_topic_name, 1, &JogArmServer::commandCB, this);
00044 
00046   robot_model_loader::RobotModelLoader model_loader("robot_description");
00047   robot_model::RobotModelPtr kinematic_model = model_loader.getModel();
00048 
00049   kinematic_state_ = boost::shared_ptr<robot_state::RobotState>(new robot_state::RobotState(kinematic_model));
00050   kinematic_state_->setToDefaultValues();
00051 
00052   joint_model_group_ = kinematic_model->getJointModelGroup(move_group_name);
00053 
00054   arm_.setPlannerId( "RRTConnectkConfigDefault" );
00055   arm_.setMaxVelocityScalingFactor( 0.1 );
00056 
00057   const std::vector<std::string> &joint_names = joint_model_group_->getJointModelNames();
00058   std::vector<double> dummy_joint_values; // Not used anywhere except copyJointGroupPositions(). Prob just needed as an argument
00059   kinematic_state_ -> copyJointGroupPositions(joint_model_group_, dummy_joint_values);
00060 
00061   // Wait for an update on the initial joints
00062   ros::topic::waitForMessage<sensor_msgs::JointState>("/joint_states");
00063   
00064   joint_names_ = arm_.getJointNames();
00065   
00066   spinner_.start();
00067 }
00068 
00069 void JogArmServer::commandCB(geometry_msgs::TwistStampedConstPtr msg)
00070 {
00071  
00072   // Convert the cmd to the MoveGroup planning frame.
00073   
00074   const std::string planning_frame = arm_.getPlanningFrame();
00075 
00076   try {
00077     listener_.waitForTransform( msg->header.frame_id, planning_frame, ros::Time::now(), ros::Duration(0.2) );
00078   } catch (tf::TransformException ex) {
00079     ROS_ERROR("JogArmServer::commandCB - Failed to transform command to planning frame.");
00080     return;
00081   }
00082   // To transform, these vectors need to be stamped. See answers.ros.org Q#199376 (Annoying! Maybe do a PR.)
00083   // Transform the linear component of the cmd message
00084   geometry_msgs::Vector3Stamped lin_vector;
00085   lin_vector.vector = msg->twist.linear;
00086   lin_vector.header.frame_id = msg->header.frame_id;
00087   listener_.transformVector(planning_frame, lin_vector, lin_vector);
00088   
00089   geometry_msgs::Vector3Stamped rot_vector;
00090   rot_vector.vector = msg->twist.angular;
00091   rot_vector.header.frame_id = msg->header.frame_id;
00092   listener_.transformVector(planning_frame, rot_vector, rot_vector);
00093   
00094   // Put these components back into a TwistStamped
00095   geometry_msgs::TwistStamped twist_cmd;
00096   twist_cmd.twist.linear = lin_vector.vector;
00097   twist_cmd.twist.angular = rot_vector.vector;
00098   
00099   // Apply scaling
00100   Vector6d scaling_factor;
00101   scaling_factor << 0.1, 0.1, 0.1, 0.1, 0.1, 0.1;  
00102   const Vector6d delta_x = scaleCommand(twist_cmd, scaling_factor);
00103 
00104   kinematic_state_->setVariableValues(current_joints_);
00105   
00106   // Convert from cartesian commands to joint commands
00107   Eigen::MatrixXd jacobian = kinematic_state_->getJacobian(joint_model_group_);
00108   const Eigen::VectorXd delta_theta = pseudoInverse(jacobian)*delta_x;
00109 
00110   // Add joint increments to current joints
00111   sensor_msgs::JointState new_theta = current_joints_;
00112   if (!addJointIncrements(new_theta, delta_theta)) {
00113     return;
00114   }
00115 
00116   // Check the Jacobian with these new joints. Halt before approaching a singularity.
00117   kinematic_state_->setVariableValues(new_theta);
00118   jacobian = kinematic_state_->getJacobian(joint_model_group_);
00119   
00120   // Set back to current joint values
00121   kinematic_state_->setVariableValues(current_joints_);
00122 
00123   // Verify that the future Jacobian is well-conditioned
00124   if (!checkConditionNumber(jacobian, 20)) {
00125     ROS_ERROR("JogArmServer::commandCB - Jacobian is ill-conditioned.");
00126     return;
00127   }
00128   
00129   // Set planning goal
00130   if (!arm_.setJointValueTarget(new_theta)) {
00131     ROS_ERROR("JogArmServer::commandCB - Failed to set joint target.");
00132     return;
00133   }
00134 
00135   if (!arm_.move()) {
00136    ROS_ERROR("JogArmServer::commandCB - Jogging movement failed.");
00137    return;
00138   }
00139 }
00140 
00141 void JogArmServer::jointStateCB(sensor_msgs::JointStateConstPtr msg)
00142 {
00143   // Check that the msg contains joints
00144   if (msg->name.empty()) {
00145     return;
00146   }
00147   
00148   // Check if this message contains the joints for our move_group
00149   std::string joint = msg->name.front();
00150   
00151   if (std::find(joint_names_.begin(), joint_names_.end(), joint) != joint_names_.end()) {
00152     current_joints_ = *msg;
00153   }
00154 }
00155 
00156 JogArmServer::Vector6d JogArmServer::scaleCommand(const geometry_msgs::TwistStamped &command, const Vector6d& scalar) const
00157 {
00158   Vector6d result;
00159   
00160   result(0) = scalar(0)*command.twist.linear.x;
00161   result(1) = scalar(1)*command.twist.linear.y;
00162   result(2) = scalar(2)*command.twist.linear.z;
00163   result(3) = scalar(3)*command.twist.angular.x;
00164   result(4) = scalar(4)*command.twist.angular.y;
00165   result(5) = scalar(5)*command.twist.angular.z;
00166   
00167   return result;
00168 }
00169 
00170 Eigen::MatrixXd JogArmServer::pseudoInverse(const Eigen::MatrixXd &J) const
00171 {
00172   Eigen::MatrixXd transpose = J.transpose();
00173   return transpose*(J*transpose).inverse();
00174 }
00175 
00176 bool JogArmServer::addJointIncrements(sensor_msgs::JointState &output, const Eigen::VectorXd &increments) const
00177 {
00178   for (std::size_t i = 0, size = increments.size(); i < size; ++i) {
00179     try {
00180       output.position.at(i) += increments(i);
00181     } catch (std::out_of_range e) {
00182       ROS_ERROR("JogArmServer::addJointIncrements - Lengths of output and increments do not match.");
00183       return false;
00184     }
00185   }
00186   
00187   return true;
00188 }
00189 
00190 bool JogArmServer::checkConditionNumber(const Eigen::MatrixXd &matrix, double threshold) const
00191 {
00192   // Get Eigenvalues
00193   Eigen::MatrixXd::EigenvaluesReturnType eigs = matrix.eigenvalues();
00194   Eigen::VectorXd eig_vector = eigs.cwiseAbs();
00195   
00196   // CN = max(eigs)/min(eigs)
00197   double min = eig_vector.minCoeff();
00198   double max = eig_vector.maxCoeff();
00199   
00200   double condition_number = max/min;
00201   
00202   return (condition_number <= threshold);
00203 }
00204 
00205 
00206 } // namespace jog_arm
00207 
00208 int main(int argc, char **argv)
00209 {
00210   ros::init(argc, argv, "jog_arm_server");
00211 
00212   // Handle command line args. Note: ROS remapping arguments are removed by ros::init above.
00213   if( argc != 4 ) {
00214     ROS_FATAL("%s: Usage: rosrun jog_arm jog_arm_server [move_group_name] [cmd_topic_name] [loop_rate])", 
00215                ros::this_node::getName().c_str() );
00216     
00217     std::cout << "Args received:\n";
00218     for ( int i = 0; i < argc; i++ ) {
00219       std::cout << "argv[" << i << "]: " << argv[i] << std::endl;
00220     }
00221     return 1; // failure
00222   }
00223   
00224   std::string move_group_name(argv[1]);
00225   std::string cmd_topic_name(argv[2]);
00226   jog_arm::JogArmServer node(move_group_name, cmd_topic_name);
00227 
00228   ros::Rate loop_rate( std::atoi( argv[3] ) );
00229   while ( ros::ok() )
00230   {
00231     ros::spinOnce();
00232     loop_rate.sleep();
00233   }
00234   
00235   return 0;
00236 }


jog_arm
Author(s): Brian O'Neil, Blake Anderson , Andy Zelenak
autogenerated on Mon Apr 3 2017 04:04:12