Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
theta_star_planner.cpp
1 // Copyright 2020 Anshumaan Singh
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <vector>
16 #include <memory>
17 #include <string>
18 
19 #include "geometry_msgs/msg/pose_stamped.hpp"
20 #include "rclcpp/rclcpp.hpp"
21 #include "nav2_ros_common/tf2_factories.hpp"
22 
23 #include "nav2_theta_star_planner/theta_star_planner.hpp"
24 #include "nav2_theta_star_planner/theta_star.hpp"
25 
26 namespace nav2_theta_star_planner
27 {
29  const nav2::LifecycleNode::WeakPtr & parent,
30  std::string name, nav2::TransformBuffer::SharedPtr tf,
31  std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
32 {
33  parent_node_ = parent;
34  auto node = parent_node_.lock();
35  logger_ = node->get_logger();
36  clock_ = node->get_clock();
37  name_ = name;
38  tf_ = tf;
39  global_frame_ = costmap_ros->getGlobalFrameID();
40 
41  // Handles storage and dynamic configuration of parameters.
42  // Returns pointer to data current param settings.
43  param_handler_ = std::make_unique<ParameterHandler>(
44  node, name_, logger_);
45  params_ = param_handler_->getParams();
46  planner_ = std::make_unique<ThetaStar>(params_);
47  planner_->costmap_ = costmap_ros->getCostmap();
48 }
49 
51 {
52  RCLCPP_INFO(logger_, "CleaningUp plugin %s of type nav2_theta_star_planner", name_.c_str());
53  planner_.reset();
54 }
55 
57 {
58  RCLCPP_INFO(logger_, "Activating plugin %s of type nav2_theta_star_planner", name_.c_str());
59  param_handler_->activate();
60 }
61 
63 {
64  RCLCPP_INFO(logger_, "Deactivating plugin %s of type nav2_theta_star_planner", name_.c_str());
65  auto node = parent_node_.lock();
66  param_handler_->deactivate();
67 }
68 
69 nav_msgs::msg::Path ThetaStarPlanner::createPlan(
70  const geometry_msgs::msg::PoseStamped & start,
71  const geometry_msgs::msg::PoseStamped & goal,
72  const std::vector<geometry_msgs::msg::PoseStamped> & viapoints,
73  std::function<bool()> cancel_checker)
74 {
75  if (!viapoints.empty()) {
76  RCLCPP_WARN(logger_, "Received %zu viapoints, but this planner ignores them",
77  viapoints.size());
78  }
79 
80  std::lock_guard<std::mutex> lock_reinit(param_handler_->getMutex());
81  nav_msgs::msg::Path global_path;
82  auto start_time = std::chrono::steady_clock::now();
83 
84  std::unique_lock<nav2_costmap_2d::Costmap2D::mutex_t> lock(*(planner_->costmap_->getMutex()));
85 
86  // Corner case of start and goal being on the same cell
87  unsigned int mx_start, my_start, mx_goal, my_goal;
88  if (!planner_->costmap_->worldToMap(
89  start.pose.position.x, start.pose.position.y, mx_start, my_start))
90  {
92  "Start Coordinates of(" + std::to_string(start.pose.position.x) + ", " +
93  std::to_string(start.pose.position.y) + ") was outside bounds");
94  }
95 
96  if (!planner_->costmap_->worldToMap(
97  goal.pose.position.x, goal.pose.position.y, mx_goal, my_goal))
98  {
100  "Goal Coordinates of(" + std::to_string(goal.pose.position.x) + ", " +
101  std::to_string(goal.pose.position.y) + ") was outside bounds");
102  }
103 
104  if (planner_->costmap_->getCost(mx_goal, my_goal) == nav2_costmap_2d::LETHAL_OBSTACLE) {
106  "Goal Coordinates of(" + std::to_string(goal.pose.position.x) + ", " +
107  std::to_string(goal.pose.position.y) + ") was in lethal cost");
108  }
109 
110  if (mx_start == mx_goal && my_start == my_goal) {
111  global_path.header.stamp = clock_->now();
112  global_path.header.frame_id = global_frame_;
113  geometry_msgs::msg::PoseStamped pose;
114  pose.header = global_path.header;
115  pose.pose.position.z = 0.0;
116 
117  pose.pose = start.pose;
118  // if we have a different start and goal orientation, set the unique path pose to the goal
119  // orientation, unless use_final_approach_orientation=true where we need it to be the start
120  // orientation to avoid movement from the local planner
121  if (start.pose.orientation != goal.pose.orientation &&
122  !params_->use_final_approach_orientation)
123  {
124  pose.pose.orientation = goal.pose.orientation;
125  }
126  global_path.poses.push_back(pose);
127  return global_path;
128  }
129 
130  planner_->clearStart();
131  planner_->setStartAndGoal(start, goal);
132  RCLCPP_DEBUG(
133  logger_, "Got the src and dst... (%i, %i) && (%i, %i)",
134  planner_->src_.x, planner_->src_.y, planner_->dst_.x, planner_->dst_.y);
135  getPlan(global_path, cancel_checker);
136  // check if a plan is generated
137  size_t plan_size = global_path.poses.size();
138  if (plan_size > 0) {
139  global_path.poses.back().pose.orientation = goal.pose.orientation;
140  }
141 
142  // If use_final_approach_orientation=true, interpolate the last pose orientation from the
143  // previous pose to set the orientation to the 'final approach' orientation of the robot so
144  // it does not rotate.
145  // And deal with corner case of plan of length 1
146  if (params_->use_final_approach_orientation) {
147  if (plan_size == 1) {
148  global_path.poses.back().pose.orientation = start.pose.orientation;
149  } else if (plan_size > 1) {
150  double dx, dy, theta;
151  auto last_pose = global_path.poses.back().pose.position;
152  auto approach_pose = global_path.poses[plan_size - 2].pose.position;
153  dx = last_pose.x - approach_pose.x;
154  dy = last_pose.y - approach_pose.y;
155  theta = atan2(dy, dx);
156  global_path.poses.back().pose.orientation =
157  nav2_util::geometry_utils::orientationAroundZAxis(theta);
158  }
159  }
160 
161  auto stop_time = std::chrono::steady_clock::now();
162  auto dur = std::chrono::duration_cast<std::chrono::microseconds>(stop_time - start_time);
163  RCLCPP_DEBUG(logger_, "the time taken is : %i", static_cast<int>(dur.count()));
164  RCLCPP_DEBUG(logger_, "the nodes_opened are: %i", planner_->nodes_opened);
165  return global_path;
166 }
167 
169  nav_msgs::msg::Path & global_path,
170  std::function<bool()> cancel_checker)
171 {
172  std::vector<coordsW> path;
173  if (planner_->isUnsafeToPlan()) {
174  global_path.poses.clear();
175  throw nav2_core::PlannerException("Either of the start or goal pose are an obstacle! ");
176  } else if (planner_->generatePath(path, cancel_checker)) {
177  global_path = linearInterpolation(path, planner_->costmap_->getResolution());
178  } else {
179  global_path.poses.clear();
180  throw nav2_core::NoValidPathCouldBeFound("Could not generate path between the given poses");
181  }
182  global_path.header.stamp = clock_->now();
183  global_path.header.frame_id = global_frame_;
184 }
185 
187  const std::vector<coordsW> & raw_path,
188  const double & dist_bw_points)
189 {
190  nav_msgs::msg::Path pa;
191 
192  geometry_msgs::msg::PoseStamped p1;
193  for (unsigned int j = 0; j < raw_path.size() - 1; j++) {
194  coordsW pt1 = raw_path[j];
195  p1.pose.position.x = pt1.x;
196  p1.pose.position.y = pt1.y;
197  pa.poses.push_back(p1);
198 
199  coordsW pt2 = raw_path[j + 1];
200  double distance = std::hypot(pt2.x - pt1.x, pt2.y - pt1.y);
201  int loops = static_cast<int>(distance / dist_bw_points);
202  double sin_alpha = (pt2.y - pt1.y) / distance;
203  double cos_alpha = (pt2.x - pt1.x) / distance;
204  for (int k = 1; k < loops; k++) {
205  p1.pose.position.x = pt1.x + k * dist_bw_points * cos_alpha;
206  p1.pose.position.y = pt1.y + k * dist_bw_points * sin_alpha;
207  pa.poses.push_back(p1);
208  }
209  }
210 
211  return pa;
212 }
213 
214 } // namespace nav2_theta_star_planner
215 
216 #include "pluginlib/class_list_macros.hpp"
Abstract interface for global planners to adhere to with pluginlib.
nav_msgs::msg::Path createPlan(const geometry_msgs::msg::PoseStamped &start, const geometry_msgs::msg::PoseStamped &goal, const std::vector< geometry_msgs::msg::PoseStamped > &viapoints, std::function< bool()> cancel_checker) override
Creating a plan from start and goal poses.
void getPlan(nav_msgs::msg::Path &global_path, std::function< bool()> cancel_checker)
the function responsible for calling the algorithm and retrieving a path from it
void cleanup() override
Method to cleanup resources used on shutdown.
static nav_msgs::msg::Path linearInterpolation(const std::vector< coordsW > &raw_path, const double &dist_bw_points)
interpolates points between the consecutive waypoints of the path
void deactivate() override
Method to deactivate planner and any threads involved in execution.
void configure(const nav2::LifecycleNode::WeakPtr &parent, std::string name, nav2::TransformBuffer::SharedPtr tf, std::shared_ptr< nav2_costmap_2d::Costmap2DROS > costmap_ros) override
void activate() override
Method to active planner and any threads involved in execution.