eband_trajectory_controller.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  *
00003  * Software License Agreement (BSD License)
00004  *
00005  *  Copyright (c) 2010, Willow Garage, Inc.
00006  *  All rights reserved.
00007  *
00008  *  Redistribution and use in source and binary forms, with or without
00009  *  modification, are permitted provided that the following conditions
00010  *  are met:
00011  *
00012  *   * Redistributions of source code must retain the above copyright
00013  *     notice, this list of conditions and the following disclaimer.
00014  *   * Redistributions in binary form must reproduce the above
00015  *     copyright notice, this list of conditions and the following
00016  *     disclaimer in the documentation and/or other materials provided
00017  *     with the distribution.
00018  *   * Neither the name of Willow Garage, Inc. nor the names of its
00019  *     contributors may be used to endorse or promote products derived
00020  *     from this software without specific prior written permission.
00021  *
00022  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033  *  POSSIBILITY OF SUCH DAMAGE.
00034  *
00035  * Author: Christian Connette
00036  *********************************************************************/
00037 
00038 #include <eband_local_planner/eband_trajectory_controller.h>
00039 #include <tf/transform_datatypes.h>
00040 
00041 
00042 namespace eband_local_planner{
00043 
00044   using std::min;
00045   using std::max;
00046 
00047 
00048   EBandTrajectoryCtrl::EBandTrajectoryCtrl() : costmap_ros_(NULL), initialized_(false), band_set_(false), visualization_(false) {}
00049 
00050 
00051   EBandTrajectoryCtrl::EBandTrajectoryCtrl(std::string name, costmap_2d::Costmap2DROS* costmap_ros)
00052     : costmap_ros_(NULL), initialized_(false), band_set_(false), visualization_(false)
00053   {
00054     // initialize planner
00055     initialize(name, costmap_ros);
00056 
00057     // Initialize pid object (note we'll be further clamping its output)
00058     pid_.initPid(1, 0, 0, 10, -10);
00059   }
00060 
00061 
00062   EBandTrajectoryCtrl::~EBandTrajectoryCtrl() {}
00063 
00064 
00065   void EBandTrajectoryCtrl::initialize(std::string name, costmap_2d::Costmap2DROS* costmap_ros)
00066   {
00067 
00068     // check if trajectory controller is already initialized
00069     if(!initialized_)
00070     {
00071       // create Node Handle with name of plugin (as used in move_base for loading)
00072       ros::NodeHandle node_private("~/" + name);
00073 
00074       in_final_goal_turn_ = false;
00075 
00076       // copy adress of costmap and Transform Listener (handed over from move_base)
00077       costmap_ros_ = costmap_ros;
00078 
00079       // init velocity for interpolation
00080       last_vel_.linear.x = 0.0;
00081       last_vel_.linear.y = 0.0;
00082       last_vel_.linear.z = 0.0;
00083       last_vel_.angular.x = 0.0;
00084       last_vel_.angular.y = 0.0;
00085       last_vel_.angular.z = 0.0;
00086 
00087       // set the general refernce frame to that in which the band is given
00088       geometry_msgs::Pose2D tmp_pose2D;
00089       tmp_pose2D.x = 0.0;
00090       tmp_pose2D.y = 0.0;
00091       tmp_pose2D.theta = 0.0;
00092       Pose2DToPose(ref_frame_band_, tmp_pose2D);
00093 
00094       // set initialized flag
00095       initialized_ = true;
00096     }
00097     else
00098     {
00099       ROS_WARN("This planner has already been initialized, doing nothing.");
00100     }
00101   }
00102 
00103 
00104   void EBandTrajectoryCtrl::reconfigure(
00105     eband_local_planner::EBandPlannerConfig& config)
00106   {
00107     max_vel_lin_ = config.max_vel_lin;
00108     max_vel_th_ = config.max_vel_th;
00109     min_vel_lin_ = config.min_vel_lin;
00110     min_vel_th_ = config.min_vel_th;
00111     min_in_place_vel_th_ = config.min_in_place_vel_th;
00112     in_place_trans_vel_ = config.in_place_trans_vel;
00113     tolerance_trans_ = config.xy_goal_tolerance;
00114     tolerance_rot_ = config.yaw_goal_tolerance;
00115     k_p_ = config.k_prop;
00116     k_nu_ = config.k_damp;
00117     ctrl_freq_ = config.Ctrl_Rate;
00118     acc_max_ = config.max_acceleration;
00119     virt_mass_ = config.virtual_mass;
00120     acc_max_trans_ = config.max_translational_acceleration;
00121     acc_max_rot_ = config.max_rotational_acceleration;
00122     rotation_correction_threshold_ = config.rotation_correction_threshold;
00123 
00124     // diffferential drive parameters
00125     differential_drive_hack_ = config.differential_drive;
00126     bubble_velocity_multiplier_ = config.bubble_velocity_multiplier;
00127     rotation_threshold_multiplier_ = config.rotation_threshold_multiplier;
00128     disallow_hysteresis_ = config.disallow_hysteresis;
00129   }
00130 
00131 
00132   void EBandTrajectoryCtrl::setVisualization(boost::shared_ptr<EBandVisualization> target_visual)
00133   {
00134     target_visual_ = target_visual;
00135 
00136     visualization_ = true;
00137   }
00138 
00139   bool EBandTrajectoryCtrl::setBand(const std::vector<Bubble>& elastic_band)
00140   {
00141     elastic_band_ = elastic_band;
00142     band_set_ = true;
00143     return true;
00144   }
00145 
00146 
00147   bool EBandTrajectoryCtrl::setOdometry(const nav_msgs::Odometry& odometry)
00148   {
00149     odom_vel_.linear.x = odometry.twist.twist.linear.x;
00150     odom_vel_.linear.y = odometry.twist.twist.linear.y;
00151     odom_vel_.linear.z = 0.0;
00152     odom_vel_.angular.x = 0.0;
00153     odom_vel_.angular.y = 0.0;
00154     odom_vel_.angular.z = odometry.twist.twist.angular.z;
00155 
00156     return true;
00157   }
00158 
00159   // Return the angular difference between the direction we're pointing
00160   // and the direction we want to move in
00161   double angularDiff (const geometry_msgs::Twist& heading,
00162       const geometry_msgs::Pose& pose)
00163   {
00164     const double pi = 3.14159265;
00165     const double t1 = atan2(heading.linear.y, heading.linear.x);
00166     const double t2 = tf::getYaw(pose.orientation);
00167     const double d = t1-t2;
00168 
00169     if (fabs(d)<pi)
00170       return d;
00171     else if (d<0)
00172       return d+2*pi;
00173     else
00174       return d-2*pi;
00175   }
00176 
00177   bool EBandTrajectoryCtrl::getTwistDifferentialDrive(geometry_msgs::Twist& twist_cmd, bool& goal_reached) {
00178     goal_reached = false;
00179 
00180     geometry_msgs::Twist robot_cmd, bubble_diff;
00181     robot_cmd.linear.x = 0.0;
00182     robot_cmd.angular.z = 0.0;
00183 
00184     bool command_provided = false;
00185 
00186     // check if plugin initialized
00187     if(!initialized_)   {
00188       ROS_ERROR("Requesting feedforward command from not initialized planner. Please call initialize() before using this planner");
00189       return false;
00190     }
00191 
00192     // check there is a plan at all (minimum 1 frame in this case, as robot + goal = plan)
00193     if( (!band_set_) || (elastic_band_.size() < 2) ) {
00194       ROS_WARN("Requesting feedforward command from empty band.");
00195       return false;
00196     }
00197 
00198     // Get the differences between the first 2 bubbles in the robot's frame
00199     bubble_diff = getFrame1ToFrame2InRefFrameNew(
00200         elastic_band_.at(0).center.pose,
00201         elastic_band_.at(1).center.pose,
00202         elastic_band_.at(0).center.pose);
00203 
00204     float distance_from_goal = -1.0f;
00205 
00206     // Check 1
00207     // We need to check if we are within the threshold of the final destination
00208     if (!command_provided) {
00209       int curr_target_bubble = 1;
00210 
00211       while(curr_target_bubble < ((int) elastic_band_.size()) - 1) {
00212         curr_target_bubble++;
00213         bubble_diff =
00214           getFrame1ToFrame2InRefFrameNew(
00215               elastic_band_.at(0).center.pose,
00216               elastic_band_.at(curr_target_bubble).center.pose,
00217               elastic_band_.at(0).center.pose);
00218       }
00219 
00220       // if you go past tolerance, then try to get closer again
00221       if (!disallow_hysteresis_) {
00222         if(fabs(bubble_diff.linear.x) > tolerance_trans_ ||
00223             fabs(bubble_diff.linear.y) > tolerance_trans_) {
00224           in_final_goal_turn_ = false;
00225         }
00226       }
00227 
00228       // Get the differences between the first 2 bubbles in the robot's frame
00229       int goal_bubble = (((int) elastic_band_.size()) - 1);
00230       bubble_diff = getFrame1ToFrame2InRefFrameNew(
00231           elastic_band_.at(0).center.pose,
00232           elastic_band_.at(goal_bubble).center.pose,
00233           elastic_band_.at(0).center.pose);
00234 
00235       distance_from_goal = sqrtf(bubble_diff.linear.x * bubble_diff.linear.x +
00236                                  bubble_diff.linear.y * bubble_diff.linear.y);
00237 
00238       // Get closer to the goal than the tolerance requires before starting the
00239       // final turn. The final turn may cause you to move slightly out of
00240       // position
00241       if((fabs(bubble_diff.linear.x) <= 0.6 * tolerance_trans_ &&
00242           fabs(bubble_diff.linear.y) <= 0.6 * tolerance_trans_) ||
00243           in_final_goal_turn_) {
00244         // Calculate orientation difference to goal orientation (not captured in bubble_diff)
00245         double robot_yaw = tf::getYaw(elastic_band_.at(0).center.pose.orientation);
00246         double goal_yaw = tf::getYaw(elastic_band_.at((int)elastic_band_.size() - 1).center.pose.orientation);
00247         float orientation_diff = angles::normalize_angle(goal_yaw - robot_yaw);
00248         if (fabs(orientation_diff) > tolerance_rot_) {
00249           in_final_goal_turn_ = true;
00250           ROS_DEBUG("Performing in place rotation for goal (diff): %f", orientation_diff);
00251           double rotation_sign = -2 * (orientation_diff < 0) + 1;
00252           robot_cmd.angular.z =
00253             rotation_sign * min_in_place_vel_th_ + k_p_ * orientation_diff;
00254           if (fabs(robot_cmd.angular.z) > max_vel_th_) { // limit max rotation
00255             robot_cmd.angular.z = rotation_sign * max_vel_th_;
00256           }
00257         } else {
00258           in_final_goal_turn_ = false; // Goal reached
00259           ROS_INFO ("TrajectoryController: Goal reached with distance %.2f, %.2f (od = %.2f)"
00260               "; sending zero velocity",
00261               bubble_diff.linear.x, bubble_diff.linear.y, orientation_diff);
00262           // goal position reached
00263           robot_cmd.linear.x = 0.0;
00264           robot_cmd.angular.z = 0.0;
00265           goal_reached = true;
00266         }
00267         command_provided = true;
00268       }
00269     }
00270 
00271     // Get the differences between the first 2 bubbles in the robot's frame
00272     bubble_diff = getFrame1ToFrame2InRefFrameNew(
00273         elastic_band_.at(0).center.pose,
00274         elastic_band_.at(1).center.pose,
00275         elastic_band_.at(0).center.pose);
00276 
00277     // Check 1 - check if the robot's current pose is too misaligned with the next bubble
00278     if (!command_provided) {
00279       ROS_DEBUG("Goal has not been reached, performing checks to move towards goal");
00280 
00281       // calculate an estimate of the in-place rotation threshold
00282       double distance_to_next_bubble = sqrt(
00283           bubble_diff.linear.x * bubble_diff.linear.x +
00284           bubble_diff.linear.y * bubble_diff.linear.y);
00285       double radius_of_next_bubble = 0.7 * elastic_band_.at(1).expansion;
00286       double in_place_rotation_threshold =
00287         rotation_threshold_multiplier_ *
00288         fabs(atan2(radius_of_next_bubble,distance_to_next_bubble));
00289       ROS_DEBUG("In-place rotation threshold: %f(%f,%f)",
00290           in_place_rotation_threshold, radius_of_next_bubble, distance_to_next_bubble);
00291 
00292       // check if we are above this threshold, if so then perform in-place rotation
00293       if (fabs(bubble_diff.angular.z) > in_place_rotation_threshold) {
00294         robot_cmd.angular.z = k_p_ * bubble_diff.angular.z;
00295         double rotation_sign = (bubble_diff.angular.z < 0) ? -1.0 : +1.0;
00296         if (fabs(robot_cmd.angular.z) < min_in_place_vel_th_) {
00297           robot_cmd.angular.z = rotation_sign * min_in_place_vel_th_;
00298         }
00299         if (fabs(robot_cmd.angular.z) > max_vel_th_) { // limit max rotation
00300           robot_cmd.angular.z = rotation_sign * max_vel_th_;
00301         }
00302         ROS_DEBUG("Performing in place rotation for start (diff): %f with rot vel: %f", bubble_diff.angular.z, robot_cmd.angular.z);
00303         command_provided = true;
00304       }
00305     }
00306 
00307     // Check 3 - If we reach here, it means we need to use our PID controller to
00308     // move towards the next bubble
00309     if (!command_provided) {
00310 
00311       // Select a linear velocity (based on the current bubble radius)
00312       double forward_sign = -2 * (bubble_diff.linear.x < 0) + 1;
00313       double bubble_radius = 0.7 * elastic_band_.at(0).expansion;
00314       double velocity_multiplier = bubble_velocity_multiplier_ * bubble_radius;
00315 
00316       double max_vel_lin = max_vel_lin_;
00317       if (distance_from_goal < 0.75f) {
00318         max_vel_lin = (max_vel_lin < 0.3) ? 0.15 : max_vel_lin / 2;
00319       }
00320 
00321       double linear_velocity = velocity_multiplier * max_vel_lin;
00322       linear_velocity *= cos(bubble_diff.angular.z); //decrease while turning
00323       if (fabs(linear_velocity) > max_vel_lin_) {
00324         linear_velocity = forward_sign * max_vel_lin_;
00325       } else if (fabs(linear_velocity) < min_vel_lin_) {
00326         linear_velocity = forward_sign * min_vel_lin_;
00327       }
00328 
00329       // Select an angular velocity (based on PID controller)
00330       double error = bubble_diff.angular.z;
00331       double rotation_sign = -2 * (bubble_diff.angular.z < 0) + 1;
00332       double angular_velocity = k_p_ * error;
00333       if (fabs(angular_velocity) > max_vel_th_) {
00334         angular_velocity = rotation_sign * max_vel_th_;
00335       } else if (fabs(angular_velocity) < min_vel_th_) {
00336         angular_velocity = rotation_sign * min_vel_th_;
00337       }
00338 
00339       ROS_DEBUG("Selected velocity: lin: %f, ang: %f",
00340           linear_velocity, angular_velocity);
00341 
00342       robot_cmd.linear.x = linear_velocity;
00343       robot_cmd.angular.z = angular_velocity;
00344       command_provided = true;
00345     }
00346 
00347     twist_cmd = robot_cmd;
00348     ROS_DEBUG("Final command: %f, %f", twist_cmd.linear.x, twist_cmd.angular.z);
00349     return true;
00350   }
00351 
00352 
00353   bool EBandTrajectoryCtrl::getTwist(geometry_msgs::Twist& twist_cmd, bool& goal_reached)
00354   {
00355     goal_reached = false;
00356     if (differential_drive_hack_) {
00357       return getTwistDifferentialDrive(twist_cmd, goal_reached);
00358     }
00359 
00360     // init twist cmd to be handed back to caller
00361     geometry_msgs::Twist robot_cmd, bubble_diff, control_deviation;
00362     robot_cmd.linear.x = 0.0;
00363     robot_cmd.linear.y = 0.0;
00364     robot_cmd.linear.z = 0.0;
00365     robot_cmd.angular.x = 0.0;
00366     robot_cmd.angular.y = 0.0;
00367     robot_cmd.angular.z = 0.0;
00368 
00369     // make sure command vector is clean
00370     twist_cmd = robot_cmd;
00371     control_deviation = robot_cmd;
00372 
00373     // check if plugin initialized
00374     if(!initialized_)
00375     {
00376       ROS_ERROR("Requesting feedforward command from not initialized planner. Please call initialize() before using this planner");
00377       return false;
00378     }
00379 
00380     // check there is a plan at all (minimum 1 frame in this case, as robot + goal = plan)
00381     if( (!band_set_) || (elastic_band_.size() < 2) )
00382     {
00383       ROS_WARN("Requesting feedforward command from empty band.");
00384       return false;
00385     }
00386 
00387     // calc intersection of bubble-radius with sequence of vector connecting the bubbles
00388 
00389     // get distance to target from bubble-expansion
00390     double scaled_radius = 0.7 * elastic_band_.at(0).expansion;
00391 
00392     // get difference and distance between bubbles in odometry frame
00393     double bubble_distance, ang_pseudo_dist;
00394     bubble_diff = getFrame1ToFrame2InRefFrame(elastic_band_.at(0).center.pose,
00395         elastic_band_.at(1).center.pose,
00396         ref_frame_band_);
00397     ang_pseudo_dist = bubble_diff.angular.z * getCircumscribedRadius(*costmap_ros_);
00398     bubble_distance = sqrt( (bubble_diff.linear.x * bubble_diff.linear.x) + (bubble_diff.linear.y * bubble_diff.linear.y) +
00399         (ang_pseudo_dist * ang_pseudo_dist) );
00400 
00401     if(visualization_)
00402     {
00403       target_visual_->publishBubble("ctrl_target", 1, target_visual_->blue, elastic_band_.at(0));
00404       target_visual_->publishBubble("ctrl_target", 2, target_visual_->blue, elastic_band_.at(1));
00405     }
00406 
00407     // by default our control deviation is the difference between the bubble centers
00408     double abs_ctrl_dev;
00409     control_deviation = bubble_diff;
00410 
00411 
00412     ang_pseudo_dist = control_deviation.angular.z * getCircumscribedRadius(*costmap_ros_);
00413     abs_ctrl_dev = sqrt( (control_deviation.linear.x * control_deviation.linear.x) +
00414         (control_deviation.linear.y * control_deviation.linear.y) +
00415         (ang_pseudo_dist * ang_pseudo_dist) );
00416 
00417     // yet depending on the expansion of our bubble we might want to adapt this point
00418     if(scaled_radius < bubble_distance)
00419     {
00420       // triviale case - simply scale bubble_diff
00421       double scale_difference = scaled_radius / bubble_distance;
00422       bubble_diff.linear.x *= scale_difference;
00423       bubble_diff.linear.y *= scale_difference;
00424       bubble_diff.angular.z *= scale_difference;
00425       // set controls
00426       control_deviation = bubble_diff;
00427     }
00428 
00429     // if scaled_radius = bubble_distance -- we have nothing to do at all
00430 
00431     if(scaled_radius > bubble_distance)
00432     {
00433       // o.k. now we have to do a little bit more -> check next but one bubble
00434       if(elastic_band_.size() > 2)
00435       {
00436         // get difference between next and next but one bubble
00437         double next_bubble_distance;
00438         geometry_msgs::Twist next_bubble_diff;
00439         next_bubble_diff = getFrame1ToFrame2InRefFrame(elastic_band_.at(1).center.pose,
00440             elastic_band_.at(2).center.pose,
00441             ref_frame_band_);
00442         ang_pseudo_dist = next_bubble_diff.angular.z * getCircumscribedRadius(*costmap_ros_);
00443         next_bubble_distance = sqrt( (next_bubble_diff.linear.x * next_bubble_diff.linear.x) +
00444             (next_bubble_diff.linear.y * next_bubble_diff.linear.y) +
00445             (ang_pseudo_dist * ang_pseudo_dist) );
00446 
00447         if(scaled_radius > (bubble_distance + next_bubble_distance) )
00448         {
00449           // we should normally not end up here - but just to be sure
00450           control_deviation.linear.x = bubble_diff.linear.x + next_bubble_diff.linear.x;
00451           control_deviation.linear.y = bubble_diff.linear.y + next_bubble_diff.linear.y;
00452           control_deviation.angular.z = bubble_diff.angular.z + next_bubble_diff.angular.z;
00453           // done
00454           if(visualization_)
00455             target_visual_->publishBubble("ctrl_target", 3, target_visual_->red, elastic_band_.at(2));
00456         }
00457         else
00458         {
00459           if(visualization_)
00460             target_visual_->publishBubble("ctrl_target", 3, target_visual_->red, elastic_band_.at(2));
00461 
00462           // we want to calculate intersection point of bubble ...
00463           // ... and vector connecting the following bubbles
00464           double b_distance, cosine_at_bub;
00465           double vec_prod, norm_vec1, norm_vec2;
00466           double ang_pseudo_dist1, ang_pseudo_dist2;
00467 
00468           // get distance between next bubble center and intersection point
00469           ang_pseudo_dist1 = bubble_diff.angular.z * getCircumscribedRadius(*costmap_ros_);
00470           ang_pseudo_dist2 = next_bubble_diff.angular.z * getCircumscribedRadius(*costmap_ros_);
00471           // careful! - we need this sign because of the direction of the vectors and the definition of the vector-product
00472           vec_prod = - ( (bubble_diff.linear.x * next_bubble_diff.linear.x) +
00473               (bubble_diff.linear.y * next_bubble_diff.linear.y) +
00474               (ang_pseudo_dist1 * ang_pseudo_dist2) );
00475 
00476           norm_vec1 = sqrt( (bubble_diff.linear.x * bubble_diff.linear.x) +
00477               (bubble_diff.linear.y * bubble_diff.linear.y) +
00478               (ang_pseudo_dist1 * ang_pseudo_dist1) );
00479 
00480           norm_vec2 = sqrt( (next_bubble_diff.linear.x * next_bubble_diff.linear.x) +
00481               (next_bubble_diff.linear.y * next_bubble_diff.linear.y) +
00482               (ang_pseudo_dist2 * ang_pseudo_dist2) );
00483 
00484           // reform the cosine-rule
00485           cosine_at_bub = vec_prod / norm_vec1 / norm_vec2;
00486           b_distance = bubble_distance * cosine_at_bub + sqrt( scaled_radius*scaled_radius -
00487               bubble_distance*bubble_distance * (1.0 - cosine_at_bub*cosine_at_bub) );
00488 
00489           // get difference vector from next_bubble to intersection point
00490           double scale_next_difference = b_distance / next_bubble_distance;
00491           next_bubble_diff.linear.x *= scale_next_difference;
00492           next_bubble_diff.linear.y *= scale_next_difference;
00493           next_bubble_diff.angular.z *= scale_next_difference;
00494 
00495           // and finally get the control deviation
00496           control_deviation.linear.x = bubble_diff.linear.x + next_bubble_diff.linear.x;
00497           control_deviation.linear.y = bubble_diff.linear.y + next_bubble_diff.linear.y;
00498           control_deviation.angular.z = bubble_diff.angular.z + next_bubble_diff.angular.z;
00499           // done
00500         }
00501       }
00502     }
00503 
00504     // plot control deviation
00505     ang_pseudo_dist = control_deviation.angular.z * getCircumscribedRadius(*costmap_ros_);
00506     abs_ctrl_dev = sqrt( (control_deviation.linear.x * control_deviation.linear.x) +
00507         (control_deviation.linear.y * control_deviation.linear.y) +
00508         (ang_pseudo_dist * ang_pseudo_dist) );
00509 
00510 
00511     if(visualization_)
00512     {
00513       // compose bubble from ctrl-target
00514       geometry_msgs::Pose2D tmp_bubble_2d, curr_bubble_2d;
00515       geometry_msgs::Pose tmp_pose;
00516       // init bubble for visualization
00517       Bubble new_bubble = elastic_band_.at(0);
00518       PoseToPose2D(elastic_band_.at(0).center.pose, curr_bubble_2d);
00519       tmp_bubble_2d.x = curr_bubble_2d.x + control_deviation.linear.x;
00520       tmp_bubble_2d.y = curr_bubble_2d.y + control_deviation.linear.y;
00521       tmp_bubble_2d.theta = curr_bubble_2d.theta + control_deviation.angular.z;
00522       Pose2DToPose(tmp_pose, tmp_bubble_2d);
00523       new_bubble.center.pose = tmp_pose;
00524       new_bubble.expansion = 0.1; // just draw a small bubble
00525       target_visual_->publishBubble("ctrl_target", 0, target_visual_->red, new_bubble);
00526     }
00527 
00528 
00529     const geometry_msgs::Point& goal = (--elastic_band_.end())->center.pose.position;
00530     const double dx = elastic_band_.at(0).center.pose.position.x - goal.x;
00531     const double dy = elastic_band_.at(0).center.pose.position.y - goal.y;
00532     const double dist_to_goal = sqrt(dx*dx + dy*dy);
00533 
00534     // Assuming we're far enough from the final goal, make sure to rotate so
00535     // we're facing the right way
00536     if (dist_to_goal > rotation_correction_threshold_)
00537     {
00538 
00539       const double angular_diff = angularDiff(control_deviation, elastic_band_.at(0).center.pose);
00540       const double vel = pid_.computeCommand(angular_diff, ros::Duration(1/ctrl_freq_));
00541       const double mult = fabs(vel) > max_vel_th_ ? max_vel_th_/fabs(vel) : 1.0;
00542       control_deviation.angular.z = vel*mult;
00543       const double abs_vel = fabs(control_deviation.angular.z);
00544 
00545       ROS_DEBUG_THROTTLE_NAMED (1.0, "angle_correction",
00546           "Angular diff is %.2f and desired angular "
00547           "vel is %.2f.  Initial translation velocity "
00548           "is %.2f, %.2f", angular_diff,
00549           control_deviation.angular.z,
00550           control_deviation.linear.x,
00551           control_deviation.linear.y);
00552       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
00553       control_deviation.linear.x *= trans_mult;
00554       control_deviation.linear.y *= trans_mult;
00555       ROS_DEBUG_THROTTLE_NAMED (1.0, "angle_correction",
00556           "Translation multiplier is %.2f and scaled "
00557           "translational velocity is %.2f, %.2f",
00558           trans_mult, control_deviation.linear.x,
00559           control_deviation.linear.y);
00560     }
00561     else
00562       ROS_DEBUG_THROTTLE_NAMED (1.0, "angle_correction",
00563           "Not applying angle correction because "
00564           "distance to goal is %.2f", dist_to_goal);
00565 
00566 
00567 
00568 
00569     // now the actual control procedure start (using attractive Potentials)
00570     geometry_msgs::Twist desired_velocity, currbub_maxvel_dir;
00571     double desvel_abs, desvel_abs_trans, currbub_maxvel_abs;
00572     double scale_des_vel;
00573     desired_velocity = robot_cmd;
00574     currbub_maxvel_dir = robot_cmd;
00575 
00576     // calculate "equilibrium velocity" (Khatib86 - Realtime Obstacle Avoidance)
00577     desired_velocity.linear.x = k_p_/k_nu_ * control_deviation.linear.x;
00578     desired_velocity.linear.y = k_p_/k_nu_ * control_deviation.linear.y;
00579     desired_velocity.angular.z = k_p_/k_nu_ * control_deviation.angular.z;
00580 
00581     //robot_cmd = desired_velocity;
00582 
00583     // get max vel for current bubble
00584     int curr_bub_num = 0;
00585     currbub_maxvel_abs = getBubbleTargetVel(curr_bub_num, elastic_band_, currbub_maxvel_dir);
00586 
00587     // if neccessarry scale desired vel to stay lower than currbub_maxvel_abs
00588     ang_pseudo_dist = desired_velocity.angular.z * getCircumscribedRadius(*costmap_ros_);
00589     desvel_abs = sqrt( (desired_velocity.linear.x * desired_velocity.linear.x) +
00590         (desired_velocity.linear.y * desired_velocity.linear.y) +
00591         (ang_pseudo_dist * ang_pseudo_dist) );
00592     if(desvel_abs > currbub_maxvel_abs)
00593     {
00594       scale_des_vel = currbub_maxvel_abs / desvel_abs;
00595       desired_velocity.linear.x *= scale_des_vel;
00596       desired_velocity.linear.y *= scale_des_vel;
00597       desired_velocity.angular.z *= scale_des_vel;
00598     }
00599 
00600     // make sure to stay within velocity bounds for the robot
00601     desvel_abs_trans = sqrt( (desired_velocity.linear.x * desired_velocity.linear.x) + (desired_velocity.linear.y * desired_velocity.linear.y) );
00602     // for translation
00603     if(desvel_abs_trans > max_vel_lin_)
00604     {
00605       scale_des_vel = max_vel_lin_ / desvel_abs_trans;
00606       desired_velocity.linear.x *= scale_des_vel;
00607       desired_velocity.linear.y *= scale_des_vel;
00608       // to make sure we are staying inside the bubble also scale rotation
00609       desired_velocity.angular.z *= scale_des_vel;
00610     }
00611 
00612     // for rotation
00613     if(fabs(desired_velocity.angular.z) > max_vel_th_)
00614     {
00615       scale_des_vel = max_vel_th_ / fabs(desired_velocity.angular.z);
00616       desired_velocity.angular.z *= scale_des_vel;
00617       // to make sure we are staying inside the bubble also scale translation
00618       desired_velocity.linear.x *= scale_des_vel;
00619       desired_velocity.linear.y *= scale_des_vel;
00620     }
00621 
00622     // calculate resulting force (accel. resp.) (Khatib86 - Realtime Obstacle Avoidance)
00623     geometry_msgs::Twist acc_desired;
00624     acc_desired = robot_cmd;
00625     acc_desired.linear.x = (1.0/virt_mass_) * k_nu_ * (desired_velocity.linear.x - last_vel_.linear.x);
00626     acc_desired.linear.y = (1.0/virt_mass_) * k_nu_ * (desired_velocity.linear.y - last_vel_.linear.y);
00627     acc_desired.angular.z = (1.0/virt_mass_) * k_nu_ * (desired_velocity.angular.z - last_vel_.angular.z);
00628 
00629     // constrain acceleration
00630     double scale_acc;
00631     double abs_acc_trans = sqrt( (acc_desired.linear.x*acc_desired.linear.x) + (acc_desired.linear.y*acc_desired.linear.y) );
00632     if(abs_acc_trans > acc_max_trans_)
00633     {
00634       scale_acc = acc_max_trans_ / abs_acc_trans;
00635       acc_desired.linear.x *= scale_acc;
00636       acc_desired.linear.y *= scale_acc;
00637       // again - keep relations - stay in bubble
00638       acc_desired.angular.z *= scale_acc;
00639     }
00640 
00641     if(fabs(acc_desired.angular.z) > acc_max_rot_)
00642     {
00643       scale_acc = fabs(acc_desired.angular.z) / acc_max_rot_;
00644       acc_desired.angular.z *= scale_acc;
00645       // again - keep relations - stay in bubble
00646       acc_desired.linear.x *= scale_acc;
00647       acc_desired.linear.y *= scale_acc;
00648     }
00649 
00650     // and get velocity-cmds by integrating them over one time-step
00651     last_vel_.linear.x = last_vel_.linear.x + acc_desired.linear.x / ctrl_freq_;
00652     last_vel_.linear.y = last_vel_.linear.y + acc_desired.linear.y / ctrl_freq_;
00653     last_vel_.angular.z = last_vel_.angular.z + acc_desired.angular.z / ctrl_freq_;
00654 
00655 
00656     // we are almost done now take into accoun stick-slip and similar nasty things
00657 
00658     // last checks - limit current twist cmd (upper and lower bounds)
00659     last_vel_ = limitTwist(last_vel_);
00660 
00661     // finally set robot_cmd (to non-zero value)
00662     robot_cmd = last_vel_;
00663 
00664     // now convert into robot-body frame
00665     robot_cmd = transformTwistFromFrame1ToFrame2(robot_cmd, ref_frame_band_, elastic_band_.at(0).center.pose);
00666 
00667     // check whether we reached the end of the band
00668     int curr_target_bubble = 1;
00669     while(fabs(bubble_diff.linear.x) <= tolerance_trans_ &&
00670         fabs(bubble_diff.linear.y) <= tolerance_trans_ &&
00671         fabs(bubble_diff.angular.z) <= tolerance_rot_)
00672     {
00673       if(curr_target_bubble < ((int) elastic_band_.size()) - 1)
00674       {
00675         curr_target_bubble++;
00676         // transform next target bubble into robot-body frame
00677         // and get difference to robot bubble
00678         bubble_diff = getFrame1ToFrame2InRefFrame(elastic_band_.at(0).center.pose, elastic_band_.at(curr_target_bubble).center.pose,
00679             ref_frame_band_);
00680       }
00681       else
00682       {
00683         ROS_DEBUG_THROTTLE_NAMED (1.0, "controller_state",
00684             "Goal reached with distance %.2f, %.2f, %.2f"
00685             "; sending zero velocity",
00686             bubble_diff.linear.x, bubble_diff.linear.y,
00687             bubble_diff.angular.z);
00688         // goal position reached
00689         robot_cmd.linear.x = 0.0;
00690         robot_cmd.linear.y = 0.0;
00691         robot_cmd.angular.z = 0.0;
00692         // reset velocity
00693         last_vel_.linear.x = 0.0;
00694         last_vel_.linear.y = 0.0;
00695         last_vel_.angular.z = 0.0;
00696         goal_reached = true;
00697         break;
00698       }
00699     }
00700 
00701     twist_cmd = robot_cmd;
00702 
00703     return true;
00704   }
00705 
00706 
00707   double EBandTrajectoryCtrl::getBubbleTargetVel(const int& target_bub_num, const std::vector<Bubble>& band, geometry_msgs::Twist& VelDir)
00708   {
00709     // init reference for direction vector
00710     VelDir.linear.x = 0.0;
00711     VelDir.linear.y = 0.0;
00712     VelDir.linear.z = 0.0;
00713     VelDir.angular.x = 0.0;
00714     VelDir.angular.y = 0.0;
00715     VelDir.angular.z = 0.0;
00716 
00717     // if we are looking at the last bubble - target vel is always zero
00718     if(target_bub_num >= ((int) band.size() - 1))
00719       return 0.0;
00720 
00721 
00722     // otherwise check for max_vel calculated from current bubble size
00723     double v_max_curr_bub, v_max_next_bub;
00724     double bubble_distance, angle_to_pseudo_vel, delta_vel_max;
00725     geometry_msgs::Twist bubble_diff;
00726 
00727     // distance for braking s = 0.5*v*v/a
00728     v_max_curr_bub = sqrt(2 * elastic_band_.at(target_bub_num).expansion * acc_max_);
00729 
00730     // get distance to next bubble center
00731     ROS_ASSERT( (target_bub_num >= 0) && ((target_bub_num +1) < (int) band.size()) );
00732     bubble_diff = getFrame1ToFrame2InRefFrame(band.at(target_bub_num).center.pose, band.at(target_bub_num + 1).center.pose,
00733         ref_frame_band_);
00734     angle_to_pseudo_vel = bubble_diff.angular.z * getCircumscribedRadius(*costmap_ros_);
00735 
00736     bubble_distance = sqrt( (bubble_diff.linear.x * bubble_diff.linear.x) + (bubble_diff.linear.y * bubble_diff.linear.y) +
00737         (angle_to_pseudo_vel * angle_to_pseudo_vel) );
00738 
00739     // calculate direction vector - norm of diference
00740     VelDir.linear.x =bubble_diff.linear.x/bubble_distance;
00741     VelDir.linear.y =bubble_diff.linear.y/bubble_distance;
00742     VelDir.angular.z =bubble_diff.angular.z/bubble_distance;
00743 
00744     // if next bubble outside this one we will always be able to break fast enough
00745     if(bubble_distance > band.at(target_bub_num).expansion )
00746       return v_max_curr_bub;
00747 
00748 
00749     // next bubble center inside this bubble - take into account restrictions on next bubble
00750     int next_bub_num = target_bub_num + 1;
00751     geometry_msgs::Twist dummy_twist;
00752     v_max_next_bub = getBubbleTargetVel(next_bub_num, band, dummy_twist); // recursive call
00753 
00754     // if velocity at next bubble bigger (or equal) than our velocity - we are on the safe side
00755     if(v_max_next_bub >= v_max_curr_bub)
00756       return v_max_curr_bub;
00757 
00758 
00759     // otherwise max. allowed vel is next vel + plus possible reduction on the way between the bubble-centers
00760     delta_vel_max = sqrt(2 * bubble_distance * acc_max_);
00761     v_max_curr_bub = v_max_next_bub + delta_vel_max;
00762 
00763     return v_max_curr_bub;
00764   }
00765 
00766 
00767   geometry_msgs::Twist EBandTrajectoryCtrl::getFrame1ToFrame2InRefFrame(const geometry_msgs::Pose& frame1, const geometry_msgs::Pose& frame2, const geometry_msgs::Pose& ref_frame)
00768   {
00769 
00770     geometry_msgs::Pose2D frame1_pose2D, frame2_pose2D, ref_frame_pose2D;
00771     geometry_msgs::Pose2D frame1_pose2D_rf, frame2_pose2D_rf;
00772     geometry_msgs::Twist frame_diff;
00773 
00774     // transform all frames to Pose2d
00775     PoseToPose2D(frame1, frame1_pose2D);
00776     PoseToPose2D(frame2, frame2_pose2D);
00777     PoseToPose2D(ref_frame, ref_frame_pose2D);
00778 
00779     // transform frame1 into ref frame
00780     frame1_pose2D_rf.x = (frame1_pose2D.x - ref_frame_pose2D.x) * cos(ref_frame_pose2D.theta) +
00781       (frame1_pose2D.y - ref_frame_pose2D.y) * sin(ref_frame_pose2D.theta);
00782     frame1_pose2D_rf.y = -(frame1_pose2D.x - ref_frame_pose2D.x) * sin(ref_frame_pose2D.theta) +
00783       (frame1_pose2D.y - ref_frame_pose2D.y) * cos(ref_frame_pose2D.theta);
00784     frame1_pose2D_rf.theta = frame1_pose2D.theta - ref_frame_pose2D.theta;
00785     frame1_pose2D_rf.theta = angles::normalize_angle(frame1_pose2D_rf.theta);
00786     // transform frame2 into ref frame
00787     frame2_pose2D_rf.x = (frame2_pose2D.x - ref_frame_pose2D.x) * cos(ref_frame_pose2D.theta) +
00788       (frame2_pose2D.y - ref_frame_pose2D.y) * sin(ref_frame_pose2D.theta);
00789     frame2_pose2D_rf.y = -(frame2_pose2D.x - ref_frame_pose2D.x) * sin(ref_frame_pose2D.theta) +
00790       (frame2_pose2D.y - ref_frame_pose2D.y) * cos(ref_frame_pose2D.theta);
00791     frame2_pose2D_rf.theta = frame2_pose2D.theta - ref_frame_pose2D.theta;
00792     frame2_pose2D_rf.theta = angles::normalize_angle(frame2_pose2D_rf.theta);
00793 
00794     // get differences
00795     frame_diff.linear.x = frame2_pose2D_rf.x - frame1_pose2D_rf.x;
00796     frame_diff.linear.y = frame2_pose2D_rf.y - frame1_pose2D_rf.y;
00797     frame_diff.linear.z = 0.0;
00798     frame_diff.angular.x = 0.0;
00799     frame_diff.angular.y = 0.0;
00800     frame_diff.angular.z = frame2_pose2D_rf.theta - frame1_pose2D_rf.theta;
00801     // normalize angle
00802     frame_diff.angular.z = angles::normalize_angle(frame_diff.angular.z);
00803 
00804     return frame_diff;
00805   }
00806 
00807   geometry_msgs::Twist EBandTrajectoryCtrl::getFrame1ToFrame2InRefFrameNew(const geometry_msgs::Pose& frame1, const geometry_msgs::Pose& frame2, const geometry_msgs::Pose& ref_frame)
00808   {
00809 
00810     double x1 = frame1.position.x - ref_frame.position.x;
00811     double y1 = frame1.position.y - ref_frame.position.y;
00812     double x2 = frame2.position.x - ref_frame.position.x;
00813     double y2 = frame2.position.y - ref_frame.position.y;
00814     double yaw_ref = tf::getYaw(ref_frame.orientation);
00815 
00816     double x_diff = x2 - x1;
00817     double y_diff = y2 - y1;
00818     double theta_diff = atan2(y_diff, x_diff);
00819 
00820     // Now project this vector on to the reference frame
00821     double rotation = angles::normalize_angle(yaw_ref);
00822     double x_final = x_diff * cos(rotation) + y_diff * sin(rotation);
00823     double y_final = - x_diff * sin(rotation) + y_diff * cos(rotation);
00824 
00825     geometry_msgs::Twist twist_msg;
00826     twist_msg.linear.x = x_final;
00827     twist_msg.linear.y = y_final;
00828     twist_msg.angular.z = angles::normalize_angle(theta_diff - yaw_ref);
00829 
00830     return twist_msg;
00831   }
00832 
00833 
00834   geometry_msgs::Twist EBandTrajectoryCtrl::transformTwistFromFrame1ToFrame2(const geometry_msgs::Twist& curr_twist,
00835       const geometry_msgs::Pose& frame1, const geometry_msgs::Pose& frame2)
00836   {
00837     geometry_msgs::Pose2D frame1_pose2D, frame2_pose2D;
00838     geometry_msgs::Twist tmp_transformed;
00839     double delta_ang;
00840 
00841     tmp_transformed = curr_twist;
00842 
00843     // transform all frames to Pose2d
00844     PoseToPose2D(frame1, frame1_pose2D);
00845     PoseToPose2D(frame2, frame2_pose2D);
00846 
00847     // get orientation diff of frames
00848     delta_ang = frame2_pose2D.theta - frame1_pose2D.theta;
00849     delta_ang = angles::normalize_angle(delta_ang);
00850 
00851     // tranform twist
00852     tmp_transformed.linear.x = curr_twist.linear.x * cos(delta_ang) + curr_twist.linear.y * sin(delta_ang);
00853     tmp_transformed.linear.y = -curr_twist.linear.x * sin(delta_ang) + curr_twist.linear.y * cos(delta_ang);
00854 
00855     return tmp_transformed;
00856   }
00857 
00858 
00859   geometry_msgs::Twist EBandTrajectoryCtrl::limitTwist(const geometry_msgs::Twist& twist)
00860   {
00861     geometry_msgs::Twist res = twist;
00862 
00863     //make sure to bound things by our velocity limits
00864     double lin_overshoot = sqrt(res.linear.x * res.linear.x + res.linear.y * res.linear.y) / max_vel_lin_;
00865     double lin_undershoot = min_vel_lin_ / sqrt(res.linear.x * res.linear.x + res.linear.y * res.linear.y);
00866     if (lin_overshoot > 1.0)
00867     {
00868       res.linear.x /= lin_overshoot;
00869       res.linear.y /= lin_overshoot;
00870       // keep relations
00871       res.angular.z /= lin_overshoot;
00872     }
00873 
00874     //we only want to enforce a minimum velocity if we're not rotating in place
00875     if(lin_undershoot > 1.0)
00876     {
00877       res.linear.x *= lin_undershoot;
00878       res.linear.y *= lin_undershoot;
00879       // we cannot keep relations here for stability reasons
00880     }
00881 
00882     if (fabs(res.angular.z) > max_vel_th_)
00883     {
00884       double scale = max_vel_th_/fabs(res.angular.z);
00885       //res.angular.z = max_vel_th_ * sign(res.angular.z);
00886       res.angular.z *= scale;
00887       // keep relations
00888       res.linear.x *= scale;
00889       res.linear.y *= scale;
00890     }
00891 
00892     if (fabs(res.angular.z) < min_vel_th_) res.angular.z = min_vel_th_ * sign(res.angular.z);
00893     // we cannot keep relations here for stability reasons
00894 
00895     //we want to check for whether or not we're desired to rotate in place
00896     if(sqrt(twist.linear.x * twist.linear.x + twist.linear.y * twist.linear.y) < in_place_trans_vel_)
00897     {
00898       if (fabs(res.angular.z) < min_in_place_vel_th_)
00899         res.angular.z = min_in_place_vel_th_ * sign(res.angular.z);
00900 
00901       res.linear.x = 0.0;
00902       res.linear.y = 0.0;
00903     }
00904 
00905     ROS_DEBUG("Angular command %f", res.angular.z);
00906     return res;
00907   }
00908 
00909 
00910 }


eband_local_planner
Author(s): Christian Connette, Bhaskara Marthi, Piyush Khandelwal
autogenerated on Thu Jun 6 2019 18:50:52