15 #ifndef NAV2_PLANNER__IS_PATH_VALID_SERVICE_HPP_
16 #define NAV2_PLANNER__IS_PATH_VALID_SERVICE_HPP_
22 #include "rclcpp/rclcpp.hpp"
23 #include "rclcpp_lifecycle/lifecycle_node.hpp"
24 #include "nav2_msgs/srv/is_path_valid.hpp"
25 #include "nav2_costmap_2d/costmap_2d_ros.hpp"
26 #include "nav2_costmap_2d/footprint_collision_checker.hpp"
27 #include "nav2_costmap_2d/cost_values.hpp"
28 #include "nav2_costmap_2d/costmap_layer.hpp"
29 #include "nav2_util/geometry_utils.hpp"
30 #include "nav2_ros_common/service_server.hpp"
31 #include "tf2/utils.hpp"
33 namespace nav2_planner
50 nav2::LifecycleNode::WeakPtr node,
51 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros,
52 const rclcpp::Duration & costmap_update_timeout)
53 : node_(node), costmap_ros_(costmap_ros), logger_(rclcpp::get_logger(
"is_path_valid_service")),
54 costmap_update_timeout_(costmap_update_timeout)
63 auto node = node_.lock();
65 throw std::runtime_error(
"Failed to lock node in initialize");
68 costmap_ = costmap_ros_->getCostmap();
70 service_ = node->create_service<nav2_msgs::srv::IsPathValid>(
73 &IsPathValidService::callback,
this,
74 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
94 if (layer_name.empty()) {
98 auto layers = costmap_ros_->getLayeredCostmap()->getPlugins();
99 for (
auto & layer : *layers) {
100 if (layer->getName() == layer_name) {
102 if (costmap_layer !=
nullptr) {
109 logger_,
"Requested layer '%s' not found or does not provide a costmap.",
121 bool getFootprintToUse(
122 const std::string & footprint_string,
123 nav2_costmap_2d::Footprint & footprint,
126 use_radius = costmap_ros_->getUseRadius();
128 if (!footprint_string.empty()) {
131 logger_,
"Invalid footprint string '%s'. Cannot validate path.",
132 footprint_string.c_str());
136 }
else if (!use_radius) {
137 footprint = costmap_ros_->getRobotFootprint();
149 unsigned int findClosestPointIndex(
150 const geometry_msgs::msg::PoseStamped & current_pose,
151 const nav_msgs::msg::Path & path)
153 unsigned int closest_point_index = 0;
154 float closest_distance = std::numeric_limits<float>::max();
155 const auto & current_point = current_pose.pose.position;
157 for (
unsigned int i = 0; i < path.poses.size(); ++i) {
158 const auto & path_point = path.poses[i].pose.position;
159 float distance = nav2_util::geometry_utils::euclidean_distance(current_point, path_point);
161 if (distance < closest_distance) {
162 closest_point_index = i;
163 closest_distance = distance;
167 return closest_point_index;
174 const std::shared_ptr<rmw_request_id_t>,
175 const std::shared_ptr<nav2_msgs::srv::IsPathValid::Request> request,
176 std::shared_ptr<nav2_msgs::srv::IsPathValid::Response> response)
178 response->success =
true;
179 response->is_valid =
true;
181 if (request->path.poses.empty()) {
182 RCLCPP_ERROR(logger_,
"Received empty path. Cannot validate path.");
183 response->success =
false;
184 response->is_valid =
false;
189 if (costmap_update_timeout_ > rclcpp::Duration(0, 0)) {
191 costmap_ros_->waitUntilCurrent(costmap_update_timeout_);
192 }
catch (
const std::exception & ex) {
193 RCLCPP_ERROR(logger_,
"Failed to wait for costmap: %s", ex.what());
194 response->success =
false;
195 response->is_valid =
false;
200 geometry_msgs::msg::PoseStamped current_pose;
201 if (!costmap_ros_->getRobotPose(current_pose)) {
202 RCLCPP_ERROR(logger_,
"Failed to get robot pose. Cannot validate path.");
203 response->success =
false;
204 response->is_valid =
false;
213 unsigned int closest_point_index = findClosestPointIndex(current_pose, request->path);
218 if (!costmap_to_check) {
219 response->success =
false;
220 response->is_valid =
false;
224 std::unique_lock<nav2_costmap_2d::Costmap2D::mutex_t> lock(
225 *(costmap_to_check->getMutex()));
230 nav2_costmap_2d::Footprint footprint;
233 if (!getFootprintToUse(request->footprint, footprint, use_radius)) {
234 response->success =
false;
235 response->is_valid =
false;
240 std::unique_ptr<nav2_costmap_2d::FootprintCollisionChecker<nav2_costmap_2d::Costmap2D *>>
244 std::make_unique<nav2_costmap_2d::FootprintCollisionChecker<nav2_costmap_2d::Costmap2D *>>(
249 const auto & poses = request->path.poses;
250 unsigned int end_index = poses.size();
251 if (request->max_lookahead_distance > 0.0) {
252 auto end_it = nav2_util::geometry_utils::first_after_integrated_distance(
253 poses.begin() + closest_point_index, poses.end(),
254 request->max_lookahead_distance);
255 end_index = std::distance(poses.begin(), end_it);
258 unsigned int cost = nav2_costmap_2d::FREE_SPACE;
259 for (
unsigned int i = closest_point_index; i < end_index; ++i) {
260 auto & position = request->path.poses[i].pose.position;
262 if (costmap_to_check->
worldToMap(position.x, position.y, mx, my)) {
263 cost = costmap_to_check->
getCost(mx, my);
265 cost = nav2_costmap_2d::LETHAL_OBSTACLE;
268 auto theta = tf2::getYaw(request->path.poses[i].pose.orientation);
269 cost =
static_cast<unsigned int>(collision_checker->footprintCostAtPose(
270 position.x, position.y, theta, footprint));
273 if (cost == nav2_costmap_2d::NO_INFORMATION && request->consider_unknown_as_obstacle) {
274 cost = nav2_costmap_2d::LETHAL_OBSTACLE;
275 }
else if (cost == nav2_costmap_2d::NO_INFORMATION) {
276 cost = nav2_costmap_2d::FREE_SPACE;
280 (cost >= request->max_cost || cost == nav2_costmap_2d::LETHAL_OBSTACLE ||
281 cost == nav2_costmap_2d::INSCRIBED_INFLATED_OBSTACLE))
283 response->is_valid =
false;
284 response->invalid_pose_indices.push_back(i);
285 if (request->stop_at_first_collision) {
288 }
else if (cost == nav2_costmap_2d::LETHAL_OBSTACLE || cost >= request->max_cost) {
289 response->is_valid =
false;
290 response->invalid_pose_indices.push_back(i);
291 if (request->stop_at_first_collision) {
298 nav2::LifecycleNode::WeakPtr node_;
299 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros_;
301 rclcpp::Logger logger_;
302 rclcpp::Duration costmap_update_timeout_;
303 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.