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 #pragma once
32 
33 
35 {
36 
37 namespace internal
38 {
39 
40 template <class Enclosure, class Member>
42 {
44  boost::shared_ptr<Member> p(&member, d);
45  return p;
46 }
47 
48 std::vector<std::string> getStrings(const ros::NodeHandle& nh, const std::string& param_name)
49 {
50  using namespace XmlRpc;
51  XmlRpcValue xml_array;
52  if (!nh.getParam(param_name, xml_array))
53  {
54  ROS_ERROR_STREAM("Could not find '" << param_name << "' parameter (namespace: " << nh.getNamespace() << ").");
55  return std::vector<std::string>();
56  }
57  if (xml_array.getType() != XmlRpcValue::TypeArray)
58  {
59  ROS_ERROR_STREAM("The '" << param_name << "' parameter is not an array (namespace: " <<
60  nh.getNamespace() << ").");
61  return std::vector<std::string>();
62  }
63 
64  std::vector<std::string> out;
65  for (int i = 0; i < xml_array.size(); ++i)
66  {
67  XmlRpc::XmlRpcValue& elem = xml_array[i];
68  if (elem.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>(elem));
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 (const auto& joint_name : joint_names)
107  {
108  urdf::JointConstSharedPtr urdf_joint = urdf.getJoint(joint_name);
109  if (urdf_joint)
110  {
111  out.push_back(urdf_joint);
112  }
113  else
114  {
115  ROS_ERROR_STREAM("Could not find joint '" << joint_name << "' 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 < getNumberOfJoints(); ++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 {
193  // The verbose parameter is for advanced use as it breaks real-time safety
194  // by enabling ROS logging services
195  if (verbose_)
196  {
198  "The joint_trajectory_controller verbose flag is enabled. "
199  << "This flag breaks real-time safety and should only be "
200  << "used for debugging");
201  }
202 }
203 
204 template <class SegmentImpl, class HardwareInterface>
206  ros::NodeHandle& root_nh,
207  ros::NodeHandle& controller_nh)
208 {
209  using namespace internal;
210 
211  // Cache controller node handle
212  controller_nh_ = controller_nh;
213 
214  // Controller name
216 
217  // State publish rate
218  double state_publish_rate = 50.0;
219  controller_nh_.getParam("state_publish_rate", state_publish_rate);
220  ROS_DEBUG_STREAM_NAMED(name_, "Controller state will be published at " << state_publish_rate << "Hz.");
221  state_publisher_period_ = ros::Duration(1.0 / state_publish_rate);
222 
223  // Action status checking update rate
224  double action_monitor_rate = 20.0;
225  controller_nh_.getParam("action_monitor_rate", action_monitor_rate);
226  action_monitor_period_ = ros::Duration(1.0 / action_monitor_rate);
227  ROS_DEBUG_STREAM_NAMED(name_, "Action status changes will be monitored at " << action_monitor_rate << "Hz.");
228 
229  // Stop trajectory duration
231  controller_nh_.getParam("stop_trajectory_duration", stop_trajectory_duration_);
232  ROS_DEBUG_STREAM_NAMED(name_, "Stop trajectory has a duration of " << stop_trajectory_duration_ << "s.");
233 
234  // Checking if partial trajectories are allowed
235  controller_nh_.param<bool>("allow_partial_joints_goal", allow_partial_joints_goal_, false);
236  if (allow_partial_joints_goal_)
237  {
238  ROS_DEBUG_NAMED(name_, "Goals with partial set of joints are allowed");
239  }
240 
241  // List of controlled joints
243  if (joint_names_.empty()) {return false;}
244  const unsigned int n_joints = joint_names_.size();
245 
246  // URDF joints
247  urdf::ModelSharedPtr urdf = getUrdf(root_nh, "robot_description");
248  if (!urdf) {return false;}
249 
250  std::vector<urdf::JointConstSharedPtr> urdf_joints = getUrdfJoints(*urdf, joint_names_);
251  if (urdf_joints.empty()) {return false;}
252  assert(n_joints == urdf_joints.size());
253 
254  // Initialize members
255  joints_.resize(n_joints);
256  angle_wraparound_.resize(n_joints);
257  for (unsigned int i = 0; i < n_joints; ++i)
258  {
259  // Joint handle
260  try {joints_[i] = hw->getHandle(joint_names_[i]);}
261  catch (...)
262  {
263  ROS_ERROR_STREAM_NAMED(name_, "Could not find joint '" << joint_names_[i] << "' in '" <<
264  this->getHardwareInterfaceType() << "'.");
265  return false;
266  }
267 
268  // Whether a joint is continuous (ie. has angle wraparound)
269  angle_wraparound_[i] = urdf_joints[i]->type == urdf::Joint::CONTINUOUS;
270  const std::string not_if = angle_wraparound_[i] ? "" : "non-";
271 
272  ROS_DEBUG_STREAM_NAMED(name_, "Found " << not_if << "continuous joint '" << joint_names_[i] << "' in '" <<
273  this->getHardwareInterfaceType() << "'.");
274  }
275 
276  assert(joints_.size() == angle_wraparound_.size());
277  ROS_DEBUG_STREAM_NAMED(name_, "Initialized controller '" << name_ << "' with:" <<
278  "\n- Number of joints: " << getNumberOfJoints() <<
279  "\n- Hardware interface type: '" << this->getHardwareInterfaceType() << "'" <<
280  "\n- Trajectory segment type: '" << hardware_interface::internal::demangledTypeName<SegmentImpl>() << "'");
281 
282  // Default tolerances
283  ros::NodeHandle tol_nh(controller_nh_, "constraints");
284  default_tolerances_ = getSegmentTolerances<Scalar>(tol_nh, joint_names_);
285 
286  // Hardware interface adapter
288 
289  // ROS API: Subscribed topics
291 
292  // ROS API: Published topics
293  state_publisher_.reset(new StatePublisher(controller_nh_, "state", 1));
294 
295  // ROS API: Action interface
296  action_server_.reset(new ActionServer(controller_nh_, "follow_joint_trajectory",
297  boost::bind(&JointTrajectoryController::goalCB, this, _1),
298  boost::bind(&JointTrajectoryController::cancelCB, this, _1),
299  false));
300  action_server_->start();
301 
302  // ROS API: Provided services
305  this);
306 
307  // Preeallocate resources
308  current_state_ = typename Segment::State(n_joints);
309  old_desired_state_ = typename Segment::State(n_joints);
310  desired_state_ = typename Segment::State(n_joints);
311  state_error_ = typename Segment::State(n_joints);
312  desired_joint_state_ = typename Segment::State(1);
313  state_joint_error_ = typename Segment::State(1);
314 
315  successful_joint_traj_ = boost::dynamic_bitset<>(getNumberOfJoints());
316 
318  assert(joint_names_.size() == hold_trajectory_ptr_->size());
319 
320  if (stop_trajectory_duration_ == 0.0)
321  {
322  hold_traj_builder_ = std::unique_ptr<TrajectoryBuilder<SegmentImpl> >(new HoldTrajectoryBuilder<SegmentImpl, HardwareInterface>(joints_));
323  }
324  else
325  {
326  hold_traj_builder_ = std::unique_ptr<TrajectoryBuilder<SegmentImpl> >(new StopTrajectoryBuilder<SegmentImpl>(stop_trajectory_duration_, desired_state_));
327  }
328 
329  {
330  state_publisher_->lock();
331  state_publisher_->msg_.joint_names = joint_names_;
332  state_publisher_->msg_.desired.positions.resize(n_joints);
333  state_publisher_->msg_.desired.velocities.resize(n_joints);
334  state_publisher_->msg_.desired.accelerations.resize(n_joints);
335  state_publisher_->msg_.actual.positions.resize(n_joints);
336  state_publisher_->msg_.actual.velocities.resize(n_joints);
337  state_publisher_->msg_.error.positions.resize(n_joints);
338  state_publisher_->msg_.error.velocities.resize(n_joints);
339  state_publisher_->unlock();
340  }
341 
342  return true;
343 }
344 
345 template <class SegmentImpl, class HardwareInterface>
347 update(const ros::Time& time, const ros::Duration& period)
348 {
349  // Get currently followed trajectory
350  TrajectoryPtr curr_traj_ptr;
351  curr_trajectory_box_.get(curr_traj_ptr);
352  Trajectory& curr_traj = *curr_traj_ptr;
353 
354  old_time_data_ = *(time_data_.readFromRT());
355 
356  // Update time data
357  TimeData time_data;
358  time_data.time = time; // Cache current time
359  time_data.period = period; // Cache current control period
360  time_data.uptime = old_time_data_.uptime + period; // Update controller uptime
361  time_data_.writeFromNonRT(time_data); // TODO: Grrr, we need a lock-free data structure here!
362 
363  // NOTE: It is very important to execute the two above code blocks in the specified sequence: first get current
364  // trajectory, then update time data. Hopefully the following paragraph sheds a bit of light on the rationale.
365  // The non-rt thread responsible for processing new commands enqueues trajectories that can start at the _next_
366  // control cycle (eg. zero start time) or later (eg. when we explicitly request a start time in the future).
367  // If we reverse the order of the two blocks above, and update the time data first; it's possible that by the time we
368  // fetch the currently followed trajectory, it has been updated by the non-rt thread with something that starts in the
369  // next control cycle, leaving the current cycle without a valid trajectory.
370 
371  updateStates(time_data.uptime, curr_traj_ptr.get());
372 
373  // Update current state and state error
374  for (unsigned int i = 0; i < getNumberOfJoints(); ++i)
375  {
376  typename TrajectoryPerJoint::const_iterator segment_it = sample(curr_traj[i], time_data.uptime.toSec(), desired_joint_state_);
377  if (curr_traj[i].end() == segment_it)
378  {
379  // Non-realtime safe, but should never happen under normal operation
381  "Unexpected error: No trajectory defined at current time. Please contact the package maintainer.");
382  return;
383  }
384 
385  // Get state error for current joint
386  state_joint_error_.position[0] = state_error_.position[i];
387  state_joint_error_.velocity[0] = state_error_.velocity[i];
388  state_joint_error_.acceleration[0] = state_error_.acceleration[i];
389 
390  //Check tolerances
391  const RealtimeGoalHandlePtr rt_segment_goal = segment_it->getGoalHandle();
392  if (rt_segment_goal && rt_segment_goal == rt_active_goal_)
393  {
394  // Check tolerances
395  if (time_data.uptime.toSec() < segment_it->endTime())
396  {
397  // Currently executing a segment: check path tolerances
398  const SegmentTolerancesPerJoint<Scalar>& joint_tolerances = segment_it->getTolerances();
400  {
401  if (verbose_)
402  {
403  ROS_ERROR_STREAM_NAMED(name_,"Path tolerances failed for joint: " << joint_names_[i]);
405  }
406  rt_segment_goal->preallocated_result_->error_code =
407  control_msgs::FollowJointTrajectoryResult::PATH_TOLERANCE_VIOLATED;
408  rt_segment_goal->setAborted(rt_segment_goal->preallocated_result_);
409  rt_active_goal_.reset();
410  successful_joint_traj_.reset();
411  }
412  }
413  else if (segment_it == --curr_traj[i].end())
414  {
415  if (verbose_)
416  ROS_DEBUG_STREAM_THROTTLE_NAMED(1,name_,"Finished executing last segment, checking goal tolerances");
417 
418  // Controller uptime
419  const ros::Time uptime = time_data_.readFromRT()->uptime;
420 
421  // Checks that we have ended inside the goal tolerances
422  const SegmentTolerancesPerJoint<Scalar>& tolerances = segment_it->getTolerances();
423  const bool inside_goal_tolerances = checkStateTolerancePerJoint(state_joint_error_, tolerances.goal_state_tolerance);
424 
425  if (inside_goal_tolerances)
426  {
427  successful_joint_traj_[i] = 1;
428  }
429  else if (uptime.toSec() < segment_it->endTime() + tolerances.goal_time_tolerance)
430  {
431  // Still have some time left to meet the goal state tolerances
432  }
433  else
434  {
435  if (verbose_)
436  {
437  ROS_ERROR_STREAM_NAMED(name_,"Goal tolerances failed for joint: "<< joint_names_[i]);
438  // Check the tolerances one more time to output the errors that occurs
440  }
441 
442  rt_segment_goal->preallocated_result_->error_code = control_msgs::FollowJointTrajectoryResult::GOAL_TOLERANCE_VIOLATED;
443  rt_segment_goal->setAborted(rt_segment_goal->preallocated_result_);
444  rt_active_goal_.reset();
445  successful_joint_traj_.reset();
446  }
447  }
448  }
449  }
450 
451  //If there is an active goal and all segments finished successfully then set goal as succeeded
452  RealtimeGoalHandlePtr current_active_goal(rt_active_goal_);
453  if (current_active_goal && successful_joint_traj_.count() == getNumberOfJoints())
454  {
455  current_active_goal->preallocated_result_->error_code = control_msgs::FollowJointTrajectoryResult::SUCCESSFUL;
456  current_active_goal->setSucceeded(current_active_goal->preallocated_result_);
457  current_active_goal.reset(); // do not publish feedback
458  rt_active_goal_.reset();
459  successful_joint_traj_.reset();
460  }
461 
462  updateFuncExtensionPoint(curr_traj, time_data);
463 
464  // Hardware interface adapter: Generate and send commands
465  hw_iface_adapter_.updateCommand(time_data.uptime, time_data.period,
467 
469 
470  publishState(time_data.uptime);
471 }
472 
473 template <class SegmentImpl, class HardwareInterface>
476 {
478  Options options;
479  options.error_string = error_string;
480  std::string error_string_tmp;
481 
482  // Preconditions
483  if (!this->isRunning())
484  {
485  error_string_tmp = "Can't accept new commands. Controller is not running.";
486  ROS_ERROR_STREAM_NAMED(name_, error_string_tmp);
487  options.setErrorString(error_string_tmp);
488  return false;
489  }
490 
491  if (!msg)
492  {
493  error_string_tmp = "Received null-pointer trajectory message, skipping.";
494  ROS_WARN_STREAM_NAMED(name_, error_string_tmp);
495  options.setErrorString(error_string_tmp);
496  return false;
497  }
498 
499  // Time data
500  TimeData* time_data = time_data_.readFromRT(); // TODO: Grrr, we need a lock-free data structure here!
501 
502  // Time of the next update
503  const ros::Time next_update_time = time_data->time + time_data->period;
504 
505  // Uptime of the next update
506  ros::Time next_update_uptime = time_data->uptime + time_data->period;
507 
508  // Hold current position if trajectory is empty
509  if (msg->points.empty())
510  {
511  setHoldPosition(time_data->uptime, gh);
512  ROS_DEBUG_NAMED(name_, "Empty trajectory command, stopping.");
513  return true;
514  }
515 
516  // Trajectory initialization options
517  TrajectoryPtr curr_traj_ptr;
518  curr_trajectory_box_.get(curr_traj_ptr);
519 
520  options.other_time_base = &next_update_uptime;
521  options.current_trajectory = curr_traj_ptr.get();
522  options.joint_names = &joint_names_;
523  options.angle_wraparound = &angle_wraparound_;
524  options.rt_goal_handle = gh;
525  options.default_tolerances = &default_tolerances_;
526  options.allow_partial_joints_goal = allow_partial_joints_goal_;
527 
528  // Update currently executing trajectory
529  try
530  {
531  TrajectoryPtr traj_ptr(new Trajectory);
532  *traj_ptr = initJointTrajectory<Trajectory>(*msg, next_update_time, options);
533  if (!traj_ptr->empty())
534  {
535  curr_trajectory_box_.set(traj_ptr);
536  }
537  else
538  {
539  return false;
540  }
541  }
542  catch(const std::exception& ex)
543  {
544  ROS_ERROR_STREAM_NAMED(name_, ex.what());
545  options.setErrorString(ex.what());
546  return false;
547  }
548  catch(...)
549  {
550  error_string_tmp = "Unexpected exception caught when initializing trajectory from ROS message data.";
551  ROS_ERROR_STREAM_NAMED(name_, error_string_tmp);
552  options.setErrorString(error_string_tmp);
553  return false;
554  }
555 
556  return true;
557 }
558 
559 template <class SegmentImpl, class HardwareInterface>
562 {
563  ROS_DEBUG_STREAM_NAMED(name_,"Received new action goal");
564 
565  // Precondition: Running controller
566  if (!this->isRunning())
567  {
568  ROS_ERROR_NAMED(name_, "Can't accept new action goals. Controller is not running.");
569  control_msgs::FollowJointTrajectoryResult result;
570  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_GOAL; // TODO: Add better error status to msg?
571  gh.setRejected(result);
572  return;
573  }
574 
575  // If partial joints goals are not allowed, goal should specify all controller joints
577  {
578  if (gh.getGoal()->trajectory.joint_names.size() != joint_names_.size())
579  {
580  ROS_ERROR_NAMED(name_, "Joints on incoming goal don't match the controller joints.");
581  control_msgs::FollowJointTrajectoryResult result;
582  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_JOINTS;
583  gh.setRejected(result);
584  return;
585  }
586  }
587 
588  // Goal should specify valid controller joints (they can be ordered differently). Reject if this is not the case
589  using internal::mapping;
590  std::vector<unsigned int> mapping_vector = mapping(gh.getGoal()->trajectory.joint_names, joint_names_);
591 
592  if (mapping_vector.empty())
593  {
594  ROS_ERROR_NAMED(name_, "Joints on incoming goal don't match the controller joints.");
595  control_msgs::FollowJointTrajectoryResult result;
596  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_JOINTS;
597  gh.setRejected(result);
598  return;
599  }
600 
601  // Try to update new trajectory
602  RealtimeGoalHandlePtr rt_goal(new RealtimeGoalHandle(gh));
603  std::string error_string;
604  const bool update_ok = updateTrajectoryCommand(internal::share_member(gh.getGoal(), gh.getGoal()->trajectory),
605  rt_goal,
606  &error_string);
607  rt_goal->preallocated_feedback_->joint_names = joint_names_;
608 
609  if (update_ok)
610  {
611  // Accept new goal
613  gh.setAccepted();
614  rt_active_goal_ = rt_goal;
615 
616  // Setup goal status checking timer
619  rt_goal);
621  }
622  else
623  {
624  // Reject invalid goal
625  control_msgs::FollowJointTrajectoryResult result;
626  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_GOAL;
627  result.error_string = error_string;
628  gh.setRejected(result);
629  }
630 }
631 
632 template <class SegmentImpl, class HardwareInterface>
635 {
636  RealtimeGoalHandlePtr current_active_goal(rt_active_goal_);
637 
638  // Check that cancel request refers to currently active goal (if any)
639  if (current_active_goal && current_active_goal->gh_ == gh)
640  {
641  // Reset current goal
642  rt_active_goal_.reset();
643 
644  // Controller uptime
645  const ros::Time uptime = time_data_.readFromRT()->uptime;
646 
647  // Enter hold current position mode
648  setHoldPosition(uptime);
649  ROS_DEBUG_NAMED(name_, "Canceling active action goal because cancel callback recieved from actionlib.");
650 
651  // Mark the current goal as canceled
652  current_active_goal->gh_.setCanceled();
653  }
654 }
655 
656 template <class SegmentImpl, class HardwareInterface>
658 queryStateService(control_msgs::QueryTrajectoryState::Request& req,
659  control_msgs::QueryTrajectoryState::Response& resp)
660 {
661  // Preconditions
662  if (!this->isRunning())
663  {
664  ROS_ERROR_NAMED(name_, "Can't sample trajectory. Controller is not running.");
665  return false;
666  }
667 
668  // Convert request time to internal monotonic representation
669  TimeData* time_data = time_data_.readFromRT();
670  const ros::Duration time_offset = req.time - time_data->time;
671  const ros::Time sample_time = time_data->uptime + time_offset;
672 
673  // Sample trajectory at requested time
674  TrajectoryPtr curr_traj_ptr;
675  curr_trajectory_box_.get(curr_traj_ptr);
676  Trajectory& curr_traj = *curr_traj_ptr;
677 
678  typename Segment::State response_point = typename Segment::State(joint_names_.size());
679 
680  for (unsigned int i = 0; i < getNumberOfJoints(); ++i)
681  {
682  typename Segment::State state;
683  typename TrajectoryPerJoint::const_iterator segment_it = sample(curr_traj[i], sample_time.toSec(), state);
684  if (curr_traj[i].end() == segment_it)
685  {
686  ROS_ERROR_STREAM_NAMED(name_, "Requested sample time precedes trajectory start time.");
687  return false;
688  }
689 
690  response_point.position[i] = state.position[0];
691  response_point.velocity[i] = state.velocity[0];
692  response_point.acceleration[i] = state.acceleration[0];
693  }
694 
695  // Populate response
696  resp.name = joint_names_;
697  resp.position = response_point.position;
698  resp.velocity = response_point.velocity;
699  resp.acceleration = response_point.acceleration;
700 
701  return true;
702 }
703 
704 template <class SegmentImpl, class HardwareInterface>
707 {
708  // Check if it's time to publish
710  {
711  if (state_publisher_ && state_publisher_->trylock())
712  {
714 
715  state_publisher_->msg_.header.stamp = time_data_.readFromRT()->time;
716  state_publisher_->msg_.desired.positions = desired_state_.position;
717  state_publisher_->msg_.desired.velocities = desired_state_.velocity;
718  state_publisher_->msg_.desired.accelerations = desired_state_.acceleration;
719  state_publisher_->msg_.actual.positions = current_state_.position;
720  state_publisher_->msg_.actual.velocities = current_state_.velocity;
721  state_publisher_->msg_.error.positions = state_error_.position;
722  state_publisher_->msg_.error.velocities = state_error_.velocity;
723 
724  state_publisher_->unlockAndPublish();
725  }
726  }
727 }
728 
729 template <class SegmentImpl, class HardwareInterface>
732 {
734  ->setStartTime(time.toSec())
735  ->setGoalHandle(gh)
736  ->buildTrajectory(hold_trajectory_ptr_.get());
737  hold_traj_builder_->reset();
739 }
740 
741 template <class SegmentImpl, class HardwareInterface>
743 updateFuncExtensionPoint(const Trajectory& curr_traj, const TimeData& time_data)
744 {
745  // To be implemented by derived class
746 }
747 
748 template <class SegmentImpl, class HardwareInterface>
751 {
752  return joints_.size();
753 }
754 
755 template <class SegmentImpl, class HardwareInterface>
757 updateStates(const ros::Time& sample_time, const Trajectory* const traj)
758 {
760 
761  for (unsigned int joint_index = 0; joint_index < getNumberOfJoints(); ++joint_index)
762  {
763  sample( (*traj)[joint_index], sample_time.toSec(), desired_joint_state_);
764 
765  current_state_.position[joint_index] = joints_[joint_index].getPosition();
766  current_state_.velocity[joint_index] = joints_[joint_index].getVelocity();
767  // There's no acceleration data available in a joint handle
768 
769  desired_state_.position[joint_index] = desired_joint_state_.position[0];
770  desired_state_.velocity[joint_index] = desired_joint_state_.velocity[0];
771  desired_state_.acceleration[joint_index] = desired_joint_state_.acceleration[0]; ;
772 
773  state_joint_error_.position[0] = angles::shortest_angular_distance(current_state_.position[joint_index],desired_joint_state_.position[0]);
774  state_joint_error_.velocity[0] = desired_joint_state_.velocity[0] - current_state_.velocity[joint_index];
775  state_joint_error_.acceleration[0] = 0.0;
776 
777  state_error_.position[joint_index] = state_joint_error_.position[0];
778  state_error_.velocity[joint_index] = state_joint_error_.velocity[0];
779  state_error_.acceleration[joint_index] = 0.0;
780  }
781 }
782 
783 template <class SegmentImpl, class HardwareInterface>
786 {
787  RealtimeGoalHandlePtr current_active_goal(rt_active_goal_);
788  if (!current_active_goal)
789  {
790  return;
791  }
792 
793  current_active_goal->preallocated_feedback_->header.stamp = time_data_.readFromRT()->time;
794  current_active_goal->preallocated_feedback_->desired.positions = desired_state_.position;
795  current_active_goal->preallocated_feedback_->desired.velocities = desired_state_.velocity;
796  current_active_goal->preallocated_feedback_->desired.accelerations = desired_state_.acceleration;
797  current_active_goal->preallocated_feedback_->actual.positions = current_state_.position;
798  current_active_goal->preallocated_feedback_->actual.velocities = current_state_.velocity;
799  current_active_goal->preallocated_feedback_->error.positions = state_error_.position;
800  current_active_goal->preallocated_feedback_->error.velocities = state_error_.velocity;
801  current_active_goal->setFeedback( current_active_goal->preallocated_feedback_ );
802 
803 }
804 
805 template <class SegmentImpl, class HardwareInterface>
808 {
809  TrajectoryPtr hold_traj {new Trajectory()};
810 
811  typename Segment::State default_state = typename Segment::State(number_of_joints);
812  typename Segment::State default_joint_state = typename Segment::State(1);
813  for (unsigned int i = 0; i < number_of_joints; ++i)
814  {
815  default_joint_state.position[0]= default_state.position[i];
816  default_joint_state.velocity[0]= default_state.velocity[i];
817  Segment hold_segment(0.0, default_joint_state, 0.0, default_joint_state);
818 
819  TrajectoryPerJoint joint_segment;
820  joint_segment.resize(1, hold_segment);
821  hold_traj->push_back(joint_segment);
822  }
823 
824  return hold_traj;
825 }
826 
827 } // namespace
d
Segment::State current_state_
Preallocated workspace variable.
void setActionFeedback()
Updates the pre-allocated feedback of the current active goal (if any) based on the current state val...
#define ROS_DEBUG_STREAM_NAMED(name, args)
#define ROS_ERROR_STREAM_NAMED(name, args)
boost::shared_ptr< const Goal > getGoal() const
Segment::State state_error_
Preallocated workspace variable.
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
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.
static TrajectoryPtr createHoldTrajectory(const unsigned int &number_of_joints)
Returns a trajectory consisting of joint trajectories with one pre-allocated segment.
Segment::State desired_joint_state_
Preallocated workspace variable.
Builder creating a trajectory stopping the robot.
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 &)
unsigned int getNumberOfJoints() const
Returns the number of joints of the robot.
void setRejected(const Result &result=Result(), const std::string &text=std::string(""))
void start()
Segment::State desired_state_
Preallocated workspace variable.
Segment::Time stop_trajectory_duration_
Duration for stop ramp. If zero, the controller stops at the actual position.
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
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.
bool param(const std::string &param_name, T &param_val, const T &default_val) const
Type const & getType() const
void updateStates(const ros::Time &sample_time, const Trajectory *const traj)
Updates the states by sampling the specified trajectory for each joint at the specified sampling time...
std::vector< JointHandle > joints_
Handles to controlled joints.
void publishState(const ros::Time &time)
Publish current controller state at a throttled frequency.
options
bool getParam(const std::string &key, std::string &s) const
Builder creating a trajectory "simply" holding (without motion) the specified position.
RealtimeGoalHandlePtr rt_active_goal_
Currently active action goal, if any.
virtual void updateFuncExtensionPoint(const Trajectory &curr_traj, const TimeData &time_data)
Allows derived classes to perform additional checks and to e.g. replace the newly calculated desired ...
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 queryStateService(control_msgs::QueryTrajectoryState::Request &req, control_msgs::QueryTrajectoryState::Response &resp)
std::vector< unsigned int > mapping(const T &t1, const T &t2)
virtual bool updateTrajectoryCommand(const JointTrajectoryConstPtr &msg, RealtimeGoalHandlePtr gh, std::string *error_string=nullptr)
std::unique_ptr< TrajectoryBuilder< SegmentImpl > > hold_traj_builder_
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
const std::string & getNamespace() const
Segment::State old_desired_state_
Preallocated workspace variable.
void stopping(const ros::Time &)
Cancels the active action goal, if any.
#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)
#define ROS_DEBUG_STREAM_THROTTLE_NAMED(period, name, 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 Fri Feb 3 2023 03:19:15