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;
160 std::vector<std::pair<double, double>> min_normal_distances;
161 std::vector<Eigen::Vector2d> left_boundary_points;
162 std::vector<Eigen::Vector2d> right_boundary_points;
163 std::vector<double> left_boundary_route_s;
164 std::vector<double> right_boundary_route_s;
165 std::vector<Eigen::Vector2d> left_boundary_intersections;
166 std::vector<Eigen::Vector2d> right_boundary_intersections;
169 Boundaries boundaries;
170 const auto remaining_route = route_planning_msgs::route_access::getRemainingRouteElements(route,
true);
171 const int ref_sample_size = trajectory_planning_msgs::trajectory_access::getSamplePointSize(reference_trajectory);
172 boundaries.left_boundary_points.reserve(remaining_route.size());
173 boundaries.right_boundary_points.reserve(remaining_route.size());
174 boundaries.left_boundary_route_s.reserve(remaining_route.size());
175 boundaries.right_boundary_route_s.reserve(remaining_route.size());
176 boundaries.min_normal_distances.reserve(ref_sample_size);
177 boundaries.left_boundary_intersections.reserve(ref_sample_size);
178 boundaries.right_boundary_intersections.reserve(ref_sample_size);
180 if (remaining_route.empty()) {
182 RCLCPP_WARN(get_logger(),
"Remaining route is empty. Do not constrain boundaries.");
184 for (
int i = 0; i < ref_sample_size; ++i) {
185 boundaries.min_normal_distances.emplace_back(NO_BOUNDARY_DISTANCE, NO_BOUNDARY_DISTANCE);
187 return boundaries.min_normal_distances;
190 for (
const auto& route_element : remaining_route) {
191 if (route_element.is_enriched) {
193 route_planning_msgs::msg::LaneElement suggested_lane =
194 route_planning_msgs::route_access::getSuggestedLaneElement(route_element);
195 boundaries.left_boundary_points.emplace_back(suggested_lane.left_boundary.point.x, suggested_lane.left_boundary.point.y);
196 boundaries.right_boundary_points.emplace_back(suggested_lane.right_boundary.point.x,
197 suggested_lane.right_boundary.point.y);
198 boundaries.left_boundary_route_s.push_back(route_element.s);
199 boundaries.right_boundary_route_s.push_back(route_element.s);
201 const auto& lane_elements = route_element.lane_elements;
202 if (!lane_elements.empty()) {
203 boundaries.left_boundary_points.emplace_back(lane_elements.front().left_boundary.point.x,
204 lane_elements.front().left_boundary.point.y);
205 boundaries.right_boundary_points.emplace_back(lane_elements.back().right_boundary.point.x,
206 lane_elements.back().right_boundary.point.y);
207 boundaries.left_boundary_route_s.push_back(route_element.s);
208 boundaries.right_boundary_route_s.push_back(route_element.s);
211 boundaries.left_boundary_points.emplace_back(route_element.left_boundary.x, route_element.left_boundary.y);
212 boundaries.right_boundary_points.emplace_back(route_element.right_boundary.x, route_element.right_boundary.y);
213 boundaries.left_boundary_route_s.push_back(route_element.s);
214 boundaries.right_boundary_route_s.push_back(route_element.s);
220 auto findIntersection = [&](
const Eigen::Vector2d& ref_pos,
double sin_yaw,
double cos_yaw,
double expected_route_s,
221 const std::vector<Eigen::Vector2d>& boundary_points,
const std::vector<double>& boundary_route_s,
222 bool isLeft) -> std::pair<double, Eigen::Vector2d> {
223 std::pair<double, Eigen::Vector2d> intersection_result = {
224 std::numeric_limits<double>::infinity(),
225 Eigen::Vector2d(std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity())};
226 double best_route_s_difference = std::numeric_limits<double>::infinity();
228 const Eigen::Vector2d normal_dir = isLeft ? Eigen::Vector2d(-sin_yaw, cos_yaw) : Eigen::Vector2d(sin_yaw, -cos_yaw);
229 const auto cross2d = [](
const Eigen::Vector2d& u,
const Eigen::Vector2d& v) {
return u.x() * v.y() - u.y() * v.x(); };
231 for (
size_t i = 0; i + 1 < boundary_points.size(); ++i) {
232 const Eigen::Vector2d& a = boundary_points[i];
233 const Eigen::Vector2d& b = boundary_points[i + 1];
234 Eigen::Vector2d seg = b - a;
235 Eigen::Vector2d ap = ref_pos - a;
237 const double denom = cross2d(seg, normal_dir);
238 if (std::abs(denom) < 1e-9) {
242 const double s = cross2d(ap, normal_dir) / denom;
243 const double t = cross2d(ap, seg) / denom;
245 if (s >= 0.0 && s <= 1.0 && t >= 0.0) {
246 Eigen::Vector2d intersection = a + s * seg;
247 double euclidean_distance = (ref_pos - intersection).norm();
248 const double intersection_route_s = boundary_route_s[i] + s * (boundary_route_s[i + 1] - boundary_route_s[i]);
249 const double route_s_difference = std::abs(intersection_route_s - expected_route_s);
251 if (route_s_difference <= MAX_ROUTE_S_DIFFERENCE &&
252 (route_s_difference < best_route_s_difference ||
253 (std::abs(route_s_difference - best_route_s_difference) < 1e-9 && euclidean_distance < intersection_result.first))) {
254 best_route_s_difference = route_s_difference;
255 intersection_result = {euclidean_distance, intersection};
259 return intersection_result;
263 double reference_progress = 0.0;
264 Eigen::Vector2d previous_ref_pos = Eigen::Vector2d::Zero();
265 const double current_route_s = remaining_route.front().s;
266 for (
int i = 0; i < ref_sample_size; ++i) {
267 Eigen::Vector2d ref_pos(trajectory_planning_msgs::trajectory_access::getX(reference_trajectory, i),
268 trajectory_planning_msgs::trajectory_access::getY(reference_trajectory, i));
269 reference_progress += (ref_pos - previous_ref_pos).norm();
270 previous_ref_pos = ref_pos;
271 const double expected_route_s = current_route_s + reference_progress;
273 double yaw = trajectory_planning_msgs::trajectory_access::getTheta(reference_trajectory, i);
274 const double sin_yaw = std::sin(yaw);
275 const double cos_yaw = std::cos(yaw);
277 auto left_intersection = findIntersection(ref_pos, sin_yaw, cos_yaw, expected_route_s, boundaries.left_boundary_points,
278 boundaries.left_boundary_route_s,
true);
279 auto right_intersection = findIntersection(ref_pos, sin_yaw, cos_yaw, expected_route_s, boundaries.right_boundary_points,
280 boundaries.right_boundary_route_s,
false);
281 if (left_intersection.first != std::numeric_limits<double>::infinity() &&
282 right_intersection.first != std::numeric_limits<double>::infinity()) {
283 boundaries.min_normal_distances.emplace_back(left_intersection.first, right_intersection.first);
284 boundaries.left_boundary_intersections.emplace_back(left_intersection.second);
285 boundaries.right_boundary_intersections.emplace_back(right_intersection.second);
286 RCLCPP_DEBUG(this->get_logger(),
"Minimum left boundary distance: %.2f m", left_intersection.first);
287 RCLCPP_DEBUG(this->get_logger(),
"Minimum right boundary distance: %.2f m", right_intersection.first);
289 boundaries.min_normal_distances.emplace_back(NO_BOUNDARY_DISTANCE, NO_BOUNDARY_DISTANCE);
290 RCLCPP_WARN(get_logger(),
291 "No boundary intersection found for trajectory point %d. Do not constrain boundaries at this point.", i);
295 vizBoundaryPoints(boundaries.left_boundary_intersections, boundaries.right_boundary_intersections);
298 return boundaries.min_normal_distances;
364 double ego_length = 0.0, ego_width = 0.0;
365 int n_ego_circles = 0;
366 std::vector<double> ego_offset2geocenter;
369 if (model_name ==
"karl") {
372 ego_offset2geocenter = {1.4895, 0.0};
374 }
else if (model_name ==
"shuttle") {
377 ego_offset2geocenter = {0.0, 0.0};
380 RCLCPP_WARN(this->get_logger(),
"Unknown model '%s'. Could not visualize ego circles.", model_name.c_str());
384 visualization_msgs::msg::MarkerArray marker_array;
386 if (x_trajectory.empty() || n_ego_circles <= 0) {
387 visualization_msgs::msg::Marker delete_marker;
388 delete_marker.action = visualization_msgs::msg::Marker::DELETEALL;
389 marker_array.markers.push_back(delete_marker);
394 const double offset_x = !ego_offset2geocenter.empty() ? ego_offset2geocenter[0] : 0.0;
395 const double offset_y = ego_offset2geocenter.size() > 1 ? ego_offset2geocenter[1] : 0.0;
397 const double radius = std::sqrt(std::pow(ego_length / (2.0 * n_ego_circles), 2) + std::pow(ego_width / 2.0, 2));
401 for (
int stage = 0; stage <=
n_shots_; ++stage) {
402 const size_t state_offset =
static_cast<size_t>(stage) *
static_cast<size_t>(state_dim);
403 const double base_x = x_trajectory[state_offset + 0];
404 const double base_y = x_trajectory[state_offset + 1];
405 const double psi = x_trajectory[state_offset + 5];
407 const double ego_center_x = base_x + offset_x * std::cos(psi) - offset_y * std::sin(psi);
408 const double ego_center_y = base_y + offset_x * std::sin(psi) + offset_y * std::cos(psi);
410 for (
int i = 0; i < n_ego_circles; ++i) {
411 const double lon_offset = -ego_length / 2.0 + (2 * i + 1) * ego_length / (2.0 * n_ego_circles);
412 const double x_offset = lon_offset * std::cos(psi);
413 const double y_offset = lon_offset * std::sin(psi);
415 visualization_msgs::msg::Marker marker;
417 marker.header.stamp = rclcpp::Time(
ego_data_.header.stamp);
418 marker.lifetime = rclcpp::Duration::from_seconds(0.5);
419 marker.ns =
"ego-circles";
420 marker.id = marker_id++;
421 marker.type = visualization_msgs::msg::Marker::CYLINDER;
422 marker.action = visualization_msgs::msg::Marker::ADD;
423 marker.pose.position.x = ego_center_x + x_offset;
424 marker.pose.position.y = ego_center_y + y_offset;
425 marker.pose.position.z = 0.0;
426 marker.pose.orientation.w = 1.0;
427 marker.scale.x = radius * 2.0;
428 marker.scale.y = radius * 2.0;
429 marker.scale.z = 0.05;
430 marker.color.a = 0.4;
431 marker.color.r = 0.0F;
432 marker.color.g = 0.6F;
433 marker.color.b = 1.0F;
434 marker_array.markers.push_back(marker);