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 nav2::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::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::declare_parameter_if_not_declared(
68 node, name +
".downsample_costmap", rclcpp::ParameterValue(
false));
69 node->get_parameter(name +
".downsample_costmap", _downsample_costmap);
70 nav2::declare_parameter_if_not_declared(
71 node, name +
".downsampling_factor", rclcpp::ParameterValue(1));
72 node->get_parameter(name +
".downsampling_factor", _downsampling_factor);
73 nav2::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::declare_parameter_if_not_declared(
78 node, name +
".allow_unknown", rclcpp::ParameterValue(
true));
79 node->get_parameter(name +
".allow_unknown", _allow_unknown);
80 nav2::declare_parameter_if_not_declared(
81 node, name +
".max_iterations", rclcpp::ParameterValue(1000000));
82 node->get_parameter(name +
".max_iterations", _max_iterations);
83 nav2::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::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::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::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::declare_parameter_if_not_declared(
99 node,
"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 std::string topic_name =
"downsampled_costmap";
144 _costmap_downsampler = std::make_unique<CostmapDownsampler>();
145 _costmap_downsampler->on_configure(
146 node, _global_frame, topic_name, _costmap, _downsampling_factor);
148 _raw_plan_publisher = node->create_publisher<nav_msgs::msg::Path>(
"unsmoothed_plan");
151 _logger,
"Configured plugin %s of type SmacPlanner2D with "
152 "tolerance %.2f, maximum iterations %i, "
153 "max on approach iterations %i, and %s.",
154 _name.c_str(), _tolerance, _max_iterations, _max_on_approach_iterations,
155 _allow_unknown ?
"allowing unknown traversal" :
"not allowing unknown traversal");
161 _logger,
"Activating plugin %s of type SmacPlanner2D",
163 _raw_plan_publisher->on_activate();
164 if (_costmap_downsampler) {
165 _costmap_downsampler->on_activate();
167 auto node = _node.lock();
169 _dyn_params_handler = node->add_on_set_parameters_callback(
176 _logger,
"Deactivating plugin %s of type SmacPlanner2D",
178 _raw_plan_publisher->on_deactivate();
179 if (_costmap_downsampler) {
180 _costmap_downsampler->on_deactivate();
183 auto node = _node.lock();
184 if (_dyn_params_handler && node) {
185 node->remove_on_set_parameters_callback(_dyn_params_handler.get());
187 _dyn_params_handler.reset();
193 _logger,
"Cleaning up plugin %s of type SmacPlanner2D",
197 if (_costmap_downsampler) {
198 _costmap_downsampler->on_cleanup();
199 _costmap_downsampler.reset();
201 _raw_plan_publisher.reset();
205 const geometry_msgs::msg::PoseStamped & start,
206 const geometry_msgs::msg::PoseStamped & goal,
207 std::function<
bool()> cancel_checker)
209 std::lock_guard<std::mutex> lock_reinit(_mutex);
210 steady_clock::time_point a = steady_clock::now();
212 std::unique_lock<nav2_costmap_2d::Costmap2D::mutex_t> lock(*(_costmap->getMutex()));
216 if (_downsample_costmap && _downsampling_factor > 1) {
217 costmap = _costmap_downsampler->downsample(_downsampling_factor);
222 _a_star->setCollisionChecker(&_collision_checker);
225 float mx_start, my_start, mx_goal, my_goal;
227 start.pose.position.x,
228 start.pose.position.y,
233 "Start Coordinates of(" + std::to_string(start.pose.position.x) +
", " +
234 std::to_string(start.pose.position.y) +
") was outside bounds");
236 _a_star->setStart(mx_start, my_start, 0);
240 goal.pose.position.x,
241 goal.pose.position.y,
246 "Goal Coordinates of(" + std::to_string(goal.pose.position.x) +
", " +
247 std::to_string(goal.pose.position.y) +
") was outside bounds");
249 _a_star->setGoal(mx_goal, my_goal, 0);
252 nav_msgs::msg::Path plan;
253 plan.header.stamp = _clock->now();
254 plan.header.frame_id = _global_frame;
255 geometry_msgs::msg::PoseStamped pose;
256 pose.header = plan.header;
257 pose.pose.position.z = 0.0;
258 pose.pose.orientation.x = 0.0;
259 pose.pose.orientation.y = 0.0;
260 pose.pose.orientation.z = 0.0;
261 pose.pose.orientation.w = 1.0;
264 if (std::floor(mx_start) == std::floor(mx_goal) && std::floor(my_start) == std::floor(my_goal)) {
265 pose.pose = start.pose;
269 if (start.pose.orientation != goal.pose.orientation && !_use_final_approach_orientation) {
270 pose.pose.orientation = goal.pose.orientation;
272 plan.poses.push_back(pose);
275 if (_raw_plan_publisher->get_subscription_count() > 0) {
276 _raw_plan_publisher->publish(plan);
283 Node2D::CoordinateVector path;
284 int num_iterations = 0;
286 if (!_a_star->createPath(
287 path, num_iterations,
288 _tolerance /
static_cast<float>(costmap->
getResolution()), cancel_checker))
291 if (num_iterations == 1) {
295 if (num_iterations < _a_star->getMaxIterations()) {
303 plan.poses.reserve(path.size());
304 for (
int i = path.size() - 1; i >= 0; --i) {
305 pose.pose = getWorldCoords(path[i].x, path[i].y, costmap);
306 plan.poses.push_back(pose);
310 if (_raw_plan_publisher->get_subscription_count() > 0) {
311 _raw_plan_publisher->publish(plan);
315 steady_clock::time_point b = steady_clock::now();
316 duration<double> time_span = duration_cast<duration<double>>(b - a);
317 double time_remaining = _max_planning_time -
static_cast<double>(time_span.count());
319 #ifdef BENCHMARK_TESTING
320 std::cout <<
"It took " << time_span.count() * 1000 <<
321 " milliseconds with " << num_iterations <<
" iterations." << std::endl;
325 _smoother->smooth(plan, costmap, time_remaining);
332 size_t plan_size = plan.poses.size();
333 if (_use_final_approach_orientation) {
334 if (plan_size == 1) {
335 plan.poses.back().pose.orientation = start.pose.orientation;
336 }
else if (plan_size > 1) {
337 double dx, dy, theta;
338 auto last_pose = plan.poses.back().pose.position;
339 auto approach_pose = plan.poses[plan_size - 2].pose.position;
340 dx = last_pose.x - approach_pose.x;
341 dy = last_pose.y - approach_pose.y;
342 theta = atan2(dy, dx);
343 plan.poses.back().pose.orientation =
344 nav2_util::geometry_utils::orientationAroundZAxis(theta);
346 }
else if (plan_size > 0) {
347 plan.poses.back().pose.orientation = goal.pose.orientation;
353 rcl_interfaces::msg::SetParametersResult
356 rcl_interfaces::msg::SetParametersResult result;
357 std::lock_guard<std::mutex> lock_reinit(_mutex);
359 bool reinit_a_star =
false;
360 bool reinit_downsampler =
false;
362 for (
auto parameter : parameters) {
363 const auto & param_type = parameter.get_type();
364 const auto & param_name = parameter.get_name();
365 if(param_name.find(_name +
".") != 0) {
368 if (param_type == ParameterType::PARAMETER_DOUBLE) {
369 if (param_name == _name +
".tolerance") {
370 _tolerance =
static_cast<float>(parameter.as_double());
371 }
else if (param_name == _name +
".cost_travel_multiplier") {
372 reinit_a_star =
true;
373 _search_info.cost_penalty = parameter.as_double();
374 }
else if (param_name == _name +
".max_planning_time") {
375 reinit_a_star =
true;
376 _max_planning_time = parameter.as_double();
378 }
else if (param_type == ParameterType::PARAMETER_BOOL) {
379 if (param_name == _name +
".downsample_costmap") {
380 reinit_downsampler =
true;
381 _downsample_costmap = parameter.as_bool();
382 }
else if (param_name == _name +
".allow_unknown") {
383 reinit_a_star =
true;
384 _allow_unknown = parameter.as_bool();
385 }
else if (param_name == _name +
".use_final_approach_orientation") {
386 _use_final_approach_orientation = parameter.as_bool();
388 }
else if (param_type == ParameterType::PARAMETER_INTEGER) {
389 if (param_name == _name +
".downsampling_factor") {
390 reinit_downsampler =
true;
391 _downsampling_factor = parameter.as_int();
392 }
else if (param_name == _name +
".max_iterations") {
393 reinit_a_star =
true;
394 _max_iterations = parameter.as_int();
395 if (_max_iterations <= 0) {
397 _logger,
"maximum iteration selected as <= 0, "
398 "disabling maximum iterations.");
399 _max_iterations = std::numeric_limits<int>::max();
401 }
else if (param_name == _name +
".max_on_approach_iterations") {
402 reinit_a_star =
true;
403 _max_on_approach_iterations = parameter.as_int();
404 if (_max_on_approach_iterations <= 0) {
406 _logger,
"On approach iteration selected as <= 0, "
407 "disabling tolerance and on approach iterations.");
408 _max_on_approach_iterations = std::numeric_limits<int>::max();
410 }
else if (param_name == _name +
".terminal_checking_interval") {
411 reinit_a_star =
true;
412 _terminal_checking_interval = parameter.as_int();
418 if (reinit_a_star || reinit_downsampler) {
421 _a_star = std::make_unique<AStarAlgorithm<Node2D>>(_motion_model, _search_info);
425 _max_on_approach_iterations,
426 _terminal_checking_interval,
433 if (reinit_downsampler) {
434 if (_downsample_costmap && _downsampling_factor > 1) {
435 auto node = _node.lock();
436 std::string topic_name =
"downsampled_costmap";
437 _costmap_downsampler = std::make_unique<CostmapDownsampler>();
438 _costmap_downsampler->on_configure(
439 node, _global_frame, topic_name, _costmap, _downsampling_factor);
440 _costmap_downsampler->on_activate();
444 result.successful =
true;
450 #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
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 configure(const nav2::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.
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(nav2::LifecycleNode::SharedPtr node, const std::string &name)
Get params from ROS parameter.