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);
254 _costmap->
worldToMap(start.pose.position.x, start.pose.position.y, mx, my);
257 NodeLattice::motion_table.getClosestAngularBin(tf2::getYaw(start.pose.orientation)));
260 _costmap->
worldToMap(goal.pose.position.x, goal.pose.position.y, mx, my);
263 NodeLattice::motion_table.getClosestAngularBin(tf2::getYaw(goal.pose.orientation)));
266 nav_msgs::msg::Path plan;
267 plan.header.stamp = _clock->now();
268 plan.header.frame_id = _global_frame;
269 geometry_msgs::msg::PoseStamped pose;
270 pose.header = plan.header;
271 pose.pose.position.z = 0.0;
272 pose.pose.orientation.x = 0.0;
273 pose.pose.orientation.y = 0.0;
274 pose.pose.orientation.z = 0.0;
275 pose.pose.orientation.w = 1.0;
278 NodeLattice::CoordinateVector path;
279 int num_iterations = 0;
282 if (!_a_star->createPath(
283 path, num_iterations, _tolerance /
static_cast<float>(_costmap->
getResolution())))
285 if (num_iterations < _a_star->getMaxIterations()) {
286 error = std::string(
"no valid path found");
288 error = std::string(
"exceeded maximum iterations");
291 }
catch (
const std::runtime_error & e) {
292 error =
"invalid use: ";
296 if (!error.empty()) {
299 "%s: failed to create plan, %s.",
300 _name.c_str(), error.c_str());
305 plan.poses.reserve(path.size());
306 geometry_msgs::msg::PoseStamped last_pose = pose;
307 for (
int i = path.size() - 1; i >= 0; --i) {
308 pose.pose = getWorldCoords(path[i].x, path[i].y, _costmap);
309 pose.pose.orientation = getWorldOrientation(path[i].theta);
310 if (fabs(pose.pose.position.x - last_pose.pose.position.x) < 1e-4 &&
311 fabs(pose.pose.position.y - last_pose.pose.position.y) < 1e-4 &&
312 fabs(tf2::getYaw(pose.pose.orientation) - tf2::getYaw(last_pose.pose.orientation)) < 1e-4)
316 "Removed a path from the path due to replication. "
317 "Make sure your minimum control set does not contain duplicate values!");
321 plan.poses.push_back(pose);
325 if (_raw_plan_publisher->get_subscription_count() > 0) {
326 _raw_plan_publisher->publish(plan);
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;
340 if (_smoother && num_iterations > 1) {
341 _smoother->smooth(plan, _costmap, time_remaining);
344 #ifdef BENCHMARK_TESTING
345 steady_clock::time_point c = steady_clock::now();
346 duration<double> time_span2 = duration_cast<duration<double>>(c - b);
347 std::cout <<
"It took " << time_span2.count() * 1000 <<
348 " milliseconds to smooth path." << std::endl;
354 rcl_interfaces::msg::SetParametersResult
357 rcl_interfaces::msg::SetParametersResult result;
358 std::lock_guard<std::mutex> lock_reinit(_mutex);
360 bool reinit_a_star =
false;
361 bool reinit_smoother =
false;
363 for (
auto parameter : parameters) {
364 const auto & type = parameter.get_type();
365 const auto & name = parameter.get_name();
367 if (type == ParameterType::PARAMETER_DOUBLE) {
368 if (name == _name +
".max_planning_time") {
369 reinit_a_star =
true;
370 _max_planning_time = parameter.as_double();
371 }
else if (name == _name +
".tolerance") {
372 _tolerance =
static_cast<float>(parameter.as_double());
373 }
else if (name == _name +
".lookup_table_size") {
374 reinit_a_star =
true;
375 _lookup_table_size = parameter.as_double();
376 }
else if (name == _name +
".reverse_penalty") {
377 reinit_a_star =
true;
378 _search_info.reverse_penalty =
static_cast<float>(parameter.as_double());
379 }
else if (name == _name +
".change_penalty") {
380 reinit_a_star =
true;
381 _search_info.change_penalty =
static_cast<float>(parameter.as_double());
382 }
else if (name == _name +
".non_straight_penalty") {
383 reinit_a_star =
true;
384 _search_info.non_straight_penalty =
static_cast<float>(parameter.as_double());
385 }
else if (name == _name +
".cost_penalty") {
386 reinit_a_star =
true;
387 _search_info.cost_penalty =
static_cast<float>(parameter.as_double());
388 }
else if (name == _name +
".rotation_penalty") {
389 reinit_a_star =
true;
390 _search_info.rotation_penalty =
static_cast<float>(parameter.as_double());
391 }
else if (name == _name +
".analytic_expansion_ratio") {
392 reinit_a_star =
true;
393 _search_info.analytic_expansion_ratio =
static_cast<float>(parameter.as_double());
394 }
else if (name == _name +
".analytic_expansion_max_length") {
395 reinit_a_star =
true;
396 _search_info.analytic_expansion_max_length =
397 static_cast<float>(parameter.as_double()) / _costmap->
getResolution();
399 }
else if (type == ParameterType::PARAMETER_BOOL) {
400 if (name == _name +
".allow_unknown") {
401 reinit_a_star =
true;
402 _allow_unknown = parameter.as_bool();
403 }
else if (name == _name +
".cache_obstacle_heuristic") {
404 reinit_a_star =
true;
405 _search_info.cache_obstacle_heuristic = parameter.as_bool();
406 }
else if (name == _name +
".allow_reverse_expansion") {
407 reinit_a_star =
true;
408 _search_info.allow_reverse_expansion = parameter.as_bool();
409 }
else if (name == _name +
".smooth_path") {
410 if (parameter.as_bool()) {
411 reinit_smoother =
true;
416 }
else if (type == ParameterType::PARAMETER_INTEGER) {
417 if (name == _name +
".max_iterations") {
418 reinit_a_star =
true;
419 _max_iterations = parameter.as_int();
420 if (_max_iterations <= 0) {
422 _logger,
"maximum iteration selected as <= 0, "
423 "disabling maximum iterations.");
424 _max_iterations = std::numeric_limits<int>::max();
427 }
else if (name == _name +
".max_on_approach_iterations") {
428 reinit_a_star =
true;
429 _max_on_approach_iterations = parameter.as_int();
430 if (_max_on_approach_iterations <= 0) {
432 _logger,
"On approach iteration selected as <= 0, "
433 "disabling tolerance and on approach iterations.");
434 _max_on_approach_iterations = std::numeric_limits<int>::max();
436 }
else if (type == ParameterType::PARAMETER_STRING) {
437 if (name == _name +
".lattice_filepath") {
438 reinit_a_star =
true;
440 reinit_smoother =
true;
442 _search_info.lattice_filepath = parameter.as_string();
444 _search_info.minimum_turning_radius =
451 if (reinit_a_star || reinit_smoother) {
453 _search_info.minimum_turning_radius =
455 float lookup_table_dim =
456 static_cast<float>(_lookup_table_size) /
460 lookup_table_dim =
static_cast<float>(
static_cast<int>(lookup_table_dim));
463 if (
static_cast<int>(lookup_table_dim) % 2 == 0) {
466 "Even sized heuristic lookup table size set %f, increasing size by 1 to make odd",
468 lookup_table_dim += 1.0;
472 if (reinit_smoother) {
473 auto node = _node.lock();
475 params.
get(node, _name);
476 _smoother = std::make_unique<Smoother>(params);
477 _smoother->initialize(_metadata.min_turning_radius);
482 _a_star = std::make_unique<AStarAlgorithm<NodeLattice>>(_motion_model, _search_info);
486 _max_on_approach_iterations,
489 _metadata.number_of_headings);
493 result.successful =
true;
499 #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.