pass_through_controllers.hpp
Go to the documentation of this file.
1 // -- BEGIN LICENSE BLOCK ----------------------------------------------
2 // Copyright 2020 FZI Forschungszentrum Informatik
3 // Created on behalf of Universal Robots A/S
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 // -- END LICENSE BLOCK ------------------------------------------------
17 
18 //-----------------------------------------------------------------------------
25 //-----------------------------------------------------------------------------
26 
27 // Project
30 
31 // Other
32 #include "ros/duration.h"
33 #include "ros/timer.h"
35 #include <sstream>
36 #include <string>
37 
38 namespace trajectory_controllers
39 {
40 template <class TrajectoryInterface>
42  ros::NodeHandle& controller_nh)
43 {
44  // Get names of joints from the parameter server
45  if (!controller_nh.getParam("joints", joint_names_))
46  {
47  ROS_ERROR_STREAM("Failed to load " << controller_nh.getNamespace() << "/joints from parameter server");
48  return false;
49  }
50 
51  // Availability checked by MultiInterfaceController
52  trajectory_interface_ = hw->get<TrajectoryInterface>();
53  if (!trajectory_interface_)
54  {
55  ROS_ERROR_STREAM(controller_nh.getNamespace() << ": No suitable trajectory interface found.");
56  return false;
57  }
58 
59  trajectory_interface_->setResources(joint_names_);
60 
61  // Use speed scaling interface if available (optional).
62  auto speed_scaling_interface = hw->get<scaled_controllers::SpeedScalingInterface>();
63  if (!speed_scaling_interface)
64  {
65  ROS_INFO_STREAM(controller_nh.getNamespace() << ": Your RobotHW seems not to provide speed scaling. Starting "
66  "without this feature.");
67  speed_scaling_ = nullptr;
68  }
69  else
70  {
71  speed_scaling_ = std::make_unique<scaled_controllers::SpeedScalingHandle>(speed_scaling_interface->getHandle("speed"
72  "_scal"
73  "ing_"
74  "facto"
75  "r"));
76  }
77 
78  // Action server
80  controller_nh,
81  std::is_same<TrajectoryInterface, hardware_interface::JointTrajectoryInterface>::value ? "follow_joint_"
82  "trajectory" :
83  "follow_cartesian_"
84  "trajectory",
85  std::bind(&PassThroughController::executeCB, this, std::placeholders::_1), false));
86 
87  // This is the cleanest method to notify the vendor robot driver of
88  // preempted requests.
89  action_server_->registerPreemptCallback(std::bind(&PassThroughController::preemptCB, this));
90 
91  // Register callback for when hardware finishes (prematurely)
92  trajectory_interface_->registerDoneCallback(std::bind(&PassThroughController::doneCB, this, std::placeholders::_1));
93 
94  action_server_->start();
95 
96  return true;
97 }
98 
99 template <class TrajectoryInterface>
101 {
102  done_ = true; // wait with update() until the next goal comes in.
103 }
104 
105 template <class TrajectoryInterface>
107 {
108  if (action_server_->isActive())
109  {
110  // Stop trajectory interpolation on the robot
111  trajectory_interface_->setCancel();
112 
113  typename Base::FollowTrajectoryResult result;
114  result.error_string = "Controller stopped.";
115  result.error_code = Base::FollowTrajectoryResult::PATH_TOLERANCE_VIOLATED;
116  action_server_->setAborted(result);
117  done_ = true;
118  }
119 }
120 
121 template <class TrajectoryInterface>
123 {
124  if (action_server_->isActive() && !done_)
125  {
126  // Measure action duration and apply speed scaling if available.
127  const double factor = (speed_scaling_) ? *speed_scaling_->getScalingFactor() : 1.0;
128  action_duration_.current += period * factor;
129 
130  typename Base::TrajectoryFeedback f = trajectory_interface_->getFeedback();
131  action_server_->publishFeedback(f);
132 
133  // Check tolerances on each call and set terminal conditions for the
134  // action server if special criteria are met.
135  // Also set the done_ flag once that happens.
136  monitorExecution(f);
137 
138  // Time is up.
139  if (action_duration_.current >= action_duration_.target)
140  {
141  if (!done_)
142  {
143  ROS_WARN_THROTTLE(3, "The trajectory should be finished by now. "
144  "Something might be wrong with the robot. "
145  "You might want to cancel this goal.");
146  }
147  }
148  }
149 }
150 
151 template <class TrajectoryInterface>
152 void PassThroughController<TrajectoryInterface>::executeCB(const typename Base::GoalConstPtr& goal)
153 {
154  // Upon entering this callback, the simple action server has already
155  // preempted the previously active goal (if any) and has accepted the new goal.
156 
157  if (!this->isRunning())
158  {
159  ROS_ERROR("Can't accept new action goals. Controller is not running.");
160  typename Base::FollowTrajectoryResult result;
161  result.error_code = Base::FollowTrajectoryResult::INVALID_GOAL;
162  action_server_->setAborted(result);
163  return;
164  }
165 
166  // Only allow correct tolerances if given
167  if (!isValid(goal))
168  {
169  return;
170  }
171  path_tolerances_ = goal->path_tolerance;
172  goal_tolerances_ = goal->goal_tolerance;
173 
174  // Notify the vendor robot control.
175  if (!trajectory_interface_->setGoal(*goal))
176  {
177  ROS_ERROR("Trajectory goal is invalid.");
178  typename Base::FollowTrajectoryResult result;
179  result.error_code = Base::FollowTrajectoryResult::INVALID_GOAL;
180  action_server_->setAborted(result);
181  return;
182  }
183 
184  // Time keeping
185  action_duration_.current = ros::Duration(0.0);
186  action_duration_.target = goal->trajectory.points.back().time_from_start + goal->goal_time_tolerance;
187 
188  done_ = false;
189  while (!done_)
190  {
191  ros::Duration(0.01).sleep();
192  }
193 
194  // When done, the action server is in one of the three states:
195  // 1) succeeded: managed in doneCB()
196  // 2) aborted: managed in update() or in doneCB()
197  // 3) preempted: managed in preemptCB()
198 }
199 
200 template <class TrajectoryInterface>
202 {
203  if (action_server_->isActive())
204  {
205  // Notify the vendor robot control.
206  trajectory_interface_->setCancel();
207  }
208 }
209 
210 template <class TrajectoryInterface>
211 void PassThroughController<TrajectoryInterface>::monitorExecution(const typename Base::TrajectoryFeedback& feedback)
212 {
213  // Preempt if any of the joints exceeds its path tolerance
214  if (!withinTolerances(feedback.error, path_tolerances_))
215  {
216  trajectory_interface_->setCancel();
217  }
218 }
219 
220 template <>
222  const Tolerance& tolerances)
223 {
224  // Check each user-given tolerance field individually.
225  // Fail if either the tolerance is exceeded or if the robot driver does not
226  // provide semantically correct data (conservative fail).
227  for (size_t i = 0; i < tolerances.size(); ++i)
228  {
229  if (tolerances[i].position > 0.0)
230  {
231  if (error.positions.size() == tolerances.size())
232  {
233  return std::abs(error.positions[i]) <= tolerances[i].position;
234  }
235  ROS_WARN("Position tolerances specified, but not fully supported by the driver implementation.");
236  return false;
237  }
238 
239  if (tolerances[i].velocity > 0.0)
240  {
241  if (error.velocities.size() == tolerances.size())
242  {
243  return std::abs(error.velocities[i]) <= tolerances[i].velocity;
244  }
245  ROS_WARN("Velocity tolerances specified, but not fully supported by the driver implementation.");
246  return false;
247  }
248 
249  if (tolerances[i].acceleration > 0.0)
250  {
251  if (error.accelerations.size() == tolerances.size())
252  {
253  return std::abs(error.accelerations[i]) <= tolerances[i].acceleration;
254  }
255  ROS_WARN("Acceleration tolerances specified, but not fully supported by the driver implementation.");
256  return false;
257  }
258  }
259 
260  // Every error is ok for uninitialized tolerances
261  return true;
262 }
263 
264 template <>
266  const typename Base::TrajectoryPoint& error, const typename Base::Tolerance& tolerances)
267 {
268  // Every error is ok for uninitialized tolerances
269  Base::Tolerance uninitialized;
270  std::stringstream str_1;
271  std::stringstream str_2;
272  str_1 << tolerances;
273  str_2 << uninitialized;
274 
275  if (str_1.str() == str_2.str())
276  {
277  return true;
278  }
279 
280  auto not_within_limits = [](auto& a, auto& b) { return a.x > b.x || a.y > b.y || a.z > b.z; };
281 
282  // Check each individual dimension separately.
283  if (not_within_limits(error.pose.position, tolerances.position_error) ||
284  not_within_limits(error.pose.orientation, tolerances.orientation_error) ||
285  not_within_limits(error.twist.linear, tolerances.twist_error.linear) ||
286  not_within_limits(error.twist.angular, tolerances.twist_error.angular) ||
287  not_within_limits(error.acceleration.linear, tolerances.acceleration_error.linear) ||
288  not_within_limits(error.acceleration.angular, tolerances.acceleration_error.angular))
289  {
290  return false;
291  }
292 
293  return true;
294 }
295 
296 template <>
298  const typename Base::GoalConstPtr& goal)
299 {
300  // If tolerances are given, they must be given for all joints.
301  if ((!goal->path_tolerance.empty() && goal->path_tolerance.size() != joint_names_.size()) ||
302  (!goal->goal_tolerance.empty() && goal->goal_tolerance.size() != joint_names_.size()))
303  {
304  ROS_ERROR("Given tolerances must match the number of joints");
305  typename Base::FollowTrajectoryResult result;
306  result.error_code = Base::FollowTrajectoryResult::INVALID_GOAL;
307  action_server_->setAborted(result);
308  return false;
309  }
310  return true;
311 }
312 
313 template <>
315  const typename Base::GoalConstPtr& goal)
316 {
317  // No plausibility check required. All possible user inputs seem fine for now.
318  return true;
319 }
320 
321 template <class TrajectoryInterface>
323 {
324  typename Base::FollowTrajectoryResult result;
325 
326  if (!action_server_->isActive())
327  {
328  return;
329  }
330 
331  switch (state)
332  {
334  result.error_string = "Trajectory aborted by the robot. Something unexpected happened.";
335  result.error_code = Base::FollowTrajectoryResult::PATH_TOLERANCE_VIOLATED;
336  action_server_->setAborted(result);
337  }
338  break;
339 
341  result.error_string = "Trajectory preempted. Possible reasons: user request | path tolerances fail.";
342  result.error_code = Base::FollowTrajectoryResult::PATH_TOLERANCE_VIOLATED;
343  action_server_->setPreempted(result);
344  }
345  break;
346 
348  // Check if we meet the goal tolerances.
349  // The most recent feedback gets us the current state.
350  typename Base::TrajectoryPoint p = trajectory_interface_->getFeedback().error;
351  if (!withinTolerances(p, goal_tolerances_))
352  {
353  result.error_string = "Trajectory finished execution but failed goal tolerances";
354  result.error_code = Base::FollowTrajectoryResult::GOAL_TOLERANCE_VIOLATED;
355  action_server_->setAborted(result);
356  }
357  else
358  {
359  result.error_string = "Trajectory execution successful";
360  result.error_code = Base::FollowTrajectoryResult::SUCCESSFUL;
361  action_server_->setSucceeded(result);
362  }
363  }
364  break;
365 
366  default:
367  result.error_string = "Trajectory finished in unknown state.";
368  result.error_code = Base::FollowTrajectoryResult::PATH_TOLERANCE_VIOLATED;
369  action_server_->setAborted(result);
370  break;
371  }
372 
373  done_ = true;
374 }
375 
376 } // namespace trajectory_controllers
hardware_interface::ExecutionState::SUCCESS
@ SUCCESS
trajectory_controllers::PassThroughController::starting
void starting(const ros::Time &time)
Definition: pass_through_controllers.hpp:100
ROS_ERROR_STREAM
#define ROS_ERROR_STREAM(args)
hardware_interface::InterfaceManager::get
T * get()
ros::NodeHandle::getParam
bool getParam(const std::string &key, bool &b) const
ROS_WARN_THROTTLE
#define ROS_WARN_THROTTLE(period,...)
trajectory_interface.h
f
f
duration.h
trajectory_controllers::PassThroughController::executeCB
void executeCB(const typename Base::GoalConstPtr &goal)
Callback method for new action goals.
Definition: pass_through_controllers.hpp:152
hardware_interface::RobotHW
speed_scaling_interface.h
trajectory_controllers::PassThroughController::update
void update(const ros::Time &time, const ros::Duration &period)
Definition: pass_through_controllers.hpp:122
trajectory_controllers::PassThroughController::init
bool init(hardware_interface::RobotHW *hw, ros::NodeHandle &root_nh, ros::NodeHandle &controller_nh)
Definition: pass_through_controllers.hpp:41
ROS_ERROR
#define ROS_ERROR(...)
trajectory_controllers::PassThroughController::withinTolerances
bool withinTolerances(const typename Base::TrajectoryPoint &error, const typename Base::Tolerance &tolerances)
Check if tolerances are met.
scaled_controllers::SpeedScalingInterface
ROS_WARN
#define ROS_WARN(...)
ROS_INFO_STREAM
#define ROS_INFO_STREAM(args)
trajectory_controllers::PassThroughController::preemptCB
void preemptCB()
Callback method for preempt requests.
Definition: pass_through_controllers.hpp:201
timer.h
hardware_interface::ExecutionState
ExecutionState
Hardware-generic done flags for trajectory execution.
Definition: trajectory_interface.h:48
hardware_interface::ExecutionState::PREEMPTED
@ PREEMPTED
pass_through_controllers.h
trajectory_controllers
Definition: pass_through_controllers.h:53
ros::Time
trajectory_controllers::PassThroughController::stopping
void stopping(const ros::Time &time)
Definition: pass_through_controllers.hpp:106
actionlib::SimpleActionServer
hardware_interface::ExecutionState::ABORTED
@ ABORTED
isValid
bool isValid(const trajectory_msgs::JointTrajectory &msg)
ros::Duration::sleep
bool sleep() const
ros::NodeHandle::getNamespace
const std::string & getNamespace() const
ros::Duration
trajectory_controllers::PassThroughController::isValid
bool isValid(const typename Base::GoalConstPtr &goal)
Check if follow trajectory goals are valid.
ros::NodeHandle
trajectory_controllers::PassThroughController::monitorExecution
void monitorExecution(const typename Base::TrajectoryFeedback &feedback)
Monitor the trajectory execution.
Definition: pass_through_controllers.hpp:211
trajectory_controllers::PassThroughController::doneCB
void doneCB(const hardware_interface::ExecutionState &state)
Will get called upon finishing the forwarded trajectory.
Definition: pass_through_controllers.hpp:322


pass_through_controllers
Author(s):
autogenerated on Tue Oct 15 2024 02:10:52