|
| 1 | +// Copyright 2025 Intelligent Robotics Lab |
| 2 | +// |
| 3 | +// This file is part of the project Easy Navigation (EasyNav in short) |
| 4 | +// licensed under the GNU General Public License v3.0. |
| 5 | +// See <http://www.gnu.org/licenses/> for details. |
| 6 | +// |
| 7 | +// Easy Navigation program is free software: you can redistribute it and/or modify |
| 8 | +// it under the terms of the GNU General Public License as published by |
| 9 | +// the Free Software Foundation, either version 3 of the License, or |
| 10 | +// (at your option) any later version. |
| 11 | +// |
| 12 | +// This program is distributed in the hope that it will be useful, |
| 13 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | +// GNU General Public License for more details. |
| 16 | +// |
| 17 | +// You should have received a copy of the GNU General Public License |
| 18 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 19 | + |
| 20 | +/// \file |
| 21 | +/// \brief Declaration of the GridMapRRTStarPlanner class implementing RRT* path planning using elevation maps. |
| 22 | + |
| 23 | +#ifndef EASYNAV_GRIDMAP_RRTSTAR_PLANNER__GRIDMAPRRTSTARPLANNER_HPP_ |
| 24 | +#define EASYNAV_GRIDMAP_RRTSTAR_PLANNER__GRIDMAPRRTSTARPLANNER_HPP_ |
| 25 | + |
| 26 | +#include <cmath> |
| 27 | +#include <limits> |
| 28 | +#include <sstream> |
| 29 | +#include <algorithm> |
| 30 | +#include <random> |
| 31 | +#include <queue> |
| 32 | +#include <unordered_set> |
| 33 | +#include <unordered_map> |
| 34 | +#include <memory> |
| 35 | + |
| 36 | +#include "grid_map_core/GridMap.hpp" |
| 37 | +#include "nav_msgs/msg/goals.hpp" |
| 38 | +#include "nav_msgs/msg/odometry.hpp" |
| 39 | +#include "nav_msgs/msg/path.hpp" |
| 40 | +#include "rclcpp/rclcpp.hpp" |
| 41 | +#include "easynav_core/PlannerMethodBase.hpp" |
| 42 | +#include "easynav_gridmap_rrtstar_planner/KDTree.hpp" |
| 43 | +#include "easynav_gridmap_rrtstar_planner/RRTNode.hpp" |
| 44 | +#include "tf2/LinearMath/Quaternion.hpp" |
| 45 | +#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp" |
| 46 | + |
| 47 | +namespace easynav |
| 48 | +{ |
| 49 | + |
| 50 | +class KDTree; |
| 51 | + |
| 52 | + /** |
| 53 | + * @brief RRT* planner implementation using a grid map with elevation support. |
| 54 | + * |
| 55 | + * This planner generates collision-free paths that respect slope constraints. |
| 56 | + * It includes RRT* optimizations, KD-tree acceleration, and path smoothing. |
| 57 | + */ |
| 58 | +class GridMapRRTStarPlanner : public PlannerMethodBase |
| 59 | +{ |
| 60 | +public: |
| 61 | + /** |
| 62 | + * @brief Constructor. |
| 63 | + * Initializes the KD-tree and sets default parameters. |
| 64 | + */ |
| 65 | + GridMapRRTStarPlanner(); |
| 66 | + |
| 67 | + /** |
| 68 | + * @brief Initializes the planner plugin. |
| 69 | + * Declares and retrieves parameters from the ROS2 node. |
| 70 | + * @return std::expected<void, std::string> Empty if successful, error message if failed. |
| 71 | + */ |
| 72 | + std::expected<void, std::string> on_initialize() override; |
| 73 | + |
| 74 | + /** |
| 75 | + * @brief Main planner update function. |
| 76 | + * Computes a path from the robot to the goal each cycle. |
| 77 | + * Publishes the path and visualization markers if there are subscribers. |
| 78 | + * @param nav_state Navigation state containing robot pose, map, and goals. |
| 79 | + */ |
| 80 | + void update(NavState & nav_state) override; |
| 81 | + |
| 82 | +protected: |
| 83 | + /** |
| 84 | + * @brief Runs RRT* algorithm to generate a path from start to goal. |
| 85 | + * @param map Grid map used for planning. |
| 86 | + * @param start Start pose in the map frame. |
| 87 | + * @param goal Goal pose in the map frame. |
| 88 | + * @return Vector of smoothed poses representing the planned path. |
| 89 | + * @note Path will be empty if start or goal is outside map bounds. |
| 90 | + */ |
| 91 | + std::vector<geometry_msgs::msg::Pose> rrt_star( |
| 92 | + const grid_map::GridMap & map, |
| 93 | + const geometry_msgs::msg::Pose & start, |
| 94 | + const geometry_msgs::msg::Pose & goal); |
| 95 | + |
| 96 | + /** |
| 97 | + * @brief Generates a random index inside the map. |
| 98 | + * @param map Grid map to sample from. |
| 99 | + * @param rng Random number generator. |
| 100 | + * @return Random index within map bounds. |
| 101 | + */ |
| 102 | + grid_map::Index random_index(const grid_map::GridMap & map, std::mt19937 & rng); |
| 103 | + |
| 104 | + /** |
| 105 | + * @brief Steers from one index towards another with a maximum step size. |
| 106 | + * @param map Grid map used for distance conversion. |
| 107 | + * @param from Starting index. |
| 108 | + * @param to Target index. |
| 109 | + * @return New index reached after stepping, or {-1,-1} if outside map. |
| 110 | + */ |
| 111 | + grid_map::Index steer( |
| 112 | + const grid_map::GridMap & map, const grid_map::Index & from, |
| 113 | + const grid_map::Index & to); |
| 114 | + |
| 115 | + /** |
| 116 | + * @brief Computes traversal cost between two indices, considering elevation and slope. |
| 117 | + * @param map Grid map used for cost evaluation. |
| 118 | + * @param to Destination index. |
| 119 | + * @param from Source index. |
| 120 | + * @return Traversal cost. Returns infinity if the path is invalid or slope exceeds max allowed. |
| 121 | + */ |
| 122 | + double traversal_cost( |
| 123 | + const grid_map::GridMap & map, const grid_map::Index & to, |
| 124 | + const grid_map::Index & from); |
| 125 | + |
| 126 | + /** |
| 127 | + * @brief Computes Euclidean distance between two indices. |
| 128 | + * @param map Grid map used to convert indices to positions. |
| 129 | + * @param a First index. |
| 130 | + * @param b Second index. |
| 131 | + * @return Euclidean distance in meters. |
| 132 | + */ |
| 133 | + double distance( |
| 134 | + const grid_map::GridMap & map, const grid_map::Index & a, |
| 135 | + const grid_map::Index & b); |
| 136 | + |
| 137 | + /** |
| 138 | + * @brief Extracts path poses from a goal node back to the start node. |
| 139 | + * @param node Goal node of RRT* tree. |
| 140 | + * @param map Grid map used for position extraction. |
| 141 | + * @param goal Goal pose for final orientation. |
| 142 | + * @return Vector of poses representing the path. |
| 143 | + */ |
| 144 | + std::vector<geometry_msgs::msg::Pose> extract_path( |
| 145 | + std::shared_ptr<RRTNode> node, |
| 146 | + const grid_map::GridMap & map, |
| 147 | + const geometry_msgs::msg::Pose & goal); |
| 148 | + |
| 149 | + /** |
| 150 | + * @brief Finds the nearest node in the tree to a target index. |
| 151 | + * @param tree Vector of RRT nodes. |
| 152 | + * @param target Index to find nearest node to. |
| 153 | + * @param map Grid map used to compute distance. |
| 154 | + * @return Shared pointer to nearest node, or nullptr if tree is empty. |
| 155 | + */ |
| 156 | + std::shared_ptr<RRTNode> find_nearest( |
| 157 | + const std::vector<std::shared_ptr<RRTNode>> & tree, |
| 158 | + const grid_map::Index & target, |
| 159 | + const grid_map::GridMap & map); |
| 160 | + |
| 161 | + /** |
| 162 | + * @brief Generates a biased sample index for RRT* exploration. |
| 163 | + * @param map Grid map used for sampling. |
| 164 | + * @param goal_idx Index of the goal. |
| 165 | + * @param best_goal_node Best goal node found so far (used for path-biased sampling). |
| 166 | + * @param rng Random number generator. |
| 167 | + * @param prob_dist Probability distribution for random sampling. |
| 168 | + * @return Index sampled for RRT* expansion. |
| 169 | + */ |
| 170 | + grid_map::Index generate_sample( |
| 171 | + const grid_map::GridMap & map, |
| 172 | + const grid_map::Index & goal_idx, |
| 173 | + std::shared_ptr<RRTNode> best_goal_node, |
| 174 | + std::mt19937 & rng, |
| 175 | + std::uniform_real_distribution<double> & prob_dist); |
| 176 | + |
| 177 | + /** |
| 178 | + * @brief Smooths a path using Catmull-Rom splines. |
| 179 | + * @param input_path Input path to smooth. |
| 180 | + * @param interpolation_points_per_segment Number of interpolated points per segment. |
| 181 | + * @return Smoothed path as a vector of poses. |
| 182 | + * @note Spacing between consecutive points is enforced by `spacing_`. |
| 183 | + */ |
| 184 | + std::vector<geometry_msgs::msg::Pose> smooth_path( |
| 185 | + const std::vector<geometry_msgs::msg::Pose> & input_path, |
| 186 | + int interpolation_points_per_segment); |
| 187 | + |
| 188 | + // ----------------------- |
| 189 | + // Planner parameters |
| 190 | + // ----------------------- |
| 191 | + double max_allowed_slope_deg_ = 50; ///< Maximum slope in degrees |
| 192 | + double max_allowed_slope_ = 50.0 * M_PI / 180.0; ///< Maximum slope in radians |
| 193 | + int max_iters_ = 2000; ///< Maximum RRT* iterations |
| 194 | + double step_size_ = 0.8; ///< Maximum step distance (m) |
| 195 | + double neighbor_radius_ = 1.5; ///< RRT* neighbor radius (m) |
| 196 | + double goal_bias_ = 0.1; ///< Probability of sampling the goal |
| 197 | + double goal_threshold_ = 1.0; ///< Distance threshold for goal connection (m) |
| 198 | + int kdtree_rebuild_interval_ = 200; ///< Interval to rebuild KD-tree |
| 199 | + double spacing_ = 0.2; ///< Minimum spacing for smoothed path points (m) |
| 200 | + |
| 201 | + // ----------------------- |
| 202 | + // Runtime state |
| 203 | + // ----------------------- |
| 204 | + rclcpp::Publisher<nav_msgs::msg::Path>::SharedPtr path_pub_; ///< Path publisher |
| 205 | + nav_msgs::msg::Path current_path_; ///< Current planned path |
| 206 | + std::unique_ptr<KDTree> kd_tree_; ///< KD-tree for nearest neighbor search |
| 207 | + std::unordered_map<uint64_t, double> cost_cache_; ///< Cache for traversal costs |
| 208 | + uint32_t cached_map_width_ = 0; |
| 209 | + uint32_t cached_map_height_ = 0; |
| 210 | + uint64_t last_map_timestamp_ = 0; |
| 211 | + |
| 212 | + // ----------------------- |
| 213 | + // Cache utilities |
| 214 | + // ----------------------- |
| 215 | + void clear_cost_cache(); |
| 216 | + static uint32_t flat_index(const grid_map::Index & idx, uint32_t width); |
| 217 | + static uint64_t edge_key(uint32_t a_flat, uint32_t b_flat); |
| 218 | +}; |
| 219 | + |
| 220 | +} // namespace easynav |
| 221 | + |
| 222 | +#endif // EASYNAV_GRIDMAP_RRTSTAR_PLANNER__GRIDMAPRRTSTARPLANNER_HPP_ |
0 commit comments