15 #ifndef NAV2_PLANNER__IS_PATH_VALID_SERVICE_HPP_
16 #define NAV2_PLANNER__IS_PATH_VALID_SERVICE_HPP_
22 #include "rclcpp/duration.hpp"
23 #include "rclcpp/logger.hpp"
24 #include "rclcpp/logging.hpp"
25 #include "rclcpp_lifecycle/lifecycle_node.hpp"
26 #include "nav2_msgs/srv/is_path_valid.hpp"
27 #include "nav2_costmap_2d/costmap_2d_ros.hpp"
28 #include "nav2_costmap_2d/footprint_collision_checker.hpp"
29 #include "nav2_costmap_2d/cost_values.hpp"
30 #include "nav2_costmap_2d/costmap_layer.hpp"
31 #include "nav2_util/geometry_utils.hpp"
32 #include "nav2_ros_common/service_server.hpp"
33 #include "tf2/utils.hpp"
35 namespace nav2_planner
52 nav2::LifecycleNode::WeakPtr node,
53 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros,
54 const rclcpp::Duration & costmap_update_timeout)
55 : node_(node), costmap_ros_(costmap_ros), logger_(rclcpp::get_logger(
"is_path_valid_service")),
56 costmap_update_timeout_(costmap_update_timeout)
65 auto node = node_.lock();
67 throw std::runtime_error(
"Failed to lock node in initialize");
70 costmap_ = costmap_ros_->getCostmap();
72 service_ = node->create_service<nav2_msgs::srv::IsPathValid>(
75 &IsPathValidService::callback,
this,
76 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
96 if (layer_name.empty()) {
100 auto layers = costmap_ros_->getLayeredCostmap()->getPlugins();
101 for (
auto & layer : *layers) {
102 if (layer->getName() == layer_name) {
104 if (costmap_layer !=
nullptr) {
111 logger_,
"Requested layer '%s' not found or does not provide a costmap.",
123 bool getFootprintToUse(
124 const std::string & footprint_string,
125 nav2_costmap_2d::Footprint & footprint,
128 use_radius = costmap_ros_->getUseRadius();
130 if (!footprint_string.empty()) {
133 logger_,
"Invalid footprint string '%s'. Cannot validate path.",
134 footprint_string.c_str());
138 }
else if (!use_radius) {
139 footprint = costmap_ros_->getRobotFootprint();
151 unsigned int findClosestPointIndex(
152 const geometry_msgs::msg::PoseStamped & current_pose,
153 const nav_msgs::msg::Path & path)
155 unsigned int closest_point_index = 0;
156 float closest_distance = std::numeric_limits<float>::max();
157 const auto & current_point = current_pose.pose.position;
159 for (
unsigned int i = 0; i < path.poses.size(); ++i) {
160 const auto & path_point = path.poses[i].pose.position;
161 float distance = nav2_util::geometry_utils::euclidean_distance(current_point, path_point);
163 if (distance < closest_distance) {
164 closest_point_index = i;
165 closest_distance = distance;
169 return closest_point_index;
176 const std::shared_ptr<rmw_request_id_t>,
177 const std::shared_ptr<nav2_msgs::srv::IsPathValid::Request> request,
178 std::shared_ptr<nav2_msgs::srv::IsPathValid::Response> response)
180 response->success =
true;
181 response->is_valid =
true;
183 if (request->path.poses.empty()) {
184 RCLCPP_ERROR(logger_,
"Received empty path. Cannot validate path.");
185 response->success =
false;
186 response->is_valid =
false;
191 if (costmap_update_timeout_ > rclcpp::Duration(0, 0)) {
193 costmap_ros_->waitUntilCurrent(costmap_update_timeout_);
194 }
catch (
const std::exception & ex) {
195 RCLCPP_ERROR(logger_,
"Failed to wait for costmap: %s", ex.what());
196 response->success =
false;
197 response->is_valid =
false;
202 geometry_msgs::msg::PoseStamped current_pose;
203 if (!costmap_ros_->getRobotPose(current_pose)) {
204 RCLCPP_ERROR(logger_,
"Failed to get robot pose. Cannot validate path.");
205 response->success =
false;
206 response->is_valid =
false;
215 unsigned int closest_point_index = findClosestPointIndex(current_pose, request->path);
220 if (!costmap_to_check) {
221 response->success =
false;
222 response->is_valid =
false;
226 std::unique_lock<nav2_costmap_2d::Costmap2D::mutex_t> lock(
227 *(costmap_to_check->getMutex()));
232 nav2_costmap_2d::Footprint footprint;
235 if (!getFootprintToUse(request->footprint, footprint, use_radius)) {
236 response->success =
false;
237 response->is_valid =
false;
242 std::unique_ptr<nav2_costmap_2d::FootprintCollisionChecker<nav2_costmap_2d::Costmap2D *>>
246 std::make_unique<nav2_costmap_2d::FootprintCollisionChecker<nav2_costmap_2d::Costmap2D *>>(
251 const auto & poses = request->path.poses;
252 unsigned int end_index = poses.size();
253 if (request->max_lookahead_distance > 0.0) {
254 auto end_it = nav2_util::geometry_utils::first_after_integrated_distance(
255 poses.begin() + closest_point_index, poses.end(),
256 request->max_lookahead_distance);
257 end_index = std::distance(poses.begin(), end_it);
260 unsigned int cost = nav2_costmap_2d::FREE_SPACE;
261 for (
unsigned int i = closest_point_index; i < end_index; ++i) {
262 auto & position = request->path.poses[i].pose.position;
264 if (costmap_to_check->
worldToMap(position.x, position.y, mx, my)) {
265 cost = costmap_to_check->
getCost(mx, my);
267 cost = nav2_costmap_2d::LETHAL_OBSTACLE;
270 auto theta = tf2::getYaw(request->path.poses[i].pose.orientation);
271 cost =
static_cast<unsigned int>(collision_checker->footprintCostAtPose(
272 position.x, position.y, theta, footprint));
275 if (cost == nav2_costmap_2d::NO_INFORMATION && request->consider_unknown_as_obstacle) {
276 cost = nav2_costmap_2d::LETHAL_OBSTACLE;
277 }
else if (cost == nav2_costmap_2d::NO_INFORMATION) {
278 cost = nav2_costmap_2d::FREE_SPACE;
282 (cost >= request->max_cost || cost == nav2_costmap_2d::LETHAL_OBSTACLE ||
283 cost == nav2_costmap_2d::INSCRIBED_INFLATED_OBSTACLE))
285 response->is_valid =
false;
286 response->invalid_pose_indices.push_back(i);
287 if (request->stop_at_first_collision) {
290 }
else if (cost == nav2_costmap_2d::LETHAL_OBSTACLE || cost >= request->max_cost) {
291 response->is_valid =
false;
292 response->invalid_pose_indices.push_back(i);
293 if (request->stop_at_first_collision) {
300 nav2::LifecycleNode::WeakPtr node_;
301 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros_;
303 rclcpp::Logger logger_;
304 rclcpp::Duration costmap_update_timeout_;
305 nav2::ServiceServer<nav2_msgs::srv::IsPathValid>::SharedPtr service_;
A 2D costmap provides a mapping between points in the world and their associated "costs".
unsigned char getCost(unsigned int mx, unsigned int my) const
Get the cost of a cell in the costmap.
bool worldToMap(double wx, double wy, unsigned int &mx, unsigned int &my) const
Convert from world coordinates to map coordinates.
A costmap layer base class for costmap plugin layers. Rather than just a layer, this object also cont...
Service to determine if a path is still valid given the current costmap state.
void reset()
Reset the service.
IsPathValidService(nav2::LifecycleNode::WeakPtr node, std::shared_ptr< nav2_costmap_2d::Costmap2DROS > costmap_ros, const rclcpp::Duration &costmap_update_timeout)
Constructor for IsPathValidService.
void initialize()
Initialize the service.
bool makeFootprintFromString(const std::string &footprint_string, std::vector< geometry_msgs::msg::Point > &footprint)
Make the footprint from the given string.