simple_planner v1.4.0
Loading...
Searching...
No Matches
grid_map_handling.cpp
Go to the documentation of this file.
1// Copyright Institute for Automotive Engineering (ika), RWTH Aachen University
2// SPDX-License-Identifier: Apache-2.0
3
4#include <algorithm>
5#include <cmath>
6#include <limits>
7#include <optional>
8#include <vector>
9
10#include <Eigen/Dense>
11
12#include <geometry_msgs/msg/point_stamped.hpp>
13
14#include <tf2/utils.h>
15
17
18namespace simple_planner {
19
20bool SimplePlannerNode::hasValidGridMap(const rclcpp::Time& stamp) const {
21 if (!grid_map_init_) {
22 return false;
23 }
24
25 const auto& origin = grid_map_.info.origin;
26 const double orientation_norm_sq = origin.orientation.x * origin.orientation.x + origin.orientation.y * origin.orientation.y +
27 origin.orientation.z * origin.orientation.z + origin.orientation.w * origin.orientation.w;
28 const bool has_valid_origin = std::isfinite(origin.position.x) && std::isfinite(origin.position.y) &&
29 std::isfinite(orientation_norm_sq) && orientation_norm_sq > 1e-12;
30 const bool has_valid_geometry = !grid_map_.header.frame_id.empty() && has_valid_origin &&
31 std::isfinite(grid_map_.info.resolution) && grid_map_.info.resolution > 0.0 &&
32 grid_map_.info.width > 0 && grid_map_.info.height > 0;
33 const size_t expected_cell_count = static_cast<size_t>(grid_map_.info.width) * static_cast<size_t>(grid_map_.info.height);
34 const bool has_complete_data = grid_map_.data.size() == expected_cell_count;
35
36 return has_valid_geometry && has_complete_data && !isMessageOutdated(grid_map_.header, grid_map_timeout_, stamp);
37}
38
39void SimplePlannerNode::applyGridMapConstraints(const std_msgs::msg::Header& target_header,
40 std::vector<SimplePathPoint>& base_path_points,
41 FollowRoutePlan& route_plan) {
42 if (!consider_grid_map_ || base_path_points.empty()) {
43 return;
44 }
45
46 std::optional<double> stop_s;
47 try {
48 stop_s = findFirstGridMapStopS(target_header, base_path_points);
49 } catch (const tf2::TransformException& ex) {
50 const std::string msg =
51 "Grid map transformation is not available: " + std::string(ex.what()) + ". Ignoring grid map for this planning cycle.";
52 setHealth(diagnostic_msgs::msg::DiagnosticStatus::WARN, msg, health_.key_value_pairs);
53 RCLCPP_WARN(this->get_logger(), "%s", msg.c_str());
54 return;
55 }
56
57 if (!stop_s.has_value()) {
58 return;
59 }
60
61 base_path_points = truncatePathAtS(base_path_points, *stop_s);
62 route_plan.stop_at_end = true;
63 route_plan.offset_to_stop_line = 0.0;
64 route_plan.reason_to_stop = "Grid map obstacle";
65
66 RCLCPP_INFO(this->get_logger(), "Applying stop for grid-map obstacle at last safe s=%f m", *stop_s);
67}
68
69std::optional<double> SimplePlannerNode::findFirstGridMapStopS(const std_msgs::msg::Header& target_header,
70 const std::vector<SimplePathPoint>& base_path_points) {
71 if (base_path_points.size() < 2) {
72 return std::nullopt;
73 }
74
75 const geometry_msgs::msg::TransformStamped tf =
76 tf2_buffer_->lookupTransform(grid_map_.header.frame_id, grid_map_.header.stamp, target_header.frame_id, target_header.stamp,
77 fixed_over_time_frame_id_, rclcpp::Duration::from_seconds(1.0));
78
79 std::vector<Eigen::Vector2d> grid_frame_path_points;
80 grid_frame_path_points.reserve(base_path_points.size());
81 for (const auto& point : base_path_points) {
82 geometry_msgs::msg::PointStamped point_msg;
83 geometry_msgs::msg::PointStamped transformed_point_msg;
84 point_msg.header = target_header;
85 point_msg.point.x = point.position.x();
86 point_msg.point.y = point.position.y();
87 point_msg.point.z = 0.0;
88 tf2::doTransform(point_msg, transformed_point_msg, tf);
89 grid_frame_path_points.emplace_back(transformed_point_msg.point.x, transformed_point_msg.point.y);
90 }
91
92 const double resolution = static_cast<double>(grid_map_.info.resolution);
93 const Eigen::Vector2d grid_origin(grid_map_.info.origin.position.x, grid_map_.info.origin.position.y);
94 const double grid_origin_yaw = tf2::getYaw(grid_map_.info.origin.orientation);
95 const double grid_length_x = static_cast<double>(grid_map_.info.width) * resolution;
96 const double grid_length_y = static_cast<double>(grid_map_.info.height) * resolution;
97 const Eigen::Vector2d ego_center_offset(ego_data_.state.reference_point.translation_to_geometric_center.x,
98 ego_data_.state.reference_point.translation_to_geometric_center.y);
99
100 // The target trajectory frame is ego-centered (normally base_link), so the ego reference point is its origin.
101 const Eigen::Vector2d ego_reference_position = Eigen::Vector2d::Zero();
102 double ego_path_s = 0.0;
103 double best_dist_sq = std::numeric_limits<double>::max();
104 for (size_t i = 0; i + 1 < base_path_points.size(); ++i) {
105 const Eigen::Vector2d segment = base_path_points[i + 1].position - base_path_points[i].position;
106 const double segment_length_sq = segment.squaredNorm();
107 if (segment_length_sq < 1e-9) {
108 continue;
109 }
110
111 const double alpha =
112 std::clamp((ego_reference_position - base_path_points[i].position).dot(segment) / segment_length_sq, 0.0, 1.0);
113 const Eigen::Vector2d projected = base_path_points[i].position + alpha * segment;
114 const double dist_sq = (projected - ego_reference_position).squaredNorm();
115 if (dist_sq >= best_dist_sq) {
116 continue;
117 }
118
119 best_dist_sq = dist_sq;
120 ego_path_s = base_path_points[i].s + alpha * (base_path_points[i + 1].s - base_path_points[i].s);
121 }
122
123 std::optional<double> last_safe_s;
124 for (size_t i = 0; i + 1 < base_path_points.size(); ++i) {
125 const Eigen::Vector2d segment = grid_frame_path_points[i + 1] - grid_frame_path_points[i];
126 const double segment_length = segment.norm();
127 if (segment_length <= 1e-3) {
128 continue;
129 }
130
131 const Eigen::Vector2d tangent = segment / segment_length;
132 const auto sample_count = static_cast<size_t>(std::ceil(segment_length / resolution));
133 for (size_t sample_idx = 0; sample_idx <= sample_count; ++sample_idx) {
134 const double sampled_distance = std::min(static_cast<double>(sample_idx) * resolution, segment_length);
135 const double alpha = sampled_distance / segment_length;
136 const double sample_s = base_path_points[i].s + alpha * (base_path_points[i + 1].s - base_path_points[i].s);
137 if (sample_s < ego_path_s) {
138 continue;
139 }
140
141 const Eigen::Vector2d reference_point = grid_frame_path_points[i] + alpha * segment;
142 const double yaw = wrap_angle_rad(std::atan2(tangent.y(), tangent.x()));
143 const Eigen::Vector2d ego_center = reference_point + rotate(ego_center_offset, yaw);
144 const OrientedBox2D ego_box = buildOrientedBox(ego_center, yaw, ego_data_.length, ego_data_.width);
145 const OrientedBox2D ego_safety_box =
147
148 const auto expanded_corners = getBoxCorners(ego_safety_box);
149 double min_local_x = std::numeric_limits<double>::max();
150 double max_local_x = std::numeric_limits<double>::lowest();
151 double min_local_y = std::numeric_limits<double>::max();
152 double max_local_y = std::numeric_limits<double>::lowest();
153 for (const auto& corner : expanded_corners) {
154 const Eigen::Vector2d local_corner = rotate(corner - grid_origin, -grid_origin_yaw);
155 min_local_x = std::min(min_local_x, local_corner.x());
156 max_local_x = std::max(max_local_x, local_corner.x());
157 min_local_y = std::min(min_local_y, local_corner.y());
158 max_local_y = std::max(max_local_y, local_corner.y());
159 }
160
161 const bool box_out_of_grid =
162 min_local_x < 0.0 || min_local_y < 0.0 || max_local_x >= grid_length_x || max_local_y >= grid_length_y;
163 if (box_out_of_grid && consider_out_of_grid_) {
164 return last_safe_s.value_or(ego_path_s);
165 }
166
167 const bool box_fully_outside =
168 max_local_x < 0.0 || max_local_y < 0.0 || min_local_x >= grid_length_x || min_local_y >= grid_length_y;
169 if (!box_fully_outside) {
170 const size_t min_cell_x = static_cast<size_t>(std::floor(std::max(min_local_x, 0.0) / resolution));
171 const size_t max_cell_x =
172 std::min(static_cast<size_t>(std::floor(max_local_x / resolution)), static_cast<size_t>(grid_map_.info.width) - 1);
173 const size_t min_cell_y = static_cast<size_t>(std::floor(std::max(min_local_y, 0.0) / resolution));
174 const size_t max_cell_y =
175 std::min(static_cast<size_t>(std::floor(max_local_y / resolution)), static_cast<size_t>(grid_map_.info.height) - 1);
176
177 for (size_t cell_y = min_cell_y; cell_y <= max_cell_y; ++cell_y) {
178 for (size_t cell_x = min_cell_x; cell_x <= max_cell_x; ++cell_x) {
179 const int8_t value = grid_map_.data[cell_y * grid_map_.info.width + cell_x];
180 if (value >= 0 && value < grid_occupied_threshold_) {
181 continue;
182 }
183
184 const Eigen::Vector2d cell_center_local((static_cast<double>(cell_x) + 0.5) * resolution,
185 (static_cast<double>(cell_y) + 0.5) * resolution);
186 const Eigen::Vector2d cell_center = grid_origin + rotate(cell_center_local, grid_origin_yaw);
187 const OrientedBox2D cell_box = buildOrientedBox(cell_center, grid_origin_yaw, resolution, resolution);
188 if (overlaps(ego_safety_box, cell_box)) {
189 return last_safe_s.value_or(ego_path_s);
190 }
191 }
192 }
193 }
194
195 last_safe_s = sample_s;
196 }
197 }
198
199 return std::nullopt;
200}
201
202} // namespace simple_planner
void applyGridMapConstraints(const std_msgs::msg::Header &target_header, std::vector< SimplePathPoint > &base_path_points, FollowRoutePlan &route_plan)
Applies occupancy-grid-based stop constraints to the base route path.
bool hasValidGridMap(const rclcpp::Time &stamp) const
Checks whether a fresh, structurally valid grid map is currently available.
std::optional< double > findFirstGridMapStopS(const std_msgs::msg::Header &target_header, const std::vector< SimplePathPoint > &base_path_points)
Finds the last safe grid-map sample before the first blocked pose.
std::unique_ptr< tf2_ros::Buffer > tf2_buffer_
nav_msgs::msg::OccupancyGrid grid_map_
static std::vector< SimplePathPoint > truncatePathAtS(const std::vector< SimplePathPoint > &path, double stop_s)
Returns a path ending exactly at the requested accumulated path coordinate.
void setHealth(const unsigned char status, const std::string &msg, const std::map< std::string, std::string > &key_value_pairs={})
Sets the health information.
Definition utils.hpp:180
struct simple_planner::SimplePlannerNode::DiagnosticStatus health_
perception_msgs::msg::EgoData ego_data_
static bool isMessageOutdated(const std_msgs::msg::Header &header, double timeout, const rclcpp::Time &stamp)
Checks whether an input message is older than the configured timeout.
Namespace for simple_planner package.
OrientedBox2D buildOrientedBox(const Eigen::Vector2d &center, double yaw, double length, double width)
Builds an oriented 2D bounding box from center pose and dimensions.
Eigen::Vector2d rotate(const Eigen::Vector2d &vec, double yaw)
Rotates a 2D vector by the given yaw angle.
double wrap_angle_rad(double angle_rad)
Wraps an angle to the range [-pi, pi].
bool overlaps(const OrientedBox2D &first_box, const OrientedBox2D &second_box)
Checks whether two oriented boxes overlap.
OrientedBox2D expandBoxWithSafetyMargins(const OrientedBox2D &box, double longitudinal_safety_distance, double lateral_safety_distance)
Expands a box by the given safety margins.
std::array< Eigen::Vector2d, 4 > getBoxCorners(const OrientedBox2D &box)
Returns the corners of an oriented box.
std::map< std::string, std::string > key_value_pairs