24 #include "nav2_navfn_planner/navfn_planner.hpp"
35 #include "nav2_navfn_planner/navfn.hpp"
36 #include "nav2_util/costmap.hpp"
37 #include "nav2_ros_common/node_utils.hpp"
38 #include "nav2_costmap_2d/cost_values.hpp"
39 #include "nav2_ros_common/tf2_factories.hpp"
41 using namespace std::chrono_literals;
42 using namespace std::chrono;
43 using rcl_interfaces::msg::ParameterType;
44 using std::placeholders::_1;
46 namespace nav2_navfn_planner
49 NavfnPlanner::NavfnPlanner()
50 : tf_(nullptr), costmap_(nullptr)
57 logger_,
"Destroying plugin %s of type NavfnPlanner",
63 const nav2::LifecycleNode::WeakPtr & parent,
64 std::string name, nav2::TransformBuffer::SharedPtr tf,
65 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
69 costmap_ = costmap_ros->getCostmap();
70 global_frame_ = costmap_ros->getGlobalFrameID();
73 auto node = parent.lock();
74 clock_ = node->get_clock();
75 logger_ = node->get_logger();
78 logger_,
"Configuring plugin %s of type NavfnPlanner",
83 param_handler_ = std::make_unique<ParameterHandler>(
84 node, name_, logger_);
85 params_ = param_handler_->getParams();
88 planner_ = std::make_unique<NavFn>(
97 logger_,
"Activating plugin %s of type NavfnPlanner",
99 param_handler_->activate();
106 logger_,
"Deactivating plugin %s of type NavfnPlanner",
108 param_handler_->deactivate();
115 logger_,
"Cleaning up plugin %s of type NavfnPlanner",
121 const geometry_msgs::msg::PoseStamped & start,
122 const geometry_msgs::msg::PoseStamped & goal,
123 const std::vector<geometry_msgs::msg::PoseStamped> & viapoints,
124 std::function<
bool()> cancel_checker)
126 std::unique_lock<nav2_costmap_2d::Costmap2D::mutex_t> costmap_lock(
127 *(costmap_->getMutex()));
129 #ifdef BENCHMARK_TESTING
130 steady_clock::time_point a = steady_clock::now();
133 if (!viapoints.empty()) {
134 RCLCPP_WARN(logger_,
"Received %zu viapoints, but this planner ignores them",
138 std::lock_guard<std::mutex> lock_reinit(param_handler_->getMutex());
139 unsigned int mx_start, my_start, mx_goal, my_goal;
140 if (!costmap_->
worldToMap(start.pose.position.x, start.pose.position.y, mx_start, my_start)) {
142 "Start Coordinates of(" + std::to_string(start.pose.position.x) +
", " +
143 std::to_string(start.pose.position.y) +
") was outside bounds");
146 if (!costmap_->
worldToMap(goal.pose.position.x, goal.pose.position.y, mx_goal, my_goal)) {
148 "Goal Coordinates of(" + std::to_string(goal.pose.position.x) +
", " +
149 std::to_string(goal.pose.position.y) +
") was outside bounds");
152 if (params_->tolerance == 0 && costmap_->
getCost(mx_goal,
153 my_goal) == nav2_costmap_2d::LETHAL_OBSTACLE)
156 "Goal Coordinates of(" + std::to_string(goal.pose.position.x) +
", " +
157 std::to_string(goal.pose.position.y) +
") was in lethal cost");
167 nav_msgs::msg::Path path;
170 if (start.pose.position.x == goal.pose.position.x &&
171 start.pose.position.y == goal.pose.position.y)
173 path.header.stamp = clock_->now();
174 path.header.frame_id = global_frame_;
175 geometry_msgs::msg::PoseStamped pose;
176 pose.header = path.header;
177 pose.pose.position.z = 0.0;
179 pose.pose = start.pose;
183 if (start.pose.orientation != goal.pose.orientation &&
184 !params_->use_final_approach_orientation)
186 pose.pose.orientation = goal.pose.orientation;
188 path.poses.push_back(pose);
192 if (!
makePlan(start.pose, goal.pose, params_->tolerance, cancel_checker, path)) {
194 "Failed to create plan with tolerance of: " + std::to_string(params_->tolerance) );
198 #ifdef BENCHMARK_TESTING
199 steady_clock::time_point b = steady_clock::now();
200 duration<double> time_span = duration_cast<duration<double>>(b - a);
201 std::cout <<
"It took " << time_span.count() * 1000 << std::endl;
210 if (!planner_.get() ||
221 const geometry_msgs::msg::Pose & start,
222 const geometry_msgs::msg::Pose & goal,
double tolerance,
223 std::function<
bool()> cancel_checker,
224 nav_msgs::msg::Path & plan)
229 plan.header.stamp = clock_->now();
230 plan.header.frame_id = global_frame_;
232 double wx = start.position.x;
233 double wy = start.position.y;
236 logger_,
"Making plan from (%.2f,%.2f) to (%.2f,%.2f)",
237 start.position.x, start.position.y, goal.position.x, goal.position.y);
239 std::unique_lock<nav2_costmap_2d::Costmap2D::mutex_t> lock(*(costmap_->getMutex()));
252 planner_->setCostmap(costmap_->
getCharMap(),
true, params_->allow_unknown);
258 wx = goal.position.x;
259 wy = goal.position.y;
266 planner_->setStart(map_goal);
267 planner_->setGoal(map_start);
268 if (params_->use_astar) {
269 planner_->calcNavFnAstar(cancel_checker);
271 planner_->calcNavFnDijkstra(cancel_checker,
true);
275 geometry_msgs::msg::Pose p, best_pose;
277 bool found_legal =
false;
281 if (potential < POT_HIGH) {
288 double best_sdist = std::numeric_limits<double>::max();
290 p.position.y = goal.position.y - tolerance;
291 while (p.position.y <= goal.position.y + tolerance) {
292 p.position.x = goal.position.x - tolerance;
293 while (p.position.x <= goal.position.x + tolerance) {
295 if (potential < POT_HIGH) {
297 if (sdist < best_sdist) {
303 p.position.x += resolution;
305 p.position.y += resolution;
318 if (params_->use_final_approach_orientation) {
319 size_t plan_size = plan.poses.size();
320 if (plan_size == 1) {
321 plan.poses.back().pose.orientation = start.orientation;
322 }
else if (plan_size > 1) {
323 double dx, dy, theta;
324 auto last_pose = plan.poses.back().pose.position;
325 auto approach_pose = plan.poses[plan_size - 2].pose.position;
327 if (std::abs(last_pose.x - approach_pose.x) < 0.0001 &&
328 std::abs(last_pose.y - approach_pose.y) < 0.0001 && plan_size > 2)
330 approach_pose = plan.poses[plan_size - 3].pose.position;
332 dx = last_pose.x - approach_pose.x;
333 dy = last_pose.y - approach_pose.y;
334 theta = atan2(dy, dx);
335 plan.poses.back().pose.orientation =
336 nav2_util::geometry_utils::orientationAroundZAxis(theta);
342 "Failed to create a plan from potential when a legal"
343 " potential was found. This shouldn't happen.");
347 return !plan.poses.empty();
352 const geometry_msgs::msg::Pose & goal,
353 nav_msgs::msg::Path & plan)
355 if (plan.poses.size() >= 2) {
356 auto second_to_last_pose = plan.poses.end()[-2];
357 auto last_pose = plan.poses.back();
364 plan.poses.back().pose = goal;
369 plan.poses.back().pose = goal;
373 geometry_msgs::msg::PoseStamped goal_copy;
374 goal_copy.pose = goal;
375 goal_copy.header = plan.header;
376 plan.poses.push_back(goal_copy);
381 const geometry_msgs::msg::Pose & goal,
382 nav_msgs::msg::Path & plan)
388 double wx = goal.position.x;
389 double wy = goal.position.y;
399 planner_->setStart(map_goal);
401 const int max_dimension =
static_cast<int>(
404 const int max_cycles = max_dimension * params_->max_cycles_factor;
406 int path_len = planner_->calcPath(max_cycles);
411 auto cost = planner_->getLastPathCost();
414 "Path found, %d steps, %f cost\n", path_len, cost);
417 float * x = planner_->getPathX();
418 float * y = planner_->getPathY();
419 int len = planner_->getPathLen();
421 for (
int i = len - 1; i >= 0; --i) {
423 double world_x, world_y;
426 geometry_msgs::msg::PoseStamped pose;
427 pose.header = plan.header;
428 pose.pose.position.x = world_x;
429 pose.pose.position.y = world_y;
430 pose.pose.position.z = 0.0;
431 pose.pose.orientation.x = 0.0;
432 pose.pose.orientation.y = 0.0;
433 pose.pose.orientation.z = 0.0;
434 pose.pose.orientation.w = 1.0;
435 plan.poses.push_back(pose);
438 return !plan.poses.empty();
445 if (!
worldToMap(world_point.x, world_point.y, mx, my)) {
446 return std::numeric_limits<double>::max();
449 unsigned int index = my * planner_->nx + mx;
450 return planner_->potarr[index];
493 if (wx < costmap_->getOriginX() || wy < costmap_->getOriginY()) {
497 mx =
static_cast<int>(
499 my =
static_cast<int>(
502 if (mx < costmap_->getSizeInCellsX() && my < costmap_->
getSizeInCellsY()) {
508 "worldToMap failed: mx,my: %d,%d, size_x,size_y: %d,%d", mx, my,
526 costmap_->
setCost(mx, my, nav2_costmap_2d::FREE_SPACE);
531 #include "pluginlib/class_list_macros.hpp"
Abstract interface for global planners to adhere to with pluginlib.
unsigned char getCost(unsigned int mx, unsigned int my) const
Get the cost of a cell in the costmap.
unsigned char * getCharMap() const
Will return a pointer to the underlying unsigned char array used as the costmap.
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.
unsigned int getSizeInCellsX() const
Accessor for the x size of the costmap in cells.
double getOriginY() const
Accessor for the y origin of the costmap.
unsigned int getSizeInCellsY() const
Accessor for the y size of the costmap in cells.
double getOriginX() const
Accessor for the x origin of the costmap.
void setCost(unsigned int mx, unsigned int my, unsigned char cost)
Set the cost of a cell in the costmap.
double squared_distance(const geometry_msgs::msg::Pose &p1, const geometry_msgs::msg::Pose &p2)
Compute the squared distance between two points.
~NavfnPlanner()
destructor
double getPointPotential(const geometry_msgs::msg::Point &world_point)
Compute the potential, or navigation cost, at a given point in the world must call computePotential f...
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.
void activate() override
Activate lifecycle node.
void mapToWorld(double mx, double my, double &wx, double &wy)
Transform a point from map to world frame.
bool makePlan(const geometry_msgs::msg::Pose &start, const geometry_msgs::msg::Pose &goal, double tolerance, std::function< bool()> cancel_checker, nav_msgs::msg::Path &plan)
Compute a plan given start and goal poses, provided in global world frame.
bool worldToMap(double wx, double wy, unsigned int &mx, unsigned int &my)
Transform a point from world to map frame.
bool isPlannerOutOfDate()
Determine if a new planner object should be made.
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.
void clearRobotCell(unsigned int mx, unsigned int my)
Set the corresponding cell cost to be free space.
void smoothApproachToGoal(const geometry_msgs::msg::Pose &goal, nav_msgs::msg::Path &plan)
Remove artifacts at the end of the path - originated from planning on a discretized world.
void cleanup() override
Cleanup lifecycle node.
void deactivate() override
Deactivate lifecycle node.
bool getPlanFromPotential(const geometry_msgs::msg::Pose &goal, nav_msgs::msg::Path &plan)
Compute a plan to a goal from a potential - must call computePotential first.