init_joint_trajectory.h
Go to the documentation of this file.
00001 
00002 // Copyright (C) 2013, PAL Robotics S.L.
00003 //
00004 // Redistribution and use in source and binary forms, with or without
00005 // modification, are permitted provided that the following conditions are met:
00006 //   * Redistributions of source code must retain the above copyright notice,
00007 //     this list of conditions and the following disclaimer.
00008 //   * Redistributions in binary form must reproduce the above copyright
00009 //     notice, this list of conditions and the following disclaimer in the
00010 //     documentation and/or other materials provided with the distribution.
00011 //   * Neither the name of PAL Robotics S.L. nor the names of its
00012 //     contributors may be used to endorse or promote products derived from
00013 //     this software without specific prior written permission.
00014 //
00015 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00016 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00017 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00018 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00019 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00020 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00021 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00022 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00023 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00024 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00025 // POSSIBILITY OF SUCH DAMAGE.
00027 
00029 
00030 #ifndef JOINT_TRAJECTORY_CONTROLLER_INIT_JOINT_TRAJECTORY_H
00031 #define JOINT_TRAJECTORY_CONTROLLER_INIT_JOINT_TRAJECTORY_H
00032 
00033 // C++ standard
00034 #include <algorithm>
00035 #include <iomanip>
00036 #include <sstream>
00037 #include <stdexcept>
00038 #include <vector>
00039 
00040 // Boost
00041 #include <boost/shared_ptr.hpp>
00042 
00043 // ROS messages
00044 #include <control_msgs/FollowJointTrajectoryAction.h>
00045 #include <trajectory_msgs/JointTrajectoryPoint.h>
00046 
00047 // ros_controls
00048 #include <realtime_tools/realtime_server_goal_handle.h>
00049 
00050 // Project
00051 #include <joint_trajectory_controller/joint_trajectory_msg_utils.h>
00052 #include <joint_trajectory_controller/joint_trajectory_segment.h>
00053 
00054 namespace joint_trajectory_controller
00055 {
00056 
00057 namespace internal
00058 {
00064 template <class T>
00065 inline std::vector<unsigned int> permutation(const T& t1, const T& t2)
00066 {
00067   typedef unsigned int SizeType;
00068 
00069   // Arguments must have the same size
00070   if (t1.size() != t2.size()) {return std::vector<SizeType>();}
00071 
00072   std::vector<SizeType> permutation_vector(t1.size()); // Return value
00073   for (typename T::const_iterator t1_it = t1.begin(); t1_it != t1.end(); ++t1_it)
00074   {
00075     typename T::const_iterator t2_it = std::find(t2.begin(), t2.end(), *t1_it);
00076     if (t2.end() == t2_it) {return std::vector<SizeType>();}
00077     else
00078     {
00079       const SizeType t1_dist = std::distance(t1.begin(), t1_it);
00080       const SizeType t2_dist = std::distance(t2.begin(), t2_it);
00081       permutation_vector[t1_dist] = t2_dist;
00082     }
00083   }
00084   return permutation_vector;
00085 }
00086 
00087 } // namespace
00088 
00093 template <class Trajectory>
00094 struct InitJointTrajectoryOptions
00095 {
00096   typedef realtime_tools::RealtimeServerGoalHandle<control_msgs::FollowJointTrajectoryAction> RealtimeGoalHandle;
00097   typedef boost::shared_ptr<RealtimeGoalHandle>                                               RealtimeGoalHandlePtr;
00098   typedef typename Trajectory::value_type::Scalar                                             Scalar;
00099 
00100   InitJointTrajectoryOptions()
00101     : current_trajectory(0),
00102       joint_names(0),
00103       angle_wraparound(0),
00104       rt_goal_handle(),
00105       default_tolerances(0),
00106       other_time_base(0)
00107   {}
00108 
00109   Trajectory*                current_trajectory;
00110   std::vector<std::string>*  joint_names;
00111   std::vector<bool>*         angle_wraparound;
00112   RealtimeGoalHandlePtr      rt_goal_handle;
00113   SegmentTolerances<Scalar>* default_tolerances;
00114   ros::Time*                 other_time_base;
00115 };
00116 
00180 // TODO: Return useful bits of current trajectory if input msg is useless?
00181 template <class Trajectory>
00182 Trajectory initJointTrajectory(const trajectory_msgs::JointTrajectory&       msg,
00183                                const ros::Time&                              time,
00184                                const InitJointTrajectoryOptions<Trajectory>& options =
00185                                InitJointTrajectoryOptions<Trajectory>())
00186 {
00187   typedef typename Trajectory::value_type Segment;
00188   typedef typename Segment::Scalar Scalar;
00189 
00190   const unsigned int n_joints = msg.joint_names.size();
00191 
00192   const ros::Time msg_start_time = internal::startTime(msg, time); // Message start time
00193 
00194   ROS_DEBUG_STREAM("Figuring out new trajectory starting at time "
00195                    << std::fixed << std::setprecision(3) << msg_start_time.toSec());
00196 
00197   // Empty trajectory
00198   if (msg.points.empty())
00199   {
00200     ROS_DEBUG("Trajectory message contains empty trajectory. Nothing to convert.");
00201     return Trajectory();
00202   }
00203 
00204   // Non strictly-monotonic waypoints
00205   if (!isTimeStrictlyIncreasing(msg))
00206   {
00207     ROS_ERROR("Trajectory message contains waypoints that are not strictly increasing in time.");
00208     return Trajectory();
00209   }
00210 
00211   // Validate options
00212   const bool has_current_trajectory = options.current_trajectory && !options.current_trajectory->empty();
00213   const bool has_joint_names        = options.joint_names        && !options.joint_names->empty();
00214   const bool has_angle_wraparound   = options.angle_wraparound   && !options.angle_wraparound->empty();
00215   const bool has_rt_goal_handle     = options.rt_goal_handle;
00216   const bool has_other_time_base    = options.other_time_base;
00217   const bool has_default_tolerances = options.default_tolerances;
00218 
00219   if (!has_current_trajectory && has_angle_wraparound)
00220   {
00221     ROS_WARN("Vector specifying whether joints wrap around will not be used because no current trajectory was given.");
00222   }
00223 
00224   // Compute trajectory start time and data extraction time associated to the 'other' time base, if it applies
00225   // The o_ prefix indicates that time values are represented in this 'other' time base.
00226   ros::Time o_time;
00227   ros::Time o_msg_start_time;
00228   if (has_other_time_base)
00229   {
00230     ros::Duration msg_start_duration = msg_start_time - time;
00231     o_time = *options.other_time_base;
00232     o_msg_start_time = o_time + msg_start_duration;
00233     ROS_DEBUG_STREAM("Using alternate time base. In it, the new trajectory starts at time "
00234                      << std::fixed << std::setprecision(3) << o_msg_start_time.toSec());
00235   }
00236   else
00237   {
00238     o_time = time;
00239     o_msg_start_time = msg_start_time;
00240   }
00241 
00242   // Permutation vector mapping the expected joint order to the message joint order
00243   // If unspecified, a trivial map (no permutation) is computed
00244   const std::vector<std::string> joint_names = has_joint_names ? *(options.joint_names) : msg.joint_names;
00245 
00246   std::vector<unsigned int> permutation_vector = internal::permutation(joint_names, msg.joint_names);
00247 
00248   if (permutation_vector.empty())
00249   {
00250     ROS_ERROR("Cannot create trajectory from message. It does not contain the expected joints.");
00251     return Trajectory();
00252   }
00253 
00254   // Tolerances to be used in all new segments
00255   SegmentTolerances<Scalar> tolerances = has_default_tolerances ?
00256                                          *(options.default_tolerances) : SegmentTolerances<Scalar>(n_joints);
00257 
00258   if (has_rt_goal_handle && options.rt_goal_handle->gh_.getGoal())
00259   {
00260     updateSegmentTolerances<Scalar>(*(options.rt_goal_handle->gh_.getGoal()), joint_names, tolerances);
00261   }
00262 
00263   // Find first point of new trajectory occurring after current time
00264   // This point is used later on in this function, but is computed here, in advance because if the trajectory message
00265   // contains a trajectory in the past, we can quickly return without spending additional computational resources
00266   std::vector<trajectory_msgs::JointTrajectoryPoint>::const_iterator
00267   it = findPoint(msg, time); // Points to last point occurring before current time (NOTE: Using time, not o_time)
00268   if (it == msg.points.end())
00269   {
00270     it = msg.points.begin();  // Entire trajectory is after current time
00271   }
00272   else
00273   {
00274     ++it;                     // Points to first point after current time OR sequence end
00275     if (it == msg.points.end())
00276     {
00277       ros::Duration last_point_dur = time - (msg_start_time + (--it)->time_from_start);
00278       ROS_WARN_STREAM("Dropping all " << msg.points.size() <<
00279                       " trajectory point(s), as they occur before the current time.\n" <<
00280                       "Last point is " << std::fixed << std::setprecision(3) << last_point_dur.toSec() <<
00281                       "s in the past.");
00282       return Trajectory();
00283     }
00284     else
00285     {
00286       ros::Duration next_point_dur = msg_start_time + it->time_from_start - time;
00287       ROS_WARN_STREAM("Dropping first " << std::distance(msg.points.begin(), it) <<
00288                       " trajectory point(s) out of " << msg.points.size() <<
00289                       ", as they occur before the current time.\n" <<
00290                       "First valid point will be reached in " << std::fixed << std::setprecision(3) <<
00291                       next_point_dur.toSec() << "s.");
00292     }
00293   }
00294 
00295   // Initialize result trajectory: combination of:
00296   // - Useful segments of currently followed trajectory
00297   // - Useful segments of new trajectory (contained in ROS message)
00298   Trajectory result_traj; // Currently empty
00299 
00300   // Initialize offsets due to wrapping joints to zero
00301   std::vector<Scalar> position_offset(n_joints, 0.0);
00302 
00303   // Bridge current trajectory to new one
00304   if (has_current_trajectory)
00305   {
00306     const Trajectory& curr_traj = *(options.current_trajectory);
00307 
00308     // Get the last time and state that will be executed from the current trajectory
00309     const typename Segment::Time last_curr_time = std::max(o_msg_start_time.toSec(), o_time.toSec()); // Important!
00310     typename Segment::State last_curr_state;
00311     sample(curr_traj, last_curr_time, last_curr_state);
00312 
00313     // Get the first time and state that will be executed from the new trajectory
00314     const typename Segment::Time first_new_time = o_msg_start_time.toSec() + (it->time_from_start).toSec();
00315     typename Segment::State first_new_state(*it, permutation_vector); // Here offsets are not yet applied
00316 
00317     // Compute offsets due to wrapping joints
00318     if (has_angle_wraparound)
00319     {
00320       position_offset = wraparoundOffset(last_curr_state.position,
00321                                          first_new_state.position,
00322                                          *(options.angle_wraparound));
00323       if (position_offset.empty())
00324       {
00325         ROS_ERROR("Cannot create trajectory from message. "
00326                   "Vector specifying whether joints wrap around has an invalid size.");
00327         return Trajectory();
00328       }
00329     }
00330 
00331     // Apply offset to first state that will be executed from the new trajectory
00332     first_new_state = typename Segment::State(*it, permutation_vector, position_offset); // Now offsets are applied
00333 
00334     // Add useful segments of current trajectory to result
00335     {
00336       typedef typename Trajectory::const_iterator TrajIter;
00337       TrajIter first = findSegment(curr_traj, o_time.toSec());   // Currently active segment
00338       TrajIter last  = findSegment(curr_traj, last_curr_time); // Segment active when new trajectory starts
00339       if (first == curr_traj.end() || last == curr_traj.end())
00340       {
00341         ROS_ERROR("Unexpected error: Could not find segments in current trajectory. Please contact the package maintainer.");
00342         return Trajectory();
00343       }
00344       result_traj.insert(result_traj.begin(), first, ++last); // Range [first,last) will still be executed
00345     }
00346 
00347     // Add segment bridging current and new trajectories to result
00348     Segment bridge_seg(last_curr_time, last_curr_state,
00349                        first_new_time, first_new_state);
00350     bridge_seg.setGoalHandle(options.rt_goal_handle);
00351     if (has_rt_goal_handle) {bridge_seg.setTolerances(tolerances);}
00352     result_traj.push_back(bridge_seg);
00353   }
00354 
00355   // Constants used in log statement at the end
00356   const unsigned int num_old_segments = result_traj.size() -1;
00357   const unsigned int num_new_segments = std::distance(it, msg.points.end()) -1;
00358 
00359   // Add useful segments of new trajectory to result
00360   // - Construct all trajectory segments occurring after current time
00361   // - As long as there remain two trajectory points we can construct the next trajectory segment
00362   while (std::distance(it, msg.points.end()) >= 2)
00363   {
00364     std::vector<trajectory_msgs::JointTrajectoryPoint>::const_iterator next_it = it; ++next_it;
00365     Segment segment(o_msg_start_time, *it, *next_it, permutation_vector, position_offset);
00366     segment.setGoalHandle(options.rt_goal_handle);
00367     if (has_rt_goal_handle) {segment.setTolerances(tolerances);}
00368     result_traj.push_back(segment);
00369     ++it;
00370   }
00371 
00372   // Useful debug info
00373   std::stringstream log_str;
00374   log_str << "Trajectory has " << result_traj.size() << " segments";
00375   if (has_current_trajectory)
00376   {
00377     log_str << ":";
00378     log_str << "\n- " << num_old_segments << " segment(s) will still be executed from previous trajectory.";
00379     log_str << "\n- 1 segment added for transitioning between the current trajectory and first point of the input message.";
00380     if (num_new_segments > 0) {log_str << "\n- " << num_new_segments << " new segments (" << (num_new_segments + 1) <<
00381                                " points) taken from the input trajectory.";}
00382   }
00383   else {log_str << ".";}
00384   ROS_DEBUG_STREAM(log_str.str());
00385 
00386   return result_traj;
00387 }
00388 
00389 } // namespace
00390 
00391 #endif // header guard


joint_trajectory_controller
Author(s): Adolfo Rodriguez Tsouroukdissian
autogenerated on Fri Aug 28 2015 12:36:48