155 const trajectory_planning_msgs::msg::Trajectory& reference_trajectory,
const route_planning_msgs::msg::Route& route) {
156 const double NO_BOUNDARY_DISTANCE = 1e6;
157 constexpr double MAX_ROUTE_S_DIFFERENCE = 20.0;
158 constexpr double LOOK_BEHIND_REMAINING_ROUTE_S = 2.0;
161 std::vector<std::pair<double, double>> min_normal_distances;
162 std::vector<Eigen::Vector2d> left_boundary_points;
163 std::vector<Eigen::Vector2d> right_boundary_points;
164 std::vector<double> left_boundary_route_s;
165 std::vector<double> right_boundary_route_s;
166 std::vector<Eigen::Vector2d> left_boundary_intersections;
167 std::vector<Eigen::Vector2d> right_boundary_intersections;
170 Boundaries boundaries;
171 auto route_elements = route_planning_msgs::route_access::getRemainingRouteElements(route,
true);
172 const int ref_sample_size = trajectory_planning_msgs::trajectory_access::getSamplePointSize(reference_trajectory);
174 if (route_elements.empty()) {
176 RCLCPP_WARN(get_logger(),
"Remaining route is empty. Do not constrain boundaries.");
178 for (
int i = 0; i < ref_sample_size; ++i) {
179 boundaries.min_normal_distances.emplace_back(NO_BOUNDARY_DISTANCE, NO_BOUNDARY_DISTANCE);
181 return boundaries.min_normal_distances;
184 const double current_route_s = route_elements.front().s;
185 const size_t current_route_index = std::min(
static_cast<size_t>(route.current_route_element_idx), route.route_elements.size());
186 size_t overlap_begin = current_route_index;
187 while (overlap_begin > 0 && current_route_s - route.route_elements[overlap_begin - 1].s <= LOOK_BEHIND_REMAINING_ROUTE_S) {
190 route_elements.insert(route_elements.begin(), route.route_elements.begin() +
static_cast<std::ptrdiff_t
>(overlap_begin),
191 route.route_elements.begin() +
static_cast<std::ptrdiff_t
>(current_route_index));
193 boundaries.left_boundary_points.reserve(route_elements.size());
194 boundaries.right_boundary_points.reserve(route_elements.size());
195 boundaries.left_boundary_route_s.reserve(route_elements.size());
196 boundaries.right_boundary_route_s.reserve(route_elements.size());
197 boundaries.min_normal_distances.reserve(ref_sample_size);
198 boundaries.left_boundary_intersections.reserve(ref_sample_size);
199 boundaries.right_boundary_intersections.reserve(ref_sample_size);
201 for (
const auto& route_element : route_elements) {
202 if (route_element.is_enriched) {
204 route_planning_msgs::msg::LaneElement suggested_lane =
205 route_planning_msgs::route_access::getSuggestedLaneElement(route_element);
206 boundaries.left_boundary_points.emplace_back(suggested_lane.left_boundary.point.x, suggested_lane.left_boundary.point.y);
207 boundaries.right_boundary_points.emplace_back(suggested_lane.right_boundary.point.x,
208 suggested_lane.right_boundary.point.y);
209 boundaries.left_boundary_route_s.push_back(route_element.s);
210 boundaries.right_boundary_route_s.push_back(route_element.s);
212 const auto& lane_elements = route_element.lane_elements;
213 if (!lane_elements.empty()) {
214 boundaries.left_boundary_points.emplace_back(lane_elements.front().left_boundary.point.x,
215 lane_elements.front().left_boundary.point.y);
216 boundaries.right_boundary_points.emplace_back(lane_elements.back().right_boundary.point.x,
217 lane_elements.back().right_boundary.point.y);
218 boundaries.left_boundary_route_s.push_back(route_element.s);
219 boundaries.right_boundary_route_s.push_back(route_element.s);
222 boundaries.left_boundary_points.emplace_back(route_element.left_boundary.x, route_element.left_boundary.y);
223 boundaries.right_boundary_points.emplace_back(route_element.right_boundary.x, route_element.right_boundary.y);
224 boundaries.left_boundary_route_s.push_back(route_element.s);
225 boundaries.right_boundary_route_s.push_back(route_element.s);
231 auto findIntersection = [&](
const Eigen::Vector2d& ref_pos,
double sin_yaw,
double cos_yaw,
double expected_route_s,
232 const std::vector<Eigen::Vector2d>& boundary_points,
const std::vector<double>& boundary_route_s,
233 bool isLeft) -> std::pair<double, Eigen::Vector2d> {
234 std::pair<double, Eigen::Vector2d> intersection_result = {
235 std::numeric_limits<double>::infinity(),
236 Eigen::Vector2d(std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity())};
237 double best_route_s_difference = std::numeric_limits<double>::infinity();
239 const Eigen::Vector2d normal_dir = isLeft ? Eigen::Vector2d(-sin_yaw, cos_yaw) : Eigen::Vector2d(sin_yaw, -cos_yaw);
240 const auto cross2d = [](
const Eigen::Vector2d& u,
const Eigen::Vector2d& v) {
return u.x() * v.y() - u.y() * v.x(); };
242 for (
size_t i = 0; i + 1 < boundary_points.size(); ++i) {
243 const Eigen::Vector2d& a = boundary_points[i];
244 const Eigen::Vector2d& b = boundary_points[i + 1];
245 Eigen::Vector2d seg = b - a;
246 Eigen::Vector2d ap = ref_pos - a;
248 const double denom = cross2d(seg, normal_dir);
249 if (std::abs(denom) < 1e-9) {
253 const double s = cross2d(ap, normal_dir) / denom;
254 const double t = cross2d(ap, seg) / denom;
256 if (s >= 0.0 && s <= 1.0 && t >= 0.0) {
257 Eigen::Vector2d intersection = a + s * seg;
258 double euclidean_distance = (ref_pos - intersection).norm();
259 const double intersection_route_s = boundary_route_s[i] + s * (boundary_route_s[i + 1] - boundary_route_s[i]);
260 const double route_s_difference = std::abs(intersection_route_s - expected_route_s);
262 if (route_s_difference <= MAX_ROUTE_S_DIFFERENCE &&
263 (route_s_difference < best_route_s_difference ||
264 (std::abs(route_s_difference - best_route_s_difference) < 1e-9 && euclidean_distance < intersection_result.first))) {
265 best_route_s_difference = route_s_difference;
266 intersection_result = {euclidean_distance, intersection};
270 return intersection_result;
274 double reference_progress = 0.0;
275 Eigen::Vector2d previous_ref_pos = Eigen::Vector2d::Zero();
276 for (
int i = 0; i < ref_sample_size; ++i) {
277 Eigen::Vector2d ref_pos(trajectory_planning_msgs::trajectory_access::getX(reference_trajectory, i),
278 trajectory_planning_msgs::trajectory_access::getY(reference_trajectory, i));
279 reference_progress += (ref_pos - previous_ref_pos).norm();
280 previous_ref_pos = ref_pos;
281 const double expected_route_s = current_route_s + reference_progress;
283 double yaw = trajectory_planning_msgs::trajectory_access::getTheta(reference_trajectory, i);
284 const double sin_yaw = std::sin(yaw);
285 const double cos_yaw = std::cos(yaw);
287 auto left_intersection = findIntersection(ref_pos, sin_yaw, cos_yaw, expected_route_s, boundaries.left_boundary_points,
288 boundaries.left_boundary_route_s,
true);
289 auto right_intersection = findIntersection(ref_pos, sin_yaw, cos_yaw, expected_route_s, boundaries.right_boundary_points,
290 boundaries.right_boundary_route_s,
false);
291 if (left_intersection.first != std::numeric_limits<double>::infinity() &&
292 right_intersection.first != std::numeric_limits<double>::infinity()) {
293 boundaries.min_normal_distances.emplace_back(left_intersection.first, right_intersection.first);
294 boundaries.left_boundary_intersections.emplace_back(left_intersection.second);
295 boundaries.right_boundary_intersections.emplace_back(right_intersection.second);
296 RCLCPP_DEBUG(this->get_logger(),
"Minimum left boundary distance: %.2f m", left_intersection.first);
297 RCLCPP_DEBUG(this->get_logger(),
"Minimum right boundary distance: %.2f m", right_intersection.first);
299 boundaries.min_normal_distances.emplace_back(NO_BOUNDARY_DISTANCE, NO_BOUNDARY_DISTANCE);
300 RCLCPP_WARN(get_logger(),
301 "No boundary intersection found for trajectory point %d. Do not constrain boundaries at this point.", i);
305 vizBoundaryPoints(boundaries.left_boundary_intersections, boundaries.right_boundary_intersections);
308 return boundaries.min_normal_distances;
374 double ego_length = 0.0, ego_width = 0.0;
375 int n_ego_circles = 0;
376 std::vector<double> ego_offset2geocenter;
379 if (model_name ==
"karl") {
382 ego_offset2geocenter = {1.4895, 0.0};
384 }
else if (model_name ==
"shuttle") {
387 ego_offset2geocenter = {0.0, 0.0};
390 RCLCPP_WARN(this->get_logger(),
"Unknown model '%s'. Could not visualize ego circles.", model_name.c_str());
394 visualization_msgs::msg::MarkerArray marker_array;
396 if (x_trajectory.empty() || n_ego_circles <= 0) {
397 visualization_msgs::msg::Marker delete_marker;
398 delete_marker.action = visualization_msgs::msg::Marker::DELETEALL;
399 marker_array.markers.push_back(delete_marker);
404 const double offset_x = !ego_offset2geocenter.empty() ? ego_offset2geocenter[0] : 0.0;
405 const double offset_y = ego_offset2geocenter.size() > 1 ? ego_offset2geocenter[1] : 0.0;
407 const double radius = std::sqrt(std::pow(ego_length / (2.0 * n_ego_circles), 2) + std::pow(ego_width / 2.0, 2));
411 for (
int stage = 0; stage <=
n_shots_; ++stage) {
412 const size_t state_offset =
static_cast<size_t>(stage) *
static_cast<size_t>(state_dim);
413 const double base_x = x_trajectory[state_offset + 0];
414 const double base_y = x_trajectory[state_offset + 1];
415 const double psi = x_trajectory[state_offset + 5];
417 const double ego_center_x = base_x + offset_x * std::cos(psi) - offset_y * std::sin(psi);
418 const double ego_center_y = base_y + offset_x * std::sin(psi) + offset_y * std::cos(psi);
420 for (
int i = 0; i < n_ego_circles; ++i) {
421 const double lon_offset = -ego_length / 2.0 + (2 * i + 1) * ego_length / (2.0 * n_ego_circles);
422 const double x_offset = lon_offset * std::cos(psi);
423 const double y_offset = lon_offset * std::sin(psi);
425 visualization_msgs::msg::Marker marker;
427 marker.header.stamp = rclcpp::Time(
ego_data_.header.stamp);
428 marker.lifetime = rclcpp::Duration::from_seconds(0.5);
429 marker.ns =
"ego-circles";
430 marker.id = marker_id++;
431 marker.type = visualization_msgs::msg::Marker::CYLINDER;
432 marker.action = visualization_msgs::msg::Marker::ADD;
433 marker.pose.position.x = ego_center_x + x_offset;
434 marker.pose.position.y = ego_center_y + y_offset;
435 marker.pose.position.z = 0.0;
436 marker.pose.orientation.w = 1.0;
437 marker.scale.x = radius * 2.0;
438 marker.scale.y = radius * 2.0;
439 marker.scale.z = 0.05;
440 marker.color.a = 0.4;
441 marker.color.r = 0.0F;
442 marker.color.g = 0.6F;
443 marker.color.b = 1.0F;
444 marker_array.markers.push_back(marker);