Nav2 Navigation Stack - kilted  kilted
ROS 2 Navigation Stack
cost_critic.cpp
1 // Copyright (c) 2022 Samsung Research America, @artofnothingness Alexey Budyakov
2 // Copyright (c) 2023 Open Navigation LLC
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 #include <cmath>
17 #include "nav2_mppi_controller/critics/cost_critic.hpp"
18 #include "nav2_core/controller_exceptions.hpp"
19 
20 namespace mppi::critics
21 {
22 
24 {
25  auto getParentParam = parameters_handler_->getParamGetter(parent_name_);
26  getParentParam(enforce_path_inversion_, "enforce_path_inversion", false);
27 
28  auto getParam = parameters_handler_->getParamGetter(name_);
29  getParam(consider_footprint_, "consider_footprint", false);
30  getParam(power_, "cost_power", 1);
31  getParam(weight_, "cost_weight", 3.81f);
32  getParam(critical_cost_, "critical_cost", 300.0f);
33  getParam(near_collision_cost_, "near_collision_cost", 253);
34  getParam(collision_cost_, "collision_cost", 1000000.0f);
35  getParam(near_goal_distance_, "near_goal_distance", 0.5f);
36  getParam(inflation_layer_name_, "inflation_layer_name", std::string(""));
37  getParam(trajectory_point_step_, "trajectory_point_step", 2);
38 
39  // Normalized by cost value to put in same regime as other weights
40  weight_ /= 254.0f;
41 
42  // Normalize weight when parameter is changed dynamically as well
43  auto weightDynamicCb = [&](
44  const rclcpp::Parameter & weight, rcl_interfaces::msg::SetParametersResult & /*result*/) {
45  weight_ = weight.as_double() / 254.0f;
46  };
47  parameters_handler_->addParamCallback(name_ + ".cost_weight", weightDynamicCb);
48 
49  collision_checker_.setCostmap(costmap_);
50  possible_collision_cost_ = findCircumscribedCost(costmap_ros_);
51 
52  if (possible_collision_cost_ < 1.0f) {
53  RCLCPP_ERROR(
54  logger_,
55  "Inflation layer either not found or inflation is not set sufficiently for "
56  "optimized non-circular collision checking capabilities. It is HIGHLY recommended to set"
57  " the inflation radius to be at MINIMUM half of the robot's largest cross-section. See "
58  "github.com/ros-planning/navigation2/tree/main/nav2_smac_planner#potential-fields"
59  " for full instructions. This will substantially impact run-time performance.");
60  }
61 
62  if (costmap_ros_->getUseRadius() == consider_footprint_) {
63  RCLCPP_WARN(
64  logger_,
65  "Inconsistent configuration in collision checking. Please verify the robot's shape settings "
66  "in both the costmap and the cost critic.");
67  if (costmap_ros_->getUseRadius()) {
69  "Considering footprint in collision checking but no robot footprint provided in the "
70  "costmap.");
71  }
72  }
73 
74  if(near_collision_cost_ > 253) {
75  RCLCPP_WARN(logger_, "Near collision cost is set higher than INSCRIBED_INFLATED_OBSTACLE");
76  }
77 
78  RCLCPP_INFO(
79  logger_,
80  "InflationCostCritic instantiated with %d power and %f / %f weights. "
81  "Critic will collision check based on %s cost.",
82  power_, critical_cost_, weight_, consider_footprint_ ?
83  "footprint" : "circular");
84 }
85 
87  std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap)
88 {
89  double result = -1.0;
90  const double circum_radius = costmap->getLayeredCostmap()->getCircumscribedRadius();
91  if (static_cast<float>(circum_radius) == circumscribed_radius_) {
92  // early return if footprint size is unchanged
93  return circumscribed_cost_;
94  }
95 
96  // check if the costmap has an inflation layer
97  const auto inflation_layer = nav2_costmap_2d::InflationLayer::getInflationLayer(
98  costmap,
99  inflation_layer_name_);
100  if (inflation_layer != nullptr) {
101  const double resolution = costmap->getCostmap()->getResolution();
102  double inflation_radius = inflation_layer->getInflationRadius();
103  if (inflation_radius < circum_radius) {
104  RCLCPP_ERROR(
105  rclcpp::get_logger("computeCircumscribedCost"),
106  "The inflation radius (%f) is smaller than the circumscribed radius (%f) "
107  "If this is an SE2-collision checking plugin, it cannot use costmap potential "
108  "field to speed up collision checking by only checking the full footprint "
109  "when robot is within possibly-inscribed radius of an obstacle. This may "
110  "significantly slow down planning times!",
111  inflation_radius, circum_radius);
112  result = 0.0;
113  return result;
114  }
115  result = inflation_layer->computeCost(circum_radius / resolution);
116  } else {
117  RCLCPP_WARN(
118  logger_,
119  "No inflation layer found in costmap configuration. "
120  "If this is an SE2-collision checking plugin, it cannot use costmap potential "
121  "field to speed up collision checking by only checking the full footprint "
122  "when robot is within possibly-inscribed radius of an obstacle. This may "
123  "significantly slow down planning times and not avoid anything but absolute collisions!");
124  }
125 
126  circumscribed_radius_ = static_cast<float>(circum_radius);
127  circumscribed_cost_ = static_cast<float>(result);
128 
129  return circumscribed_cost_;
130 }
131 
133 {
134  if (!enabled_) {
135  return;
136  }
137 
138  geometry_msgs::msg::Pose goal = utils::getCriticGoal(data, enforce_path_inversion_);
139 
140  // Setup cost information for various parts of the critic
141  is_tracking_unknown_ = costmap_ros_->getLayeredCostmap()->isTrackingUnknown();
142  auto * costmap = collision_checker_.getCostmap();
143  origin_x_ = static_cast<float>(costmap->getOriginX());
144  origin_y_ = static_cast<float>(costmap->getOriginY());
145  resolution_ = static_cast<float>(costmap->getResolution());
146  size_x_ = costmap->getSizeInCellsX();
147  size_y_ = costmap->getSizeInCellsY();
148 
149  if (consider_footprint_) {
150  // footprint may have changed since initialization if user has dynamic footprints
151  possible_collision_cost_ = findCircumscribedCost(costmap_ros_);
152  }
153 
154  // If near the goal, don't apply the preferential term since the goal is near obstacles
155  bool near_goal = false;
156  if (utils::withinPositionGoalTolerance(near_goal_distance_, data.state.pose.pose, goal)) {
157  near_goal = true;
158  }
159 
160  Eigen::ArrayXf repulsive_cost(data.costs.rows());
161  repulsive_cost.setZero();
162  bool all_trajectories_collide = true;
163 
164  int strided_traj_cols = floor((data.trajectories.x.cols() - 1) / trajectory_point_step_) + 1;
165  int strided_traj_rows = data.trajectories.x.rows();
166  int outer_stride = strided_traj_rows * trajectory_point_step_;
167 
168  const auto traj_x = Eigen::Map<const Eigen::ArrayXXf, 0,
169  Eigen::Stride<-1, -1>>(data.trajectories.x.data(), strided_traj_rows, strided_traj_cols,
170  Eigen::Stride<-1, -1>(outer_stride, 1));
171  const auto traj_y = Eigen::Map<const Eigen::ArrayXXf, 0,
172  Eigen::Stride<-1, -1>>(data.trajectories.y.data(), strided_traj_rows, strided_traj_cols,
173  Eigen::Stride<-1, -1>(outer_stride, 1));
174  const auto traj_yaw = Eigen::Map<const Eigen::ArrayXXf, 0,
175  Eigen::Stride<-1, -1>>(data.trajectories.yaws.data(), strided_traj_rows, strided_traj_cols,
176  Eigen::Stride<-1, -1>(outer_stride, 1));
177 
178  for (int i = 0; i < strided_traj_rows; ++i) {
179  bool trajectory_collide = false;
180  float pose_cost = 0.0f;
181  float & traj_cost = repulsive_cost(i);
182 
183  for (int j = 0; j < strided_traj_cols; j++) {
184  float Tx = traj_x(i, j);
185  float Ty = traj_y(i, j);
186  unsigned int x_i = 0u, y_i = 0u;
187 
188  // The getCost doesn't use orientation
189  // The footprintCostAtPose will always return "INSCRIBED" if footprint is over it
190  // So the center point has more information than the footprint
191  if (!worldToMapFloat(Tx, Ty, x_i, y_i)) {
192  pose_cost = 255.0f; // NO_INFORMATION in float
193  } else {
194  pose_cost = static_cast<float>(costmap->getCost(getIndex(x_i, y_i)));
195  if (pose_cost < 1.0f) {
196  continue; // In free space
197  }
198  }
199 
200  if (inCollision(pose_cost, Tx, Ty, traj_yaw(i, j))) {
201  traj_cost = collision_cost_;
202  trajectory_collide = true;
203  break;
204  }
205 
206  // Let near-collision trajectory points be punished severely
207  // Note that we collision check based on the footprint actual,
208  // but score based on the center-point cost regardless
209  if (pose_cost >= static_cast<float>(near_collision_cost_)) {
210  traj_cost += critical_cost_;
211  } else if (!near_goal) { // Generally prefer trajectories further from obstacles
212  traj_cost += pose_cost;
213  }
214  }
215 
216  all_trajectories_collide &= trajectory_collide;
217  }
218 
219  if (power_ > 1u) {
220  data.costs += (repulsive_cost *
221  (weight_ / static_cast<float>(strided_traj_cols))).pow(power_);
222  } else {
223  data.costs += repulsive_cost * (weight_ / static_cast<float>(strided_traj_cols));
224  }
225 
226  data.fail_flag = all_trajectories_collide;
227 }
228 
229 } // namespace mppi::critics
230 
231 #include <pluginlib/class_list_macros.hpp>
232 
233 PLUGINLIB_EXPORT_CLASS(
auto getParamGetter(const std::string &ns)
Get an object to retrieve parameters.
void addParamCallback(const std::string &name, T &&callback)
register a function to be called when setting a parameter
Critic objective function for avoiding obstacles using costmap's inflated cost.
Definition: cost_critic.hpp:36
bool worldToMapFloat(float wx, float wy, unsigned int &mx, unsigned int &my) const
An implementation of worldToMap fully using floats.
unsigned int getIndex(unsigned int mx, unsigned int my) const
A local implementation of getIndex.
bool inCollision(float cost, float x, float y, float theta)
Checks if cost represents a collision.
Definition: cost_critic.hpp:59
float findCircumscribedCost(std::shared_ptr< nav2_costmap_2d::Costmap2DROS > costmap)
Find the min cost of the inflation decay function for which the robot MAY be in collision in any orie...
Definition: cost_critic.cpp:86
void initialize() override
Initialize critic.
Definition: cost_critic.cpp:23
void score(CriticData &data) override
Evaluate cost related to obstacle avoidance.
Abstract critic objective function to score trajectories.
CostmapT getCostmap()
Get the current costmap object.
void setCostmap(CostmapT costmap)
Set the current costmap object to use for collision detection.
Data to pass to critics for scoring, including state, trajectories, pruned path, global goal,...
Definition: critic_data.hpp:40