21 #include "nav2_smac_planner/smac_planner_2d.hpp"
22 #include "nav2_util/geometry_utils.hpp"
26 namespace nav2_smac_planner
28 using namespace std::chrono;
29 using rcl_interfaces::msg::ParameterType;
30 using std::placeholders::_1;
34 _collision_checker(nullptr, 1, nullptr),
37 _costmap_downsampler(nullptr)
44 _logger,
"Destroying plugin %s of type SmacPlanner2D",
49 const rclcpp_lifecycle::LifecycleNode::WeakPtr & parent,
50 std::string name, std::shared_ptr<tf2_ros::Buffer>,
51 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
54 auto node = parent.lock();
55 _logger = node->get_logger();
56 _clock = node->get_clock();
57 _costmap = costmap_ros->getCostmap();
59 _global_frame = costmap_ros->getGlobalFrameID();
61 RCLCPP_INFO(_logger,
"Configuring %s of type SmacPlanner2D", name.c_str());
64 nav2_util::declare_parameter_if_not_declared(
65 node, name +
".tolerance", rclcpp::ParameterValue(0.125));
66 _tolerance =
static_cast<float>(node->get_parameter(name +
".tolerance").as_double());
67 nav2_util::declare_parameter_if_not_declared(
68 node, name +
".downsample_costmap", rclcpp::ParameterValue(
false));
69 node->get_parameter(name +
".downsample_costmap", _downsample_costmap);
70 nav2_util::declare_parameter_if_not_declared(
71 node, name +
".downsampling_factor", rclcpp::ParameterValue(1));
72 node->get_parameter(name +
".downsampling_factor", _downsampling_factor);
73 nav2_util::declare_parameter_if_not_declared(
74 node, name +
".cost_travel_multiplier", rclcpp::ParameterValue(1.0));
75 node->get_parameter(name +
".cost_travel_multiplier", _search_info.cost_penalty);
77 nav2_util::declare_parameter_if_not_declared(
78 node, name +
".allow_unknown", rclcpp::ParameterValue(
true));
79 node->get_parameter(name +
".allow_unknown", _allow_unknown);
80 nav2_util::declare_parameter_if_not_declared(
81 node, name +
".max_iterations", rclcpp::ParameterValue(1000000));
82 node->get_parameter(name +
".max_iterations", _max_iterations);
83 nav2_util::declare_parameter_if_not_declared(
84 node, name +
".max_on_approach_iterations", rclcpp::ParameterValue(1000));
85 node->get_parameter(name +
".max_on_approach_iterations", _max_on_approach_iterations);
86 nav2_util::declare_parameter_if_not_declared(
87 node, name +
".terminal_checking_interval", rclcpp::ParameterValue(5000));
88 node->get_parameter(name +
".terminal_checking_interval", _terminal_checking_interval);
89 nav2_util::declare_parameter_if_not_declared(
90 node, name +
".use_final_approach_orientation", rclcpp::ParameterValue(
false));
91 node->get_parameter(name +
".use_final_approach_orientation", _use_final_approach_orientation);
93 nav2_util::declare_parameter_if_not_declared(
94 node, name +
".max_planning_time", rclcpp::ParameterValue(2.0));
95 node->get_parameter(name +
".max_planning_time", _max_planning_time);
98 nav2_util::declare_parameter_if_not_declared(
99 node,
"service_introspection_mode", rclcpp::ParameterValue(
"disabled"));
101 _motion_model = MotionModel::TWOD;
103 if (_max_on_approach_iterations <= 0) {
105 _logger,
"On approach iteration selected as <= 0, "
106 "disabling tolerance and on approach iterations.");
107 _max_on_approach_iterations = std::numeric_limits<int>::max();
110 if (_max_iterations <= 0) {
112 _logger,
"maximum iteration selected as <= 0, "
113 "disabling maximum iterations.");
114 _max_iterations = std::numeric_limits<int>::max();
120 costmap_ros->getRobotFootprint(),
125 _a_star = std::make_unique<AStarAlgorithm<Node2D>>(_motion_model, _search_info);
129 _max_on_approach_iterations,
130 _terminal_checking_interval,
137 params.
get(node, name);
138 params.holonomic_ =
true;
139 _smoother = std::make_unique<Smoother>(params);
140 _smoother->initialize(1e-50 );
143 if (_downsample_costmap && _downsampling_factor > 1) {
144 std::string topic_name =
"downsampled_costmap";
145 _costmap_downsampler = std::make_unique<CostmapDownsampler>();
146 _costmap_downsampler->on_configure(
147 node, _global_frame, topic_name, _costmap, _downsampling_factor);
150 _raw_plan_publisher = node->create_publisher<nav_msgs::msg::Path>(
"unsmoothed_plan", 1);
153 _logger,
"Configured plugin %s of type SmacPlanner2D with "
154 "tolerance %.2f, maximum iterations %i, "
155 "max on approach iterations %i, and %s.",
156 _name.c_str(), _tolerance, _max_iterations, _max_on_approach_iterations,
157 _allow_unknown ?
"allowing unknown traversal" :
"not allowing unknown traversal");
163 _logger,
"Activating plugin %s of type SmacPlanner2D",
165 _raw_plan_publisher->on_activate();
166 if (_costmap_downsampler) {
167 _costmap_downsampler->on_activate();
169 auto node = _node.lock();
171 _dyn_params_handler = node->add_on_set_parameters_callback(
178 _logger,
"Deactivating plugin %s of type SmacPlanner2D",
180 _raw_plan_publisher->on_deactivate();
181 if (_costmap_downsampler) {
182 _costmap_downsampler->on_deactivate();
185 auto node = _node.lock();
186 if (_dyn_params_handler && node) {
187 node->remove_on_set_parameters_callback(_dyn_params_handler.get());
189 _dyn_params_handler.reset();
195 _logger,
"Cleaning up plugin %s of type SmacPlanner2D",
199 if (_costmap_downsampler) {
200 _costmap_downsampler->on_cleanup();
201 _costmap_downsampler.reset();
203 _raw_plan_publisher.reset();
207 const geometry_msgs::msg::PoseStamped & start,
208 const geometry_msgs::msg::PoseStamped & goal,
209 std::function<
bool()> cancel_checker)
211 std::lock_guard<std::mutex> lock_reinit(_mutex);
212 steady_clock::time_point a = steady_clock::now();
214 std::unique_lock<nav2_costmap_2d::Costmap2D::mutex_t> lock(*(_costmap->getMutex()));
218 if (_costmap_downsampler) {
219 costmap = _costmap_downsampler->downsample(_downsampling_factor);
224 _a_star->setCollisionChecker(&_collision_checker);
227 float mx_start, my_start, mx_goal, my_goal;
229 start.pose.position.x,
230 start.pose.position.y,
235 "Start Coordinates of(" + std::to_string(start.pose.position.x) +
", " +
236 std::to_string(start.pose.position.y) +
") was outside bounds");
238 _a_star->setStart(mx_start, my_start, 0);
242 goal.pose.position.x,
243 goal.pose.position.y,
248 "Goal Coordinates of(" + std::to_string(goal.pose.position.x) +
", " +
249 std::to_string(goal.pose.position.y) +
") was outside bounds");
251 _a_star->setGoal(mx_goal, my_goal, 0);
254 nav_msgs::msg::Path plan;
255 plan.header.stamp = _clock->now();
256 plan.header.frame_id = _global_frame;
257 geometry_msgs::msg::PoseStamped pose;
258 pose.header = plan.header;
259 pose.pose.position.z = 0.0;
260 pose.pose.orientation.x = 0.0;
261 pose.pose.orientation.y = 0.0;
262 pose.pose.orientation.z = 0.0;
263 pose.pose.orientation.w = 1.0;
266 if (std::floor(mx_start) == std::floor(mx_goal) && std::floor(my_start) == std::floor(my_goal)) {
267 pose.pose = start.pose;
271 if (start.pose.orientation != goal.pose.orientation && !_use_final_approach_orientation) {
272 pose.pose.orientation = goal.pose.orientation;
274 plan.poses.push_back(pose);
277 if (_raw_plan_publisher->get_subscription_count() > 0) {
278 _raw_plan_publisher->publish(plan);
285 Node2D::CoordinateVector path;
286 int num_iterations = 0;
288 if (!_a_star->createPath(
289 path, num_iterations,
290 _tolerance /
static_cast<float>(costmap->
getResolution()), cancel_checker))
293 if (num_iterations == 1) {
297 if (num_iterations < _a_star->getMaxIterations()) {
305 plan.poses.reserve(path.size());
306 for (
int i = path.size() - 1; i >= 0; --i) {
307 pose.pose = getWorldCoords(path[i].x, path[i].y, costmap);
308 plan.poses.push_back(pose);
312 if (_raw_plan_publisher->get_subscription_count() > 0) {
313 _raw_plan_publisher->publish(plan);
317 steady_clock::time_point b = steady_clock::now();
318 duration<double> time_span = duration_cast<duration<double>>(b - a);
319 double time_remaining = _max_planning_time -
static_cast<double>(time_span.count());
321 #ifdef BENCHMARK_TESTING
322 std::cout <<
"It took " << time_span.count() * 1000 <<
323 " milliseconds with " << num_iterations <<
" iterations." << std::endl;
327 _smoother->smooth(plan, costmap, time_remaining);
334 size_t plan_size = plan.poses.size();
335 if (_use_final_approach_orientation) {
336 if (plan_size == 1) {
337 plan.poses.back().pose.orientation = start.pose.orientation;
338 }
else if (plan_size > 1) {
339 double dx, dy, theta;
340 auto last_pose = plan.poses.back().pose.position;
341 auto approach_pose = plan.poses[plan_size - 2].pose.position;
342 dx = last_pose.x - approach_pose.x;
343 dy = last_pose.y - approach_pose.y;
344 theta = atan2(dy, dx);
345 plan.poses.back().pose.orientation =
346 nav2_util::geometry_utils::orientationAroundZAxis(theta);
348 }
else if (plan_size > 0) {
349 plan.poses.back().pose.orientation = goal.pose.orientation;
355 rcl_interfaces::msg::SetParametersResult
358 rcl_interfaces::msg::SetParametersResult result;
359 std::lock_guard<std::mutex> lock_reinit(_mutex);
361 bool reinit_a_star =
false;
362 bool reinit_downsampler =
false;
364 for (
auto parameter : parameters) {
365 const auto & param_type = parameter.get_type();
366 const auto & param_name = parameter.get_name();
367 if(param_name.find(_name +
".") != 0) {
370 if (param_type == ParameterType::PARAMETER_DOUBLE) {
371 if (param_name == _name +
".tolerance") {
372 _tolerance =
static_cast<float>(parameter.as_double());
373 }
else if (param_name == _name +
".cost_travel_multiplier") {
374 reinit_a_star =
true;
375 _search_info.cost_penalty = parameter.as_double();
376 }
else if (param_name == _name +
".max_planning_time") {
377 reinit_a_star =
true;
378 _max_planning_time = parameter.as_double();
380 }
else if (param_type == ParameterType::PARAMETER_BOOL) {
381 if (param_name == _name +
".downsample_costmap") {
382 reinit_downsampler =
true;
383 _downsample_costmap = parameter.as_bool();
384 }
else if (param_name == _name +
".allow_unknown") {
385 reinit_a_star =
true;
386 _allow_unknown = parameter.as_bool();
387 }
else if (param_name == _name +
".use_final_approach_orientation") {
388 _use_final_approach_orientation = parameter.as_bool();
390 }
else if (param_type == ParameterType::PARAMETER_INTEGER) {
391 if (param_name == _name +
".downsampling_factor") {
392 reinit_downsampler =
true;
393 _downsampling_factor = parameter.as_int();
394 }
else if (param_name == _name +
".max_iterations") {
395 reinit_a_star =
true;
396 _max_iterations = parameter.as_int();
397 if (_max_iterations <= 0) {
399 _logger,
"maximum iteration selected as <= 0, "
400 "disabling maximum iterations.");
401 _max_iterations = std::numeric_limits<int>::max();
403 }
else if (param_name == _name +
".max_on_approach_iterations") {
404 reinit_a_star =
true;
405 _max_on_approach_iterations = parameter.as_int();
406 if (_max_on_approach_iterations <= 0) {
408 _logger,
"On approach iteration selected as <= 0, "
409 "disabling tolerance and on approach iterations.");
410 _max_on_approach_iterations = std::numeric_limits<int>::max();
412 }
else if (param_name == _name +
".terminal_checking_interval") {
413 reinit_a_star =
true;
414 _terminal_checking_interval = parameter.as_int();
420 if (reinit_a_star || reinit_downsampler) {
423 _a_star = std::make_unique<AStarAlgorithm<Node2D>>(_motion_model, _search_info);
427 _max_on_approach_iterations,
428 _terminal_checking_interval,
435 if (reinit_downsampler) {
436 if (_downsample_costmap && _downsampling_factor > 1) {
437 auto node = _node.lock();
438 std::string topic_name =
"downsampled_costmap";
439 _costmap_downsampler = std::make_unique<CostmapDownsampler>();
440 _costmap_downsampler->on_configure(
441 node, _global_frame, topic_name, _costmap, _downsampling_factor);
445 result.successful =
true;
451 #include "pluginlib/class_list_macros.hpp"
Abstract interface for global planners to adhere to with pluginlib.
A 2D costmap provides a mapping between points in the world and their associated "costs".
double getResolution() const
Accessor for the resolution of the costmap.
bool worldToMapContinuous(double wx, double wy, float &mx, float &my) const
Convert from world coordinates to map coordinates.
A costmap grid collision checker.
void setFootprint(const nav2_costmap_2d::Footprint &footprint, const bool &radius, const double &possible_collision_cost)
A constructor for nav2_smac_planner::GridCollisionChecker for use when irregular bin intervals are ap...
void activate() override
Activate lifecycle node.
SmacPlanner2D()
constructor
void configure(const rclcpp_lifecycle::LifecycleNode::WeakPtr &parent, std::string name, std::shared_ptr< tf2_ros::Buffer > tf, std::shared_ptr< nav2_costmap_2d::Costmap2DROS > costmap_ros) override
Configuring plugin.
rcl_interfaces::msg::SetParametersResult dynamicParametersCallback(std::vector< rclcpp::Parameter > parameters)
Callback executed when a parameter change is detected.
~SmacPlanner2D()
destructor
void cleanup() override
Cleanup lifecycle node.
void deactivate() override
Deactivate lifecycle node.
nav_msgs::msg::Path createPlan(const geometry_msgs::msg::PoseStamped &start, const geometry_msgs::msg::PoseStamped &goal, std::function< bool()> cancel_checker) override
Creating a plan from start and goal poses.
Parameters for the smoother cost function.
void get(std::shared_ptr< rclcpp_lifecycle::LifecycleNode > node, const std::string &name)
Get params from ROS parameter.