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>
167 trajectoryCommandCB(const JointTrajectoryConstPtr& msg)
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
216  name_ = getLeafNamespace(controller_nh_);
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
231  stop_trajectory_duration_ = 0.0;
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
250  joint_names_ = getStrings(controller_nh_, "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  ROS_INFO_STREAM("Loading joint " << joint_names_[i] << " (" << i << ")");
284  }
285 
286  assert(joints_.size() == angle_wraparound_.size());
287  ROS_DEBUG_STREAM_NAMED(name_, "Initialized controller '" << name_ << "' with:" <<
288  "\n- Number of joints: " << joints_.size() <<
289  "\n- Hardware interface type: '" << this->getHardwareInterfaceType() << "'" <<
290  "\n- Trajectory segment type: '" << hardware_interface::internal::demangledTypeName<SegmentImpl>() << "'");
291 
292  // Default tolerances
293  ros::NodeHandle tol_nh(controller_nh_, "constraints");
294  default_tolerances_ = getSegmentTolerances<Scalar>(tol_nh, joint_names_);
295 
296  // Hardware interface adapter
297  hw_iface_adapter_.init(joints_, controller_nh_);
298 
299  // ROS API: Subscribed topics
300  trajectory_command_sub_ = controller_nh_.subscribe("command", 1, &JointTrajectoryController::trajectoryCommandCB, this);
301 
302  // ROS API: Published topics
303  state_publisher_.reset(new StatePublisher(controller_nh_, "state", 1));
304 
305  // ROS API: Action interface
306  action_server_.reset(new ActionServer(controller_nh_, "follow_joint_trajectory",
307  boost::bind(&JointTrajectoryController::goalCB, this, _1),
308  boost::bind(&JointTrajectoryController::cancelCB, this, _1),
309  false));
310  action_server_->start();
311 
312  // ROS API: Provided services
313  query_state_service_ = controller_nh_.advertiseService("query_state",
315  this);
316 
317  // Preeallocate resources
318  current_state_ = typename Segment::State(n_joints);
319  desired_state_ = typename Segment::State(n_joints);
320  state_error_ = typename Segment::State(n_joints);
321  desired_joint_state_ = typename Segment::State(1);
322  state_joint_error_ = typename Segment::State(1);
323 
324  successful_joint_traj_ = boost::dynamic_bitset<>(joints_.size());
325 
326  // Initialize trajectory with all joints
327  typename Segment::State current_joint_state_ = typename Segment::State(1);
328  for (unsigned int i = 0; i < n_joints; ++i)
329  {
330  current_joint_state_.position[0]= current_state_.position[i];
331  current_joint_state_.velocity[0]= current_state_.velocity[i];
332  Segment hold_segment(0.0, current_joint_state_, 0.0, current_joint_state_);
333 
334  TrajectoryPerJoint joint_segment;
335  joint_segment.resize(1, hold_segment);
336  hold_trajectory_ptr_->push_back(joint_segment);
337  }
338 
339  {
340  state_publisher_->lock();
341  state_publisher_->msg_.joint_names = joint_names_;
342  state_publisher_->msg_.desired.positions.resize(n_joints);
343  state_publisher_->msg_.desired.velocities.resize(n_joints);
344  state_publisher_->msg_.desired.accelerations.resize(n_joints);
345  state_publisher_->msg_.actual.positions.resize(n_joints);
346  state_publisher_->msg_.actual.velocities.resize(n_joints);
347  state_publisher_->msg_.error.positions.resize(n_joints);
348  state_publisher_->msg_.error.velocities.resize(n_joints);
349  state_publisher_->unlock();
350  }
351 
352  //
353  // List of mimic joints
354  //
355  if (controller_nh_.hasParam("mimic_joints"))
356  {
357  mimic_joint_names_ = getStrings(controller_nh_, "mimic_joints");
358  const unsigned int n_mimic_joints = mimic_joint_names_.size();
359  mimic_joints_.resize(n_mimic_joints);
360  mimic_joint_ids_.resize(n_mimic_joints);
361  mimic_urdf_joints_.resize(n_mimic_joints);
362  for (unsigned int i = 0; i < n_mimic_joints; ++i)
363  {
364  // Mimic Joint handle
365  try {
366  mimic_joints_[i] = hw->hardware_interface::ResourceManager<JointHandle>::getHandle(mimic_joint_names_[i]);
367  }
368  catch (...)
369  {
370  ROS_ERROR_STREAM_NAMED(name_, "Could not find joint '" << mimic_joint_names_[i] << "' in '" <<
371  this->getHardwareInterfaceType() << "'.");
372  return false;
373  }
374  mimic_urdf_joints_[i] = urdf->getJoint(mimic_joint_names_[i]);
375  if (!(mimic_urdf_joints_[i] && mimic_urdf_joints_[i]->mimic))
376  {
377  ROS_ERROR_STREAM_NAMED(name_, "Could not find joint '" << mimic_joint_names_[i] << "' in URDF");
378  }
379  mimic_joint_ids_[i] = std::distance(joint_names_.begin(), std::find(joint_names_.begin(), joint_names_.end(), mimic_urdf_joints_[i]->mimic->joint_name));
380  if (mimic_joint_ids_[i] == joint_names_.size())
381  {
382  ROS_ERROR_STREAM_NAMED(name_, "Could not find joint '" << mimic_urdf_joints_[i]->mimic->joint_name << "' in joint_names");
383  }
384  ROS_INFO_STREAM("Loading mimic_joint " << mimic_joint_names_[i] << " = " << joint_names_[mimic_joint_ids_[i]] << " (" << mimic_joint_ids_[i] << ") * " << mimic_urdf_joints_[i]->mimic->multiplier << " + " << mimic_urdf_joints_[i]->mimic->offset);
385  }
386  // Hardware interface adapter
387  mimic_hw_iface_adapter_.init(mimic_joints_, controller_nh_);
388 
389  mimic_current_state_ = typename Segment::State(n_mimic_joints);
390  mimic_desired_state_ = typename Segment::State(n_mimic_joints);
391  mimic_state_error_ = typename Segment::State(n_mimic_joints);
392 
393  // Initialize trajectory with all joints
394  typename Segment::State mimic_current_joint_state_ = typename Segment::State(1);
395  for (unsigned int i = 0; i < n_mimic_joints; ++i)
396  {
397  mimic_current_joint_state_.position[0]= mimic_current_state_.position[i];
398  mimic_current_joint_state_.velocity[0]= mimic_current_state_.velocity[i];
399  }
400  }
401 
402  return true;
403 }
404 
405 template <class SegmentImpl, class HardwareInterface>
407 update(const ros::Time& time, const ros::Duration& period)
408 {
409  // Get currently followed trajectory
410  TrajectoryPtr curr_traj_ptr;
411  curr_trajectory_box_.get(curr_traj_ptr);
412  Trajectory& curr_traj = *curr_traj_ptr;
413 
414  // Update time data
415  TimeData time_data;
416  time_data.time = time; // Cache current time
417  time_data.period = period; // Cache current control period
418  time_data.uptime = time_data_.readFromRT()->uptime + period; // Update controller uptime
419  time_data_.writeFromNonRT(time_data); // TODO: Grrr, we need a lock-free data structure here!
420 
421  // NOTE: It is very important to execute the two above code blocks in the specified sequence: first get current
422  // trajectory, then update time data. Hopefully the following paragraph sheds a bit of light on the rationale.
423  // The non-rt thread responsible for processing new commands enqueues trajectories that can start at the _next_
424  // control cycle (eg. zero start time) or later (eg. when we explicitly request a start time in the future).
425  // If we reverse the order of the two blocks above, and update the time data first; it's possible that by the time we
426  // fetch the currently followed trajectory, it has been updated by the non-rt thread with something that starts in the
427  // next control cycle, leaving the current cycle without a valid trajectory.
428 
429  // Update current state and state error
430  for (unsigned int i = 0; i < joints_.size(); ++i)
431  {
432  current_state_.position[i] = joints_[i].getPosition();
433  current_state_.velocity[i] = joints_[i].getVelocity();
434  // There's no acceleration data available in a joint handle
435 
436  typename TrajectoryPerJoint::const_iterator segment_it = sample(curr_traj[i], time_data.uptime.toSec(), desired_joint_state_);
437  if (curr_traj[i].end() == segment_it)
438  {
439  // Non-realtime safe, but should never happen under normal operation
440  ROS_ERROR_NAMED(name_,
441  "Unexpected error: No trajectory defined at current time. Please contact the package maintainer.");
442  return;
443  }
444  desired_state_.position[i] = desired_joint_state_.position[0];
445  desired_state_.velocity[i] = desired_joint_state_.velocity[0];
446  desired_state_.acceleration[i] = desired_joint_state_.acceleration[0]; ;
447 
448  state_joint_error_.position[0] = angles::shortest_angular_distance(current_state_.position[i],desired_joint_state_.position[0]);
449  state_joint_error_.velocity[0] = desired_joint_state_.velocity[0] - current_state_.velocity[i];
450  state_joint_error_.acceleration[0] = 0.0;
451 
452  state_error_.position[i] = angles::shortest_angular_distance(current_state_.position[i],desired_joint_state_.position[0]);
453  state_error_.velocity[i] = desired_joint_state_.velocity[0] - current_state_.velocity[i];
454  state_error_.acceleration[i] = 0.0;
455 
456  //Check tolerances
457  const RealtimeGoalHandlePtr rt_segment_goal = segment_it->getGoalHandle();
458  if (rt_segment_goal && rt_segment_goal == rt_active_goal_)
459  {
460  // Check tolerances
461  if (time_data.uptime.toSec() < segment_it->endTime())
462  {
463  // Currently executing a segment: check path tolerances
464  const SegmentTolerancesPerJoint<Scalar>& joint_tolerances = segment_it->getTolerances();
465  if (!checkStateTolerancePerJoint(state_joint_error_, joint_tolerances.state_tolerance))
466  {
467  if (verbose_)
468  {
469  ROS_ERROR_STREAM_NAMED(name_,"Path tolerances failed for joint: " << joint_names_[i]);
470  checkStateTolerancePerJoint(state_joint_error_, joint_tolerances.state_tolerance, true);
471  }
472  rt_segment_goal->preallocated_result_->error_code =
473  control_msgs::FollowJointTrajectoryResult::PATH_TOLERANCE_VIOLATED;
474  rt_segment_goal->setAborted(rt_segment_goal->preallocated_result_);
475  rt_active_goal_.reset();
476  successful_joint_traj_.reset();
477  }
478  }
479  else if (segment_it == --curr_traj[i].end())
480  {
481  if (verbose_)
482  ROS_DEBUG_STREAM_THROTTLE_NAMED(1,name_,"Finished executing last segment, checking goal tolerances");
483 
484  // Controller uptime
485  const ros::Time uptime = time_data_.readFromRT()->uptime;
486 
487  // Checks that we have ended inside the goal tolerances
488  const SegmentTolerancesPerJoint<Scalar>& tolerances = segment_it->getTolerances();
489  const bool inside_goal_tolerances = checkStateTolerancePerJoint(state_joint_error_, tolerances.goal_state_tolerance);
490 
491  if (inside_goal_tolerances)
492  {
493  successful_joint_traj_[i] = 1;
494  }
495  else if (uptime.toSec() < segment_it->endTime() + tolerances.goal_time_tolerance)
496  {
497  // Still have some time left to meet the goal state tolerances
498  }
499  else
500  {
501  if (verbose_)
502  {
503  ROS_ERROR_STREAM_NAMED(name_,"Goal tolerances failed for joint: "<< joint_names_[i]);
504  // Check the tolerances one more time to output the errors that occurs
505  checkStateTolerancePerJoint(state_joint_error_, tolerances.goal_state_tolerance, true);
506  }
507 
508  rt_segment_goal->preallocated_result_->error_code = control_msgs::FollowJointTrajectoryResult::GOAL_TOLERANCE_VIOLATED;
509  rt_segment_goal->setAborted(rt_segment_goal->preallocated_result_);
510  rt_active_goal_.reset();
511  successful_joint_traj_.reset();
512  }
513  }
514  }
515  }
516 
517  //If there is an active goal and all segments finished successfully then set goal as succeeded
518  RealtimeGoalHandlePtr current_active_goal(rt_active_goal_);
519  if (current_active_goal and successful_joint_traj_.count() == joints_.size())
520  {
521  current_active_goal->preallocated_result_->error_code = control_msgs::FollowJointTrajectoryResult::SUCCESSFUL;
522  current_active_goal->setSucceeded(current_active_goal->preallocated_result_);
523  current_active_goal.reset(); // do not publish feedback
524  rt_active_goal_.reset();
525  successful_joint_traj_.reset();
526  }
527 
528  // Hardware interface adapter: Generate and send commands
529  hw_iface_adapter_.updateCommand(time_data.uptime, time_data.period,
530  desired_state_, state_error_);
531 
532  // Set action feedback
533  if (current_active_goal)
534  {
535  current_active_goal->preallocated_feedback_->header.stamp = time_data_.readFromRT()->time;
536  current_active_goal->preallocated_feedback_->desired.positions = desired_state_.position;
537  current_active_goal->preallocated_feedback_->desired.velocities = desired_state_.velocity;
538  current_active_goal->preallocated_feedback_->desired.accelerations = desired_state_.acceleration;
539  current_active_goal->preallocated_feedback_->actual.positions = current_state_.position;
540  current_active_goal->preallocated_feedback_->actual.velocities = current_state_.velocity;
541  current_active_goal->preallocated_feedback_->error.positions = state_error_.position;
542  current_active_goal->preallocated_feedback_->error.velocities = state_error_.velocity;
543  current_active_goal->setFeedback( current_active_goal->preallocated_feedback_ );
544  }
545 
546  // Publish state
547  publishState(time_data.uptime);
548 
549  // Mimic Joints
550  // Update current state and state error for mimic_joint
551  for (unsigned int i = 0; i < mimic_joints_.size(); ++i)
552  {
553  mimic_current_state_.position[i] = mimic_joints_[i].getPosition();
554  mimic_current_state_.velocity[i] = mimic_joints_[i].getVelocity();
555  // There's no acceleration data available in a joint handle
556 
557  unsigned int mimic_joint_id = mimic_joint_ids_[i];
558  mimic_desired_state_.position[i] = desired_state_.position[mimic_joint_id] * mimic_urdf_joints_[i]->mimic->multiplier + mimic_urdf_joints_[i]->mimic->offset;
559  mimic_desired_state_.velocity[i] = desired_state_.velocity[mimic_joint_id];
560  mimic_desired_state_.acceleration[i] = desired_state_.acceleration[mimic_joint_id];
561 
562  mimic_state_error_.position[i] = angles::shortest_angular_distance(mimic_current_state_.position[i],mimic_desired_state_.position[i]);
563  mimic_state_error_.velocity[i] = mimic_desired_state_.velocity[i] - mimic_current_state_.velocity[i];
564  mimic_state_error_.acceleration[i] = 0.0;
565  }
566 
567  // Send commands to mimic joints_
568  if (mimic_joints_.size() > 0)
569  {
570  mimic_hw_iface_adapter_.updateCommand(time_data.uptime, time_data.period,
571  mimic_desired_state_, mimic_state_error_);
572  }
573 
574 }
575 
576 template <class SegmentImpl, class HardwareInterface>
578 updateTrajectoryCommand(const JointTrajectoryConstPtr& msg, RealtimeGoalHandlePtr gh, std::string* error_string)
579 {
581  Options options;
582  options.error_string = error_string;
583  std::string error_string_tmp;
584 
585  // Preconditions
586  if (!this->isRunning())
587  {
588  error_string_tmp = "Can't accept new commands. Controller is not running.";
589  ROS_ERROR_STREAM_NAMED(name_, error_string_tmp);
590  options.setErrorString(error_string_tmp);
591  return false;
592  }
593 
594  if (!msg)
595  {
596  error_string_tmp = "Received null-pointer trajectory message, skipping.";
597  ROS_WARN_STREAM_NAMED(name_, error_string_tmp);
598  options.setErrorString(error_string_tmp);
599  return false;
600  }
601 
602  // Time data
603  TimeData* time_data = time_data_.readFromRT(); // TODO: Grrr, we need a lock-free data structure here!
604 
605  // Time of the next update
606  const ros::Time next_update_time = time_data->time + time_data->period;
607 
608  // Uptime of the next update
609  ros::Time next_update_uptime = time_data->uptime + time_data->period;
610 
611  // Hold current position if trajectory is empty
612  if (msg->points.empty())
613  {
614  setHoldPosition(time_data->uptime, gh);
615  ROS_DEBUG_NAMED(name_, "Empty trajectory command, stopping.");
616  return true;
617  }
618 
619  // Trajectory initialization options
620  TrajectoryPtr curr_traj_ptr;
621  curr_trajectory_box_.get(curr_traj_ptr);
622 
623  options.other_time_base = &next_update_uptime;
624  options.current_trajectory = curr_traj_ptr.get();
625  options.joint_names = &joint_names_;
626  options.angle_wraparound = &angle_wraparound_;
627  options.rt_goal_handle = gh;
628  options.default_tolerances = &default_tolerances_;
629  options.allow_partial_joints_goal = allow_partial_joints_goal_;
630 
631  // Update currently executing trajectory
632  try
633  {
634  TrajectoryPtr traj_ptr(new Trajectory);
635  *traj_ptr = initJointTrajectory<Trajectory>(*msg, next_update_time, options);
636  if (!traj_ptr->empty())
637  {
638  curr_trajectory_box_.set(traj_ptr);
639  }
640  else
641  {
642  return false;
643  }
644  }
645  catch(const std::invalid_argument& ex)
646  {
647  ROS_ERROR_STREAM_NAMED(name_, ex.what());
648  options.setErrorString(ex.what());
649  return false;
650  }
651  catch(...)
652  {
653  error_string_tmp = "Unexpected exception caught when initializing trajectory from ROS message data.";
654  ROS_ERROR_STREAM_NAMED(name_, error_string_tmp);
655  options.setErrorString(error_string_tmp);
656  return false;
657  }
658 
659  return true;
660 }
661 
662 template <class SegmentImpl, class HardwareInterface>
664 goalCB(GoalHandle gh)
665 {
666  ROS_DEBUG_STREAM_NAMED(name_,"Received new action goal");
667 
668  // Precondition: Running controller
669  if (!this->isRunning())
670  {
671  ROS_ERROR_NAMED(name_, "Can't accept new action goals. Controller is not running.");
672  control_msgs::FollowJointTrajectoryResult result;
673  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_GOAL; // TODO: Add better error status to msg?
674  gh.setRejected(result);
675  return;
676  }
677 
678  // If partial joints goals are not allowed, goal should specify all controller joints
679  if (!allow_partial_joints_goal_)
680  {
681  if (gh.getGoal()->trajectory.joint_names.size() != joint_names_.size())
682  {
683  ROS_ERROR_NAMED(name_, "Joints on incoming goal don't match the controller joints.");
684  control_msgs::FollowJointTrajectoryResult result;
685  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_JOINTS;
686  gh.setRejected(result);
687  return;
688  }
689  }
690 
691  // Goal should specify valid controller joints (they can be ordered differently). Reject if this is not the case
692  using internal::mapping;
693  std::vector<unsigned int> mapping_vector = mapping(gh.getGoal()->trajectory.joint_names, joint_names_);
694 
695  if (mapping_vector.empty())
696  {
697  ROS_ERROR_NAMED(name_, "Joints on incoming goal don't match the controller joints.");
698  control_msgs::FollowJointTrajectoryResult result;
699  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_JOINTS;
700  gh.setRejected(result);
701  return;
702  }
703 
704  // Try to update new trajectory
705  RealtimeGoalHandlePtr rt_goal(new RealtimeGoalHandle(gh));
706  std::string error_string;
707  const bool update_ok = updateTrajectoryCommand(internal::share_member(gh.getGoal(), gh.getGoal()->trajectory),
708  rt_goal,
709  &error_string);
710  rt_goal->preallocated_feedback_->joint_names = joint_names_;
711 
712  if (update_ok)
713  {
714  // Accept new goal
715  preemptActiveGoal();
716  gh.setAccepted();
717  rt_active_goal_ = rt_goal;
718 
719  // Setup goal status checking timer
720  goal_handle_timer_ = controller_nh_.createTimer(action_monitor_period_,
721  &RealtimeGoalHandle::runNonRealtime,
722  rt_goal);
723  goal_handle_timer_.start();
724  }
725  else
726  {
727  // Reject invalid goal
728  control_msgs::FollowJointTrajectoryResult result;
729  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_GOAL;
730  result.error_string = error_string;
731  gh.setRejected(result);
732  }
733 }
734 
735 template <class SegmentImpl, class HardwareInterface>
737 cancelCB(GoalHandle gh)
738 {
739  RealtimeGoalHandlePtr current_active_goal(rt_active_goal_);
740 
741  // Check that cancel request refers to currently active goal (if any)
742  if (current_active_goal && current_active_goal->gh_ == gh)
743  {
744  // Reset current goal
745  rt_active_goal_.reset();
746 
747  // Controller uptime
748  const ros::Time uptime = time_data_.readFromRT()->uptime;
749 
750  // Enter hold current position mode
751  setHoldPosition(uptime);
752  ROS_DEBUG_NAMED(name_, "Canceling active action goal because cancel callback recieved from actionlib.");
753 
754  // Mark the current goal as canceled
755  current_active_goal->gh_.setCanceled();
756  }
757 }
758 
759 template <class SegmentImpl, class HardwareInterface>
761 queryStateService(control_msgs::QueryTrajectoryState::Request& req,
762  control_msgs::QueryTrajectoryState::Response& resp)
763 {
764  // Preconditions
765  if (!this->isRunning())
766  {
767  ROS_ERROR_NAMED(name_, "Can't sample trajectory. Controller is not running.");
768  return false;
769  }
770 
771  // Convert request time to internal monotonic representation
772  TimeData* time_data = time_data_.readFromRT();
773  const ros::Duration time_offset = req.time - time_data->time;
774  const ros::Time sample_time = time_data->uptime + time_offset;
775 
776  // Sample trajectory at requested time
777  TrajectoryPtr curr_traj_ptr;
778  curr_trajectory_box_.get(curr_traj_ptr);
779  Trajectory& curr_traj = *curr_traj_ptr;
780 
781  typename Segment::State response_point = typename Segment::State(joint_names_.size());
782 
783  for (unsigned int i = 0; i < joints_.size(); ++i)
784  {
785  typename Segment::State state;
786  typename TrajectoryPerJoint::const_iterator segment_it = sample(curr_traj[i], sample_time.toSec(), state);
787  if (curr_traj[i].end() == segment_it)
788  {
789  ROS_ERROR_STREAM_NAMED(name_, "Requested sample time precedes trajectory start time.");
790  return false;
791  }
792 
793  response_point.position[i] = state.position[0];
794  response_point.velocity[i] = state.velocity[0];
795  response_point.acceleration[i] = state.acceleration[0];
796  }
797 
798  // Populate response
799  resp.name = joint_names_;
800  resp.position = response_point.position;
801  resp.velocity = response_point.velocity;
802  resp.acceleration = response_point.acceleration;
803 
804  return true;
805 }
806 
807 template <class SegmentImpl, class HardwareInterface>
809 publishState(const ros::Time& time)
810 {
811  // Check if it's time to publish
812  if (!state_publisher_period_.isZero() && last_state_publish_time_ + state_publisher_period_ < time)
813  {
814  if (state_publisher_ && state_publisher_->trylock())
815  {
816  last_state_publish_time_ += state_publisher_period_;
817 
818  state_publisher_->msg_.header.stamp = time_data_.readFromRT()->time;
819  state_publisher_->msg_.desired.positions = desired_state_.position;
820  state_publisher_->msg_.desired.velocities = desired_state_.velocity;
821  state_publisher_->msg_.desired.accelerations = desired_state_.acceleration;
822  state_publisher_->msg_.actual.positions = current_state_.position;
823  state_publisher_->msg_.actual.velocities = current_state_.velocity;
824  state_publisher_->msg_.error.positions = state_error_.position;
825  state_publisher_->msg_.error.velocities = state_error_.velocity;
826 
827  state_publisher_->unlockAndPublish();
828  }
829  }
830 }
831 
832 template <class SegmentImpl, class HardwareInterface>
834 setHoldPosition(const ros::Time& time, RealtimeGoalHandlePtr gh)
835 {
836  assert(joint_names_.size() == hold_trajectory_ptr_->size());
837 
838  typename Segment::State hold_start_state_ = typename Segment::State(1);
839  typename Segment::State hold_end_state_ = typename Segment::State(1);
840  const unsigned int n_joints = joints_.size();
841  const typename Segment::Time start_time = time.toSec();
842 
843  if(stop_trajectory_duration_ == 0.0)
844  {
845  // stop at current actual position
846  for (unsigned int i = 0; i < n_joints; ++i)
847  {
848  hold_start_state_.position[0] = joints_[i].getPosition();
849  hold_start_state_.velocity[0] = 0.0;
850  hold_start_state_.acceleration[0] = 0.0;
851  (*hold_trajectory_ptr_)[i].front().init(start_time, hold_start_state_,
852  start_time, hold_start_state_);
853  // Set goal handle for the segment
854  (*hold_trajectory_ptr_)[i].front().setGoalHandle(gh);
855  }
856  }
857  else
858  {
859  // Settle position in a fixed time. We do the following:
860  // - Create segment that goes from current (pos,vel) to (pos,-vel) in 2x the desired stop time
861  // - Assuming segment symmetry, sample segment at its midpoint (desired stop time). It should have zero velocity
862  // - Create segment that goes from current state to above zero velocity state, in the desired time
863  // NOTE: The symmetry assumption from the second point above might not hold for all possible segment types
864 
865  const typename Segment::Time end_time = time.toSec() + stop_trajectory_duration_;
866  const typename Segment::Time end_time_2x = time.toSec() + 2.0 * stop_trajectory_duration_;
867 
868  // Create segment that goes from current (pos,vel) to (pos,-vel)
869  for (unsigned int i = 0; i < n_joints; ++i)
870  {
871  // If there is a time delay in the system it is better to calculate the hold trajectory starting from the
872  // desired position. Otherwise there would be a jerk in the motion.
873  hold_start_state_.position[0] = desired_state_.position[i];
874  hold_start_state_.velocity[0] = desired_state_.velocity[i];
875  hold_start_state_.acceleration[0] = 0.0;
876 
877  hold_end_state_.position[0] = desired_state_.position[i];
878  hold_end_state_.velocity[0] = -desired_state_.velocity[i];
879  hold_end_state_.acceleration[0] = 0.0;
880 
881  (*hold_trajectory_ptr_)[i].front().init(start_time, hold_start_state_,
882  end_time_2x, hold_end_state_);
883 
884  // Sample segment at its midpoint, that should have zero velocity
885  (*hold_trajectory_ptr_)[i].front().sample(end_time, hold_end_state_);
886 
887  // Now create segment that goes from current state to one with zero end velocity
888  (*hold_trajectory_ptr_)[i].front().init(start_time, hold_start_state_,
889  end_time, hold_end_state_);
890 
891  // Set goal handle for the segment
892  (*hold_trajectory_ptr_)[i].front().setGoalHandle(gh);
893  }
894  }
895  curr_trajectory_box_.set(hold_trajectory_ptr_);
896 }
897 
898 } // namespace
899 
900 #endif // header guard
d
#define ROS_DEBUG_STREAM_NAMED(name, args)
#define ROS_ERROR_STREAM_NAMED(name, args)
int size() const
Trajectory::const_iterator sample(const Trajectory &trajectory, const typename Trajectory::value_type::Time &time, typename Trajectory::value_type::State &state)
#define ROS_DEBUG_STREAM_THROTTLE_NAMED(rate, name, args)
Type const & getType() const
#define ROS_WARN(...)
void update(const ros::Time &time, const ros::Duration &period)
std::vector< urdf::JointConstSharedPtr > getUrdfJoints(const urdf::Model &urdf, const std::vector< std::string > &joint_names)
virtual void trajectoryCommandCB(const JointTrajectoryConstPtr &msg)
bool checkStateTolerancePerJoint(const State &state_error, const StateTolerances< typename State::Scalar > &state_tolerance, bool show_errors=false)
#define ROS_DEBUG_NAMED(name,...)
void setHoldPosition(const ros::Time &time, RealtimeGoalHandlePtr gh=RealtimeGoalHandlePtr())
options
const std::string & getNamespace() const
bool init(HardwareInterface *hw, ros::NodeHandle &root_nh, ros::NodeHandle &controller_nh)
#define ROS_WARN_STREAM(args)
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)
#define ROS_INFO_STREAM(args)
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)
resp
def shortest_angular_distance(from_angle, to_angle)
#define ROS_WARN_STREAM_NAMED(name, args)


gundam_rx78_control
Author(s): Kei Okada , Naoki Hiraoka
autogenerated on Wed Sep 2 2020 03:33:33