eband_trajectory_controller.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  *
3  * Software License Agreement (BSD License)
4  *
5  * Copyright (c) 2010, Willow Garage, 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 Willow Garage, Inc. 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  * Author: Christian Connette
36  *********************************************************************/
37 
39 #include <tf/transform_datatypes.h>
40 
41 
42 namespace eband_local_planner{
43 
44  using std::min;
45  using std::max;
46 
47 
48  EBandTrajectoryCtrl::EBandTrajectoryCtrl() : costmap_ros_(NULL), initialized_(false), band_set_(false), visualization_(false) {}
49 
50 
52  : costmap_ros_(NULL), initialized_(false), band_set_(false), visualization_(false)
53  {
54  // initialize planner
55  initialize(name, costmap_ros);
56 
57  // Initialize pid object (note we'll be further clamping its output)
58  pid_.initPid(1, 0, 0, 10, -10);
59  }
60 
61 
63 
64 
65  void EBandTrajectoryCtrl::initialize(std::string name, costmap_2d::Costmap2DROS* costmap_ros)
66  {
67 
68  // check if trajectory controller is already initialized
69  if(!initialized_)
70  {
71  // create Node Handle with name of plugin (as used in move_base for loading)
72  ros::NodeHandle node_private("~/" + name);
73 
74  in_final_goal_turn_ = false;
75 
76  // copy adress of costmap and Transform Listener (handed over from move_base)
77  costmap_ros_ = costmap_ros;
78 
79  // init velocity for interpolation
80  last_vel_.linear.x = 0.0;
81  last_vel_.linear.y = 0.0;
82  last_vel_.linear.z = 0.0;
83  last_vel_.angular.x = 0.0;
84  last_vel_.angular.y = 0.0;
85  last_vel_.angular.z = 0.0;
86 
87  // set the general refernce frame to that in which the band is given
88  geometry_msgs::Pose2D tmp_pose2D;
89  tmp_pose2D.x = 0.0;
90  tmp_pose2D.y = 0.0;
91  tmp_pose2D.theta = 0.0;
92  Pose2DToPose(ref_frame_band_, tmp_pose2D);
93 
94  // set initialized flag
95  initialized_ = true;
96  }
97  else
98  {
99  ROS_WARN("This planner has already been initialized, doing nothing.");
100  }
101  }
102 
103 
105  eband_local_planner::EBandPlannerConfig& config)
106  {
107  max_vel_lin_ = config.max_vel_lin;
108  max_vel_th_ = config.max_vel_th;
109  min_vel_lin_ = config.min_vel_lin;
110  min_vel_th_ = config.min_vel_th;
111  min_in_place_vel_th_ = config.min_in_place_vel_th;
112  in_place_trans_vel_ = config.in_place_trans_vel;
113  tolerance_trans_ = config.xy_goal_tolerance;
114  tolerance_rot_ = config.yaw_goal_tolerance;
115  k_p_ = config.k_prop;
116  k_nu_ = config.k_damp;
117  ctrl_freq_ = config.Ctrl_Rate;
118  acc_max_ = config.max_acceleration;
119  virt_mass_ = config.virtual_mass;
120  acc_max_trans_ = config.max_translational_acceleration;
121  acc_max_rot_ = config.max_rotational_acceleration;
122  rotation_correction_threshold_ = config.rotation_correction_threshold;
123 
124  // diffferential drive parameters
125  differential_drive_hack_ = config.differential_drive;
126  bubble_velocity_multiplier_ = config.bubble_velocity_multiplier;
127  rotation_threshold_multiplier_ = config.rotation_threshold_multiplier;
128  disallow_hysteresis_ = config.disallow_hysteresis;
129  }
130 
131 
133  {
134  target_visual_ = target_visual;
135 
136  visualization_ = true;
137  }
138 
139  bool EBandTrajectoryCtrl::setBand(const std::vector<Bubble>& elastic_band)
140  {
141  elastic_band_ = elastic_band;
142  band_set_ = true;
143  return true;
144  }
145 
146 
147  bool EBandTrajectoryCtrl::setOdometry(const nav_msgs::Odometry& odometry)
148  {
149  odom_vel_.linear.x = odometry.twist.twist.linear.x;
150  odom_vel_.linear.y = odometry.twist.twist.linear.y;
151  odom_vel_.linear.z = 0.0;
152  odom_vel_.angular.x = 0.0;
153  odom_vel_.angular.y = 0.0;
154  odom_vel_.angular.z = odometry.twist.twist.angular.z;
155 
156  return true;
157  }
158 
159  // Return the angular difference between the direction we're pointing
160  // and the direction we want to move in
161  double angularDiff (const geometry_msgs::Twist& heading,
162  const geometry_msgs::Pose& pose)
163  {
164  const double pi = 3.14159265;
165  const double t1 = atan2(heading.linear.y, heading.linear.x);
166  const double t2 = tf::getYaw(pose.orientation);
167  const double d = t1-t2;
168 
169  if (fabs(d)<pi)
170  return d;
171  else if (d<0)
172  return d+2*pi;
173  else
174  return d-2*pi;
175  }
176 
177  bool EBandTrajectoryCtrl::getTwistDifferentialDrive(geometry_msgs::Twist& twist_cmd, bool& goal_reached) {
178  goal_reached = false;
179 
180  geometry_msgs::Twist robot_cmd, bubble_diff;
181  robot_cmd.linear.x = 0.0;
182  robot_cmd.angular.z = 0.0;
183 
184  bool command_provided = false;
185 
186  // check if plugin initialized
187  if(!initialized_) {
188  ROS_ERROR("Requesting feedforward command from not initialized planner. Please call initialize() before using this planner");
189  return false;
190  }
191 
192  // check there is a plan at all (minimum 1 frame in this case, as robot + goal = plan)
193  if( (!band_set_) || (elastic_band_.size() < 2) ) {
194  ROS_WARN("Requesting feedforward command from empty band.");
195  return false;
196  }
197 
198  // Get the differences between the first 2 bubbles in the robot's frame
199  bubble_diff = getFrame1ToFrame2InRefFrameNew(
200  elastic_band_.at(0).center.pose,
201  elastic_band_.at(1).center.pose,
202  elastic_band_.at(0).center.pose);
203 
204  float distance_from_goal = -1.0f;
205 
206  // Check 1
207  // We need to check if we are within the threshold of the final destination
208  if (!command_provided) {
209  int curr_target_bubble = 1;
210 
211  while(curr_target_bubble < ((int) elastic_band_.size()) - 1) {
212  curr_target_bubble++;
213  bubble_diff =
215  elastic_band_.at(0).center.pose,
216  elastic_band_.at(curr_target_bubble).center.pose,
217  elastic_band_.at(0).center.pose);
218  }
219 
220  // if you go past tolerance, then try to get closer again
221  if (!disallow_hysteresis_) {
222  if(fabs(bubble_diff.linear.x) > tolerance_trans_ ||
223  fabs(bubble_diff.linear.y) > tolerance_trans_) {
224  in_final_goal_turn_ = false;
225  }
226  }
227 
228  // Get the differences between the first 2 bubbles in the robot's frame
229  int goal_bubble = (((int) elastic_band_.size()) - 1);
230  bubble_diff = getFrame1ToFrame2InRefFrameNew(
231  elastic_band_.at(0).center.pose,
232  elastic_band_.at(goal_bubble).center.pose,
233  elastic_band_.at(0).center.pose);
234 
235  distance_from_goal = sqrtf(bubble_diff.linear.x * bubble_diff.linear.x +
236  bubble_diff.linear.y * bubble_diff.linear.y);
237 
238  // Get closer to the goal than the tolerance requires before starting the
239  // final turn. The final turn may cause you to move slightly out of
240  // position
241  if((fabs(bubble_diff.linear.x) <= 0.6 * tolerance_trans_ &&
242  fabs(bubble_diff.linear.y) <= 0.6 * tolerance_trans_) ||
244  // Calculate orientation difference to goal orientation (not captured in bubble_diff)
245  double robot_yaw = tf::getYaw(elastic_band_.at(0).center.pose.orientation);
246  double goal_yaw = tf::getYaw(elastic_band_.at((int)elastic_band_.size() - 1).center.pose.orientation);
247  float orientation_diff = angles::normalize_angle(goal_yaw - robot_yaw);
248  if (fabs(orientation_diff) > tolerance_rot_) {
249  in_final_goal_turn_ = true;
250  ROS_DEBUG("Performing in place rotation for goal (diff): %f", orientation_diff);
251  double rotation_sign = -2 * (orientation_diff < 0) + 1;
252  robot_cmd.angular.z =
253  rotation_sign * min_in_place_vel_th_ + k_p_ * orientation_diff;
254  if (fabs(robot_cmd.angular.z) > max_vel_th_) { // limit max rotation
255  robot_cmd.angular.z = rotation_sign * max_vel_th_;
256  }
257  } else {
258  in_final_goal_turn_ = false; // Goal reached
259  ROS_INFO ("TrajectoryController: Goal reached with distance %.2f, %.2f (od = %.2f)"
260  "; sending zero velocity",
261  bubble_diff.linear.x, bubble_diff.linear.y, orientation_diff);
262  // goal position reached
263  robot_cmd.linear.x = 0.0;
264  robot_cmd.angular.z = 0.0;
265  goal_reached = true;
266  }
267  command_provided = true;
268  }
269  }
270 
271  // Get the differences between the first 2 bubbles in the robot's frame
272  bubble_diff = getFrame1ToFrame2InRefFrameNew(
273  elastic_band_.at(0).center.pose,
274  elastic_band_.at(1).center.pose,
275  elastic_band_.at(0).center.pose);
276 
277  // Check 1 - check if the robot's current pose is too misaligned with the next bubble
278  if (!command_provided) {
279  ROS_DEBUG("Goal has not been reached, performing checks to move towards goal");
280 
281  // calculate an estimate of the in-place rotation threshold
282  double distance_to_next_bubble = sqrt(
283  bubble_diff.linear.x * bubble_diff.linear.x +
284  bubble_diff.linear.y * bubble_diff.linear.y);
285  double radius_of_next_bubble = 0.7 * elastic_band_.at(1).expansion;
286  double in_place_rotation_threshold =
288  fabs(atan2(radius_of_next_bubble,distance_to_next_bubble));
289  ROS_DEBUG("In-place rotation threshold: %f(%f,%f)",
290  in_place_rotation_threshold, radius_of_next_bubble, distance_to_next_bubble);
291 
292  // check if we are above this threshold, if so then perform in-place rotation
293  if (fabs(bubble_diff.angular.z) > in_place_rotation_threshold) {
294  robot_cmd.angular.z = k_p_ * bubble_diff.angular.z;
295  double rotation_sign = (bubble_diff.angular.z < 0) ? -1.0 : +1.0;
296  if (fabs(robot_cmd.angular.z) < min_in_place_vel_th_) {
297  robot_cmd.angular.z = rotation_sign * min_in_place_vel_th_;
298  }
299  if (fabs(robot_cmd.angular.z) > max_vel_th_) { // limit max rotation
300  robot_cmd.angular.z = rotation_sign * max_vel_th_;
301  }
302  ROS_DEBUG("Performing in place rotation for start (diff): %f with rot vel: %f", bubble_diff.angular.z, robot_cmd.angular.z);
303  command_provided = true;
304  }
305  }
306 
307  // Check 3 - If we reach here, it means we need to use our PID controller to
308  // move towards the next bubble
309  if (!command_provided) {
310 
311  // Select a linear velocity (based on the current bubble radius)
312  double forward_sign = -2 * (bubble_diff.linear.x < 0) + 1;
313  double bubble_radius = 0.7 * elastic_band_.at(0).expansion;
314  double velocity_multiplier = bubble_velocity_multiplier_ * bubble_radius;
315 
316  double max_vel_lin = max_vel_lin_;
317  if (distance_from_goal < 0.75f) {
318  max_vel_lin = (max_vel_lin < 0.3) ? 0.15 : max_vel_lin / 2;
319  }
320 
321  double linear_velocity = velocity_multiplier * max_vel_lin;
322  linear_velocity *= cos(bubble_diff.angular.z); //decrease while turning
323  if (fabs(linear_velocity) > max_vel_lin_) {
324  linear_velocity = forward_sign * max_vel_lin_;
325  } else if (fabs(linear_velocity) < min_vel_lin_) {
326  linear_velocity = forward_sign * min_vel_lin_;
327  }
328 
329  // Select an angular velocity (based on PID controller)
330  double error = bubble_diff.angular.z;
331  double rotation_sign = -2 * (bubble_diff.angular.z < 0) + 1;
332  double angular_velocity = k_p_ * error;
333  if (fabs(angular_velocity) > max_vel_th_) {
334  angular_velocity = rotation_sign * max_vel_th_;
335  } else if (fabs(angular_velocity) < min_vel_th_) {
336  angular_velocity = rotation_sign * min_vel_th_;
337  }
338 
339  ROS_DEBUG("Selected velocity: lin: %f, ang: %f",
340  linear_velocity, angular_velocity);
341 
342  robot_cmd.linear.x = linear_velocity;
343  robot_cmd.angular.z = angular_velocity;
344  command_provided = true;
345  }
346 
347  twist_cmd = robot_cmd;
348  ROS_DEBUG("Final command: %f, %f", twist_cmd.linear.x, twist_cmd.angular.z);
349  return true;
350  }
351 
352 
353  bool EBandTrajectoryCtrl::getTwist(geometry_msgs::Twist& twist_cmd, bool& goal_reached)
354  {
355  goal_reached = false;
357  return getTwistDifferentialDrive(twist_cmd, goal_reached);
358  }
359 
360  // init twist cmd to be handed back to caller
361  geometry_msgs::Twist robot_cmd, bubble_diff, control_deviation;
362  robot_cmd.linear.x = 0.0;
363  robot_cmd.linear.y = 0.0;
364  robot_cmd.linear.z = 0.0;
365  robot_cmd.angular.x = 0.0;
366  robot_cmd.angular.y = 0.0;
367  robot_cmd.angular.z = 0.0;
368 
369  // make sure command vector is clean
370  twist_cmd = robot_cmd;
371  control_deviation = robot_cmd;
372 
373  // check if plugin initialized
374  if(!initialized_)
375  {
376  ROS_ERROR("Requesting feedforward command from not initialized planner. Please call initialize() before using this planner");
377  return false;
378  }
379 
380  // check there is a plan at all (minimum 1 frame in this case, as robot + goal = plan)
381  if( (!band_set_) || (elastic_band_.size() < 2) )
382  {
383  ROS_WARN("Requesting feedforward command from empty band.");
384  return false;
385  }
386 
387  // calc intersection of bubble-radius with sequence of vector connecting the bubbles
388 
389  // get distance to target from bubble-expansion
390  double scaled_radius = 0.7 * elastic_band_.at(0).expansion;
391 
392  // get difference and distance between bubbles in odometry frame
393  double bubble_distance, ang_pseudo_dist;
394  bubble_diff = getFrame1ToFrame2InRefFrame(elastic_band_.at(0).center.pose,
395  elastic_band_.at(1).center.pose,
397  ang_pseudo_dist = bubble_diff.angular.z * getCircumscribedRadius(*costmap_ros_);
398  bubble_distance = sqrt( (bubble_diff.linear.x * bubble_diff.linear.x) + (bubble_diff.linear.y * bubble_diff.linear.y) +
399  (ang_pseudo_dist * ang_pseudo_dist) );
400 
401  if(visualization_)
402  {
403  target_visual_->publishBubble("ctrl_target", 1, target_visual_->blue, elastic_band_.at(0));
404  target_visual_->publishBubble("ctrl_target", 2, target_visual_->blue, elastic_band_.at(1));
405  }
406 
407  // by default our control deviation is the difference between the bubble centers
408  double abs_ctrl_dev;
409  control_deviation = bubble_diff;
410 
411 
412  ang_pseudo_dist = control_deviation.angular.z * getCircumscribedRadius(*costmap_ros_);
413  abs_ctrl_dev = sqrt( (control_deviation.linear.x * control_deviation.linear.x) +
414  (control_deviation.linear.y * control_deviation.linear.y) +
415  (ang_pseudo_dist * ang_pseudo_dist) );
416 
417  // yet depending on the expansion of our bubble we might want to adapt this point
418  if(scaled_radius < bubble_distance)
419  {
420  // triviale case - simply scale bubble_diff
421  double scale_difference = scaled_radius / bubble_distance;
422  bubble_diff.linear.x *= scale_difference;
423  bubble_diff.linear.y *= scale_difference;
424  bubble_diff.angular.z *= scale_difference;
425  // set controls
426  control_deviation = bubble_diff;
427  }
428 
429  // if scaled_radius = bubble_distance -- we have nothing to do at all
430 
431  if(scaled_radius > bubble_distance)
432  {
433  // o.k. now we have to do a little bit more -> check next but one bubble
434  if(elastic_band_.size() > 2)
435  {
436  // get difference between next and next but one bubble
437  double next_bubble_distance;
438  geometry_msgs::Twist next_bubble_diff;
439  next_bubble_diff = getFrame1ToFrame2InRefFrame(elastic_band_.at(1).center.pose,
440  elastic_band_.at(2).center.pose,
442  ang_pseudo_dist = next_bubble_diff.angular.z * getCircumscribedRadius(*costmap_ros_);
443  next_bubble_distance = sqrt( (next_bubble_diff.linear.x * next_bubble_diff.linear.x) +
444  (next_bubble_diff.linear.y * next_bubble_diff.linear.y) +
445  (ang_pseudo_dist * ang_pseudo_dist) );
446 
447  if(scaled_radius > (bubble_distance + next_bubble_distance) )
448  {
449  // we should normally not end up here - but just to be sure
450  control_deviation.linear.x = bubble_diff.linear.x + next_bubble_diff.linear.x;
451  control_deviation.linear.y = bubble_diff.linear.y + next_bubble_diff.linear.y;
452  control_deviation.angular.z = bubble_diff.angular.z + next_bubble_diff.angular.z;
453  // done
454  if(visualization_)
455  target_visual_->publishBubble("ctrl_target", 3, target_visual_->red, elastic_band_.at(2));
456  }
457  else
458  {
459  if(visualization_)
460  target_visual_->publishBubble("ctrl_target", 3, target_visual_->red, elastic_band_.at(2));
461 
462  // we want to calculate intersection point of bubble ...
463  // ... and vector connecting the following bubbles
464  double b_distance, cosine_at_bub;
465  double vec_prod, norm_vec1, norm_vec2;
466  double ang_pseudo_dist1, ang_pseudo_dist2;
467 
468  // get distance between next bubble center and intersection point
469  ang_pseudo_dist1 = bubble_diff.angular.z * getCircumscribedRadius(*costmap_ros_);
470  ang_pseudo_dist2 = next_bubble_diff.angular.z * getCircumscribedRadius(*costmap_ros_);
471  // careful! - we need this sign because of the direction of the vectors and the definition of the vector-product
472  vec_prod = - ( (bubble_diff.linear.x * next_bubble_diff.linear.x) +
473  (bubble_diff.linear.y * next_bubble_diff.linear.y) +
474  (ang_pseudo_dist1 * ang_pseudo_dist2) );
475 
476  norm_vec1 = sqrt( (bubble_diff.linear.x * bubble_diff.linear.x) +
477  (bubble_diff.linear.y * bubble_diff.linear.y) +
478  (ang_pseudo_dist1 * ang_pseudo_dist1) );
479 
480  norm_vec2 = sqrt( (next_bubble_diff.linear.x * next_bubble_diff.linear.x) +
481  (next_bubble_diff.linear.y * next_bubble_diff.linear.y) +
482  (ang_pseudo_dist2 * ang_pseudo_dist2) );
483 
484  // reform the cosine-rule
485  cosine_at_bub = vec_prod / norm_vec1 / norm_vec2;
486  b_distance = bubble_distance * cosine_at_bub + sqrt( scaled_radius*scaled_radius -
487  bubble_distance*bubble_distance * (1.0 - cosine_at_bub*cosine_at_bub) );
488 
489  // get difference vector from next_bubble to intersection point
490  double scale_next_difference = b_distance / next_bubble_distance;
491  next_bubble_diff.linear.x *= scale_next_difference;
492  next_bubble_diff.linear.y *= scale_next_difference;
493  next_bubble_diff.angular.z *= scale_next_difference;
494 
495  // and finally get the control deviation
496  control_deviation.linear.x = bubble_diff.linear.x + next_bubble_diff.linear.x;
497  control_deviation.linear.y = bubble_diff.linear.y + next_bubble_diff.linear.y;
498  control_deviation.angular.z = bubble_diff.angular.z + next_bubble_diff.angular.z;
499  // done
500  }
501  }
502  }
503 
504  // plot control deviation
505  ang_pseudo_dist = control_deviation.angular.z * getCircumscribedRadius(*costmap_ros_);
506  abs_ctrl_dev = sqrt( (control_deviation.linear.x * control_deviation.linear.x) +
507  (control_deviation.linear.y * control_deviation.linear.y) +
508  (ang_pseudo_dist * ang_pseudo_dist) );
509 
510 
511  if(visualization_)
512  {
513  // compose bubble from ctrl-target
514  geometry_msgs::Pose2D tmp_bubble_2d, curr_bubble_2d;
515  geometry_msgs::Pose tmp_pose;
516  // init bubble for visualization
517  Bubble new_bubble = elastic_band_.at(0);
518  PoseToPose2D(elastic_band_.at(0).center.pose, curr_bubble_2d);
519  tmp_bubble_2d.x = curr_bubble_2d.x + control_deviation.linear.x;
520  tmp_bubble_2d.y = curr_bubble_2d.y + control_deviation.linear.y;
521  tmp_bubble_2d.theta = curr_bubble_2d.theta + control_deviation.angular.z;
522  Pose2DToPose(tmp_pose, tmp_bubble_2d);
523  new_bubble.center.pose = tmp_pose;
524  new_bubble.expansion = 0.1; // just draw a small bubble
525  target_visual_->publishBubble("ctrl_target", 0, target_visual_->red, new_bubble);
526  }
527 
528 
529  const geometry_msgs::Point& goal = (--elastic_band_.end())->center.pose.position;
530  const double dx = elastic_band_.at(0).center.pose.position.x - goal.x;
531  const double dy = elastic_band_.at(0).center.pose.position.y - goal.y;
532  const double dist_to_goal = sqrt(dx*dx + dy*dy);
533 
534  // Assuming we're far enough from the final goal, make sure to rotate so
535  // we're facing the right way
536  if (dist_to_goal > rotation_correction_threshold_)
537  {
538 
539  const double angular_diff = angularDiff(control_deviation, elastic_band_.at(0).center.pose);
540  const double vel = pid_.computeCommand(angular_diff, ros::Duration(1/ctrl_freq_));
541  const double mult = fabs(vel) > max_vel_th_ ? max_vel_th_/fabs(vel) : 1.0;
542  control_deviation.angular.z = vel*mult;
543  const double abs_vel = fabs(control_deviation.angular.z);
544 
545  ROS_DEBUG_THROTTLE_NAMED (1.0, "angle_correction",
546  "Angular diff is %.2f and desired angular "
547  "vel is %.2f. Initial translation velocity "
548  "is %.2f, %.2f", angular_diff,
549  control_deviation.angular.z,
550  control_deviation.linear.x,
551  control_deviation.linear.y);
552  const double trans_mult = max(0.01, 1.0 - abs_vel/max_vel_th_); // There are some weird tf errors if I let it be 0
553  control_deviation.linear.x *= trans_mult;
554  control_deviation.linear.y *= trans_mult;
555  ROS_DEBUG_THROTTLE_NAMED (1.0, "angle_correction",
556  "Translation multiplier is %.2f and scaled "
557  "translational velocity is %.2f, %.2f",
558  trans_mult, control_deviation.linear.x,
559  control_deviation.linear.y);
560  }
561  else
562  ROS_DEBUG_THROTTLE_NAMED (1.0, "angle_correction",
563  "Not applying angle correction because "
564  "distance to goal is %.2f", dist_to_goal);
565 
566 
567 
568 
569  // now the actual control procedure start (using attractive Potentials)
570  geometry_msgs::Twist desired_velocity, currbub_maxvel_dir;
571  double desvel_abs, desvel_abs_trans, currbub_maxvel_abs;
572  double scale_des_vel;
573  desired_velocity = robot_cmd;
574  currbub_maxvel_dir = robot_cmd;
575 
576  // calculate "equilibrium velocity" (Khatib86 - Realtime Obstacle Avoidance)
577  desired_velocity.linear.x = k_p_/k_nu_ * control_deviation.linear.x;
578  desired_velocity.linear.y = k_p_/k_nu_ * control_deviation.linear.y;
579  desired_velocity.angular.z = k_p_/k_nu_ * control_deviation.angular.z;
580 
581  //robot_cmd = desired_velocity;
582 
583  // get max vel for current bubble
584  int curr_bub_num = 0;
585  currbub_maxvel_abs = getBubbleTargetVel(curr_bub_num, elastic_band_, currbub_maxvel_dir);
586 
587  // if neccessarry scale desired vel to stay lower than currbub_maxvel_abs
588  ang_pseudo_dist = desired_velocity.angular.z * getCircumscribedRadius(*costmap_ros_);
589  desvel_abs = sqrt( (desired_velocity.linear.x * desired_velocity.linear.x) +
590  (desired_velocity.linear.y * desired_velocity.linear.y) +
591  (ang_pseudo_dist * ang_pseudo_dist) );
592  if(desvel_abs > currbub_maxvel_abs)
593  {
594  scale_des_vel = currbub_maxvel_abs / desvel_abs;
595  desired_velocity.linear.x *= scale_des_vel;
596  desired_velocity.linear.y *= scale_des_vel;
597  desired_velocity.angular.z *= scale_des_vel;
598  }
599 
600  // make sure to stay within velocity bounds for the robot
601  desvel_abs_trans = sqrt( (desired_velocity.linear.x * desired_velocity.linear.x) + (desired_velocity.linear.y * desired_velocity.linear.y) );
602  // for translation
603  if(desvel_abs_trans > max_vel_lin_)
604  {
605  scale_des_vel = max_vel_lin_ / desvel_abs_trans;
606  desired_velocity.linear.x *= scale_des_vel;
607  desired_velocity.linear.y *= scale_des_vel;
608  // to make sure we are staying inside the bubble also scale rotation
609  desired_velocity.angular.z *= scale_des_vel;
610  }
611 
612  // for rotation
613  if(fabs(desired_velocity.angular.z) > max_vel_th_)
614  {
615  scale_des_vel = max_vel_th_ / fabs(desired_velocity.angular.z);
616  desired_velocity.angular.z *= scale_des_vel;
617  // to make sure we are staying inside the bubble also scale translation
618  desired_velocity.linear.x *= scale_des_vel;
619  desired_velocity.linear.y *= scale_des_vel;
620  }
621 
622  // calculate resulting force (accel. resp.) (Khatib86 - Realtime Obstacle Avoidance)
623  geometry_msgs::Twist acc_desired;
624  acc_desired = robot_cmd;
625  acc_desired.linear.x = (1.0/virt_mass_) * k_nu_ * (desired_velocity.linear.x - last_vel_.linear.x);
626  acc_desired.linear.y = (1.0/virt_mass_) * k_nu_ * (desired_velocity.linear.y - last_vel_.linear.y);
627  acc_desired.angular.z = (1.0/virt_mass_) * k_nu_ * (desired_velocity.angular.z - last_vel_.angular.z);
628 
629  // constrain acceleration
630  double scale_acc;
631  double abs_acc_trans = sqrt( (acc_desired.linear.x*acc_desired.linear.x) + (acc_desired.linear.y*acc_desired.linear.y) );
632  if(abs_acc_trans > acc_max_trans_)
633  {
634  scale_acc = acc_max_trans_ / abs_acc_trans;
635  acc_desired.linear.x *= scale_acc;
636  acc_desired.linear.y *= scale_acc;
637  // again - keep relations - stay in bubble
638  acc_desired.angular.z *= scale_acc;
639  }
640 
641  if(fabs(acc_desired.angular.z) > acc_max_rot_)
642  {
643  scale_acc = fabs(acc_desired.angular.z) / acc_max_rot_;
644  acc_desired.angular.z *= scale_acc;
645  // again - keep relations - stay in bubble
646  acc_desired.linear.x *= scale_acc;
647  acc_desired.linear.y *= scale_acc;
648  }
649 
650  // and get velocity-cmds by integrating them over one time-step
651  last_vel_.linear.x = last_vel_.linear.x + acc_desired.linear.x / ctrl_freq_;
652  last_vel_.linear.y = last_vel_.linear.y + acc_desired.linear.y / ctrl_freq_;
653  last_vel_.angular.z = last_vel_.angular.z + acc_desired.angular.z / ctrl_freq_;
654 
655 
656  // we are almost done now take into accoun stick-slip and similar nasty things
657 
658  // last checks - limit current twist cmd (upper and lower bounds)
660 
661  // finally set robot_cmd (to non-zero value)
662  robot_cmd = last_vel_;
663 
664  // now convert into robot-body frame
665  robot_cmd = transformTwistFromFrame1ToFrame2(robot_cmd, ref_frame_band_, elastic_band_.at(0).center.pose);
666 
667  // check whether we reached the end of the band
668  int curr_target_bubble = 1;
669  while(fabs(bubble_diff.linear.x) <= tolerance_trans_ &&
670  fabs(bubble_diff.linear.y) <= tolerance_trans_ &&
671  fabs(bubble_diff.angular.z) <= tolerance_rot_)
672  {
673  if(curr_target_bubble < ((int) elastic_band_.size()) - 1)
674  {
675  curr_target_bubble++;
676  // transform next target bubble into robot-body frame
677  // and get difference to robot bubble
678  bubble_diff = getFrame1ToFrame2InRefFrame(elastic_band_.at(0).center.pose, elastic_band_.at(curr_target_bubble).center.pose,
680  }
681  else
682  {
683  ROS_DEBUG_THROTTLE_NAMED (1.0, "controller_state",
684  "Goal reached with distance %.2f, %.2f, %.2f"
685  "; sending zero velocity",
686  bubble_diff.linear.x, bubble_diff.linear.y,
687  bubble_diff.angular.z);
688  // goal position reached
689  robot_cmd.linear.x = 0.0;
690  robot_cmd.linear.y = 0.0;
691  robot_cmd.angular.z = 0.0;
692  // reset velocity
693  last_vel_.linear.x = 0.0;
694  last_vel_.linear.y = 0.0;
695  last_vel_.angular.z = 0.0;
696  goal_reached = true;
697  break;
698  }
699  }
700 
701  twist_cmd = robot_cmd;
702 
703  return true;
704  }
705 
706 
707  double EBandTrajectoryCtrl::getBubbleTargetVel(const int& target_bub_num, const std::vector<Bubble>& band, geometry_msgs::Twist& VelDir)
708  {
709  // init reference for direction vector
710  VelDir.linear.x = 0.0;
711  VelDir.linear.y = 0.0;
712  VelDir.linear.z = 0.0;
713  VelDir.angular.x = 0.0;
714  VelDir.angular.y = 0.0;
715  VelDir.angular.z = 0.0;
716 
717  // if we are looking at the last bubble - target vel is always zero
718  if(target_bub_num >= ((int) band.size() - 1))
719  return 0.0;
720 
721 
722  // otherwise check for max_vel calculated from current bubble size
723  double v_max_curr_bub, v_max_next_bub;
724  double bubble_distance, angle_to_pseudo_vel, delta_vel_max;
725  geometry_msgs::Twist bubble_diff;
726 
727  // distance for braking s = 0.5*v*v/a
728  v_max_curr_bub = sqrt(2 * elastic_band_.at(target_bub_num).expansion * acc_max_);
729 
730  // get distance to next bubble center
731  ROS_ASSERT( (target_bub_num >= 0) && ((target_bub_num +1) < (int) band.size()) );
732  bubble_diff = getFrame1ToFrame2InRefFrame(band.at(target_bub_num).center.pose, band.at(target_bub_num + 1).center.pose,
734  angle_to_pseudo_vel = bubble_diff.angular.z * getCircumscribedRadius(*costmap_ros_);
735 
736  bubble_distance = sqrt( (bubble_diff.linear.x * bubble_diff.linear.x) + (bubble_diff.linear.y * bubble_diff.linear.y) +
737  (angle_to_pseudo_vel * angle_to_pseudo_vel) );
738 
739  // calculate direction vector - norm of diference
740  VelDir.linear.x =bubble_diff.linear.x/bubble_distance;
741  VelDir.linear.y =bubble_diff.linear.y/bubble_distance;
742  VelDir.angular.z =bubble_diff.angular.z/bubble_distance;
743 
744  // if next bubble outside this one we will always be able to break fast enough
745  if(bubble_distance > band.at(target_bub_num).expansion )
746  return v_max_curr_bub;
747 
748 
749  // next bubble center inside this bubble - take into account restrictions on next bubble
750  int next_bub_num = target_bub_num + 1;
751  geometry_msgs::Twist dummy_twist;
752  v_max_next_bub = getBubbleTargetVel(next_bub_num, band, dummy_twist); // recursive call
753 
754  // if velocity at next bubble bigger (or equal) than our velocity - we are on the safe side
755  if(v_max_next_bub >= v_max_curr_bub)
756  return v_max_curr_bub;
757 
758 
759  // otherwise max. allowed vel is next vel + plus possible reduction on the way between the bubble-centers
760  delta_vel_max = sqrt(2 * bubble_distance * acc_max_);
761  v_max_curr_bub = v_max_next_bub + delta_vel_max;
762 
763  return v_max_curr_bub;
764  }
765 
766 
767  geometry_msgs::Twist EBandTrajectoryCtrl::getFrame1ToFrame2InRefFrame(const geometry_msgs::Pose& frame1, const geometry_msgs::Pose& frame2, const geometry_msgs::Pose& ref_frame)
768  {
769 
770  geometry_msgs::Pose2D frame1_pose2D, frame2_pose2D, ref_frame_pose2D;
771  geometry_msgs::Pose2D frame1_pose2D_rf, frame2_pose2D_rf;
772  geometry_msgs::Twist frame_diff;
773 
774  // transform all frames to Pose2d
775  PoseToPose2D(frame1, frame1_pose2D);
776  PoseToPose2D(frame2, frame2_pose2D);
777  PoseToPose2D(ref_frame, ref_frame_pose2D);
778 
779  // transform frame1 into ref frame
780  frame1_pose2D_rf.x = (frame1_pose2D.x - ref_frame_pose2D.x) * cos(ref_frame_pose2D.theta) +
781  (frame1_pose2D.y - ref_frame_pose2D.y) * sin(ref_frame_pose2D.theta);
782  frame1_pose2D_rf.y = -(frame1_pose2D.x - ref_frame_pose2D.x) * sin(ref_frame_pose2D.theta) +
783  (frame1_pose2D.y - ref_frame_pose2D.y) * cos(ref_frame_pose2D.theta);
784  frame1_pose2D_rf.theta = frame1_pose2D.theta - ref_frame_pose2D.theta;
785  frame1_pose2D_rf.theta = angles::normalize_angle(frame1_pose2D_rf.theta);
786  // transform frame2 into ref frame
787  frame2_pose2D_rf.x = (frame2_pose2D.x - ref_frame_pose2D.x) * cos(ref_frame_pose2D.theta) +
788  (frame2_pose2D.y - ref_frame_pose2D.y) * sin(ref_frame_pose2D.theta);
789  frame2_pose2D_rf.y = -(frame2_pose2D.x - ref_frame_pose2D.x) * sin(ref_frame_pose2D.theta) +
790  (frame2_pose2D.y - ref_frame_pose2D.y) * cos(ref_frame_pose2D.theta);
791  frame2_pose2D_rf.theta = frame2_pose2D.theta - ref_frame_pose2D.theta;
792  frame2_pose2D_rf.theta = angles::normalize_angle(frame2_pose2D_rf.theta);
793 
794  // get differences
795  frame_diff.linear.x = frame2_pose2D_rf.x - frame1_pose2D_rf.x;
796  frame_diff.linear.y = frame2_pose2D_rf.y - frame1_pose2D_rf.y;
797  frame_diff.linear.z = 0.0;
798  frame_diff.angular.x = 0.0;
799  frame_diff.angular.y = 0.0;
800  frame_diff.angular.z = frame2_pose2D_rf.theta - frame1_pose2D_rf.theta;
801  // normalize angle
802  frame_diff.angular.z = angles::normalize_angle(frame_diff.angular.z);
803 
804  return frame_diff;
805  }
806 
807  geometry_msgs::Twist EBandTrajectoryCtrl::getFrame1ToFrame2InRefFrameNew(const geometry_msgs::Pose& frame1, const geometry_msgs::Pose& frame2, const geometry_msgs::Pose& ref_frame)
808  {
809 
810  double x1 = frame1.position.x - ref_frame.position.x;
811  double y1 = frame1.position.y - ref_frame.position.y;
812  double x2 = frame2.position.x - ref_frame.position.x;
813  double y2 = frame2.position.y - ref_frame.position.y;
814  double yaw_ref = tf::getYaw(ref_frame.orientation);
815 
816  double x_diff = x2 - x1;
817  double y_diff = y2 - y1;
818  double theta_diff = atan2(y_diff, x_diff);
819 
820  // Now project this vector on to the reference frame
821  double rotation = angles::normalize_angle(yaw_ref);
822  double x_final = x_diff * cos(rotation) + y_diff * sin(rotation);
823  double y_final = - x_diff * sin(rotation) + y_diff * cos(rotation);
824 
825  geometry_msgs::Twist twist_msg;
826  twist_msg.linear.x = x_final;
827  twist_msg.linear.y = y_final;
828  twist_msg.angular.z = angles::normalize_angle(theta_diff - yaw_ref);
829 
830  return twist_msg;
831  }
832 
833 
834  geometry_msgs::Twist EBandTrajectoryCtrl::transformTwistFromFrame1ToFrame2(const geometry_msgs::Twist& curr_twist,
835  const geometry_msgs::Pose& frame1, const geometry_msgs::Pose& frame2)
836  {
837  geometry_msgs::Pose2D frame1_pose2D, frame2_pose2D;
838  geometry_msgs::Twist tmp_transformed;
839  double delta_ang;
840 
841  tmp_transformed = curr_twist;
842 
843  // transform all frames to Pose2d
844  PoseToPose2D(frame1, frame1_pose2D);
845  PoseToPose2D(frame2, frame2_pose2D);
846 
847  // get orientation diff of frames
848  delta_ang = frame2_pose2D.theta - frame1_pose2D.theta;
849  delta_ang = angles::normalize_angle(delta_ang);
850 
851  // tranform twist
852  tmp_transformed.linear.x = curr_twist.linear.x * cos(delta_ang) + curr_twist.linear.y * sin(delta_ang);
853  tmp_transformed.linear.y = -curr_twist.linear.x * sin(delta_ang) + curr_twist.linear.y * cos(delta_ang);
854 
855  return tmp_transformed;
856  }
857 
858 
859  geometry_msgs::Twist EBandTrajectoryCtrl::limitTwist(const geometry_msgs::Twist& twist)
860  {
861  geometry_msgs::Twist res = twist;
862 
863  //make sure to bound things by our velocity limits
864  double lin_overshoot = sqrt(res.linear.x * res.linear.x + res.linear.y * res.linear.y) / max_vel_lin_;
865  double lin_undershoot = min_vel_lin_ / sqrt(res.linear.x * res.linear.x + res.linear.y * res.linear.y);
866  if (lin_overshoot > 1.0)
867  {
868  res.linear.x /= lin_overshoot;
869  res.linear.y /= lin_overshoot;
870  // keep relations
871  res.angular.z /= lin_overshoot;
872  }
873 
874  //we only want to enforce a minimum velocity if we're not rotating in place
875  if(lin_undershoot > 1.0)
876  {
877  res.linear.x *= lin_undershoot;
878  res.linear.y *= lin_undershoot;
879  // we cannot keep relations here for stability reasons
880  }
881 
882  if (fabs(res.angular.z) > max_vel_th_)
883  {
884  double scale = max_vel_th_/fabs(res.angular.z);
885  //res.angular.z = max_vel_th_ * sign(res.angular.z);
886  res.angular.z *= scale;
887  // keep relations
888  res.linear.x *= scale;
889  res.linear.y *= scale;
890  }
891 
892  if (fabs(res.angular.z) < min_vel_th_) res.angular.z = min_vel_th_ * sign(res.angular.z);
893  // we cannot keep relations here for stability reasons
894 
895  //we want to check for whether or not we're desired to rotate in place
896  if(sqrt(twist.linear.x * twist.linear.x + twist.linear.y * twist.linear.y) < in_place_trans_vel_)
897  {
898  if (fabs(res.angular.z) < min_in_place_vel_th_)
899  res.angular.z = min_in_place_vel_th_ * sign(res.angular.z);
900 
901  res.linear.x = 0.0;
902  res.linear.y = 0.0;
903  }
904 
905  ROS_DEBUG("Angular command %f", res.angular.z);
906  return res;
907  }
908 
909 
910 }
d
geometry_msgs::Twist getFrame1ToFrame2InRefFrameNew(const geometry_msgs::Pose &frame1, const geometry_msgs::Pose &frame2, const geometry_msgs::Pose &ref_frame)
geometry_msgs::Twist transformTwistFromFrame1ToFrame2(const geometry_msgs::Twist &curr_twist, const geometry_msgs::Pose &frame1, const geometry_msgs::Pose &frame2)
bool getTwist(geometry_msgs::Twist &twist_cmd, bool &goal_reached)
calculates a twist feedforward command from the current band
f
geometry_msgs::Twist getFrame1ToFrame2InRefFrame(const geometry_msgs::Pose &frame1, const geometry_msgs::Pose &frame2, const geometry_msgs::Pose &ref_frame)
Transforms Pose of frame 1 and 2 into reference frame and gets difference of frame 1 and 2...
bool setBand(const std::vector< Bubble > &elastic_band)
This sets the elastic_band to the trajectory controller.
#define ROS_DEBUG_THROTTLE_NAMED(rate, name,...)
double sign(double n)
defines sign of a double
static double getYaw(const Quaternion &bt_q)
void reconfigure(EBandPlannerConfig &config)
Reconfigures the parameters of the planner.
void Pose2DToPose(geometry_msgs::Pose &pose, const geometry_msgs::Pose2D pose2D)
Converts a frame of type Pose to type Pose2D (mainly -> conversion of orientation from euler angles t...
bool setOdometry(const nav_msgs::Odometry &odometry)
This sets the robot velocity as provided by odometry.
#define ROS_WARN(...)
double angularDiff(const geometry_msgs::Twist &heading, const geometry_msgs::Pose &pose)
void initPid(double p, double i, double d, double i_max, double i_min, bool antiwindup=false)
bool getTwistDifferentialDrive(geometry_msgs::Twist &twist_cmd, bool &goal_reached)
double getCircumscribedRadius(costmap_2d::Costmap2DROS &costmap)
Gets the footprint of the robot and computes the circumscribed radius for the eband approach...
double computeCommand(double error, ros::Duration dt)
#define ROS_INFO(...)
INLINE Rall1d< T, V, S > sqrt(const Rall1d< T, V, S > &arg)
def normalize_angle(angle)
void initialize(std::string name, costmap_2d::Costmap2DROS *costmap_ros)
Initializes the elastic band class by accesing costmap and loading parameters.
double getBubbleTargetVel(const int &target_bub_num, const std::vector< Bubble > &band, geometry_msgs::Twist &VelDir)
gets the max velocity allowed within this bubble depending on size of the bubble and pose and size of...
void setVisualization(boost::shared_ptr< EBandVisualization > target_visual)
passes a reference to the eband visualization object which can be used to visualize the band optimiza...
boost::shared_ptr< EBandVisualization > target_visual_
INLINE Rall1d< T, V, S > atan2(const Rall1d< T, V, S > &y, const Rall1d< T, V, S > &x)
geometry_msgs::Twist limitTwist(const geometry_msgs::Twist &twist)
limits the twist to the allowed range
geometry_msgs::PoseStamped center
costmap_2d::Costmap2DROS * costmap_ros_
pointer to costmap
INLINE Rall1d< T, V, S > cos(const Rall1d< T, V, S > &arg)
#define ROS_ASSERT(cond)
double max(double a, double b)
#define ROS_ERROR(...)
INLINE Rall1d< T, V, S > sin(const Rall1d< T, V, S > &arg)
void PoseToPose2D(const geometry_msgs::Pose pose, geometry_msgs::Pose2D &pose2D)
Converts a frame of type Pose to type Pose2D (mainly -> conversion of orientation from quaternions to...
#define ROS_DEBUG(...)


eband_local_planner
Author(s): Christian Connette, Bhaskara Marthi, Piyush Khandelwal
autogenerated on Sat Feb 22 2020 04:03:28