22 #include "nav2_smac_planner/smac_planner_lattice.hpp"
26 namespace nav2_smac_planner
29 using namespace std::chrono;
30 using rcl_interfaces::msg::ParameterType;
34 _collision_checker(nullptr, 1, nullptr),
43 _logger,
"Destroying plugin %s of type SmacPlannerLattice",
48 const rclcpp_lifecycle::LifecycleNode::WeakPtr & parent,
49 std::string name, std::shared_ptr<tf2_ros::Buffer>,
50 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
53 auto node = parent.lock();
54 _logger = node->get_logger();
55 _clock = node->get_clock();
56 _costmap = costmap_ros->getCostmap();
57 _costmap_ros = costmap_ros;
59 _global_frame = costmap_ros->getGlobalFrameID();
60 _raw_plan_publisher = node->create_publisher<nav_msgs::msg::Path>(
"unsmoothed_plan", 1);
62 RCLCPP_INFO(_logger,
"Configuring %s of type SmacPlannerLattice", name.c_str());
65 double analytic_expansion_max_length_m;
68 nav2_util::declare_parameter_if_not_declared(
69 node, name +
".tolerance", rclcpp::ParameterValue(0.25));
70 _tolerance =
static_cast<float>(node->get_parameter(name +
".tolerance").as_double());
71 nav2_util::declare_parameter_if_not_declared(
72 node, name +
".allow_unknown", rclcpp::ParameterValue(
true));
73 node->get_parameter(name +
".allow_unknown", _allow_unknown);
74 nav2_util::declare_parameter_if_not_declared(
75 node, name +
".max_iterations", rclcpp::ParameterValue(1000000));
76 node->get_parameter(name +
".max_iterations", _max_iterations);
77 nav2_util::declare_parameter_if_not_declared(
78 node, name +
".max_on_approach_iterations", rclcpp::ParameterValue(1000));
79 node->get_parameter(name +
".max_on_approach_iterations", _max_on_approach_iterations);
80 nav2_util::declare_parameter_if_not_declared(
81 node, name +
".smooth_path", rclcpp::ParameterValue(
true));
82 node->get_parameter(name +
".smooth_path", smooth_path);
85 nav2_util::declare_parameter_if_not_declared(
86 node, name +
".lattice_filepath", rclcpp::ParameterValue(
87 ament_index_cpp::get_package_share_directory(
"nav2_smac_planner") +
88 "/sample_primitives/5cm_resolution/0.5m_turning_radius/ackermann/output.json"));
89 node->get_parameter(name +
".lattice_filepath", _search_info.lattice_filepath);
90 nav2_util::declare_parameter_if_not_declared(
91 node, name +
".cache_obstacle_heuristic", rclcpp::ParameterValue(
false));
92 node->get_parameter(name +
".cache_obstacle_heuristic", _search_info.cache_obstacle_heuristic);
93 nav2_util::declare_parameter_if_not_declared(
94 node, name +
".reverse_penalty", rclcpp::ParameterValue(2.0));
95 node->get_parameter(name +
".reverse_penalty", _search_info.reverse_penalty);
96 nav2_util::declare_parameter_if_not_declared(
97 node, name +
".change_penalty", rclcpp::ParameterValue(0.05));
98 node->get_parameter(name +
".change_penalty", _search_info.change_penalty);
99 nav2_util::declare_parameter_if_not_declared(
100 node, name +
".non_straight_penalty", rclcpp::ParameterValue(1.05));
101 node->get_parameter(name +
".non_straight_penalty", _search_info.non_straight_penalty);
102 nav2_util::declare_parameter_if_not_declared(
103 node, name +
".cost_penalty", rclcpp::ParameterValue(2.0));
104 node->get_parameter(name +
".cost_penalty", _search_info.cost_penalty);
105 nav2_util::declare_parameter_if_not_declared(
106 node, name +
".retrospective_penalty", rclcpp::ParameterValue(0.015));
107 node->get_parameter(name +
".retrospective_penalty", _search_info.retrospective_penalty);
108 nav2_util::declare_parameter_if_not_declared(
109 node, name +
".rotation_penalty", rclcpp::ParameterValue(5.0));
110 node->get_parameter(name +
".rotation_penalty", _search_info.rotation_penalty);
111 nav2_util::declare_parameter_if_not_declared(
112 node, name +
".analytic_expansion_ratio", rclcpp::ParameterValue(3.5));
113 node->get_parameter(name +
".analytic_expansion_ratio", _search_info.analytic_expansion_ratio);
114 nav2_util::declare_parameter_if_not_declared(
115 node, name +
".analytic_expansion_max_length", rclcpp::ParameterValue(3.0));
116 node->get_parameter(name +
".analytic_expansion_max_length", analytic_expansion_max_length_m);
117 _search_info.analytic_expansion_max_length =
120 nav2_util::declare_parameter_if_not_declared(
121 node, name +
".max_planning_time", rclcpp::ParameterValue(5.0));
122 node->get_parameter(name +
".max_planning_time", _max_planning_time);
123 nav2_util::declare_parameter_if_not_declared(
124 node, name +
".lookup_table_size", rclcpp::ParameterValue(20.0));
125 node->get_parameter(name +
".lookup_table_size", _lookup_table_size);
126 nav2_util::declare_parameter_if_not_declared(
127 node, name +
".allow_reverse_expansion", rclcpp::ParameterValue(
false));
128 node->get_parameter(name +
".allow_reverse_expansion", _search_info.allow_reverse_expansion);
131 _search_info.minimum_turning_radius =
133 _motion_model = MotionModel::STATE_LATTICE;
135 if (_max_on_approach_iterations <= 0) {
137 _logger,
"On approach iteration selected as <= 0, "
138 "disabling tolerance and on approach iterations.");
139 _max_on_approach_iterations = std::numeric_limits<int>::max();
142 if (_max_iterations <= 0) {
144 _logger,
"maximum iteration selected as <= 0, "
145 "disabling maximum iterations.");
146 _max_iterations = std::numeric_limits<int>::max();
149 float lookup_table_dim =
150 static_cast<float>(_lookup_table_size) /
154 lookup_table_dim =
static_cast<float>(
static_cast<int>(lookup_table_dim));
157 if (
static_cast<int>(lookup_table_dim) % 2 == 0) {
160 "Even sized heuristic lookup table size set %f, increasing size by 1 to make odd",
162 lookup_table_dim += 1.0;
174 costmap_ros->getRobotFootprint(),
175 costmap_ros->getUseRadius(),
176 findCircumscribedCost(costmap_ros));
179 _a_star = std::make_unique<AStarAlgorithm<NodeLattice>>(_motion_model, _search_info);
183 _max_on_approach_iterations,
186 _metadata.number_of_headings);
191 params.
get(node, name);
192 _smoother = std::make_unique<Smoother>(params);
193 _smoother->initialize(_metadata.min_turning_radius);
197 _logger,
"Configured plugin %s of type SmacPlannerLattice with "
198 "maximum iterations %i, max on approach iterations %i, "
199 "and %s. Tolerance %.2f. Using motion model: %s. State lattice file: %s.",
200 _name.c_str(), _max_iterations, _max_on_approach_iterations,
201 _allow_unknown ?
"allowing unknown traversal" :
"not allowing unknown traversal",
202 _tolerance, toString(_motion_model).c_str(), _search_info.lattice_filepath.c_str());
208 _logger,
"Activating plugin %s of type SmacPlannerLattice",
210 _raw_plan_publisher->on_activate();
211 auto node = _node.lock();
213 _dyn_params_handler = node->add_on_set_parameters_callback(
220 _logger,
"Deactivating plugin %s of type SmacPlannerLattice",
222 _raw_plan_publisher->on_deactivate();
223 _dyn_params_handler.reset();
229 _logger,
"Cleaning up plugin %s of type SmacPlannerLattice",
233 _raw_plan_publisher.reset();
237 const geometry_msgs::msg::PoseStamped & start,
238 const geometry_msgs::msg::PoseStamped & goal)
240 std::lock_guard<std::mutex> lock_reinit(_mutex);
241 steady_clock::time_point a = steady_clock::now();
243 std::unique_lock<nav2_costmap_2d::Costmap2D::mutex_t> lock(*(_costmap->getMutex()));
247 _costmap_ros->getRobotFootprint(),
248 _costmap_ros->getUseRadius(),
249 findCircumscribedCost(_costmap_ros));
250 _a_star->setCollisionChecker(&_collision_checker);
253 unsigned int mx_start, my_start, mx_goal, my_goal;
254 _costmap->
worldToMap(start.pose.position.x, start.pose.position.y, mx_start, my_start);
255 unsigned int start_bin =
256 NodeLattice::motion_table.getClosestAngularBin(tf2::getYaw(start.pose.orientation));
258 mx_start, my_start, start_bin);
261 _costmap->
worldToMap(goal.pose.position.x, goal.pose.position.y, mx_goal, my_goal);
262 unsigned int goal_bin =
263 NodeLattice::motion_table.getClosestAngularBin(tf2::getYaw(goal.pose.orientation));
265 mx_goal, my_goal, goal_bin);
268 nav_msgs::msg::Path plan;
269 plan.header.stamp = _clock->now();
270 plan.header.frame_id = _global_frame;
271 geometry_msgs::msg::PoseStamped pose;
272 pose.header = plan.header;
273 pose.pose.position.z = 0.0;
274 pose.pose.orientation.x = 0.0;
275 pose.pose.orientation.y = 0.0;
276 pose.pose.orientation.z = 0.0;
277 pose.pose.orientation.w = 1.0;
280 if (std::floor(mx_start) == std::floor(mx_goal) &&
281 std::floor(my_start) == std::floor(my_goal) &&
282 start_bin == goal_bin)
284 pose.pose = start.pose;
285 pose.pose.orientation = goal.pose.orientation;
286 plan.poses.push_back(pose);
289 if (_raw_plan_publisher->get_subscription_count() > 0) {
290 _raw_plan_publisher->publish(plan);
297 NodeLattice::CoordinateVector path;
298 int num_iterations = 0;
301 if (!_a_star->createPath(
302 path, num_iterations, _tolerance /
static_cast<float>(_costmap->
getResolution())))
304 if (num_iterations < _a_star->getMaxIterations()) {
305 error = std::string(
"no valid path found");
307 error = std::string(
"exceeded maximum iterations");
310 }
catch (
const std::runtime_error & e) {
311 error =
"invalid use: ";
315 if (!error.empty()) {
318 "%s: failed to create plan, %s.",
319 _name.c_str(), error.c_str());
324 plan.poses.reserve(path.size());
325 geometry_msgs::msg::PoseStamped last_pose = pose;
326 for (
int i = path.size() - 1; i >= 0; --i) {
327 pose.pose = getWorldCoords(path[i].x, path[i].y, _costmap);
328 pose.pose.orientation = getWorldOrientation(path[i].theta);
329 if (fabs(pose.pose.position.x - last_pose.pose.position.x) < 1e-4 &&
330 fabs(pose.pose.position.y - last_pose.pose.position.y) < 1e-4 &&
331 fabs(tf2::getYaw(pose.pose.orientation) - tf2::getYaw(last_pose.pose.orientation)) < 1e-4)
335 "Removed a path from the path due to replication. "
336 "Make sure your minimum control set does not contain duplicate values!");
340 plan.poses.push_back(pose);
344 if (_raw_plan_publisher->get_subscription_count() > 0) {
345 _raw_plan_publisher->publish(plan);
349 steady_clock::time_point b = steady_clock::now();
350 duration<double> time_span = duration_cast<duration<double>>(b - a);
351 double time_remaining = _max_planning_time -
static_cast<double>(time_span.count());
353 #ifdef BENCHMARK_TESTING
354 std::cout <<
"It took " << time_span.count() * 1000 <<
355 " milliseconds with " << num_iterations <<
" iterations." << std::endl;
359 if (_smoother && num_iterations > 1) {
360 _smoother->smooth(plan, _costmap, time_remaining);
363 #ifdef BENCHMARK_TESTING
364 steady_clock::time_point c = steady_clock::now();
365 duration<double> time_span2 = duration_cast<duration<double>>(c - b);
366 std::cout <<
"It took " << time_span2.count() * 1000 <<
367 " milliseconds to smooth path." << std::endl;
373 rcl_interfaces::msg::SetParametersResult
376 rcl_interfaces::msg::SetParametersResult result;
377 std::lock_guard<std::mutex> lock_reinit(_mutex);
379 bool reinit_a_star =
false;
380 bool reinit_smoother =
false;
382 for (
auto parameter : parameters) {
383 const auto & type = parameter.get_type();
384 const auto & name = parameter.get_name();
386 if (type == ParameterType::PARAMETER_DOUBLE) {
387 if (name == _name +
".max_planning_time") {
388 reinit_a_star =
true;
389 _max_planning_time = parameter.as_double();
390 }
else if (name == _name +
".tolerance") {
391 _tolerance =
static_cast<float>(parameter.as_double());
392 }
else if (name == _name +
".lookup_table_size") {
393 reinit_a_star =
true;
394 _lookup_table_size = parameter.as_double();
395 }
else if (name == _name +
".reverse_penalty") {
396 reinit_a_star =
true;
397 _search_info.reverse_penalty =
static_cast<float>(parameter.as_double());
398 }
else if (name == _name +
".change_penalty") {
399 reinit_a_star =
true;
400 _search_info.change_penalty =
static_cast<float>(parameter.as_double());
401 }
else if (name == _name +
".non_straight_penalty") {
402 reinit_a_star =
true;
403 _search_info.non_straight_penalty =
static_cast<float>(parameter.as_double());
404 }
else if (name == _name +
".cost_penalty") {
405 reinit_a_star =
true;
406 _search_info.cost_penalty =
static_cast<float>(parameter.as_double());
407 }
else if (name == _name +
".rotation_penalty") {
408 reinit_a_star =
true;
409 _search_info.rotation_penalty =
static_cast<float>(parameter.as_double());
410 }
else if (name == _name +
".analytic_expansion_ratio") {
411 reinit_a_star =
true;
412 _search_info.analytic_expansion_ratio =
static_cast<float>(parameter.as_double());
413 }
else if (name == _name +
".analytic_expansion_max_length") {
414 reinit_a_star =
true;
415 _search_info.analytic_expansion_max_length =
416 static_cast<float>(parameter.as_double()) / _costmap->
getResolution();
418 }
else if (type == ParameterType::PARAMETER_BOOL) {
419 if (name == _name +
".allow_unknown") {
420 reinit_a_star =
true;
421 _allow_unknown = parameter.as_bool();
422 }
else if (name == _name +
".cache_obstacle_heuristic") {
423 reinit_a_star =
true;
424 _search_info.cache_obstacle_heuristic = parameter.as_bool();
425 }
else if (name == _name +
".allow_reverse_expansion") {
426 reinit_a_star =
true;
427 _search_info.allow_reverse_expansion = parameter.as_bool();
428 }
else if (name == _name +
".smooth_path") {
429 if (parameter.as_bool()) {
430 reinit_smoother =
true;
435 }
else if (type == ParameterType::PARAMETER_INTEGER) {
436 if (name == _name +
".max_iterations") {
437 reinit_a_star =
true;
438 _max_iterations = parameter.as_int();
439 if (_max_iterations <= 0) {
441 _logger,
"maximum iteration selected as <= 0, "
442 "disabling maximum iterations.");
443 _max_iterations = std::numeric_limits<int>::max();
446 }
else if (name == _name +
".max_on_approach_iterations") {
447 reinit_a_star =
true;
448 _max_on_approach_iterations = parameter.as_int();
449 if (_max_on_approach_iterations <= 0) {
451 _logger,
"On approach iteration selected as <= 0, "
452 "disabling tolerance and on approach iterations.");
453 _max_on_approach_iterations = std::numeric_limits<int>::max();
455 }
else if (type == ParameterType::PARAMETER_STRING) {
456 if (name == _name +
".lattice_filepath") {
457 reinit_a_star =
true;
459 reinit_smoother =
true;
461 _search_info.lattice_filepath = parameter.as_string();
463 _search_info.minimum_turning_radius =
470 if (reinit_a_star || reinit_smoother) {
472 _search_info.minimum_turning_radius =
474 float lookup_table_dim =
475 static_cast<float>(_lookup_table_size) /
479 lookup_table_dim =
static_cast<float>(
static_cast<int>(lookup_table_dim));
482 if (
static_cast<int>(lookup_table_dim) % 2 == 0) {
485 "Even sized heuristic lookup table size set %f, increasing size by 1 to make odd",
487 lookup_table_dim += 1.0;
491 if (reinit_smoother) {
492 auto node = _node.lock();
494 params.
get(node, _name);
495 _smoother = std::make_unique<Smoother>(params);
496 _smoother->initialize(_metadata.min_turning_radius);
501 _a_star = std::make_unique<AStarAlgorithm<NodeLattice>>(_motion_model, _search_info);
505 _max_on_approach_iterations,
508 _metadata.number_of_headings);
512 result.successful =
true;
518 #include "pluginlib/class_list_macros.hpp"
Abstract interface for global planners to adhere to with pluginlib.
bool worldToMap(double wx, double wy, unsigned int &mx, unsigned int &my) const
Convert from world coordinates to map coordinates.
double getResolution() const
Accessor for the resolution of the costmap.
A costmap grid collision checker.
void setFootprint(const nav2_costmap_2d::Footprint &footprint, const bool &radius, const double &possible_inscribed_cost)
A constructor for nav2_smac_planner::GridCollisionChecker for use when irregular bin intervals are ap...
void deactivate() override
Deactivate lifecycle node.
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.
nav_msgs::msg::Path createPlan(const geometry_msgs::msg::PoseStamped &start, const geometry_msgs::msg::PoseStamped &goal) override
Creating a plan from start and goal poses.
~SmacPlannerLattice()
destructor
rcl_interfaces::msg::SetParametersResult dynamicParametersCallback(std::vector< rclcpp::Parameter > parameters)
Callback executed when a paramter change is detected.
void cleanup() override
Cleanup lifecycle node.
SmacPlannerLattice()
constructor
void activate() override
Activate lifecycle node.
static LatticeMetadata getLatticeMetadata(const std::string &lattice_filepath)
Get file metadata needed.
Parameters for the smoother cost function.
void get(std::shared_ptr< rclcpp_lifecycle::LifecycleNode > node, const std::string &name)
Get params from ROS parameter.