-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
Copy pathpath_handler.cpp
151 lines (130 loc) · 5.52 KB
/
path_handler.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright (c) 2022 Samsung Research America
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <string>
#include <limits>
#include <memory>
#include <vector>
#include <utility>
#include "nav2_regulated_pure_pursuit_controller/path_handler.hpp"
#include "nav2_core/controller_exceptions.hpp"
#include "nav2_util/node_utils.hpp"
#include "nav2_util/geometry_utils.hpp"
namespace nav2_regulated_pure_pursuit_controller
{
using nav2_util::geometry_utils::euclidean_distance;
PathHandler::PathHandler(
tf2::Duration transform_tolerance,
std::shared_ptr<tf2_ros::Buffer> tf,
std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
: transform_tolerance_(transform_tolerance), tf_(tf), costmap_ros_(costmap_ros)
{
}
double PathHandler::getCostmapMaxExtent() const
{
const double max_costmap_dim_meters = std::max(
costmap_ros_->getCostmap()->getSizeInMetersX(),
costmap_ros_->getCostmap()->getSizeInMetersY());
return max_costmap_dim_meters / 2.0;
}
nav_msgs::msg::Path PathHandler::transformGlobalPlan(
const geometry_msgs::msg::PoseStamped & pose,
double max_robot_pose_search_dist,
bool reject_unit_path)
{
if (global_plan_.poses.empty()) {
throw nav2_core::InvalidPath("Received plan with zero length");
}
if (reject_unit_path && global_plan_.poses.size() == 1) {
throw nav2_core::InvalidPath("Received plan with length of one");
}
// let's get the pose of the robot in the frame of the plan
geometry_msgs::msg::PoseStamped robot_pose;
if (!transformPose(global_plan_.header.frame_id, pose, robot_pose)) {
throw nav2_core::ControllerTFError("Unable to transform robot pose into global plan's frame");
}
auto closest_pose_upper_bound =
nav2_util::geometry_utils::first_after_integrated_distance(
global_plan_.poses.begin(), global_plan_.poses.end(), max_robot_pose_search_dist);
// First find the closest pose on the path to the robot
// bounded by when the path turns around (if it does) so we don't get a pose from a later
// portion of the path
auto transformation_begin =
nav2_util::geometry_utils::min_by(
global_plan_.poses.begin(), closest_pose_upper_bound,
[&robot_pose](const geometry_msgs::msg::PoseStamped & ps) {
return euclidean_distance(robot_pose, ps);
});
// Make sure we always have at least 2 points on the transformed plan and that we don't prune
// the global plan below 2 points in order to have always enough point to interpolate the
// end of path direction
if (global_plan_.poses.begin() != closest_pose_upper_bound && global_plan_.poses.size() > 1 &&
transformation_begin == std::prev(closest_pose_upper_bound))
{
transformation_begin = std::prev(std::prev(closest_pose_upper_bound));
}
// We'll discard points on the plan that are outside the local costmap
const double max_costmap_extent = getCostmapMaxExtent();
auto transformation_end = std::find_if(
transformation_begin, global_plan_.poses.end(),
[&](const auto & global_plan_pose) {
return euclidean_distance(global_plan_pose, robot_pose) > max_costmap_extent;
});
// Lambda to transform a PoseStamped from global frame to local
auto transformGlobalPoseToLocal = [&](const auto & global_plan_pose) {
geometry_msgs::msg::PoseStamped stamped_pose, transformed_pose;
stamped_pose.header.frame_id = global_plan_.header.frame_id;
stamped_pose.header.stamp = robot_pose.header.stamp;
stamped_pose.pose = global_plan_pose.pose;
if (!transformPose(costmap_ros_->getBaseFrameID(), stamped_pose, transformed_pose)) {
throw nav2_core::ControllerTFError("Unable to transform plan pose into local frame");
}
transformed_pose.pose.position.z = 0.0;
return transformed_pose;
};
// Transform the near part of the global plan into the robot's frame of reference.
nav_msgs::msg::Path transformed_plan;
std::transform(
transformation_begin, transformation_end,
std::back_inserter(transformed_plan.poses),
transformGlobalPoseToLocal);
transformed_plan.header.frame_id = costmap_ros_->getBaseFrameID();
transformed_plan.header.stamp = robot_pose.header.stamp;
// Remove the portion of the global plan that we've already passed so we don't
// process it on the next iteration (this is called path pruning)
global_plan_.poses.erase(begin(global_plan_.poses), transformation_begin);
if (transformed_plan.poses.empty()) {
throw nav2_core::InvalidPath("Resulting plan has 0 poses in it.");
}
return transformed_plan;
}
bool PathHandler::transformPose(
const std::string frame,
const geometry_msgs::msg::PoseStamped & in_pose,
geometry_msgs::msg::PoseStamped & out_pose) const
{
if (in_pose.header.frame_id == frame) {
out_pose = in_pose;
return true;
}
try {
tf_->transform(in_pose, out_pose, frame, transform_tolerance_);
out_pose.header.frame_id = frame;
return true;
} catch (tf2::TransformException & ex) {
RCLCPP_ERROR(logger_, "Exception in transformPose: %s", ex.what());
}
return false;
}
} // namespace nav2_regulated_pure_pursuit_controller