Nav2 Navigation Stack - lyrical  lyrical
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_costmap_2d/inflation_layer_interface.hpp"
19 #include "nav2_core/controller_exceptions.hpp"
20 
21 namespace mppi::critics
22 {
23 
25 {
26  auto getParentParam = parameters_handler_->getParamGetter(parent_name_);
27  auto getParam = parameters_handler_->getParamGetter(name_);
28  getParam(consider_footprint_, "consider_footprint", false);
29  getParam(power_, "cost_power", 1);
30  getParam(weight_, "cost_weight", 3.81f);
31  getParam(critical_cost_, "critical_cost", 300.0f);
32  getParam(near_collision_cost_, "near_collision_cost", 253);
33  getParam(collision_cost_, "collision_cost", 1000000.0f);
34  getParam(near_goal_distance_, "near_goal_distance", 0.5f);
35  getParam(inflation_layer_name_, "inflation_layer_name", std::string(""));
36  getParam(trajectory_point_step_, "trajectory_point_step", 2);
37 
38  // Normalized by cost value to put in same regime as other weights
39  weight_ /= 254.0f;
40 
41  // Normalize weight when parameter is changed dynamically as well
42  auto weightDynamicCb = [&](
43  const rclcpp::Parameter & weight) {
44  weight_ = weight.as_double() / 254.0f;
45  };
46  parameters_handler_->addParamCallback(name_ + ".cost_weight", weightDynamicCb);
47 
48  collision_checker_.setCostmap(costmap_);
49  possible_collision_cost_ = findCircumscribedCost(costmap_ros_);
50 
51  if (possible_collision_cost_ < 1.0f) {
52  RCLCPP_ERROR(
53  logger_,
54  "Inflation layer either not found or inflation is not set sufficiently for "
55  "optimized non-circular collision checking capabilities. It is HIGHLY recommended to set"
56  " the inflation radius to be at MINIMUM half of the robot's largest cross-section. See "
57  "github.com/ros-planning/navigation2/tree/main/nav2_smac_planner#potential-fields"
58  " for full instructions. This will substantially impact run-time performance.");
59  }
60 
61  if (costmap_ros_->getUseRadius() == consider_footprint_) {
62  RCLCPP_WARN(
63  logger_,
64  "Inconsistent configuration in collision checking. Please verify the robot's shape settings "
65  "in both the costmap and the cost critic.");
66  if (costmap_ros_->getUseRadius()) {
68  "Considering footprint in CostCritic but no robot footprint provided in the "
69  "costmap (robot radius used instead). Disable considering footprint.");
70  }
71  }
72 
73  if (near_collision_cost_ > 253) {
74  RCLCPP_WARN(logger_, "Near collision cost is set higher than INSCRIBED_INFLATED_OBSTACLE");
75  }
76 
77  RCLCPP_INFO(
78  logger_,
79  "InflationCostCritic instantiated with %d power and %f / %f weights. "
80  "Critic will collision check based on %s cost.",
81  power_, critical_cost_, weight_, consider_footprint_ ?
82  "footprint" : "circular");
83 }
84 
86  std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap)
87 {
88  double result = -1.0;
89  const double circum_radius = costmap->getLayeredCostmap()->getCircumscribedRadius();
90  if (static_cast<float>(circum_radius) == circumscribed_radius_) {
91  // early return if footprint size is unchanged
92  return circumscribed_cost_;
93  }
94 
95  // check if the costmap has an inflation layer
97  costmap,
98  inflation_layer_name_);
99  if (inflation_layer != nullptr) {
100  const double resolution = costmap->getCostmap()->getResolution();
101  double inflation_radius = inflation_layer->getInflationRadius();
102  if (inflation_radius < circum_radius) {
103  RCLCPP_ERROR(
104  rclcpp::get_logger("computeCircumscribedCost"),
105  "The inflation radius (%f) is smaller than the circumscribed radius (%f) "
106  "If this is an SE2-collision checking plugin, it cannot use costmap potential "
107  "field to speed up collision checking by only checking the full footprint "
108  "when robot is within possibly-inscribed radius of an obstacle. This may "
109  "significantly slow down planning times!",
110  inflation_radius, circum_radius);
111  result = 0.0;
112  return result;
113  }
114  result = inflation_layer->computeCost(circum_radius / resolution);
115  } else {
116  RCLCPP_WARN(
117  logger_,
118  "No inflation layer found in costmap configuration. "
119  "If this is an SE2-collision checking plugin, it cannot use costmap potential "
120  "field to speed up collision checking by only checking the full footprint "
121  "when robot is within possibly-inscribed radius of an obstacle. This may "
122  "significantly slow down planning times and not avoid anything but absolute collisions!");
123  }
124 
125  circumscribed_radius_ = static_cast<float>(circum_radius);
126  circumscribed_cost_ = static_cast<float>(result);
127 
128  return circumscribed_cost_;
129 }
130 
132 {
133  if (!enabled_) {
134  return;
135  }
136 
137  // Setup cost information for various parts of the critic
138  is_tracking_unknown_ = costmap_ros_->getLayeredCostmap()->isTrackingUnknown();
139  auto * costmap = collision_checker_.getCostmap();
140  origin_x_ = static_cast<float>(costmap->getOriginX());
141  origin_y_ = static_cast<float>(costmap->getOriginY());
142  resolution_ = static_cast<float>(costmap->getResolution());
143  size_x_ = costmap->getSizeInCellsX();
144  size_y_ = costmap->getSizeInCellsY();
145 
146  if (consider_footprint_) {
147  // footprint may have changed since initialization if user has dynamic footprints
148  possible_collision_cost_ = findCircumscribedCost(costmap_ros_);
149  }
150 
151  // If near the goal, don't apply the preferential term since the goal is near obstacles
152  bool near_goal = false;
153  if (data.state.local_path_length < near_goal_distance_) {
154  near_goal = true;
155  }
156 
157  Eigen::ArrayXf repulsive_cost(data.costs.rows());
158  repulsive_cost.setZero();
159  bool all_trajectories_collide = true;
160 
161  auto & collisions = data.trajectories_in_collision;
162  const bool track_collisions = !collisions.empty();
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>>(
170  data.trajectories.x.data(), strided_traj_rows, strided_traj_cols,
171  Eigen::Stride<-1, -1>(outer_stride, 1));
172  const auto traj_y = Eigen::Map<const Eigen::ArrayXXf, 0,
173  Eigen::Stride<-1, -1>>(
174  data.trajectories.y.data(), strided_traj_rows, strided_traj_cols,
175  Eigen::Stride<-1, -1>(outer_stride, 1));
176  const auto traj_yaw = Eigen::Map<const Eigen::ArrayXXf, 0,
177  Eigen::Stride<-1, -1>>(
178  data.trajectories.yaws.data(), strided_traj_rows, strided_traj_cols,
179  Eigen::Stride<-1, -1>(outer_stride, 1));
180 
181  for (int i = 0; i < strided_traj_rows; ++i) {
182  bool trajectory_collide = false;
183  float pose_cost = 0.0f;
184  float & traj_cost = repulsive_cost(i);
185 
186  for (int j = 0; j < strided_traj_cols; j++) {
187  float Tx = traj_x(i, j);
188  float Ty = traj_y(i, j);
189  unsigned int x_i = 0u, y_i = 0u;
190 
191  // The getCost doesn't use orientation
192  // The footprintCostAtPose will always return "INSCRIBED" if footprint is over it
193  // So the center point has more information than the footprint
194  if (!worldToMapFloat(Tx, Ty, x_i, y_i)) {
195  pose_cost = 255.0f; // NO_INFORMATION in float
196  } else {
197  pose_cost = static_cast<float>(costmap->getCost(getIndex(x_i, y_i)));
198  if (pose_cost < 1.0f) {
199  continue; // In free space
200  }
201  }
202 
203  if (inCollision(pose_cost, Tx, Ty, traj_yaw(i, j))) {
204  traj_cost = collision_cost_;
205  trajectory_collide = true;
206  if (track_collisions) {collisions[i] = true;}
207  break;
208  }
209 
210  // Let near-collision trajectory points be punished severely
211  // Note that we collision check based on the footprint actual,
212  // but score based on the center-point cost regardless
213  if (pose_cost >= static_cast<float>(near_collision_cost_)) {
214  traj_cost += critical_cost_;
215  } else if (!near_goal) { // Generally prefer trajectories further from obstacles
216  traj_cost += pose_cost;
217  }
218  }
219 
220  all_trajectories_collide &= trajectory_collide;
221  }
222 
223  if (power_ > 1u) {
224  data.costs += (repulsive_cost *
225  (weight_ / static_cast<float>(strided_traj_cols))).pow(power_);
226  } else {
227  data.costs += repulsive_cost * (weight_ / static_cast<float>(strided_traj_cols));
228  }
229 
230  data.fail_flag = all_trajectories_collide;
231 }
232 
233 } // namespace mppi::critics
234 
235 #include <pluginlib/class_list_macros.hpp>
236 
237 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:85
void initialize() override
Initialize critic.
Definition: cost_critic.cpp:24
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.
static std::shared_ptr< InflationLayerInterface > getInflationLayer(std::shared_ptr< nav2_costmap_2d::Costmap2DROS > &costmap_ros, const std::string layer_name="")
Get the inflation layer from a costmap, checking for both InflationLayer and LegacyInflationLayer imp...
Data to pass to critics for scoring, including state, trajectories, pruned path, global goal,...
Definition: critic_data.hpp:40