follow_joint_trajectory.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2014-2015, Fetch Robotics Inc.
5  * Copyright (c) 2013, Unbounded Robotics Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided
17  * with the distribution.
18  * * Neither the name of Unbounded Robotics nor the names of its
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *********************************************************************/
35 
36 /* Author: Michael Ferguson */
37 
40 
42 
44 
45 namespace robot_controllers
46 {
47 
48 FollowJointTrajectoryController::FollowJointTrajectoryController() :
49  initialized_(false)
50 {
51 }
52 
54 {
55  // We absolutely need access to the controller manager
56  if (!manager)
57  {
58  initialized_ = false;
59  return -1;
60  }
61 
62  Controller::init(nh, manager);
63  manager_ = manager;
64 
65  // No initial sampler
66  boost::mutex::scoped_lock lock(sampler_mutex_);
67  sampler_.reset();
68  preempted_ = false;
69 
70  // Get Joint Names
71  joint_names_.clear();
72  XmlRpc::XmlRpcValue names;
73  if (!nh.getParam("joints", names))
74  {
75  ROS_ERROR_STREAM("No joints given for " << nh.getNamespace());
76  return -1;
77  }
79  {
80  ROS_ERROR_STREAM("Joints not in a list for " << nh.getNamespace());
81  return -1;
82  }
83  for (int i = 0; i < names.size(); ++i)
84  {
85  XmlRpc::XmlRpcValue &name_value = names[i];
86  if (name_value.getType() != XmlRpc::XmlRpcValue::TypeString)
87  {
88  ROS_ERROR_STREAM("Not all joint names are strings for " << nh.getNamespace());
89  return -1;
90  }
91  joint_names_.push_back(static_cast<std::string>(name_value));
92  }
93 
94  // Get parameters
95  nh.param<bool>("stop_with_action", stop_with_action_, false);
96  nh.param<bool>("stop_on_path_violation", stop_on_path_violation_, false);
97 
98  // Get Joint Handles, setup feedback
99  joints_.clear();
100  for (size_t i = 0; i < joint_names_.size(); ++i)
101  {
103  feedback_.joint_names.push_back(j->getName());
104  joints_.push_back(j);
105  continuous_.push_back(j->isContinuous());
106  }
107 
108  // Update feedback
109  feedback_.desired.positions.resize(joints_.size());
110  feedback_.desired.velocities.resize(joints_.size());
111  feedback_.desired.accelerations.resize(joints_.size());
112  feedback_.actual.positions.resize(joints_.size());
113  feedback_.actual.velocities.resize(joints_.size());
114  feedback_.actual.effort.resize(joints_.size());
115  feedback_.error.positions.resize(joints_.size());
116  feedback_.error.velocities.resize(joints_.size());
117 
118  // Update tolerances
119  path_tolerance_.q.resize(joints_.size());
120  path_tolerance_.qd.resize(joints_.size());
121  path_tolerance_.qdd.resize(joints_.size());
122  goal_tolerance_.q.resize(joints_.size());
123  goal_tolerance_.qd.resize(joints_.size());
124  goal_tolerance_.qdd.resize(joints_.size());
125 
126  // Setup ROS interfaces
127  server_.reset(new server_t(nh, "",
128  boost::bind(&FollowJointTrajectoryController::executeCb, this, _1),
129  false));
130  server_->start();
131 
132  initialized_ = true;
133  return 0;
134 }
135 
137 {
138  if (!initialized_)
139  {
140  ROS_ERROR_NAMED("FollowJointTrajectoryController",
141  "Unable to start, not initialized.");
142  return false;
143  }
144 
145  if (!server_->isActive())
146  {
147  ROS_ERROR_NAMED("FollowJointTrajectoryController",
148  "Unable to start, action server is not active.");
149  return false;
150  }
151 
152  return true;
153 }
154 
156 {
157  if (!initialized_)
158  return true;
159 
160  if (server_->isActive())
161  {
162  if (force)
163  {
164  // Shut down the action
165  control_msgs::FollowJointTrajectoryResult result;
166  server_->setAborted(result, "Controller manager forced preemption.");
167  return true;
168  }
169  // Do not abort unless forced
170  return false;
171  }
172 
173  // Just holding position, go ahead and stop us
174  return true;
175 }
176 
178 {
179  stop(true); // force stop ourselves
180  return (manager_->requestStop(getName()) == 0);
181 }
182 
184 {
185  if (!initialized_)
186  return;
187 
188  // Is trajectory active?
189  if (server_->isActive() && sampler_)
190  {
191  boost::mutex::scoped_lock lock(sampler_mutex_);
192 
193  // Interpolate trajectory
194  TrajectoryPoint p = sampler_->sample(now.toSec());
196  last_sample_ = p;
197 
198  // Update joints
199  if (p.q.size() == joints_.size())
200  {
201  // Position is good
202  for (size_t i = 0; i < joints_.size(); ++i)
203  {
204  feedback_.desired.positions[i] = p.q[i];
205  }
206 
207  if (p.qd.size() == joints_.size())
208  {
209  // Velocity is good
210  for (size_t i = 0; i < joints_.size(); ++i)
211  {
212  feedback_.desired.velocities[i] = p.qd[i];
213  }
214 
215  if (p.qdd.size() == joints_.size())
216  {
217  // Acceleration is good
218  for (size_t i = 0; i < joints_.size(); ++i)
219  {
220  feedback_.desired.accelerations[i] = p.qdd[i];
221  }
222  }
223  }
224 
225  // Fill in actual
226  for (size_t j = 0; j < joints_.size(); ++j)
227  {
228  feedback_.actual.positions[j] = joints_[j]->getPosition();
229  feedback_.actual.velocities[j] = joints_[j]->getVelocity();
230  feedback_.actual.effort[j] = joints_[j]->getEffort();
231  }
232 
233  // Fill in error
234  for (size_t j = 0; j < joints_.size(); ++j)
235  {
236  feedback_.error.positions[j] = shortest_angular_distance(feedback_.desired.positions[j],
237  feedback_.actual.positions[j]);
238  feedback_.error.velocities[j] = feedback_.actual.velocities[j] -
239  feedback_.desired.velocities[j];
240  }
241 
242  // Check that we are within path tolerance
244  {
245  for (size_t j = 0; j < joints_.size(); ++j)
246  {
247  if ((path_tolerance_.q[j] > 0) &&
248  (fabs(feedback_.error.positions[j]) > path_tolerance_.q[j]))
249  {
250  control_msgs::FollowJointTrajectoryResult result;
251  result.error_code = control_msgs::FollowJointTrajectoryResult::PATH_TOLERANCE_VIOLATED;
252  server_->setAborted(result, "Trajectory path tolerances violated (position).");
253  ROS_ERROR("Trajectory path tolerances violated (position).");
255  {
257  }
258  break;
259  }
260 
261  if ((path_tolerance_.qd[j] > 0) &&
262  (fabs(feedback_.error.velocities[j]) > path_tolerance_.qd[j]))
263  {
264  control_msgs::FollowJointTrajectoryResult result;
265  result.error_code = control_msgs::FollowJointTrajectoryResult::PATH_TOLERANCE_VIOLATED;
266  server_->setAborted(result, "Trajectory path tolerances violated (velocity).");
267  ROS_ERROR("Trajectory path tolerances violated (velocity).");
269  {
271  }
272  break;
273  }
274  }
275  }
276 
277  // Check that we are within goal tolerance
278  if (now.toSec() >= sampler_->end_time())
279  {
280  bool inside_tolerances = true;
281  for (size_t j = 0; j < joints_.size(); ++j)
282  {
283  if ((goal_tolerance_.q[j] > 0) &&
284  (fabs(feedback_.error.positions[j]) > goal_tolerance_.q[j]))
285  {
286  inside_tolerances = false;
287  }
288  }
289 
290  if (inside_tolerances)
291  {
292  control_msgs::FollowJointTrajectoryResult result;
293  result.error_code = control_msgs::FollowJointTrajectoryResult::SUCCESSFUL;
294  server_->setSucceeded(result, "Trajectory succeeded.");
295  ROS_DEBUG("Trajectory succeeded");
296  }
297  else if (now.toSec() > (sampler_->end_time() + goal_time_tolerance_ + 0.6)) // 0.6s matches PR2
298  {
299  control_msgs::FollowJointTrajectoryResult result;
300  result.error_code = control_msgs::FollowJointTrajectoryResult::GOAL_TOLERANCE_VIOLATED;
301  server_->setAborted(result, "Trajectory not executed within time limits.");
302  ROS_ERROR("Trajectory not executed within time limits");
303  }
304  }
305 
306  // Update joints
307  for (size_t j = 0; j < joints_.size(); ++j)
308  {
309  joints_[j]->setPosition(feedback_.desired.positions[j],
310  feedback_.desired.velocities[j],
311  0.0);
312  }
313  }
314  }
315  else if (last_sample_.q.size() == joints_.size())
316  {
317  // Hold Position Unless Path Tolerance Violated
319  {
320  for (size_t j = 0; j < joints_.size(); ++j)
321  {
322  if ((path_tolerance_.q[j] > 0) &&
323  (fabs(joints_[j]->getPosition() - last_sample_.q[j]) > path_tolerance_.q[j]))
324  {
326  break;
327  }
328  }
329  }
330 
331  for (size_t j = 0; j < joints_.size(); ++j)
332  {
333  joints_[j]->setPosition(last_sample_.q[j],
334  0.0,
335  0.0);
336  }
337  }
338 }
339 
340 /*
341  * Specification is basically the message:
342  * http://ros.org/doc/indigo/api/control_msgs/html/action/FollowJointTrajectory.html
343  */
344 void FollowJointTrajectoryController::executeCb(const control_msgs::FollowJointTrajectoryGoalConstPtr& goal)
345 {
346  control_msgs::FollowJointTrajectoryResult result;
347 
348  if (!initialized_)
349  {
350  server_->setAborted(result, "Controller is not initialized.");
351  return;
352  }
353 
354  if (goal->trajectory.points.empty())
355  {
356  // Stop
358  return;
359  }
360 
361  if (goal->trajectory.joint_names.size() != joints_.size())
362  {
363  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_JOINTS;
364  server_->setAborted(result, "Trajectory goal size does not match controlled joints size.");
365  ROS_ERROR("Trajectory goal size does not match controlled joints size.");
366  return;
367  }
368 
369  Trajectory new_trajectory;
370  Trajectory executable_trajectory;
371  goal_time = goal->trajectory.header.stamp;
372 
373  // Make a trajectory from our message
374  if (!trajectoryFromMsg(goal->trajectory, joint_names_, &new_trajectory))
375  {
376  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_JOINTS;
377  server_->setAborted(result, "Trajectory goal does not match controlled joints");
378  ROS_ERROR("Trajectory goal does not match controlled joints");
379  return;
380  }
381 
382  // If preempted, need to splice on things together
383  if (preempted_)
384  {
385  // If the sampler had a large trajectory, we may just be cutting into it
386  if (sampler_ && (sampler_->getTrajectory().size() > 2))
387  {
388  if (!spliceTrajectories(sampler_->getTrajectory(),
389  new_trajectory,
390  ros::Time::now().toSec(),
391  &executable_trajectory))
392  {
393  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_JOINTS;
394  server_->setAborted(result, "Unable to splice trajectory");
395  ROS_ERROR("Unable to splice trajectory");
396  return;
397  }
398  }
399  else
400  {
401  // Previous trajectory was only 2 points, use last_sample + new trajectory
402  Trajectory t;
403  t.points.push_back(last_sample_);
404  if (!spliceTrajectories(t,
405  new_trajectory,
406  0.0, /* take all points */
407  &executable_trajectory))
408  {
409  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_JOINTS;
410  server_->setAborted(result, "Unable to splice trajectory");
411  ROS_ERROR("Unable to splice trajectory");
412  return;
413  }
414  }
415  }
416  else
417  {
418  if (new_trajectory.size() > 1)
419  {
420  // use the generated trajectory
421  executable_trajectory = new_trajectory;
422 
423  // if this hasn't started yet or if the header stamp is in the future,
424  // need to insert current position
425  if (goal->trajectory.points[0].time_from_start.toSec() > 0.0 ||
426  executable_trajectory.points[0].time > ros::Time::now().toSec())
427  {
428  executable_trajectory.points.insert(
429  executable_trajectory.points.begin(),
430  getPointFromCurrent(new_trajectory.points[0].qd.size() > 0,
431  new_trajectory.points[0].qdd.size() > 0,
432  true));
433  }
434  }
435  else
436  {
437  // A single point, with nothing in the queue!
438  executable_trajectory.points.push_back(
439  getPointFromCurrent(new_trajectory.points[0].qd.size() > 0,
440  new_trajectory.points[0].qdd.size() > 0,
441  true));
442  executable_trajectory.points.push_back(new_trajectory.points[0]);
443  }
444  }
445 
446  // Windup executable trajectory so spline is smooth
447  windupTrajectory(continuous_, executable_trajectory);
448 
449  // Create trajectory sampler
450  {
451  boost::mutex::scoped_lock lock(sampler_mutex_);
452  sampler_.reset(new SplineTrajectorySampler(executable_trajectory));
453  }
454 
455  // Convert the path tolerances into a more usable form
456  if (goal->path_tolerance.size() == joints_.size())
457  {
458  has_path_tolerance_ = true;
459  for (size_t j = 0; j < joints_.size(); ++j)
460  {
461  // Find corresponding indices between path_tolerance and joints
462  int index = -1;
463  for (size_t i = 0; i < goal->path_tolerance.size(); ++i)
464  {
465  if (joints_[j]->getName() == goal->path_tolerance[i].name)
466  {
467  index = i;
468  break;
469  }
470  }
471  if (index == -1)
472  {
473  // Every joint must have a tolerance and this one does not
474  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_JOINTS;
475  server_->setAborted(result, "Unable to convert path tolerances");
476  ROS_ERROR("Unable to convert path tolerances");
477  return;
478  }
479  path_tolerance_.q[j] = goal->path_tolerance[index].position;
480  path_tolerance_.qd[j] = goal->path_tolerance[index].velocity;
481  path_tolerance_.qdd[j] = goal->path_tolerance[index].acceleration;
482  }
483  }
484  else
485  {
486  has_path_tolerance_ = false;
487  }
488 
489  // Convert the goal tolerances into a more usable form
490  if (goal->goal_tolerance.size() == joints_.size())
491  {
492  for (size_t j = 0; j < joints_.size(); ++j)
493  {
494  // Find corresponding indices between goal_tolerance and joints
495  int index = -1;
496  for (size_t i = 0; i < goal->goal_tolerance.size(); ++i)
497  {
498  if (joints_[j]->getName() == goal->goal_tolerance[i].name)
499  {
500  index = i;
501  break;
502  }
503  }
504  if (index == -1)
505  {
506  // Every joint must have a tolerance and this one does not
507  result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_JOINTS;
508  server_->setAborted(result, "Unable to convert goal tolerances");
509  ROS_ERROR("Unable to convert goal tolerances");
510  return;
511  }
512  goal_tolerance_.q[j] = goal->goal_tolerance[index].position;
513  goal_tolerance_.qd[j] = goal->goal_tolerance[index].velocity;
514  goal_tolerance_.qdd[j] = goal->goal_tolerance[index].acceleration;
515  }
516  }
517  else
518  {
519  // Set defaults
520  for (size_t j = 0; j < joints_.size(); ++j)
521  {
522  goal_tolerance_.q[j] = 0.02; // tolerance is same as PR2
523  goal_tolerance_.qd[j] = 0.02;
524  goal_tolerance_.qdd[j] = 0.02;
525  }
526  }
527  goal_time_tolerance_ = goal->goal_time_tolerance.toSec();
528 
529  ROS_DEBUG("Executing new trajectory");
530 
531  if (manager_->requestStart(getName()) != 0)
532  {
533  result.error_code = control_msgs::FollowJointTrajectoryResult::GOAL_TOLERANCE_VIOLATED;
534  server_->setAborted(result, "Cannot execute trajectory, unable to start controller.");
535  ROS_ERROR("Cannot execute trajectory, unable to start controller.");
536  return;
537  }
538 
539  preempted_ = false;
540  while (server_->isActive())
541  {
542  if (server_->isPreemptRequested())
543  {
544  control_msgs::FollowJointTrajectoryResult result;
545  server_->setPreempted(result, "Trajectory preempted");
546  ROS_DEBUG("Trajectory preempted");
547  preempted_ = true;
548  break;
549  }
550 
551  // Publish feedback
552  feedback_.header.stamp = ros::Time::now();
553  feedback_.desired.time_from_start = feedback_.header.stamp - goal_time;
554  feedback_.actual.time_from_start = feedback_.header.stamp - goal_time;
555  feedback_.error.time_from_start = feedback_.header.stamp - goal_time;
556  server_->publishFeedback(feedback_);
557  ros::Duration(1/50.0).sleep();
558  }
559 
560  {
561  boost::mutex::scoped_lock lock(sampler_mutex_);
562  sampler_.reset();
563  }
564 
565  // Stop this controller if desired (and not preempted)
568 
569  ROS_DEBUG("Done executing trajectory");
570 }
571 
573  bool incl_vel, bool incl_acc, bool zero_vel)
574 {
575  TrajectoryPoint point;
576 
577  point.q.resize(joints_.size());
578  for (size_t j = 0; j < joints_.size(); ++j)
579  point.q[j] = joints_[j]->getPosition();
580 
581  if (incl_vel && zero_vel)
582  {
583  point.qd.resize(joints_.size());
584  for (size_t j = 0; j < joints_.size(); ++j)
585  point.qd[j] = 0.0;
586  }
587  else if (incl_vel)
588  {
589  point.qd.resize(joints_.size());
590  for (size_t j = 0; j < joints_.size(); ++j)
591  point.qd[j] = joints_[j]->getVelocity();
592  }
593 
594  if (incl_acc)
595  {
596  point.qdd.resize(joints_.size());
597  // Currently no good measure of acceleration, assume none
598  for (size_t j = 0; j < joints_.size(); ++j)
599  point.qdd[j] = 0.0;
600  }
601 
602  point.time = ros::Time::now().toSec();
603 
604  return point;
605 }
606 
608 {
609  return joint_names_;
610 }
611 
613 {
614  return joint_names_;
615 }
616 
617 } // namespace robot_controllers
Basis for a Trajectory Point.
Definition: trajectory.h:51
bool trajectoryFromMsg(const trajectory_msgs::JointTrajectory &message, const std::vector< std::string > joints, Trajectory *trajectory)
Convert message into Trajectory.
Definition: trajectory.h:76
control_msgs::FollowJointTrajectoryFeedback feedback_
std::vector< double > qdd
Definition: trajectory.h:55
result
This ROS interface implements a FollowJointTrajectoryAction interface for controlling (primarily) rob...
virtual bool stop(bool force)
Attempt to stop the controller. This should be called only by the ControllerManager instance...
int size() const
std::vector< TrajectoryPoint > points
Definition: trajectory.h:61
bool sleep() const
virtual std::vector< std::string > getClaimedNames()
Get the names of joints/controllers which this controller exclusively claims.
bool windupTrajectory(std::vector< bool > continuous, Trajectory &trajectory)
Windup the trajectory so that continuous joints do not wrap.
Definition: trajectory.h:253
virtual int requestStart(const std::string &name)
Type const & getType() const
virtual int requestStop(const std::string &name)
void executeCb(const control_msgs::FollowJointTrajectoryGoalConstPtr &goal)
Callback for goal.
actionlib::SimpleActionServer< control_msgs::FollowJointTrajectoryAction > server_t
bool param(const std::string &param_name, T &param_val, const T &default_val) const
virtual std::vector< std::string > getCommandedNames()
Get the names of joints/controllers which this controller commands.
const std::string & getNamespace() const
virtual void update(const ros::Time &now, const ros::Duration &dt)
This is the update loop for the controller.
std::vector< double > qd
Definition: trajectory.h:54
virtual bool reset()
Cleanly reset the controller to it&#39;s initial state. Some controllers may choose to stop themselves...
std::vector< double > q
Definition: trajectory.h:53
virtual int init(ros::NodeHandle &nh, ControllerManager *manager)
bool getParam(const std::string &key, std::string &s) const
#define ROS_ERROR_NAMED(name,...)
static Time now()
virtual bool start()
Attempt to start the controller. This should be called only by the ControllerManager instance...
TrajectoryPoint getPointFromCurrent(bool incl_vel, bool incl_acc, bool zero_vel)
Get a trajectory point from the current position/velocity/acceleration.
JointHandlePtr getJointHandle(const std::string &name)
bool unwindTrajectoryPoint(std::vector< bool > continuous, TrajectoryPoint &p)
Definition: trajectory.h:285
#define ROS_ERROR_STREAM(args)
bool spliceTrajectories(const Trajectory &t1, const Trajectory &t2, const double time, Trajectory *t)
Splice two trajectories.
Definition: trajectory.h:132
virtual int init(ros::NodeHandle &nh, ControllerManager *manager)
Initialize the controller and any required data structures.
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
#define ROS_ERROR(...)
def shortest_angular_distance(from_angle, to_angle)
#define ROS_DEBUG(...)


robot_controllers
Author(s): Michael Ferguson
autogenerated on Sun Sep 27 2020 03:22:39