joint_trajectory_action.cpp
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (BSD License)
00003  *
00004  * Copyright (c) 2011, Southwest Research Institute
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions are met:
00009  *
00010  *       * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *       * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *       * Neither the name of the Southwest Research Institute, nor the names
00016  *       of its contributors may be used to endorse or promote products derived
00017  *       from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00020  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00021  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00022  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00023  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00024  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00025  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00026  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00027  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00028  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00029  * POSSIBILITY OF SUCH DAMAGE.
00030  */
00031 
00032 #include <ros/ros.h>
00033 #include <actionlib/server/action_server.h>
00034 
00035 #include <trajectory_msgs/JointTrajectory.h>
00036 #include <control_msgs/FollowJointTrajectoryAction.h>
00037 #include <control_msgs/FollowJointTrajectoryFeedback.h>
00038 #include <industrial_msgs/RobotStatus.h>
00039 
00040 const double DEFAULT_GOAL_THRESHOLD = 0.01;
00041 
00042 class JointTrajectoryExecuter
00043 {
00044 private:
00045   typedef actionlib::ActionServer<control_msgs::FollowJointTrajectoryAction> JTAS;
00046   typedef JTAS::GoalHandle GoalHandle;
00047 public:
00048   JointTrajectoryExecuter(ros::NodeHandle &n) :
00049       node_(n), action_server_(node_, "joint_trajectory_action",
00050                                boost::bind(&JointTrajectoryExecuter::goalCB, this, _1),
00051                                boost::bind(&JointTrajectoryExecuter::cancelCB, this, _1), false), has_active_goal_(
00052           false)
00053   {
00054     using namespace XmlRpc;
00055     ros::NodeHandle pn("~");
00056 
00057     joint_names_.push_back("joint_s");
00058     joint_names_.push_back("joint_l");
00059     joint_names_.push_back("joint_e");
00060     joint_names_.push_back("joint_u");
00061     joint_names_.push_back("joint_r");
00062     joint_names_.push_back("joint_b");
00063     joint_names_.push_back("joint_t");
00064 
00065     pn.param("constraints/goal_time", goal_time_constraint_, 0.0);
00066 
00067     // Gets the constraints for each joint.
00068     for (size_t i = 0; i < joint_names_.size(); ++i)
00069     {
00070       std::string ns = std::string("constraints/") + joint_names_[i];
00071       double g, t;
00072       pn.param(ns + "/goal", g, DEFAULT_GOAL_THRESHOLD);
00073       pn.param(ns + "/trajectory", t, -1.0);
00074       goal_constraints_[joint_names_[i]] = g;
00075       trajectory_constraints_[joint_names_[i]] = t;
00076     }
00077     pn.param("constraints/stopped_velocity_tolerance", stopped_velocity_tolerance_, 0.01);
00078 
00079     pub_controller_command_ = node_.advertise<trajectory_msgs::JointTrajectory>("command", 1);
00080     sub_controller_state_ = node_.subscribe("feedback_states", 1, &JointTrajectoryExecuter::controllerStateCB, this);
00081     sub_robot_status_ = node_.subscribe("robot_status", 1, &JointTrajectoryExecuter::robotStatusCB, this);
00082 
00083     action_server_.start();
00084   }
00085 
00086   ~JointTrajectoryExecuter()
00087   {
00088     pub_controller_command_.shutdown();
00089     sub_controller_state_.shutdown();
00090     watchdog_timer_.stop();
00091   }
00092 
00093   void robotStatusCB(const industrial_msgs::RobotStatusConstPtr &msg)
00094   {
00095     last_robot_status_ = *msg;  //caching robot status for later use.
00096   }
00097 
00098   bool withinGoalConstraints(const control_msgs::FollowJointTrajectoryFeedbackConstPtr &msg,
00099                              const std::map<std::string, double>& constraints,
00100                              const trajectory_msgs::JointTrajectory& traj)
00101   {
00102     ROS_DEBUG("Checking goal contraints");
00103     int last = traj.points.size() - 1;
00104     for (size_t i = 0; i < msg->joint_names.size(); ++i)
00105     {
00106       double abs_error = fabs(msg->actual.positions[i] - traj.points[last].positions[i]);
00107       double goal_constraint = constraints.at(msg->joint_names[i]);
00108       if (goal_constraint >= 0 && abs_error > goal_constraint)
00109       {
00110         ROS_DEBUG("Bad constraint: %f, abs_errs: %f", goal_constraint, abs_error);
00111         return false;
00112       }
00113       ROS_DEBUG("Checking constraint: %f, abs_errs: %f", goal_constraint, abs_error);
00114     }
00115     return true;
00116   }
00117 
00118 private:
00119 
00120   static bool setsEqual(const std::vector<std::string> &a, const std::vector<std::string> &b)
00121   {
00122     if (a.size() != b.size())
00123       return false;
00124 
00125     for (size_t i = 0; i < a.size(); ++i)
00126     {
00127       if (count(b.begin(), b.end(), a[i]) != 1)
00128         return false;
00129     }
00130     for (size_t i = 0; i < b.size(); ++i)
00131     {
00132       if (count(a.begin(), a.end(), b[i]) != 1)
00133         return false;
00134     }
00135 
00136     return true;
00137   }
00138 
00139   void watchdog(const ros::TimerEvent &e)
00140   {
00141     ros::Time now = ros::Time::now();
00142 
00143     // Aborts the active goal if the controller does not appear to be active.
00144     if (has_active_goal_)
00145     {
00146       bool should_abort = false;
00147       if (!last_controller_state_)
00148       {
00149         should_abort = true;
00150         ROS_WARN("Aborting goal because we have never heard a controller state message.");
00151       }
00152       else if ((now - last_controller_state_->header.stamp) > ros::Duration(5.0))
00153       {
00154         should_abort = true;
00155         ROS_WARN(
00156             "Aborting goal because we haven't heard from the controller in %.3lf seconds", (now - last_controller_state_->header.stamp).toSec());
00157       }
00158 
00159       if (should_abort)
00160       {
00161         // Stops the controller.
00162         trajectory_msgs::JointTrajectory empty;
00163         empty.joint_names = joint_names_;
00164         pub_controller_command_.publish(empty);
00165 
00166         // Marks the current goal as aborted.
00167         active_goal_.setAborted();
00168         has_active_goal_ = false;
00169       }
00170     }
00171   }
00172 
00173   void goalCB(GoalHandle gh)
00174   {
00175     // Ensures that the joints in the goal match the joints we are commanding.
00176     ROS_DEBUG("Received goal: goalCB");
00177     if (!setsEqual(joint_names_, gh.getGoal()->trajectory.joint_names))
00178     {
00179       ROS_ERROR("Joints on incoming goal don't match our joints");
00180       gh.setRejected();
00181       return;
00182     }
00183 
00184     // Cancels the currently active goal.
00185     if (has_active_goal_)
00186     {
00187       ROS_DEBUG("Received new goal, canceling current goal");
00188       // Stops the controller.
00189       trajectory_msgs::JointTrajectory empty;
00190       empty.joint_names = joint_names_;
00191       pub_controller_command_.publish(empty);
00192 
00193       // Marks the current goal as canceled.
00194       active_goal_.setCanceled();
00195       has_active_goal_ = false;
00196     }
00197 
00198     // Sends the trajectory along to the controller
00199     if (withinGoalConstraints(last_controller_state_, goal_constraints_, gh.getGoal()->trajectory))
00200     {
00201       ROS_INFO_STREAM("Already within goal constraints");
00202       gh.setAccepted();
00203       gh.setSucceeded();
00204     }
00205     else
00206     {
00207       gh.setAccepted();
00208       active_goal_ = gh;
00209       has_active_goal_ = true;
00210 
00211       ROS_INFO("Publishing trajectory");
00212 
00213       current_traj_ = active_goal_.getGoal()->trajectory;
00214       pub_controller_command_.publish(current_traj_);
00215     }
00216   }
00217 
00218   void cancelCB(GoalHandle gh)
00219   {
00220     ROS_DEBUG("Received action cancel request");
00221     if (active_goal_ == gh)
00222     {
00223       // Stops the controller.
00224       trajectory_msgs::JointTrajectory empty;
00225       empty.joint_names = joint_names_;
00226       pub_controller_command_.publish(empty);
00227 
00228       // Marks the current goal as canceled.
00229       active_goal_.setCanceled();
00230       has_active_goal_ = false;
00231     }
00232   }
00233 
00234   ros::NodeHandle node_;
00235   JTAS action_server_;
00236   ros::Publisher pub_controller_command_;
00237   ros::Subscriber sub_controller_state_;
00238   ros::Subscriber sub_robot_status_;
00239   ros::Timer watchdog_timer_;
00240 
00241   bool has_active_goal_;
00242   GoalHandle active_goal_;
00243   trajectory_msgs::JointTrajectory current_traj_;
00244 
00245   std::vector<std::string> joint_names_;
00246   std::map<std::string, double> goal_constraints_;
00247   std::map<std::string, double> trajectory_constraints_;
00248   double goal_time_constraint_;
00249   double stopped_velocity_tolerance_;
00250 
00251   control_msgs::FollowJointTrajectoryFeedbackConstPtr last_controller_state_;
00252   industrial_msgs::RobotStatus last_robot_status_;
00253 
00254   void controllerStateCB(const control_msgs::FollowJointTrajectoryFeedbackConstPtr &msg)
00255   {
00256     //ROS_DEBUG("Checking controller state feedback");
00257     last_controller_state_ = msg;
00258     ros::Time now = ros::Time::now();
00259 
00260     if (!has_active_goal_)
00261     {
00262       //ROS_DEBUG("No active goal, ignoring feedback");
00263       return;
00264     }
00265     if (current_traj_.points.empty())
00266     {
00267       ROS_DEBUG("Current trajectory is empty, ignoring feedback");
00268       return;
00269     }
00270     /* NOT CONCERNED ABOUT TRAJECTORY TIMING AT THIS POINT
00271      if (now < current_traj_.header.stamp + current_traj_.points[0].time_from_start)
00272      return;
00273      */
00274 
00275     if (!setsEqual(joint_names_, msg->joint_names))
00276     {
00277       ROS_ERROR("Joint names from the controller don't match our joint names.");
00278       return;
00279     }
00280 
00281     // Checking for goal constraints
00282     // Checks that we have ended inside the goal constraints and has motion stopped
00283 
00284     ROS_DEBUG("Checking goal constraints");
00285     if (withinGoalConstraints(msg, goal_constraints_, current_traj_))
00286     {
00287       // Additional check for motion stoppage since the controller goal may still
00288       // be moving.  The current robot driver calls a motion stop if it receives
00289       // a new trajectory while it is still moving.  If the driver is not publishing
00290       // the motion state (i.e. old driver), this will still work, but it warns you.
00291       if (last_robot_status_.in_motion.val == industrial_msgs::TriState::FALSE)
00292       {
00293         ROS_INFO("Inside goal constraints, stopped moving, return success for action");
00294         active_goal_.setSucceeded();
00295         has_active_goal_ = false;
00296       }
00297       else if (last_robot_status_.in_motion.val == industrial_msgs::TriState::UNKNOWN)
00298       {
00299         ROS_INFO("Inside goal constraints, return success for action");
00300         ROS_WARN("Robot status: in motion unknown, the robot driver node and controller code should be updated");
00301         active_goal_.setSucceeded();
00302         has_active_goal_ = false;
00303       }
00304       else
00305       {
00306         ROS_DEBUG("Within goal constraints but robot is still moving");
00307       }
00308     }
00309     // Verifies that the controller has stayed within the trajectory constraints.
00310     /*  DISABLING THIS MORE COMPLICATED GOAL CHECKING AND ERROR DETECTION
00311 
00312 
00313      int last = current_traj_.points.size() - 1;
00314      ros::Time end_time = current_traj_.header.stamp + current_traj_.points[last].time_from_start;
00315      if (now < end_time)
00316      {
00317      // Checks that the controller is inside the trajectory constraints.
00318      for (size_t i = 0; i < msg->joint_names.size(); ++i)
00319      {
00320      double abs_error = fabs(msg->error.positions[i]);
00321      double constraint = trajectory_constraints_[msg->joint_names[i]];
00322      if (constraint >= 0 && abs_error > constraint)
00323      {
00324      // Stops the controller.
00325      trajectory_msgs::JointTrajectory empty;
00326      empty.joint_names = joint_names_;
00327      pub_controller_command_.publish(empty);
00328 
00329      active_goal_.setAborted();
00330      has_active_goal_ = false;
00331      ROS_WARN("Aborting because we would up outside the trajectory constraints");
00332      return;
00333      }
00334      }
00335      }
00336      else
00337      {
00338      // Checks that we have ended inside the goal constraints
00339      bool inside_goal_constraints = true;
00340      for (size_t i = 0; i < msg->joint_names.size() && inside_goal_constraints; ++i)
00341      {
00342      double abs_error = fabs(msg->error.positions[i]);
00343      double goal_constraint = goal_constraints_[msg->joint_names[i]];
00344      if (goal_constraint >= 0 && abs_error > goal_constraint)
00345      inside_goal_constraints = false;
00346 
00347      // It's important to be stopped if that's desired.
00348      if (fabs(msg->desired.velocities[i]) < 1e-6)
00349      {
00350      if (fabs(msg->actual.velocities[i]) > stopped_velocity_tolerance_)
00351      inside_goal_constraints = false;
00352      }
00353      }
00354 
00355      if (inside_goal_constraints)
00356      {
00357      active_goal_.setSucceeded();
00358      has_active_goal_ = false;
00359      }
00360      else if (now < end_time + ros::Duration(goal_time_constraint_))
00361      {
00362      // Still have some time left to make it.
00363      }
00364      else
00365      {
00366      ROS_WARN("Aborting because we wound up outside the goal constraints");
00367      active_goal_.setAborted();
00368      has_active_goal_ = false;
00369      }
00370 
00371      }
00372      */
00373   }
00374 };
00375 
00376 int main(int argc, char** argv)
00377 {
00378   ros::init(argc, argv, "joint_trajectory_action_node");
00379   ros::NodeHandle node; //("~");
00380   JointTrajectoryExecuter jte(node);
00381 
00382   ros::spin();
00383 
00384   return 0;
00385 }
00386 


dx100
Author(s): Shaun Edwards (Southwest Research Institute)
autogenerated on Mon Oct 6 2014 02:25:33