eband_local_planner源码解析(5)eband_trajectory_controller.h/cpp 解读

这两个文件规划速度控制, 核心的两个函数是如下两个: 一个是差分速度控制, 一个是全向速度控制

/**
       * @brief calculates a twist feedforward command from the current band
       * @param refernce to the twist cmd
       */
      // 全向速度控制, 差分速度控制在里面作为一个 if 分支
      bool getTwist(geometry_msgs::Twist& twist_cmd, bool& goal_reached);
      // 差分速度控制
      bool getTwistDifferentialDrive(geometry_msgs::Twist& twist_cmd, bool& goal_reached);

eband_trajectory_controller.h

/*********************************************************************
 *
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2010, Willow Garage, Inc.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of Willow Garage, Inc. nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 * Author: Christian Connette
 *********************************************************************/

#ifndef EBAND_TRAJECTORY_CONTROLLER_H_
#define EBAND_TRAJECTORY_CONTROLLER_H_

#include <ros/ros.h>
#include <ros/assert.h>

// classes which are part of this package
#include <eband_local_planner/conversions_and_types.h>
#include <eband_local_planner/eband_visualization.h>
#include <eband_local_planner/EBandPlannerConfig.h>

// geometry_msg
#include <geometry_msgs/PoseStamped.h>
#include <geometry_msgs/Pose.h>
#include <geometry_msgs/Pose2D.h>
#include <geometry_msgs/Twist.h>

// nav_msgs
#include <nav_msgs/Odometry.h>

// geometry
#include <angles/angles.h>

// PID control library
#include <control_toolbox/pid.h>

namespace eband_local_planner{

  /**
   * @class EBandPlanner
   * @brief Implements the Elastic Band Method for SE2-Manifold (mobile Base)
   */
  class EBandTrajectoryCtrl{

    public:

      /**
       * @brief Default constructor
       */
      EBandTrajectoryCtrl();

      /**
       * @brief Constructs the elastic band object
       * @param name The name to give this instance of the elastic band local planner
       * @param costmap The cost map to use for assigning costs to trajectories
       */
      EBandTrajectoryCtrl(std::string name, costmap_2d::Costmap2DROS* costmap_ros);

      /**
       * @brief  Destructor
       */
      ~EBandTrajectoryCtrl();

      /**
       * @brief Initializes the elastic band class by accesing costmap and loading parameters
       * @param name The name to give this instance of the trajectory planner
       * @param costmap The cost map to use for assigning costs to trajectories
       */
      void initialize(std::string name, costmap_2d::Costmap2DROS* costmap_ros);


      /**
       * @brief Reconfigures the parameters of the planner
       * @param config The dynamic reconfigure configuration
       */
      void reconfigure(EBandPlannerConfig& config);

      /**
       * @brief passes a reference to the eband visualization object which can be used to visualize the band optimization
       * @param pointer to visualization object
       */
      void setVisualization(boost::shared_ptr<EBandVisualization> target_visual);

      /**
       * @brief This sets the elastic_band to the trajectory controller
       * @param reference via which the band is passed
       */
      bool setBand(const std::vector<Bubble>& elastic_band);

      /**
       * @brief This sets the robot velocity as provided by odometry
       * @param reference via which odometry is passed
       */
      // 接收里程计, 读取速度信息
      // eband_local_planner 会订阅 /odom 话题获取里程计
      bool setOdometry(const nav_msgs::Odometry& odometry);

      /**
       * @brief calculates a twist feedforward command from the current band
       * @param refernce to the twist cmd
       */
      // 全向速度控制, 差分速度控制在里面作为一个 if 分支
      bool getTwist(geometry_msgs::Twist& twist_cmd, bool& goal_reached);
      // 差分速度控制
      bool getTwistDifferentialDrive(geometry_msgs::Twist& twist_cmd, bool& goal_reached);

    private:

      // pointer to external objects (do NOT delete object)
      costmap_2d::Costmap2DROS* costmap_ros_; ///<@brief pointer to costmap
      boost::shared_ptr<EBandVisualization> target_visual_; // pointer to visualization object

      control_toolbox::Pid pid_;

      // parameters
      bool differential_drive_hack_;
      double k_p_, k_nu_, ctrl_freq_;
      double acc_max_, virt_mass_;
      double max_vel_lin_, max_vel_th_, min_vel_lin_, min_vel_th_;
      double min_in_place_vel_th_;
      double in_place_trans_vel_;
      double tolerance_trans_, tolerance_rot_;
      double acc_max_trans_, acc_max_rot_;
      double rotation_correction_threshold_; // We'll do rotation correction if we're at least this far from the goal

      // diff drive only parameters
      double bubble_velocity_multiplier_;
      double rotation_threshold_multiplier_;
      bool disallow_hysteresis_;
      bool in_final_goal_turn_;

      // flags
      bool initialized_, band_set_, visualization_;

      // data
      std::vector<Bubble> elastic_band_;
      geometry_msgs::Twist odom_vel_;
      geometry_msgs::Twist last_vel_;
      geometry_msgs::Pose ref_frame_band_;

      ///@brief defines sign of a double
      inline double sign(double n)
      {
        return n < 0.0 ? -1.0 : 1.0;
      }

      /**
       * @brief Transforms Pose of frame 1 and 2 into reference frame and gets difference of frame 1 and 2
       * @param refernce to pose of frame1
       * @param reference to pose of frame2
       * @param reference to pose of reference frame
       * @return vector from frame1 to frame2 in coordinates of the reference frame 
       */
      // 这俩函数差不多, 把坐标系1的速度按参考系转到2, 一个用于全向轮, 一个用于差速轮,其实都差不多
      geometry_msgs::Twist getFrame1ToFrame2InRefFrame(const geometry_msgs::Pose& frame1, const geometry_msgs::Pose& frame2, const geometry_msgs::Pose& ref_frame);
      geometry_msgs::Twist getFrame1ToFrame2InRefFrameNew(const geometry_msgs::Pose& frame1, const geometry_msgs::Pose& frame2, const geometry_msgs::Pose& ref_frame);

      /**
       * @param Transforms twist into a given reference frame
       * @param Twist that shall be transformed
       * @param refernce to pose of frame1
       * @param reference to pose of frame2
       * @return transformed twist
       */
      // 这个函数简单的计算位置差,并直接把差值按 1:1 赋值给速度返回,没有scale
      geometry_msgs::Twist transformTwistFromFrame1ToFrame2(const geometry_msgs::Twist& curr_twist, const geometry_msgs::Pose& frame1,
          const geometry_msgs::Pose& frame2);

      /**
       * @brief limits the twist to the allowed range
       * @param reference to unconstrained twist
       * @return twist in allowed range
       */
      // 阈值控制函数
      geometry_msgs::Twist limitTwist(const geometry_msgs::Twist& twist);

      /**
       * @brief gets the max velocity allowed within this bubble depending on size of the bubble and pose and size of the following bubble
       * @param number of the bubble of interest within the band
       * @param band in which the bubble is in
       * @return absolute value of maximum allowed velocity within this bubble
       */
      double getBubbleTargetVel(const int& target_bub_num, const std::vector<Bubble>& band, geometry_msgs::Twist& VelDir);

  };
};
#endif

eband_trajectory_controller.cpp 主要讲最核心的 getTwist(400行) 和 getTwistDifferentialDrive(200行)

// 差速速度控制
bool EBandTrajectoryCtrl::getTwistDifferentialDrive(
    geometry_msgs::Twist &twist_cmd, bool &goal_reached) {
  goal_reached = false;

  geometry_msgs::Twist robot_cmd, bubble_diff;
  robot_cmd.linear.x = 0.0;
  robot_cmd.angular.z = 0.0;

  bool command_provided = false;

  // check if plugin initialized
  if (!initialized_) {
    ROS_ERROR("Requesting feedforward command from not initialized planner. "
              "Please call initialize() before using this planner");
    return false;
  }

  // check there is a plan at all (minimum 1 frame in this case, as robot + goal
  // = plan)
  // 路径太少则退出, 说明差不多到终点了或者路径规划错误
  if ((!band_set_) || (elastic_band_.size() < 2)) {
    ROS_WARN("Requesting feedforward command from empty band.");
    return false;
  }

  // Get the differences between the first 2 bubbles in the robot's frame
  // 获取机器人当前位姿到规划路径上即将到达的下一个点的距离向量, 是个Twist
  bubble_diff = getFrame1ToFrame2InRefFrameNew(elastic_band_.at(0).center.pose,
                                               elastic_band_.at(1).center.pose,
                                               elastic_band_.at(0).center.pose);

  float distance_from_goal = -1.0f;

  // Check 1 快到终点的情况
  // We need to check if we are within the threshold of the final destination
  if (!command_provided) {
    int curr_target_bubble = 1;

    while (curr_target_bubble < ((int)elastic_band_.size()) - 1) {
      curr_target_bubble++;
      bubble_diff = getFrame1ToFrame2InRefFrameNew(
          elastic_band_.at(0).center.pose,
          elastic_band_.at(curr_target_bubble).center.pose,
          elastic_band_.at(0).center.pose);
    }

    // if you go past tolerance, then try to get closer again
    if (!disallow_hysteresis_) {
      if (fabs(bubble_diff.linear.x) > tolerance_trans_ ||
          fabs(bubble_diff.linear.y) > tolerance_trans_) {
        in_final_goal_turn_ = false;
      }
    }

    // Get the differences between the first 2 bubbles in the robot's frame
    int goal_bubble = (((int)elastic_band_.size()) - 1);

    // 计算当前位姿与局部地图下最远的目标点(截断全局路径的最后一个元素)的距离
    bubble_diff = getFrame1ToFrame2InRefFrameNew(
        elastic_band_.at(0).center.pose,
        elastic_band_.at(goal_bubble).center.pose,
        elastic_band_.at(0).center.pose);

    distance_from_goal = sqrtf(bubble_diff.linear.x * bubble_diff.linear.x +
                               bubble_diff.linear.y * bubble_diff.linear.y);

    // Get closer to the goal than the tolerance requires before starting the
    // final turn. The final turn may cause you to move slightly out of
    // position
    if ((fabs(bubble_diff.linear.x) <= 0.6 * tolerance_trans_ &&
         fabs(bubble_diff.linear.y) <= 0.6 * tolerance_trans_) ||
        in_final_goal_turn_) {
      // Calculate orientation difference to goal orientation (not captured in
      // bubble_diff)
      double robot_yaw =
          tf2::getYaw(elastic_band_.at(0).center.pose.orientation);
      double goal_yaw =
          tf2::getYaw(elastic_band_.at((int)elastic_band_.size() - 1)
                          .center.pose.orientation);
      float orientation_diff = angles::normalize_angle(goal_yaw - robot_yaw);

      // 如果当前点与目标点方向角大于设置的旋转允差,那么原地旋转直到角度小于允差为止
      if (fabs(orientation_diff) > tolerance_rot_) {
        in_final_goal_turn_ = true;
        ROS_DEBUG("Performing in place rotation for goal (diff): %f",
                  orientation_diff);
        double rotation_sign = -2 * (orientation_diff < 0) + 1;
        robot_cmd.angular.z =
            rotation_sign * min_in_place_vel_th_ + k_p_ * orientation_diff;
        if (fabs(robot_cmd.angular.z) > max_vel_th_) { // limit max rotation
          robot_cmd.angular.z = rotation_sign * max_vel_th_;
        }
      } else {
        in_final_goal_turn_ = false; // Goal reached
        // goal position reached
        robot_cmd.linear.x = 0.0;
        robot_cmd.angular.z = 0.0;
        goal_reached = true;
      }
      command_provided = true;
    }
  }

  // 2.未到终点的情况

  // Get the differences between the first 2 bubbles in the robot's frame
  bubble_diff = getFrame1ToFrame2InRefFrameNew(elastic_band_.at(0).center.pose,
                                               elastic_band_.at(1).center.pose,
                                               elastic_band_.at(0).center.pose);

  // Check 2 - check if the robot's current pose is too misaligned with the next
  // bubble
  // 检查是否需要旋转
  if (!command_provided) {
    ROS_DEBUG(
        "Goal has not been reached, performing checks to move towards goal");

    // calculate an estimate of the in-place rotation threshold
    // 到下一个泡泡的距离,即到规划路径上下一个点的距离
    double distance_to_next_bubble =
        sqrt(bubble_diff.linear.x * bubble_diff.linear.x +
             bubble_diff.linear.y * bubble_diff.linear.y);
    
    // 注意, 这里代码写死了 0.7 倍的泡泡半径, 有需要可以调调看看效果
    // 这个 expansion 在 eband_local_planner 中由 costmap 计算的到障碍物的距离转化得来
    double radius_of_next_bubble = 0.7 * elastic_band_.at(1).expansion;

    // 这里定义了一个原地旋转阈值
    // rotation_threshold_multiplier_ 作为参数在 yaml 文件中配置
    // 把这个参数调大可以增大旋转阈值
    // 这样机器人更倾向于边走边转, 而不是原地旋转到正确方向再走
    // 调大这个参数就有一点儿全向移动那味儿了
    double in_place_rotation_threshold =
        rotation_threshold_multiplier_ *
        fabs(atan2(radius_of_next_bubble, distance_to_next_bubble));
    ROS_DEBUG("In-place rotation threshold: %f(%f,%f)",
              in_place_rotation_threshold, radius_of_next_bubble,
              distance_to_next_bubble);

    // check if we are above this threshold, if so then perform in-place
    // rotation
    // 当前泡泡(位姿)到下一个泡泡的角度差大于原地旋转阈值, 则原地旋转
    if (fabs(bubble_diff.angular.z) > in_place_rotation_threshold) {
   

      robot_cmd.angular.z = k_p_ * bubble_diff.angular.z;
      double rotation_sign = (bubble_diff.angular.z < 0) ? -1.0 : +1.0;
      if (fabs(robot_cmd.angular.z) < min_in_place_vel_th_) {
        robot_cmd.angular.z = rotation_sign * min_in_place_vel_th_;
      }
      if (fabs(robot_cmd.angular.z) > max_vel_th_) { // limit max rotation
        robot_cmd.angular.z = rotation_sign * max_vel_th_;
      }
      ROS_DEBUG(
          "Performing in place rotation for start (diff): %f with rot vel: %f",
          bubble_diff.angular.z, robot_cmd.angular.z);
      command_provided = true;
    } else {
      ROS_DEBUG("Don't need in-place rotation!");
    }
  }

  // Check 3 - If we reach here, it means we need to use our PID controller to
  // move towards the next bubble
  // 以下是用 pid 计算速度的操作
  if (!command_provided) {

    // Select a linear velocity (based on the current bubble radius)
    double forward_sign = -2 * (bubble_diff.linear.x < 0) + 1;
    double bubble_radius = 0.7 * elastic_band_.at(0).expansion;
    double velocity_multiplier = bubble_velocity_multiplier_ * bubble_radius;

    double max_vel_lin = max_vel_lin_;
    if (distance_from_goal < 0.75f) {
      max_vel_lin = (max_vel_lin < 0.3) ? 0.15 : max_vel_lin / 2;
      // 这里可以改成直接除2, 下面作者又会乘2, 所以不超过最大速度, 
      // 但最小速度可以小于 0.15
      // max_vel_lin = max_vel_lin / 2;
    }

    // 差速控制比较简单
    // 线速度由于只有x 方向, 直接置成最大线速度
    double linear_velocity = velocity_multiplier * max_vel_lin;

    linear_velocity *= cos(bubble_diff.angular.z); // decrease while turning
    if (fabs(linear_velocity) > max_vel_lin_) {
      linear_velocity = forward_sign * max_vel_lin_;
    } else if (fabs(linear_velocity) < min_vel_lin_) {
      linear_velocity = forward_sign * min_vel_lin_;
    }

    // Select an angular velocity (based on PID controller)
    
    double error = bubble_diff.angular.z;
    double rotation_sign = -2 * (bubble_diff.angular.z < 0) + 1;

    // 角速度由位姿差给出
    double angular_velocity = k_p_ * error;
    if (fabs(angular_velocity) > max_vel_th_) {
      angular_velocity = rotation_sign * max_vel_th_;
    } else if (fabs(angular_velocity) < min_vel_th_) {
      angular_velocity = rotation_sign * min_vel_th_;
    }

    ROS_DEBUG("Selected velocity: lin: %f, ang: %f", linear_velocity,
              angular_velocity);

    robot_cmd.linear.x = linear_velocity;
    robot_cmd.angular.z = angular_velocity;
    command_provided = true;
  }

  twist_cmd = robot_cmd;
  ROS_DEBUG("Final command: %f, %f", twist_cmd.linear.x, twist_cmd.angular.z);
  return true;
}
// 总的速度控制器
bool EBandTrajectoryCtrl::getTwist(geometry_msgs::Twist &twist_cmd,
                                   bool &goal_reached) {
  goal_reached = false;
  if (differential_drive_hack_) {
    // 调用差速控制器
    return getTwistDifferentialDrive(twist_cmd, goal_reached);
  }

  // 以下则为全向速度控制

  // init twist cmd to be handed back to caller
  geometry_msgs::Twist robot_cmd, bubble_diff, control_deviation;
  robot_cmd.linear.x = 0.0;
  robot_cmd.linear.y = 0.0;
  robot_cmd.linear.z = 0.0;
  robot_cmd.angular.x = 0.0;
  robot_cmd.angular.y = 0.0;
  robot_cmd.angular.z = 0.0;

  // make sure command vector is clean
  twist_cmd = robot_cmd;
  control_deviation = robot_cmd;

  // check if plugin initialized
  if (!initialized_) {
    ROS_ERROR("Requesting feedforward command from not initialized planner. "
              "Please call initialize() before using this planner");
    return false;
  }

  // check there is a plan at all (minimum 1 frame in this case, as robot + goal
  // = plan)
  if ((!band_set_) || (elastic_band_.size() < 2)) {
    ROS_WARN("Requesting feedforward command from empty band.");
    return false;
  }

  // calc intersection of bubble-radius with sequence of vector connecting the
  // bubbles

  // get distance to target from bubble-expansion
  double scaled_radius = 0.7 * elastic_band_.at(0).expansion;

  // get difference and distance between bubbles in odometry frame
  double bubble_distance, ang_pseudo_dist;
  bubble_diff = getFrame1ToFrame2InRefFrame(elastic_band_.at(0).center.pose,
                                            elastic_band_.at(1).center.pose,
                                            ref_frame_band_);
  // 这里会计算一个伪距离, diff_z*机器人外接圆半径(乘积是一段弧长)
  ang_pseudo_dist =
      bubble_diff.angular.z * getCircumscribedRadius(*costmap_ros_);
  bubble_distance = sqrt((bubble_diff.linear.x * bubble_diff.linear.x) +
                         (bubble_diff.linear.y * bubble_diff.linear.y) +
                         (ang_pseudo_dist * ang_pseudo_dist));

  if (visualization_) {
    target_visual_->publishBubble("ctrl_target", 1, target_visual_->blue,
                                  elastic_band_.at(0));
    target_visual_->publishBubble("ctrl_target", 2, target_visual_->blue,
                                  elastic_band_.at(1));
  }

  // by default our control deviation is the difference between the bubble
  // centers
  double abs_ctrl_dev;
  control_deviation = bubble_diff;

  // diff_z*机器人外接圆半径(一段弧长)
  ang_pseudo_dist =
      control_deviation.angular.z * getCircumscribedRadius(*costmap_ros_);
  // 点到点距离+diff_z*机器人外接圆半径(一段弧长)
  abs_ctrl_dev =
      sqrt((control_deviation.linear.x * control_deviation.linear.x) +
           (control_deviation.linear.y * control_deviation.linear.y) +
           (ang_pseudo_dist * ang_pseudo_dist));

  // yet depending on the expansion of our bubble we might want to adapt this
  // point
  // 如果放缩的泡泡半径 < 到下一个泡泡的距离
  // 说明相邻两个泡泡间隔较大,重叠部分较小
  if (scaled_radius < bubble_distance) {
    // ROS_INFO("scaled_radius < bubble_distance");
    // triviale case - simply scale bubble_diff
    // scale_difference < 1
    double scale_difference = scaled_radius / bubble_distance;
    // 两个泡泡间隔较大,重叠部分较小,说明路径两个点间距离较大
    // 故减小速度, 以应对突发情况
    bubble_diff.linear.x *= scale_difference;
    bubble_diff.linear.y *= scale_difference;
    bubble_diff.angular.z *= scale_difference;
    // set controls
    // control_deviation 这个很关键, 参与速度cmd计算
    control_deviation = bubble_diff;
  }

  // if scaled_radius = bubble_distance -- we have nothing to do at all

  // 如果放缩的泡泡半径 > 到下一个泡泡的距离
  // 说明相邻两个泡泡间隔较小,重叠部分较大
  if (scaled_radius > bubble_distance) {
   //  ROS_INFO("scaled_radius > bubble_distance, check next bubble");
    // o.k. now we have to do a little bit more -> check next but one bubble
    if (elastic_band_.size() > 2) {
      // get difference between next and next but one bubble
      double next_bubble_distance;

      // 计算当前点往后走两个泡泡的距离
      geometry_msgs::Twist next_bubble_diff;
      next_bubble_diff = getFrame1ToFrame2InRefFrame(
          elastic_band_.at(1).center.pose, elastic_band_.at(2).center.pose,
          ref_frame_band_);
      ang_pseudo_dist =
          next_bubble_diff.angular.z * getCircumscribedRadius(*costmap_ros_);
      next_bubble_distance =
          sqrt((next_bubble_diff.linear.x * next_bubble_diff.linear.x) +
               (next_bubble_diff.linear.y * next_bubble_diff.linear.y) +
               (ang_pseudo_dist * ang_pseudo_dist));

      // 如果放缩的泡泡半径 > 到下两个泡泡的距离
      // 说明相邻两个泡泡间隔较小,重叠部分很大
      // 即一个单位的放缩的泡泡半径可以覆盖到两个泡泡,泡泡间隔非常紧
      if (scaled_radius > (bubble_distance + next_bubble_distance)) {
        // ROS_INFO("scaled_radius > bubble_distance + next_bubble_distance");
        // we should normally not end up here - but just to be sure
        // 适当增大速度,要不然太小了
        control_deviation.linear.x =
            bubble_diff.linear.x + next_bubble_diff.linear.x;
        control_deviation.linear.y =
            bubble_diff.linear.y + next_bubble_diff.linear.y;
        control_deviation.angular.z =
            bubble_diff.angular.z + next_bubble_diff.angular.z;
        // done
        if (visualization_)
          target_visual_->publishBubble("ctrl_target", 3, target_visual_->red,
                                        elastic_band_.at(2));
      } else {
       //  ROS_INFO("scaled_radius < bubble_distance + next_bubble_distance");
        // 如果 到下一个泡泡的距离 < 放缩的泡泡半径 < 到下两个泡泡的距离
        if (visualization_) {
          target_visual_->publishBubble("ctrl_target", 3, target_visual_->red,
                                        elastic_band_.at(2));
        }

        // we want to calculate intersection point of bubble ...
        // ... and vector connecting the following bubbles
        double b_distance, cosine_at_bub;
        double vec_prod, norm_vec1, norm_vec2;
        double ang_pseudo_dist1, ang_pseudo_dist2;

        // get distance between next bubble center and intersection point
        ang_pseudo_dist1 =
            bubble_diff.angular.z * getCircumscribedRadius(*costmap_ros_);
        ang_pseudo_dist2 =
            next_bubble_diff.angular.z * getCircumscribedRadius(*costmap_ros_);
        // careful! - we need this sign because of the direction of the vectors
        // and the definition of the vector-product
        // 注意这里有个负号
        vec_prod = -((bubble_diff.linear.x * next_bubble_diff.linear.x) +
                     (bubble_diff.linear.y * next_bubble_diff.linear.y) +
                     (ang_pseudo_dist1 * ang_pseudo_dist2));

        // 到第一个泡泡中心的距离(加了一个伪弧长)
        norm_vec1 = sqrt((bubble_diff.linear.x * bubble_diff.linear.x) +
                         (bubble_diff.linear.y * bubble_diff.linear.y) +
                         (ang_pseudo_dist1 * ang_pseudo_dist1));

        // 第一个泡泡中心到第二个泡泡中心的距离(加了一个伪弧长)
        norm_vec2 =
            sqrt((next_bubble_diff.linear.x * next_bubble_diff.linear.x) +
                 (next_bubble_diff.linear.y * next_bubble_diff.linear.y) +
                 (ang_pseudo_dist2 * ang_pseudo_dist2));

        // reform the cosine-rule
        // 这里用的某个规则重置 cos
        cosine_at_bub = vec_prod / norm_vec1 / norm_vec2;
        b_distance = bubble_distance * cosine_at_bub +
                     sqrt(scaled_radius * scaled_radius -
                          bubble_distance * bubble_distance *
                              (1.0 - cosine_at_bub * cosine_at_bub));

        // get difference vector from next_bubble to intersection point
        // 这里计算了一个放缩因子
        double scale_next_difference = b_distance / next_bubble_distance;
        next_bubble_diff.linear.x *= scale_next_difference;
        next_bubble_diff.linear.y *= scale_next_difference;
        next_bubble_diff.angular.z *= scale_next_difference;

        // and finally get the control deviation
        control_deviation.linear.x =
            bubble_diff.linear.x + next_bubble_diff.linear.x;
        control_deviation.linear.y =
            bubble_diff.linear.y + next_bubble_diff.linear.y;
        control_deviation.angular.z =
            bubble_diff.angular.z + next_bubble_diff.angular.z;
        // done
      }
    }
  }

  // plot control deviation
  ang_pseudo_dist =
      control_deviation.angular.z * getCircumscribedRadius(*costmap_ros_);
  abs_ctrl_dev =
      sqrt((control_deviation.linear.x * control_deviation.linear.x) +
           (control_deviation.linear.y * control_deviation.linear.y) +
           (ang_pseudo_dist * ang_pseudo_dist));

  if (visualization_) {
    // compose bubble from ctrl-target
    geometry_msgs::Pose2D tmp_bubble_2d, curr_bubble_2d;
    geometry_msgs::Pose tmp_pose;
    // init bubble for visualization
    Bubble new_bubble = elastic_band_.at(0);
    PoseToPose2D(elastic_band_.at(0).center.pose, curr_bubble_2d);
    tmp_bubble_2d.x = curr_bubble_2d.x + control_deviation.linear.x;
    tmp_bubble_2d.y = curr_bubble_2d.y + control_deviation.linear.y;
    tmp_bubble_2d.theta = curr_bubble_2d.theta + control_deviation.angular.z;
    Pose2DToPose(tmp_pose, tmp_bubble_2d);
    new_bubble.center.pose = tmp_pose;
    new_bubble.expansion = 0.1; // just draw a small bubble
    target_visual_->publishBubble("ctrl_target", 0, target_visual_->red,
                                  new_bubble);
  }


  // 这里计算当前位姿到终点还有多远
  const geometry_msgs::Point &goal =
      (--elastic_band_.end())->center.pose.position;
  const double dx = elastic_band_.at(0).center.pose.position.x - goal.x;
  const double dy = elastic_band_.at(0).center.pose.position.y - goal.y;
  const double dist_to_goal = sqrt(dx * dx + dy * dy);

  // Assuming we're far enough from the final goal, make sure to rotate so
  // we're facing the right way

  // 如果到最终目标点距离大于阈值,出发前要校正方向
  // 即使是全向轮,也要校正方向
  // 调大这个阈值, 只要在阈值范围内, 规划出来的路径可以全向移动!
  if (dist_to_goal > rotation_correction_threshold_) {

    const double angular_diff =
        angularDiff(control_deviation, elastic_band_.at(0).center.pose);
    const double vel =
        pid_.computeCommand(angular_diff, ros::Duration(1 / ctrl_freq_));
    const double mult = fabs(vel) > max_vel_th_ ? max_vel_th_ / fabs(vel) : 1.0;
    control_deviation.angular.z = vel * mult;
    const double abs_vel = fabs(control_deviation.angular.z);

    ROS_DEBUG_THROTTLE_NAMED(1.0, "angle_correction",
                             "Angular diff is %.2f and desired angular "
                             "vel is %.2f.  Initial translation velocity "
                             "is %.2f, %.2f",
                             angular_diff, control_deviation.angular.z,
                             control_deviation.linear.x,
                             control_deviation.linear.y);

    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
    control_deviation.linear.x *= trans_mult;
    control_deviation.linear.y *= trans_mult;
    ROS_DEBUG_THROTTLE_NAMED(1.0, "angle_correction",
                             "Translation multiplier is %.2f and scaled "
                             "translational velocity is %.2f, %.2f",
                             trans_mult, control_deviation.linear.x,
                             control_deviation.linear.y);
  } else
    ROS_DEBUG_THROTTLE_NAMED(1.0, "angle_correction",
                             "Not applying angle correction because "
                             "distance to goal is %.2f",
                             dist_to_goal);

  // 以下主要是通过 Pid 控制速度的 scale 以及连续性

  // now the actual control procedure start (using attractive Potentials)
  geometry_msgs::Twist desired_velocity, currbub_maxvel_dir;
  double desvel_abs, desvel_abs_trans, currbub_maxvel_abs;
  double scale_des_vel;
  desired_velocity = robot_cmd;
  currbub_maxvel_dir = robot_cmd;

  // calculate "equilibrium velocity" (Khatib86 - Realtime Obstacle Avoidance)
  // k_p_, k_nu_ 分别是参数文件里的 k_prop 和 k_damp
  desired_velocity.linear.x = k_p_ / k_nu_ * control_deviation.linear.x;
  desired_velocity.linear.y = k_p_ / k_nu_ * control_deviation.linear.y;
  desired_velocity.angular.z = k_p_ / k_nu_ * control_deviation.angular.z;

  // robot_cmd = desired_velocity;

  // get max vel for current bubble
  int curr_bub_num = 0;
  currbub_maxvel_abs =
      getBubbleTargetVel(curr_bub_num, elastic_band_, currbub_maxvel_dir);

  // if neccessarry scale desired vel to stay lower than currbub_maxvel_abs
  ang_pseudo_dist =
      desired_velocity.angular.z * getCircumscribedRadius(*costmap_ros_);
  desvel_abs = sqrt((desired_velocity.linear.x * desired_velocity.linear.x) +
                    (desired_velocity.linear.y * desired_velocity.linear.y) +
                    (ang_pseudo_dist * ang_pseudo_dist));
  if (desvel_abs > currbub_maxvel_abs) {
    scale_des_vel = currbub_maxvel_abs / desvel_abs;
    desired_velocity.linear.x *= scale_des_vel;
    desired_velocity.linear.y *= scale_des_vel;
    desired_velocity.angular.z *= scale_des_vel;
  }

  // make sure to stay within velocity bounds for the robot
  desvel_abs_trans =
      sqrt((desired_velocity.linear.x * desired_velocity.linear.x) +
           (desired_velocity.linear.y * desired_velocity.linear.y));
  // for translation
  if (desvel_abs_trans > max_vel_lin_) {
    scale_des_vel = max_vel_lin_ / desvel_abs_trans;
    desired_velocity.linear.x *= scale_des_vel;
    desired_velocity.linear.y *= scale_des_vel;
    // to make sure we are staying inside the bubble also scale rotation
    desired_velocity.angular.z *= scale_des_vel;
  }

  // for rotation
  if (fabs(desired_velocity.angular.z) > max_vel_th_) {
    scale_des_vel = max_vel_th_ / fabs(desired_velocity.angular.z);
    desired_velocity.angular.z *= scale_des_vel;
    // to make sure we are staying inside the bubble also scale translation
    desired_velocity.linear.x *= scale_des_vel;
    desired_velocity.linear.y *= scale_des_vel;
  }

  // calculate resulting force (accel. resp.) (Khatib86 - Realtime Obstacle
  // Avoidance)
  geometry_msgs::Twist acc_desired;
  acc_desired = robot_cmd;
  acc_desired.linear.x = (1.0 / virt_mass_) * k_nu_ *
                         (desired_velocity.linear.x - last_vel_.linear.x);
  acc_desired.linear.y = (1.0 / virt_mass_) * k_nu_ *
                         (desired_velocity.linear.y - last_vel_.linear.y);
  acc_desired.angular.z = (1.0 / virt_mass_) * k_nu_ *
                          (desired_velocity.angular.z - last_vel_.angular.z);

  // constrain acceleration
  double scale_acc;
  double abs_acc_trans = sqrt((acc_desired.linear.x * acc_desired.linear.x) +
                              (acc_desired.linear.y * acc_desired.linear.y));
  if (abs_acc_trans > acc_max_trans_) {
    scale_acc = acc_max_trans_ / abs_acc_trans;
    acc_desired.linear.x *= scale_acc;
    acc_desired.linear.y *= scale_acc;
    // again - keep relations - stay in bubble
    acc_desired.angular.z *= scale_acc;
  }

  if (fabs(acc_desired.angular.z) > acc_max_rot_) {
    scale_acc = fabs(acc_desired.angular.z) / acc_max_rot_;
    acc_desired.angular.z *= scale_acc;
    // again - keep relations - stay in bubble
    acc_desired.linear.x *= scale_acc;
    acc_desired.linear.y *= scale_acc;
  }

  // and get velocity-cmds by integrating them over one time-step
  last_vel_.linear.x = last_vel_.linear.x + acc_desired.linear.x / ctrl_freq_;
  last_vel_.linear.y = last_vel_.linear.y + acc_desired.linear.y / ctrl_freq_;
  last_vel_.angular.z =
      last_vel_.angular.z + acc_desired.angular.z / ctrl_freq_;

  // we are almost done now take into accoun stick-slip and similar nasty things

  // last checks - limit current twist cmd (upper and lower bounds)
  last_vel_ = limitTwist(last_vel_);

  // finally set robot_cmd (to non-zero value)
  robot_cmd = last_vel_;

  // now convert into robot-body frame
  robot_cmd = transformTwistFromFrame1ToFrame2(robot_cmd, ref_frame_band_,
                                               elastic_band_.at(0).center.pose);

  // check whether we reached the end of the band
  // 快到终点了
  int curr_target_bubble = 1;
  while (fabs(bubble_diff.linear.x) <= tolerance_trans_ &&
         fabs(bubble_diff.linear.y) <= tolerance_trans_ &&
         fabs(bubble_diff.angular.z) <= tolerance_rot_) {
    if (curr_target_bubble < ((int)elastic_band_.size()) - 1) {
      curr_target_bubble++;
      // transform next target bubble into robot-body frame
      // and get difference to robot bubble
      bubble_diff = getFrame1ToFrame2InRefFrame(
          elastic_band_.at(0).center.pose,
          elastic_band_.at(curr_target_bubble).center.pose, ref_frame_band_);
    } else {
      ROS_DEBUG_THROTTLE_NAMED(1.0, "controller_state",
                               "Goal reached with distance %.2f, %.2f, %.2f"
                               "; sending zero velocity",
                               bubble_diff.linear.x, bubble_diff.linear.y,
                               bubble_diff.angular.z);

      // goal position reached
      robot_cmd.linear.x = 0.0;
      robot_cmd.linear.y = 0.0;
      robot_cmd.angular.z = 0.0;
      // reset velocity
      last_vel_.linear.x = 0.0;
      last_vel_.linear.y = 0.0;
      last_vel_.angular.z = 0.0;
      goal_reached = true;
      break;
    }
  }
  
  twist_cmd = robot_cmd;

  return true;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zjyspeed

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值