joint_trajectory_controller_impl.h
Go to the documentation of this file.
1 // Copyright (C) 2013, PAL Robotics S.L.
3 // Copyright (c) 2008, Willow Garage, Inc.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 // * Redistributions of source code must retain the above copyright notice,
8 // this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above copyright
10 // notice, this list of conditions and the following disclaimer in the
11 // documentation and/or other materials provided with the distribution.
12 // * Neither the name of PAL Robotics S.L. nor the names of its
13 // contributors may be used to endorse or promote products derived from
14 // this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 // POSSIBILITY OF SUCH DAMAGE.
28 
30 
31 #ifndef JOINT_TRAJECTORY_CONTROLLER_JOINT_TRAJECTORY_CONTROLLER_IMP_H
32 #define JOINT_TRAJECTORY_CONTROLLER_JOINT_TRAJECTORY_CONTROLLER_IMP_H
33 
34 
36 {
37 
38 namespace internal
39 {
40 
41 template <class Enclosure, class Member>
43 {
45  boost::shared_ptr<Member> p(&member, d);
46  return p;
47 }
48 
49 std::vector<std::string> getStrings(const ros::NodeHandle& nh, const std::string& param_name)
50 {
51  using namespace XmlRpc;
52  XmlRpcValue xml_array;
53  if (!nh.getParam(param_name, xml_array))
54  {
55  ROS_ERROR_STREAM("Could not find '" << param_name << "' parameter (namespace: " << nh.getNamespace() << ").");
56  return std::vector<std::string>();
57  }
58  if (xml_array.getType() != XmlRpcValue::TypeArray)
59  {
60  ROS_ERROR_STREAM("The '" << param_name << "' parameter is not an array (namespace: " <<
61  nh.getNamespace() << ").");
62  return std::vector<std::string>();
63  }
64 
65  std::vector<std::string> out;
66  for (int i = 0; i < xml_array.size(); ++i)
67  {
68  if (xml_array[i].getType() != XmlRpcValue::TypeString)
69  {
70  ROS_ERROR_STREAM("The '" << param_name << "' parameter contains a non-string element (namespace: " <<
71  nh.getNamespace() << ").");
72  return std::vector<std::string>();
73  }
74  out.push_back(static_cast<std::string>(xml_array[i]));
75  }
76  return out;
77 }
78 
79 urdf::ModelSharedPtr getUrdf(const ros::NodeHandle& nh, const std::string& param_name)
80 {
81  urdf::ModelSharedPtr urdf(new urdf::Model);
82 
83  std::string urdf_str;
84  // Check for robot_description in proper namespace
85  if (nh.getParam(param_name, urdf_str))
86  {
87  if (!urdf->initString(urdf_str))
88  {
89  ROS_ERROR_STREAM("Failed to parse URDF contained in '" << param_name << "' parameter (namespace: " <<
90  nh.getNamespace() << ").");
91  return urdf::ModelSharedPtr();
92  }
93  }
94  // Check for robot_description in root
95  else if (!urdf->initParam("robot_description"))
96  {
97  ROS_ERROR_STREAM("Failed to parse URDF contained in '" << param_name << "' parameter");
98  return urdf::ModelSharedPtr();
99  }
100  return urdf;
101 }
102 
103 std::vector<urdf::JointConstSharedPtr> getUrdfJoints(const urdf::Model& urdf, const std::vector<std::string>& joint_names)
104 {
105  std::vector<urdf::JointConstSharedPtr> out;
106  for (unsigned int i = 0; i < joint_names.size(); ++i)
107  {
108  urdf::JointConstSharedPtr urdf_joint = urdf.getJoint(joint_names[i]);
109  if (urdf_joint)
110  {
111  out.push_back(urdf_joint);
112  }
113  else
114  {
115  ROS_ERROR_STREAM("Could not find joint '" << joint_names[i] << "' in URDF model.");
116  return std::vector<urdf::JointConstSharedPtr>();
117  }
118  }
119  return out;
120 }
121 
122 std::string getLeafNamespace(const ros::NodeHandle& nh)
123 {
124  const std::string complete_ns = nh.getNamespace();
125  std::size_t id = complete_ns.find_last_of("/");
126  return complete_ns.substr(id + 1);
127 }
128 
129 } // namespace
130 
131 template <class SegmentImpl, class HardwareInterface>
133 starting(const ros::Time& time)
134 {
135  // Update time data
136  TimeData time_data;
137  time_data.time = time;
138  time_data.uptime = ros::Time(0.0);
139  time_data_.initRT(time_data);
140 
141  // Initialize the desired_state with the current state on startup
142  for (unsigned int i = 0; i < joints_.size(); ++i)
143  {
144  desired_state_.position[i] = joints_[i].getPosition();
145  desired_state_.velocity[i] = joints_[i].getVelocity();
146  }
147 
148  // Hold current position
149  setHoldPosition(time_data.uptime);
150 
151  // Initialize last state update time
152  last_state_publish_time_ = time_data.uptime;
153 
154  // Hardware interface adapter
155  hw_iface_adapter_.starting(time_data.uptime);
156 }
157 
158 template <class SegmentImpl, class HardwareInterface>
160 stopping(const ros::Time& /*time*/)
161 {
162  preemptActiveGoal();
163 }
164 
165 template <class SegmentImpl, class HardwareInterface>
168 {
169  const bool update_ok = updateTrajectoryCommand(msg, RealtimeGoalHandlePtr());
170  if (update_ok) {preemptActiveGoal();}
171 }
172 
173 template <class SegmentImpl, class HardwareInterface>
176 {
177  RealtimeGoalHandlePtr current_active_goal(rt_active_goal_);
178 
179  // Cancels the currently active goal
180  if (current_active_goal)
181  {
182  // Marks the current goal as canceled
183  rt_active_goal_.reset();
184  current_active_goal->gh_.setCanceled();
185  }
186 }
187 
188 template <class SegmentImpl, class HardwareInterface>
191  : verbose_(false), // Set to true during debugging
192  hold_trajectory_ptr_(new Trajectory)
193 {
194  // The verbose parameter is for advanced use as it breaks real-time safety
195  // by enabling ROS logging services
196  if (verbose_)
197  {
199  "The joint_trajectory_controller verbose flag is enabled. "
200  << "This flag breaks real-time safety and should only be "
201  << "used for debugging");
202  }
203 }
204 
205 template <class SegmentImpl, class HardwareInterface>
207  ros::NodeHandle& root_nh,
208  ros::NodeHandle& controller_nh)
209 {
210  using namespace internal;
211 
212  // Cache controller node handle
213  controller_nh_ = controller_nh;
214 
215  // Controller name
217 
218  // State publish rate
219  double state_publish_rate = 50.0;
220  controller_nh_.getParam("state_publish_rate", state_publish_rate);
221  ROS_DEBUG_STREAM_NAMED(name_, "Controller state will be published at " << state_publish_rate << "Hz.");
222  state_publisher_period_ = ros::Duration(1.0 / state_publish_rate);
223 
224  // Action status checking update rate
225  double action_monitor_rate = 20.0;
226  controller_nh_.getParam("action_monitor_rate", action_monitor_rate);
227  action_monitor_period_ = ros::Duration(1.0 / action_monitor_rate);
228  ROS_DEBUG_STREAM_NAMED(name_, "Action status changes will be monitored at " << action_monitor_rate << "Hz.");
229 
230  // Stop trajectory duration
232  if (!controller_nh_.getParam("stop_trajectory_duration", stop_trajectory_duration_))
233  {
234  // TODO: Remove this check/warning in Indigo
235  if (controller_nh_.getParam("hold_trajectory_duration", stop_trajectory_duration_))
236  {
237  ROS_WARN("The 'hold_trajectory_duration' has been deprecated in favor of the 'stop_trajectory_duration' parameter. Please update your controller configuration.");
238  }
239  }
240  ROS_DEBUG_STREAM_NAMED(name_, "Stop trajectory has a duration of " << stop_trajectory_duration_ << "s.");
241 
242  // Checking if partial trajectories are allowed
243  controller_nh_.param<bool>("allow_partial_joints_goal", allow_partial_joints_goal_, false);
244  if (allow_partial_joints_goal_)
245  {
246  ROS_DEBUG_NAMED(name_, "Goals with partial set of joints are allowed");
247  }
248 
249  // List of controlled joints
251  if (joint_names_.empty()) {return false;}
252  const unsigned int n_joints = joint_names_.size();
253 
254  // URDF joints
255  urdf::ModelSharedPtr urdf = getUrdf(root_nh, "robot_description");
256  if (!urdf) {return false;}
257 
258  std::vector<urdf::JointConstSharedPtr> urdf_joints = getUrdfJoints(*urdf, joint_names_);
259  if (urdf_joints.empty()) {return false;}
260  assert(n_joints == urdf_joints.size());
261 
262  // Initialize members
263  joints_.resize(n_joints);
264  angle_wraparound_.resize(n_joints);
265  for (unsigned int i = 0; i < n_joints; ++i)
266  {
267  // Joint handle
268  try {joints_[i] = hw->getHandle(joint_names_[i]);}
269  catch (...)
270  {
271  ROS_ERROR_STREAM_NAMED(name_, "Could not find joint '" << joint_names_[i] << "' in '" <<
272  this->getHardwareInterfaceType() << "'.");
273  return false;
274  }
275 
276  // Whether a joint is continuous (ie. has angle wraparound)
277  angle_wraparound_[i] = urdf_joints[i]->type == urdf::Joint::CONTINUOUS;
278  const std::string not_if = angle_wraparound_[i] ? "" : "non-";
279 
280  ROS_DEBUG_STREAM_NAMED(name_, "Found " << not_if << "continuous joint '" << joint_names_[i] << "' in '" <<
281  this->getHardwareInterfaceType() << "'.");
282  }
283 
284  assert(joints_.size() == angle_wraparound_.size());
285  ROS_DEBUG_STREAM_NAMED(name_, "Initialized controller '" << name_ << "' with:" <<
286  "\n- Number of joints: " << joints_.size() <<
287  "\n- Hardware interface type: '" << this->getHardwareInterfaceType() << "'" <<
288  "\n- Trajectory segment type: '" << hardware_interface::internal::demangledTypeName<SegmentImpl>() << "'");
289 
290  // Default tolerances
291  ros::NodeHandle tol_nh(controller_nh_, "constraints");
292  default_tolerances_ = getSegmentTolerances<Scalar>(tol_nh, joint_names_);
293 
294  // Hardware interface adapter
296 
297  // ROS API: Subscribed topics
299 
300  // ROS API: Published topics
301  state_publisher_.reset(new StatePublisher(controller_nh_, "state", 1));
302 
303  // ROS API: Action interface
304  action_server_.reset(new ActionServer(controller_nh_, "follow_joint_trajectory",
305  boost::bind(&JointTrajectoryController::goalCB, this, _1),
306  boost::bind(&JointTrajectoryController::cancelCB, this, _1),
307  false));
308  action_server_->start();
309 
310  // ROS API: Provided services
313  this);
314 
315  // Preeallocate resources
316  current_state_ = typename Segment::State(n_joints);
317  desired_state_ = typename Segment::State(n_joints);
318  state_error_ = typename Segment::State(n_joints);
319  desired_joint_state_ = typename Segment::State(1);
320  state_joint_error_ = typename Segment::State(1);
321 
322  successful_joint_traj_ = boost::dynamic_bitset<>(joints_.size());
323 
324  // Initialize trajectory with all joints
325  typename Segment::State current_joint_state_ = typename Segment::State(1);
326  for (unsigned int i = 0; i < n_joints; ++i)
327  {
328  current_joint_state_.position[0]= current_state_.position[i];
329  current_joint_state_.velocity[0]= current_state_.velocity[i];
330  Segment hold_segment(0.0, current_joint_state_, 0.0, current_joint_state_);
331 
332  TrajectoryPerJoint joint_segment;
333  joint_segment.resize(1, hold_segment);
334  hold_trajectory_ptr_->push_back(joint_segment);
335  }
336 
337  {
338  state_publisher_->lock();
339  state_publisher_->msg_.joint_names = joint_names_;
340  state_publisher_->msg_.desired.positions.resize(n_joints);
341  state_publisher_->msg_.desired.velocities.resize(n_joints);
342  state_publisher_->msg_.desired.accelerations.resize(n_joints);
343  state_publisher_->msg_.actual.positions.resize(n_joints);
344  state_publisher_->msg_.actual.velocities.resize(n_joints);
345  state_publisher_->msg_.error.positions.resize(n_joints);
346  state_publisher_->msg_.error.velocities.resize(n_joints);
347  state_publisher_->unlock();
348  }
349 
350  return true;
351 }
352 
353 template <class SegmentImpl, class HardwareInterface>
355 update(const ros::Time& time, const ros::Duration& period)
356 {
357  // Get currently followed trajectory
358  TrajectoryPtr curr_traj_ptr;
359  curr_trajectory_box_.get(curr_traj_ptr);
360  Trajectory& curr_traj = *curr_traj_ptr;
361 
362  // Update time data
363  TimeData time_data;
364  time_data.time = time; // Cache current time
365  time_data.period = period; // Cache current control period
366  time_data.uptime = time_data_.readFromRT()->uptime + period; // Update controller uptime
367  time_data_.writeFromNonRT(time_data); // TODO: Grrr, we need a lock-free data structure here!
368 
369  // NOTE: It is very important to execute the two above code blocks in the specified sequence: first get current
370  // trajectory, then update time data. Hopefully the following paragraph sheds a bit of light on the rationale.
371  // The non-rt thread responsible for processing new commands enqueues trajectories that can start at the _next_
372  // control cycle (eg. zero start time) or later (eg. when we explicitly request a start time in the future).
373  // If we reverse the order of the two blocks above, and update the time data first; it's possible that by the time we
374  // fetch the currently followed trajectory, it has been updated by the non-rt thread with something that starts in the
375  // next control cycle, leaving the current cycle without a valid trajectory.
376 
377  // Update current state and state error
378  for (unsigned int i = 0; i < joints_.size(); ++i)
379  {
380  current_state_.position[i] = joints_[i].getPosition();
381  current_state_.velocity[i] = joints_[i].getVelocity();
382  // There's no acceleration data available in a joint handle
383 
384  typename TrajectoryPerJoint::const_iterator segment_it = sample(curr_traj[i], time_data.uptime.toSec(), desired_joint_state_);
385  if (curr_traj[i].end() == segment_it)
386  {
387  // Non-realtime safe, but should never happen under normal operation
389  "Unexpected error: No trajectory defined at current time. Please contact the package maintainer.");
390  return;
391  }
392  desired_state_.position[i] = desired_joint_state_.position[0];
393  desired_state_.velocity[i] = desired_joint_state_.velocity[0];
394  desired_state_.acceleration[i] = desired_joint_state_.acceleration[0]; ;
395 
397  state_joint_error_.velocity[0] = desired_joint_state_.velocity[0] - current_state_.velocity[i];
398  state_joint_error_.acceleration[0] = 0.0;
399 
401  state_error_.velocity[i] = desired_joint_state_.velocity[0] - current_state_.velocity[i];
402  state_error_.acceleration[i] = 0.0;
403 
404  //Check tolerances
405  const RealtimeGoalHandlePtr rt_segment_goal = segment_it->getGoalHandle();
406  if (rt_segment_goal && rt_segment_goal == rt_active_goal_)
407  {
408  // Check tolerances
409  if (time_data.uptime.toSec() < segment_it->endTime())
410  {
411  // Currently executing a segment: check path tolerances
412  const SegmentTolerancesPerJoint<Scalar>& joint_tolerances = segment_it->getTolerances();
414  {
415  if (verbose_)
416  {
417  ROS_ERROR_STREAM_NAMED(name_,"Path tolerances failed for joint: " << joint_names_[i]);
419  }
420  rt_segment_goal->preallocated_result_->error_code =
421  control_msgs::FollowJointTrajectoryResult::PATH_TOLERANCE_VIOLATED;
422  rt_segment_goal->setAborted(rt_segment_goal->preallocated_result_);
423  rt_active_goal_.reset();
424  successful_joint_traj_.reset();
425  }
426  }
427  else if (segment_it == --curr_traj[i].end())
428  {
429  if (verbose_)
430  ROS_DEBUG_STREAM_THROTTLE_NAMED(1,name_,"Finished executing last segment, checking goal tolerances");
431 
432  // Controller uptime
433  const ros::Time uptime = time_data_.readFromRT()->uptime;
434 
435  // Checks that we have ended inside the goal tolerances
436  const SegmentTolerancesPerJoint<Scalar>& tolerances = segment_it->getTolerances();
437  const bool inside_goal_tolerances = checkStateTolerancePerJoint(state_joint_error_, tolerances.goal_state_tolerance);
438 
439  if (inside_goal_tolerances)
440  {
441  successful_joint_traj_[i] = 1;
442  }
443  else if (uptime.toSec() < segment_it->endTime() + tolerances.goal_time_tolerance)
444  {
445  // Still have some time left to meet the goal state tolerances
446  }
447  else
448  {
449  if (verbose_)
450  {
451  ROS_ERROR_STREAM_NAMED(name_,"Goal tolerances failed for joint: "<< joint_names_[i]);
452  // Check the tolerances one more time to output the errors that occurs
454  }
455 
456  rt_segment_goal->preallocated_result_->error_code = control_msgs::FollowJointTrajectoryResult::GOAL_TOLERANCE_VIOLATED;
457  rt_segment_goal->setAborted(rt_segment_goal->preallocated_result_);
458  rt_active_goal_.reset();
459  successful_joint_traj_.reset();
460  }
461  }
462  }
463  }
464 
465  //If there is an active goal and all segments finished successfully then set goal as succeeded
466  RealtimeGoalHandlePtr current_active_goal(rt_active_goal_);
467  if (current_active_goal and successful_joint_traj_.count() == joints_.size())
468  {
469  current_active_goal->preallocated_result_->error_code = control_msgs::FollowJointTrajectoryResult::SUCCESSFUL;
470  current_active_goal->setSucceeded(current_active_goal->preallocated_result_);
471  current_active_goal.reset(); // do not publish feedback
472  rt_active_goal_.reset();
473  successful_joint_traj_.reset();
474  }
475 
476  // Hardware interface adapter: Generate and send commands
477  hw_iface_adapter_.updateCommand(time_data.uptime, time_data.period,
479 
480  // Set action feedback
481  if (current_active_goal)
482  {
483  current_active_goal->preallocated_feedback_->header.stamp = time_data_.readFromRT()->time;
484  current_active_goal->preallocated_feedback_->desired.positions = desired_state_.position;
485  current_active_goal->preallocated_feedback_->desired.velocities = desired_state_.velocity;
486  current_active_goal->preallocated_feedback_->desired.accelerations = desired_state_.acceleration;
487  current_active_goal->preallocated_feedback_->actual.positions = current_state_.position;
488  current_active_goal->preallocated_feedback_->actual.velocities = current_state_.velocity;
489  current_active_goal->preallocated_feedback_->error.positions = state_error_.position;
490  current_active_goal->preallocated_feedback_->error.velocities = state_error_.velocity;
491  current_active_goal->setFeedback( current_active_goal->preallocated_feedback_ );
492  }
493 
494  // Publish state
495  publishState(time_data.uptime);
496 }
497 
498 template <class SegmentImpl, class HardwareInterface>
501 {
503  Options options;
504  options.error_string = error_string;
505  std::string error_string_tmp;
506 
507  // Preconditions
508  if (!this->isRunning())
509  {
510  error_string_tmp = "Can't accept new commands. Controller is not running.";
511  ROS_ERROR_STREAM_NAMED(name_, error_string_tmp);
512  options.setErrorString(error_string_tmp);
513  return false;
514  }
515 
516  if (!msg)
517  {
518  error_string_tmp = "Received null-pointer trajectory message, skipping.";
519  ROS_WARN_STREAM_NAMED(name_, error_string_tmp);
520  options.setErrorString(error_string_tmp);
521  return false;
522  }
523 
524  // Time data
525  TimeData* time_data = time_data_.readFromRT(); // TODO: Grrr, we need a lock-free data structure here!
526 
527  // Time of the next update
528  const ros::Time next_update_time = time_data->time + time_data->period;
529 
530  // Uptime of the next update
531  ros::Time next_update_uptime = time_data->uptime + time_data->period;
532 
533  // Hold current position if trajectory is empty
534  if (msg->points.empty())
535  {
536  setHoldPosition(time_data->uptime, gh);
537  ROS_DEBUG_NAMED(name_, "Empty trajectory command, stopping.");
538  return true;
539  }
540 
541  // Trajectory initialization options
542  TrajectoryPtr curr_traj_ptr;
543  curr_trajectory_box_.get(curr_traj_ptr);
544 
545  options.other_time_base = &next_update_uptime;
546  options.current_trajectory = curr_traj_ptr.get();
547  options.joint_names = &joint_names_;
548  options.angle_wraparound = &angle_wraparound_;
549  options.rt_goal_handle = gh;
550  options.default_tolerances = &default_tolerances_;
551  options.allow_partial_joints_goal = allow_partial_joints_goal_;
552 
553  // Update currently executing trajectory
554  try
555  {
556  TrajectoryPtr traj_ptr(new Trajectory);
557  *traj_ptr = initJointTrajectory<Trajectory>(*msg, next_update_time, options);
558  if (!traj_ptr->empty())
559  {
560  curr_trajectory_box_.set(traj_ptr);
561  }
562  else
563  {
564  return false;
565  }
566  }
567  catch(const std::exception& ex)
568  {
569  ROS_ERROR_STREAM_NAMED(name_, ex.what());
570  options.setErrorString(ex.what());
571  return false;
572  }
573  catch(...)
574  {
575  error_string_tmp = "Unexpected exception caught when initializing trajectory from ROS message data.";
576  ROS_ERROR_STREAM_NAMED(name_, error_string_tmp);
577  options.setErrorString(error_string_tmp);
578  return false;
579  }
580 
581  return true;
582 }
583 
584 template <class SegmentImpl, class HardwareInterface>
587 {
588  ROS_DEBUG_STREAM_NAMED(name_,"Received new action goal");
589 
590  // Precondition: Running controller
591  if (!this->isRunning())
592  {
593  ROS_ERROR_NAMED(name_, "Can't accept new action goals. Controller is not running.");
594  control_msgs::FollowJointTrajectoryResult result;
595  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_GOAL; // TODO: Add better error status to msg?
596  gh.setRejected(result);
597  return;
598  }
599 
600  // If partial joints goals are not allowed, goal should specify all controller joints
602  {
603  if (gh.getGoal()->trajectory.joint_names.size() != joint_names_.size())
604  {
605  ROS_ERROR_NAMED(name_, "Joints on incoming goal don't match the controller joints.");
606  control_msgs::FollowJointTrajectoryResult result;
607  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_JOINTS;
608  gh.setRejected(result);
609  return;
610  }
611  }
612 
613  // Goal should specify valid controller joints (they can be ordered differently). Reject if this is not the case
614  using internal::mapping;
615  std::vector<unsigned int> mapping_vector = mapping(gh.getGoal()->trajectory.joint_names, joint_names_);
616 
617  if (mapping_vector.empty())
618  {
619  ROS_ERROR_NAMED(name_, "Joints on incoming goal don't match the controller joints.");
620  control_msgs::FollowJointTrajectoryResult result;
621  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_JOINTS;
622  gh.setRejected(result);
623  return;
624  }
625 
626  // Try to update new trajectory
627  RealtimeGoalHandlePtr rt_goal(new RealtimeGoalHandle(gh));
628  std::string error_string;
629  const bool update_ok = updateTrajectoryCommand(internal::share_member(gh.getGoal(), gh.getGoal()->trajectory),
630  rt_goal,
631  &error_string);
632  rt_goal->preallocated_feedback_->joint_names = joint_names_;
633 
634  if (update_ok)
635  {
636  // Accept new goal
638  gh.setAccepted();
639  rt_active_goal_ = rt_goal;
640 
641  // Setup goal status checking timer
644  rt_goal);
646  }
647  else
648  {
649  // Reject invalid goal
650  control_msgs::FollowJointTrajectoryResult result;
651  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_GOAL;
652  result.error_string = error_string;
653  gh.setRejected(result);
654  }
655 }
656 
657 template <class SegmentImpl, class HardwareInterface>
660 {
661  RealtimeGoalHandlePtr current_active_goal(rt_active_goal_);
662 
663  // Check that cancel request refers to currently active goal (if any)
664  if (current_active_goal && current_active_goal->gh_ == gh)
665  {
666  // Reset current goal
667  rt_active_goal_.reset();
668 
669  // Controller uptime
670  const ros::Time uptime = time_data_.readFromRT()->uptime;
671 
672  // Enter hold current position mode
673  setHoldPosition(uptime);
674  ROS_DEBUG_NAMED(name_, "Canceling active action goal because cancel callback recieved from actionlib.");
675 
676  // Mark the current goal as canceled
677  current_active_goal->gh_.setCanceled();
678  }
679 }
680 
681 template <class SegmentImpl, class HardwareInterface>
683 queryStateService(control_msgs::QueryTrajectoryState::Request& req,
684  control_msgs::QueryTrajectoryState::Response& resp)
685 {
686  // Preconditions
687  if (!this->isRunning())
688  {
689  ROS_ERROR_NAMED(name_, "Can't sample trajectory. Controller is not running.");
690  return false;
691  }
692 
693  // Convert request time to internal monotonic representation
694  TimeData* time_data = time_data_.readFromRT();
695  const ros::Duration time_offset = req.time - time_data->time;
696  const ros::Time sample_time = time_data->uptime + time_offset;
697 
698  // Sample trajectory at requested time
699  TrajectoryPtr curr_traj_ptr;
700  curr_trajectory_box_.get(curr_traj_ptr);
701  Trajectory& curr_traj = *curr_traj_ptr;
702 
703  typename Segment::State response_point = typename Segment::State(joint_names_.size());
704 
705  for (unsigned int i = 0; i < joints_.size(); ++i)
706  {
707  typename Segment::State state;
708  typename TrajectoryPerJoint::const_iterator segment_it = sample(curr_traj[i], sample_time.toSec(), state);
709  if (curr_traj[i].end() == segment_it)
710  {
711  ROS_ERROR_STREAM_NAMED(name_, "Requested sample time precedes trajectory start time.");
712  return false;
713  }
714 
715  response_point.position[i] = state.position[0];
716  response_point.velocity[i] = state.velocity[0];
717  response_point.acceleration[i] = state.acceleration[0];
718  }
719 
720  // Populate response
721  resp.name = joint_names_;
722  resp.position = response_point.position;
723  resp.velocity = response_point.velocity;
724  resp.acceleration = response_point.acceleration;
725 
726  return true;
727 }
728 
729 template <class SegmentImpl, class HardwareInterface>
732 {
733  // Check if it's time to publish
735  {
736  if (state_publisher_ && state_publisher_->trylock())
737  {
739 
740  state_publisher_->msg_.header.stamp = time_data_.readFromRT()->time;
741  state_publisher_->msg_.desired.positions = desired_state_.position;
742  state_publisher_->msg_.desired.velocities = desired_state_.velocity;
743  state_publisher_->msg_.desired.accelerations = desired_state_.acceleration;
744  state_publisher_->msg_.actual.positions = current_state_.position;
745  state_publisher_->msg_.actual.velocities = current_state_.velocity;
746  state_publisher_->msg_.error.positions = state_error_.position;
747  state_publisher_->msg_.error.velocities = state_error_.velocity;
748 
749  state_publisher_->unlockAndPublish();
750  }
751  }
752 }
753 
754 template <class SegmentImpl, class HardwareInterface>
757 {
758  assert(joint_names_.size() == hold_trajectory_ptr_->size());
759 
760  typename Segment::State hold_start_state_ = typename Segment::State(1);
761  typename Segment::State hold_end_state_ = typename Segment::State(1);
762  const unsigned int n_joints = joints_.size();
763  const typename Segment::Time start_time = time.toSec();
764 
765  if(stop_trajectory_duration_ == 0.0)
766  {
767  // stop at current actual position
768  for (unsigned int i = 0; i < n_joints; ++i)
769  {
770  hold_start_state_.position[0] = joints_[i].getPosition();
771  hold_start_state_.velocity[0] = 0.0;
772  hold_start_state_.acceleration[0] = 0.0;
773  (*hold_trajectory_ptr_)[i].front().init(start_time, hold_start_state_,
774  start_time, hold_start_state_);
775  // Set goal handle for the segment
776  (*hold_trajectory_ptr_)[i].front().setGoalHandle(gh);
777  }
778  }
779  else
780  {
781  // Settle position in a fixed time. We do the following:
782  // - Create segment that goes from current (pos,vel) to (pos,-vel) in 2x the desired stop time
783  // - Assuming segment symmetry, sample segment at its midpoint (desired stop time). It should have zero velocity
784  // - Create segment that goes from current state to above zero velocity state, in the desired time
785  // NOTE: The symmetry assumption from the second point above might not hold for all possible segment types
786 
787  const typename Segment::Time end_time = time.toSec() + stop_trajectory_duration_;
788  const typename Segment::Time end_time_2x = time.toSec() + 2.0 * stop_trajectory_duration_;
789 
790  // Create segment that goes from current (pos,vel) to (pos,-vel)
791  for (unsigned int i = 0; i < n_joints; ++i)
792  {
793  // If there is a time delay in the system it is better to calculate the hold trajectory starting from the
794  // desired position. Otherwise there would be a jerk in the motion.
795  hold_start_state_.position[0] = desired_state_.position[i];
796  hold_start_state_.velocity[0] = desired_state_.velocity[i];
797  hold_start_state_.acceleration[0] = 0.0;
798 
799  hold_end_state_.position[0] = desired_state_.position[i];
800  hold_end_state_.velocity[0] = -desired_state_.velocity[i];
801  hold_end_state_.acceleration[0] = 0.0;
802 
803  (*hold_trajectory_ptr_)[i].front().init(start_time, hold_start_state_,
804  end_time_2x, hold_end_state_);
805 
806  // Sample segment at its midpoint, that should have zero velocity
807  (*hold_trajectory_ptr_)[i].front().sample(end_time, hold_end_state_);
808 
809  // Now create segment that goes from current state to one with zero end velocity
810  (*hold_trajectory_ptr_)[i].front().init(start_time, hold_start_state_,
811  end_time, hold_end_state_);
812 
813  // Set goal handle for the segment
814  (*hold_trajectory_ptr_)[i].front().setGoalHandle(gh);
815  }
816  }
818 }
819 
820 } // namespace
821 
822 #endif // header guard
d
Segment::State current_state_
Preallocated workspace variable.
#define ROS_DEBUG_STREAM_NAMED(name, args)
#define ROS_ERROR_STREAM_NAMED(name, args)
Segment::State state_error_
Preallocated workspace variable.
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
HwIfaceAdapter hw_iface_adapter_
Adapts desired trajectory state to HW interface.
Segment::State desired_joint_state_
Preallocated workspace variable.
int size() const
Trajectory::const_iterator sample(const Trajectory &trajectory, const typename Trajectory::value_type::Time &time, typename Trajectory::value_type::State &state)
Sample a trajectory at a specified time.
trajectory_msgs::JointTrajectory::ConstPtr JointTrajectoryConstPtr
bool init(std::vector< typename HardwareInterface::ResourceHandleType > &, ros::NodeHandle &)
#define ROS_DEBUG_STREAM_THROTTLE_NAMED(rate, name, args)
void setRejected(const Result &result=Result(), const std::string &text=std::string(""))
void start()
Segment::State desired_state_
Preallocated workspace variable.
boost::shared_ptr< const Goal > getGoal() const
Segment::Time stop_trajectory_duration_
Duration for stop ramp. If zero, the controller stops at the actual position.
void init(const trajectory_msgs::JointTrajectoryPoint &point, const std::vector< Scalar > &position_offset=std::vector< Scalar >())
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
Type const & getType() const
#define ROS_WARN(...)
void update(const ros::Time &time, const ros::Duration &period)
bool verbose_
Hard coded verbose flag to help in debugging.
void starting(const ros::Time &time)
Holds the current position.
std::vector< urdf::JointConstSharedPtr > getUrdfJoints(const urdf::Model &urdf, const std::vector< std::string > &joint_names)
realtime_tools::RealtimeServerGoalHandle< control_msgs::FollowJointTrajectoryAction > RealtimeGoalHandle
virtual void trajectoryCommandCB(const JointTrajectoryConstPtr &msg)
realtime_tools::RealtimePublisher< control_msgs::JointTrajectoryControllerState > StatePublisher
void setAccepted(const std::string &text=std::string(""))
bool checkStateTolerancePerJoint(const State &state_error, const StateTolerances< typename State::Scalar > &state_tolerance, bool show_errors=false)
Definition: tolerances.h:168
#define ROS_DEBUG_NAMED(name,...)
void setHoldPosition(const ros::Time &time, RealtimeGoalHandlePtr gh=RealtimeGoalHandlePtr())
Hold the current position.
std::vector< JointHandle > joints_
Handles to controlled joints.
void publishState(const ros::Time &time)
Publish current controller state at a throttled frequency.
options
bool param(const std::string &param_name, T &param_val, const T &default_val) const
RealtimeGoalHandlePtr rt_active_goal_
Currently active action goal, if any.
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
const std::string & getNamespace() const
bool init(HardwareInterface *hw, ros::NodeHandle &root_nh, ros::NodeHandle &controller_nh)
void set(const T &value)
void updateCommand(const ros::Time &, const ros::Duration &, const State &, const State &)
#define ROS_WARN_STREAM(args)
TrajectoryPtr hold_trajectory_ptr_
Last hold trajectory values.
Segment::State state_joint_error_
Preallocated workspace variable.
ros::Time uptime
Controller uptime. Set to zero at every restart.
Class representing a multi-dimensional quintic spline segment with a start and end time...
void runNonRealtime(const ros::TimerEvent &te)
virtual bool updateTrajectoryCommand(const JointTrajectoryConstPtr &msg, RealtimeGoalHandlePtr gh, std::string *error_string=0)
virtual bool queryStateService(control_msgs::QueryTrajectoryState::Request &req, control_msgs::QueryTrajectoryState::Response &resp)
std::vector< unsigned int > mapping(const T &t1, const T &t2)
SegmentTolerances< Scalar > default_tolerances_
Default trajectory segment tolerances.
std::vector< std::string > joint_names_
Controlled joint names.
Options used when initializing a joint trajectory from ROS message data.
Trajectory segment tolerances per Joint.
Definition: tolerances.h:95
void stopping(const ros::Time &)
Cancels the active action goal, if any.
bool getParam(const std::string &key, std::string &s) const
#define ROS_ERROR_NAMED(name,...)
boost::shared_ptr< Member > share_member(boost::shared_ptr< Enclosure > enclosure, Member &member)
urdf::ModelSharedPtr getUrdf(const ros::NodeHandle &nh, const std::string &param_name)
#define ROS_ERROR_STREAM(args)
std::string getLeafNamespace(const ros::NodeHandle &nh)
std::vector< std::string > getStrings(const ros::NodeHandle &nh, const std::string &param_name)
std::vector< bool > angle_wraparound_
Whether controlled joints wrap around or not.
def shortest_angular_distance(from_angle, to_angle)
actionlib::ActionServer< control_msgs::FollowJointTrajectoryAction > ActionServer
#define ROS_WARN_STREAM_NAMED(name, args)


joint_trajectory_controller
Author(s): Adolfo Rodriguez Tsouroukdissian
autogenerated on Sat Apr 18 2020 03:58:18