15 #ifndef NAV2_SMAC_PLANNER__SMAC_PLANNER_2D_IMPL_HPP_
16 #define NAV2_SMAC_PLANNER__SMAC_PLANNER_2D_IMPL_HPP_
25 #include "nav2_smac_planner/smac_planner_2d.hpp"
26 #include "nav2_util/geometry_utils.hpp"
27 #include "nav2_ros_common/tf2_factories.hpp"
31 namespace nav2_smac_planner
33 using namespace std::chrono;
34 using rcl_interfaces::msg::ParameterType;
35 using std::placeholders::_1;
37 template<
typename NodeT>
40 _collision_checker(nullptr, 1, nullptr),
43 _costmap_downsampler(nullptr)
47 template<
typename NodeT>
51 _logger,
"Destroying plugin %s of type SmacPlanner2D",
55 template<
typename NodeT>
57 const nav2::LifecycleNode::WeakPtr & parent,
58 std::string name, nav2::TransformBuffer::SharedPtr,
59 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
62 auto node = parent.lock();
63 _logger = node->get_logger();
64 _clock = node->get_clock();
65 _costmap = costmap_ros->getCostmap();
66 _costmap_ros = costmap_ros;
68 _global_frame = costmap_ros->getGlobalFrameID();
70 RCLCPP_INFO(_logger,
"Configuring %s of type SmacPlanner2D", name.c_str());
73 _tolerance =
static_cast<float>(node->declare_or_get_parameter(name +
".tolerance", 0.125));
74 _downsample_costmap = node->declare_or_get_parameter(name +
".downsample_costmap",
false);
75 _downsampling_factor = node->declare_or_get_parameter(name +
".downsampling_factor", 1);
76 _search_info.cost_penalty =
77 node->declare_or_get_parameter(name +
".cost_travel_multiplier", 1.0);
79 _allow_unknown = node->declare_or_get_parameter(name +
".allow_unknown",
true);
80 _max_iterations = node->declare_or_get_parameter(name +
".max_iterations", 1000000);
81 _max_on_approach_iterations =
82 node->declare_or_get_parameter(name +
".max_on_approach_iterations", 1000);
83 _terminal_checking_interval =
84 node->declare_or_get_parameter(name +
".terminal_checking_interval", 5000);
85 _use_final_approach_orientation =
86 node->declare_or_get_parameter(name +
".use_final_approach_orientation",
false);
88 _max_planning_time = node->declare_or_get_parameter(name +
".max_planning_time", 2.0);
90 _motion_model = MotionModel::TWOD;
92 if (_max_on_approach_iterations <= 0) {
94 _logger,
"On approach iteration selected as <= 0, "
95 "disabling tolerance and on approach iterations.");
96 _max_on_approach_iterations = std::numeric_limits<int>::max();
99 if (_max_iterations <= 0) {
101 _logger,
"maximum iteration selected as <= 0, "
102 "disabling maximum iterations.");
103 _max_iterations = std::numeric_limits<int>::max();
108 _collision_checker.setFootprint(
109 costmap_ros->getRobotFootprint(),
114 _a_star = std::make_unique<AStarAlgorithm<NodeT>>(_motion_model, _search_info);
118 _max_on_approach_iterations,
119 _terminal_checking_interval,
126 params.
get(node, name);
127 params.holonomic_ =
true;
128 _smoother = std::make_unique<Smoother>(params);
129 _smoother->initialize(1e-50 );
132 std::string topic_name =
"downsampled_costmap";
133 _costmap_downsampler = std::make_unique<CostmapDownsampler>();
134 _costmap_downsampler->on_configure(
135 node, _global_frame, topic_name, _costmap, _downsampling_factor);
137 _raw_plan_publisher = node->create_publisher<nav_msgs::msg::Path>(
"unsmoothed_plan");
140 _logger,
"Configured plugin %s of type SmacPlanner2D with "
141 "tolerance %.2f, maximum iterations %i, "
142 "max on approach iterations %i, and %s.",
143 _name.c_str(), _tolerance, _max_iterations, _max_on_approach_iterations,
144 _allow_unknown ?
"allowing unknown traversal" :
"not allowing unknown traversal");
147 template<
typename NodeT>
151 _logger,
"Activating plugin %s of type SmacPlanner2D",
153 _raw_plan_publisher->on_activate();
154 if (_costmap_downsampler) {
155 _costmap_downsampler->on_activate();
157 auto node = _node.lock();
159 _post_set_params_handler = node->add_post_set_parameters_callback(
162 this, std::placeholders::_1));
163 _on_set_params_handler = node->add_on_set_parameters_callback(
166 this, std::placeholders::_1));
169 template<
typename NodeT>
173 _logger,
"Deactivating plugin %s of type SmacPlanner2D",
175 _raw_plan_publisher->on_deactivate();
176 if (_costmap_downsampler) {
177 _costmap_downsampler->on_deactivate();
180 auto node = _node.lock();
181 if (_post_set_params_handler && node) {
182 node->remove_post_set_parameters_callback(_post_set_params_handler.get());
184 _post_set_params_handler.reset();
185 if (_on_set_params_handler && node) {
186 node->remove_on_set_parameters_callback(_on_set_params_handler.get());
188 _on_set_params_handler.reset();
191 template<
typename NodeT>
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();
206 template<
typename NodeT>
208 const geometry_msgs::msg::PoseStamped & start,
209 const geometry_msgs::msg::PoseStamped & goal,
210 const std::vector<geometry_msgs::msg::PoseStamped> & viapoints,
211 std::function<
bool()> cancel_checker)
213 if (!viapoints.empty()) {
214 RCLCPP_WARN(_logger,
"Received %zu viapoints, but this planner ignores them",
218 std::lock_guard<std::mutex> lock_reinit(_mutex);
219 steady_clock::time_point a = steady_clock::now();
221 std::unique_lock<nav2_costmap_2d::Costmap2D::mutex_t> lock(*(_costmap->getMutex()));
225 if (_downsample_costmap && _downsampling_factor > 1) {
226 costmap = _costmap_downsampler->downsample(_downsampling_factor);
227 _collision_checker.setCostmap(costmap);
231 _a_star->setCollisionChecker(&_collision_checker);
234 float mx_start, my_start, mx_goal, my_goal;
236 start.pose.position.x,
237 start.pose.position.y,
242 "Start Coordinates of(" + std::to_string(start.pose.position.x) +
", " +
243 std::to_string(start.pose.position.y) +
") was outside bounds");
245 _a_star->setStart(mx_start, my_start, 0);
249 goal.pose.position.x,
250 goal.pose.position.y,
255 "Goal Coordinates of(" + std::to_string(goal.pose.position.x) +
", " +
256 std::to_string(goal.pose.position.y) +
") was outside bounds");
258 _a_star->setGoal(mx_goal, my_goal, 0);
261 nav_msgs::msg::Path plan;
262 plan.header.stamp = _clock->now();
263 plan.header.frame_id = _global_frame;
264 geometry_msgs::msg::PoseStamped pose;
265 pose.header = plan.header;
266 pose.pose.position.z = 0.0;
267 pose.pose.orientation.x = 0.0;
268 pose.pose.orientation.y = 0.0;
269 pose.pose.orientation.z = 0.0;
270 pose.pose.orientation.w = 1.0;
273 if (std::floor(mx_start) == std::floor(mx_goal) && std::floor(my_start) == std::floor(my_goal)) {
274 pose.pose = goal.pose;
278 if (start.pose.orientation != goal.pose.orientation && _use_final_approach_orientation) {
279 pose.pose.orientation = start.pose.orientation;
281 plan.poses.push_back(pose);
284 if (_raw_plan_publisher->get_subscription_count() > 0) {
285 auto msg = std::make_unique<nav_msgs::msg::Path>(plan);
286 _raw_plan_publisher->publish(std::move(msg));
293 typename NodeT::CoordinateVector path;
294 int num_iterations = 0;
296 if (!_a_star->createPath(
297 path, num_iterations,
298 _tolerance /
static_cast<float>(costmap->
getResolution()), cancel_checker))
301 if (num_iterations == 1) {
305 if (num_iterations < _a_star->getMaxIterations()) {
312 const bool reached_goal_cell =
313 std::floor(path.front().x) == std::floor(mx_goal) &&
314 std::floor(path.front().y) == std::floor(my_goal);
317 plan.poses.reserve(path.size());
318 for (
int i = path.size() - 1; i >= 0; --i) {
319 pose.pose = getWorldCoords(path[i].x, path[i].y, costmap);
320 plan.poses.push_back(pose);
324 if (_raw_plan_publisher->get_subscription_count() > 0) {
325 auto msg = std::make_unique<nav_msgs::msg::Path>(plan);
326 _raw_plan_publisher->publish(std::move(msg));
330 steady_clock::time_point b = steady_clock::now();
331 duration<double> time_span = duration_cast<duration<double>>(b - a);
332 double time_remaining = _max_planning_time -
static_cast<double>(time_span.count());
334 #ifdef BENCHMARK_TESTING
335 std::cout <<
"It took " << time_span.count() * 1000 <<
336 " milliseconds with " << num_iterations <<
" iterations." << std::endl;
344 _costmap_ros->getUseRadius() ? std::vector<geometry_msgs::msg::Point>() :
345 _costmap_ros->getRobotFootprint());
352 size_t plan_size = plan.poses.size();
353 if (reached_goal_cell && plan_size > 0) {
354 plan.poses.back().pose.position = goal.pose.position;
357 if (_use_final_approach_orientation) {
358 if (plan_size == 1) {
359 plan.poses.back().pose.orientation = start.pose.orientation;
360 }
else if (plan_size > 1) {
361 double dx, dy, theta;
362 auto last_pose = plan.poses.back().pose.position;
363 auto approach_pose = plan.poses[plan_size - 2].pose.position;
364 dx = last_pose.x - approach_pose.x;
365 dy = last_pose.y - approach_pose.y;
366 theta = atan2(dy, dx);
367 plan.poses.back().pose.orientation =
368 nav2_util::geometry_utils::orientationAroundZAxis(theta);
370 }
else if (plan_size > 0) {
371 plan.poses.back().pose.orientation = goal.pose.orientation;
377 template<
typename NodeT>
378 rcl_interfaces::msg::SetParametersResult
380 const std::vector<rclcpp::Parameter> & parameters)
382 rcl_interfaces::msg::SetParametersResult result;
383 result.successful =
true;
384 for (
const auto & parameter : parameters) {
385 const auto & param_type = parameter.get_type();
386 const auto & param_name = parameter.get_name();
387 if (param_name.find(_name +
".") != 0) {
390 if (param_type == ParameterType::PARAMETER_DOUBLE) {
391 if (parameter.as_double() < 0.0) {
393 _logger,
"The value of parameter '%s' is incorrectly set to %f, "
394 "it should be >=0. Ignoring parameter update.",
395 param_name.c_str(), parameter.as_double());
396 result.successful =
false;
398 }
else if (param_type == ParameterType::PARAMETER_INTEGER) {
399 if (parameter.as_int() <= 0 &&
400 (param_name != _name +
".max_on_approach_iterations" &&
401 param_name != _name +
".max_iterations"))
404 _logger,
"The value of parameter '%s' is incorrectly set to %ld, "
405 "it should be >0. Ignoring parameter update.",
406 param_name.c_str(), parameter.as_int());
407 result.successful =
false;
414 template<
typename NodeT>
418 std::lock_guard<std::mutex> lock_reinit(_mutex);
420 bool reinit_a_star =
false;
421 bool reinit_downsampler =
false;
423 for (
const auto & parameter : parameters) {
424 const auto & param_type = parameter.get_type();
425 const auto & param_name = parameter.get_name();
426 if (param_name.find(_name +
".") != 0) {
429 if (param_type == ParameterType::PARAMETER_DOUBLE) {
430 if (param_name == _name +
".tolerance") {
431 _tolerance =
static_cast<float>(parameter.as_double());
432 }
else if (param_name == _name +
".cost_travel_multiplier") {
433 reinit_a_star =
true;
434 _search_info.cost_penalty = parameter.as_double();
435 }
else if (param_name == _name +
".max_planning_time") {
436 reinit_a_star =
true;
437 _max_planning_time = parameter.as_double();
439 }
else if (param_type == ParameterType::PARAMETER_BOOL) {
440 if (param_name == _name +
".downsample_costmap") {
441 reinit_downsampler =
true;
442 _downsample_costmap = parameter.as_bool();
443 }
else if (param_name == _name +
".allow_unknown") {
444 reinit_a_star =
true;
445 _allow_unknown = parameter.as_bool();
446 }
else if (param_name == _name +
".use_final_approach_orientation") {
447 _use_final_approach_orientation = parameter.as_bool();
449 }
else if (param_type == ParameterType::PARAMETER_INTEGER) {
450 if (param_name == _name +
".downsampling_factor") {
451 reinit_downsampler =
true;
452 _downsampling_factor = parameter.as_int();
453 }
else if (param_name == _name +
".max_iterations") {
454 reinit_a_star =
true;
455 _max_iterations = parameter.as_int();
456 if (_max_iterations <= 0) {
458 _logger,
"maximum iteration selected as <= 0, "
459 "disabling maximum iterations.");
460 _max_iterations = std::numeric_limits<int>::max();
462 }
else if (param_name == _name +
".max_on_approach_iterations") {
463 reinit_a_star =
true;
464 _max_on_approach_iterations = parameter.as_int();
465 if (_max_on_approach_iterations <= 0) {
467 _logger,
"On approach iteration selected as <= 0, "
468 "disabling tolerance and on approach iterations.");
469 _max_on_approach_iterations = std::numeric_limits<int>::max();
471 }
else if (param_name == _name +
".terminal_checking_interval") {
472 reinit_a_star =
true;
473 _terminal_checking_interval = parameter.as_int();
479 if (reinit_a_star || reinit_downsampler) {
482 _a_star->setSearchInfo(_search_info);
486 _max_on_approach_iterations,
487 _terminal_checking_interval,
494 if (reinit_downsampler) {
495 if (_downsample_costmap && _downsampling_factor > 1) {
496 auto node = _node.lock();
497 std::string topic_name =
"downsampled_costmap";
498 _costmap_downsampler = std::make_unique<CostmapDownsampler>();
499 _costmap_downsampler->on_configure(
500 node, _global_frame, topic_name, _costmap, _downsampling_factor);
501 _costmap_downsampler->on_activate();
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.
A templated 2D planner that allows custom node types.
rcl_interfaces::msg::SetParametersResult validateParameterUpdatesCallback(const std::vector< rclcpp::Parameter > ¶meters)
Validate incoming parameter updates before applying them. This callback is triggered when one or more...
SmacPlanner2DT()
constructor
void activate() override
Activate lifecycle node.
void updateParametersCallback(const std::vector< rclcpp::Parameter > ¶meters)
Apply parameter updates after validation This callback is executed when parameters have been successf...
void deactivate() override
Deactivate lifecycle node.
void configure(const nav2::LifecycleNode::WeakPtr &parent, std::string name, nav2::TransformBuffer::SharedPtr tf, std::shared_ptr< nav2_costmap_2d::Costmap2DROS > costmap_ros) override
Configuring plugin.
~SmacPlanner2DT()
destructor
void cleanup() override
Cleanup lifecycle node.
nav_msgs::msg::Path createPlan(const geometry_msgs::msg::PoseStamped &start, const geometry_msgs::msg::PoseStamped &goal, const std::vector< geometry_msgs::msg::PoseStamped > &viapoints, std::function< bool()> cancel_checker) override
Creating a plan from start and goal poses.
Parameters for the smoother cost function.
void get(nav2::LifecycleNode::SharedPtr node, const std::string &name)
Get params from ROS parameter.