From f9ddbb6b79d473f5857bcb9045cc355556cb5a3a Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Tue, 12 Nov 2024 08:04:30 +0900 Subject: [PATCH 01/21] implement function to compute terminal lane change path Signed-off-by: mohammad alqudah --- .../scene.hpp | 4 +- .../utils/base_class.hpp | 1 + .../utils/data_structs.hpp | 16 ++ .../src/scene.cpp | 203 ++++++------------ 4 files changed, 89 insertions(+), 135 deletions(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp index 4c8cbe05f5535..f3122f74c3d09 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp @@ -154,9 +154,7 @@ class NormalLaneChange : public LaneChangeBase bool check_candidate_path_safety( const LaneChangePath & candidate_path, const lane_change::TargetObjects & target_objects) const; - std::optional calcTerminalLaneChangePath( - const lanelet::ConstLanelets & current_lanes, - const lanelet::ConstLanelets & target_lanes) const; + std::optional get_terminal_lane_change_path() const; bool isValidPath(const PathWithLaneId & path) const override; diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/base_class.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/base_class.hpp index 634f3b737942b..abdf5b77686c0 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/base_class.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/base_class.hpp @@ -279,6 +279,7 @@ class LaneChangeBase FilteredByLanesExtendedObjects filtered_objects_{}; BehaviorModuleOutput prev_module_output_{}; std::optional lane_change_stop_pose_{std::nullopt}; + mutable std::optional terminal_lane_change_path_{std::nullopt}; PathWithLaneId prev_approved_path_{}; diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/data_structs.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/data_structs.hpp index d970976d80a8d..21721743d7b27 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/data_structs.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/data_structs.hpp @@ -260,6 +260,22 @@ struct Info terminal_lane_changing_velocity = _lc_metrics.velocity; shift_line = _shift_line; } + + void set_prepare(const PhaseMetrics & _prep_metrics) + { + longitudinal_acceleration.prepare = _prep_metrics.actual_lon_accel; + velocity.prepare = _prep_metrics.velocity; + duration.prepare = _prep_metrics.duration; + length.prepare = _prep_metrics.length; + } + + void set_lane_changing(const PhaseMetrics & _lc_metrics) + { + longitudinal_acceleration.prepare = _lc_metrics.actual_lon_accel; + velocity.prepare = _lc_metrics.velocity; + duration.prepare = _lc_metrics.duration; + length.prepare = _lc_metrics.length; + } }; template diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp index f56e9189852a8..839975e5b4e38 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp @@ -351,42 +351,25 @@ LaneChangePath NormalLaneChange::getLaneChangePath() const BehaviorModuleOutput NormalLaneChange::getTerminalLaneChangePath() const { - auto output = prev_module_output_; - - if (isAbortState() && abort_path_) { - output.path = abort_path_->path; - extendOutputDrivableArea(output); - const auto current_seg_idx = planner_data_->findEgoSegmentIndex(output.path.points); - output.turn_signal_info = planner_data_->turn_signal_decider.overwrite_turn_signal( - output.path, getEgoPose(), current_seg_idx, prev_module_output_.turn_signal_info, - output.turn_signal_info, planner_data_->parameters.ego_nearest_dist_threshold, - planner_data_->parameters.ego_nearest_yaw_threshold); - return output; - } - const auto & current_lanes = get_current_lanes(); if (current_lanes.empty()) { RCLCPP_DEBUG(logger_, "Current lanes not found. Returning previous module's path as output."); return prev_module_output_; } - const auto terminal_path = - calcTerminalLaneChangePath(current_lanes, get_lane_change_lanes(current_lanes)); - if (!terminal_path) { + const auto terminal_lc_path = get_terminal_lane_change_path(); + + if (!terminal_lc_path) { RCLCPP_DEBUG(logger_, "Terminal path not found. Returning previous module's path as output."); return prev_module_output_; } - output.path = terminal_path->path; - output.turn_signal_info = updateOutputTurnSignal(); + auto output = prev_module_output_; - extendOutputDrivableArea(output); + output.path = *terminal_lc_path; + output.turn_signal_info = get_current_turn_signal_info(); - const auto current_seg_idx = planner_data_->findEgoSegmentIndex(output.path.points); - output.turn_signal_info = planner_data_->turn_signal_decider.overwrite_turn_signal( - output.path, getEgoPose(), current_seg_idx, prev_module_output_.turn_signal_info, - output.turn_signal_info, planner_data_->parameters.ego_nearest_dist_threshold, - planner_data_->parameters.ego_nearest_yaw_threshold); + extendOutputDrivableArea(output); return output; } @@ -1464,130 +1447,86 @@ bool NormalLaneChange::check_candidate_path_safety( return safety_check_with_normal_rss.is_safe; } -std::optional NormalLaneChange::calcTerminalLaneChangePath( - const lanelet::ConstLanelets & current_lanes, const lanelet::ConstLanelets & target_lanes) const +std::optional NormalLaneChange::get_terminal_lane_change_path() const { - const auto is_empty = [&](const auto & data, const auto & s) { - if (!data.empty()) return false; - RCLCPP_WARN(logger_, "%s is empty. Not expected.", s); - return true; - }; + const auto & transient_data = common_data_ptr_->transient_data; + const auto dist_to_terminal_start = transient_data.dist_to_terminal_start; + const auto min_lc_velocity = lane_change_parameters_->minimum_lane_changing_velocity; + const auto current_velocity = getEgoVelocity(); - const auto target_lane_neighbors_polygon_2d = - common_data_ptr_->lanes_polygon_ptr->target_neighbor; - if ( - is_empty(current_lanes, "current_lanes") || is_empty(target_lanes, "target_lanes") || - is_empty(target_lane_neighbors_polygon_2d, "target_lane_neighbors_polygon_2d")) { + PathWithLaneId prepare_segment; + try { + if (!get_prepare_segment(prepare_segment, dist_to_terminal_start)) { + RCLCPP_DEBUG(logger_, "failed to get valid prepare segment for terminal LC path"); + return {}; + } + } catch (const std::exception & e) { + RCLCPP_DEBUG(logger_, "failed to get terminal LC path: %s", e.what()); return {}; } - const auto & route_handler = *getRouteHandler(); - - const auto minimum_lane_changing_velocity = - lane_change_parameters_->minimum_lane_changing_velocity; - - const auto is_goal_in_route = route_handler.isInGoalRouteSection(target_lanes.back()); - - const auto current_min_dist_buffer = common_data_ptr_->transient_data.current_dist_buffer.min; - const auto next_min_dist_buffer = common_data_ptr_->transient_data.next_dist_buffer.min; - - const auto sorted_lane_ids = utils::lane_change::get_sorted_lane_ids(common_data_ptr_); - - // lane changing start getEgoPose() is at the end of prepare segment - const auto current_lane_terminal_point = - lanelet::utils::conversion::toGeomMsgPt(current_lanes.back().centerline3d().back()); - - double distance_to_terminal_from_goal = 0; - if (is_goal_in_route) { - distance_to_terminal_from_goal = - utils::getDistanceToEndOfLane(route_handler.getGoalPose(), current_lanes); - } - - const auto lane_changing_start_pose = autoware::motion_utils::calcLongitudinalOffsetPose( - prev_module_output_.path.points, current_lane_terminal_point, - -(current_min_dist_buffer + next_min_dist_buffer + distance_to_terminal_from_goal)); - - if (!lane_changing_start_pose) { - RCLCPP_DEBUG(logger_, "Reject: lane changing start pose not found!!!"); - return {}; - } + const auto & lane_changing_start_pose = prepare_segment.points.back().point.pose; - const auto target_length_from_lane_change_start_pose = utils::getArcLengthToTargetLanelet( - current_lanes, target_lanes.front(), lane_changing_start_pose.value()); + // t = 2 * d / (v1 + v2) + const auto duration_to_lc_start = + 2.0 * dist_to_terminal_start / (current_velocity + min_lc_velocity); + const auto lon_accel = (min_lc_velocity - current_velocity) / duration_to_lc_start; + const LaneChangePhaseMetrics prep_metric( + duration_to_lc_start, dist_to_terminal_start, min_lc_velocity, lon_accel, lon_accel, 0.0); - // Check if the lane changing start point is not on the lanes next to target lanes, - if (target_length_from_lane_change_start_pose > 0.0) { - RCLCPP_DEBUG(logger_, "lane change start getEgoPose() is behind target lanelet!"); - return {}; + if (terminal_lane_change_path_) { + terminal_lane_change_path_->info.set_prepare(prep_metric); + if (prepare_segment.points.empty()) { + terminal_lane_change_path_->path = terminal_lane_change_path_->shifted_path.path; + return terminal_lane_change_path_->path; + } + prepare_segment.points.pop_back(); + terminal_lane_change_path_->path = + utils::combinePath(prepare_segment, terminal_lane_change_path_->shifted_path.path); + return terminal_lane_change_path_->path; } - const auto shift_length = lanelet::utils::getLateralDistanceToClosestLanelet( - target_lanes, lane_changing_start_pose.value()); - - const auto [min_lateral_acc, max_lateral_acc] = - lane_change_parameters_->lane_change_lat_acc_map.find(minimum_lane_changing_velocity); - - const auto lane_changing_time = autoware::motion_utils::calc_shift_time_from_jerk( - shift_length, lane_change_parameters_->lane_changing_lateral_jerk, max_lateral_acc); - - const auto target_lane_length = common_data_ptr_->transient_data.target_lane_length; - const auto target_segment = getTargetSegment( - target_lanes, lane_changing_start_pose.value(), target_lane_length, current_min_dist_buffer, - minimum_lane_changing_velocity, next_min_dist_buffer); + const auto & target_lanes = common_data_ptr_->lanes_ptr->target; + const auto shift_length = + lanelet::utils::getLateralDistanceToClosestLanelet(target_lanes, lane_changing_start_pose); - if (target_segment.points.empty()) { - RCLCPP_DEBUG(logger_, "Reject: target segment is empty!! something wrong..."); - return {}; - } + const auto dist_lc_start_to_end_of_lanes = calculation::calc_dist_from_pose_to_terminal_end( + common_data_ptr_, common_data_ptr_->lanes_ptr->target_neighbor, + prepare_segment.points.back().point.pose); - LaneChangeInfo lane_change_info; - lane_change_info.longitudinal_acceleration = LaneChangePhaseInfo{0.0, 0.0}; - lane_change_info.duration = LaneChangePhaseInfo{0.0, lane_changing_time}; - lane_change_info.velocity = - LaneChangePhaseInfo{minimum_lane_changing_velocity, minimum_lane_changing_velocity}; - lane_change_info.length = LaneChangePhaseInfo{0.0, current_min_dist_buffer}; - lane_change_info.lane_changing_start = lane_changing_start_pose.value(); - lane_change_info.lane_changing_end = target_segment.points.front().point.pose; - lane_change_info.lateral_acceleration = max_lateral_acc; - lane_change_info.terminal_lane_changing_velocity = minimum_lane_changing_velocity; + const auto max_lane_changing_length = std::invoke([&]() { + double max_length = transient_data.dist_to_terminal_end - prep_metric.length; + auto target_lane_buffer = lane_change_parameters_->lane_change_finish_judge_buffer + + transient_data.next_dist_buffer.min; + if ( + std::abs(getRouteHandler()->getNumLaneToPreferredLane(target_lanes.back(), direction_)) > 0) { + target_lane_buffer += lane_change_parameters_->backward_length_buffer_for_end_of_lane; + } + max_length = std::min(max_length, dist_lc_start_to_end_of_lanes - target_lane_buffer); + return max_length; + }); - if (!is_valid_start_point(common_data_ptr_, lane_changing_start_pose.value())) { - RCLCPP_DEBUG( - logger_, - "Reject: lane changing points are not inside of the target preferred lanes or its " - "neighbors"); - return {}; - } + const auto max_path_velocity = prepare_segment.points.back().point.longitudinal_velocity_mps; + const auto lane_changing_metrics = calculation::calc_shift_phase_metrics( + common_data_ptr_, shift_length, prep_metric.velocity, max_path_velocity, + prep_metric.sampled_lon_accel, max_lane_changing_length); - const auto resample_interval = utils::lane_change::calcLaneChangeResampleInterval( - current_min_dist_buffer, minimum_lane_changing_velocity); - const auto target_lane_reference_path = utils::lane_change::get_reference_path_from_target_Lane( - common_data_ptr_, lane_changing_start_pose.value(), current_min_dist_buffer, resample_interval); + const auto sorted_lane_ids = utils::lane_change::get_sorted_lane_ids(common_data_ptr_); - if (target_lane_reference_path.points.empty()) { - RCLCPP_DEBUG(logger_, "Reject: target_lane_reference_path is empty!!"); - return {}; + LaneChangePath candidate_path; + for (const auto & lc_metric : lane_changing_metrics) { + try { + candidate_path = get_candidate_path( + prep_metric, lc_metric, prepare_segment, sorted_lane_ids, lane_changing_start_pose, + shift_length); + } catch (const std::exception & e) { + continue; + } + terminal_lane_change_path_ = candidate_path; + return candidate_path.path; } - lane_change_info.shift_line = utils::lane_change::get_lane_changing_shift_line( - lane_changing_start_pose.value(), target_segment.points.front().point.pose, - target_lane_reference_path, shift_length); - - auto reference_segment = prev_module_output_.path; - const double length_to_lane_changing_start = autoware::motion_utils::calcSignedArcLength( - reference_segment.points, reference_segment.points.front().point.pose.position, - lane_changing_start_pose->position); - utils::clipPathLength(reference_segment, 0, length_to_lane_changing_start, 0.0); - // remove terminal points because utils::clipPathLength() calculates extra long path - reference_segment.points.pop_back(); - reference_segment.points.back().point.longitudinal_velocity_mps = - static_cast(minimum_lane_changing_velocity); - - const auto terminal_lane_change_path = utils::lane_change::construct_candidate_path( - common_data_ptr_, lane_change_info, reference_segment, target_lane_reference_path, - sorted_lane_ids); - - return terminal_lane_change_path; + return {}; } PathSafetyStatus NormalLaneChange::isApprovedPathSafe() const From 9ee52bb1bd2151b29e81bbc89c1a82759389fef9 Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Wed, 13 Nov 2024 10:51:39 +0900 Subject: [PATCH 02/21] push terminal path to candidate paths if no other valid candidate path is found Signed-off-by: mohammad alqudah --- .../src/scene.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp index 5b714b77ffa8f..81521749a7e36 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp @@ -222,6 +222,10 @@ std::pair NormalLaneChange::getSafePath(LaneChangePath & safe_path) bool found_safe_path = get_lane_change_paths(valid_paths); // if no safe path is found and ego is stuck, try to find a path with a small margin + if (valid_paths.empty() && terminal_lane_change_path_) { + valid_paths.push_back(terminal_lane_change_path_.value()); + } + lane_change_debug_.valid_paths = valid_paths; if (valid_paths.empty()) { @@ -1486,13 +1490,8 @@ std::optional NormalLaneChange::get_terminal_lane_change_path() const auto max_lane_changing_length = std::invoke([&]() { double max_length = transient_data.dist_to_terminal_end - prep_metric.length; - auto target_lane_buffer = lane_change_parameters_->lane_change_finish_judge_buffer + - transient_data.next_dist_buffer.min; - if ( - std::abs(getRouteHandler()->getNumLaneToPreferredLane(target_lanes.back(), direction_)) > 0) { - target_lane_buffer += lane_change_parameters_->backward_length_buffer_for_end_of_lane; - } - max_length = std::min(max_length, dist_lc_start_to_end_of_lanes - target_lane_buffer); + max_length = + std::min(max_length, dist_lc_start_to_end_of_lanes - transient_data.next_dist_buffer.min); return max_length; }); From 46b9bf33534ab26bd59b9e08b3b3181d94a86d14 Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Wed, 13 Nov 2024 10:53:34 +0900 Subject: [PATCH 03/21] use terminal path in LC interface planWaitingApproval function Signed-off-by: mohammad alqudah --- .../src/interface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/interface.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/interface.cpp index 8fb1459f62c98..af5cad4222104 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/interface.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/interface.cpp @@ -152,7 +152,7 @@ BehaviorModuleOutput LaneChangeInterface::planWaitingApproval() { *prev_approved_path_ = getPreviousModuleOutput().path; - BehaviorModuleOutput out = getPreviousModuleOutput(); + BehaviorModuleOutput out = module_type_->getTerminalLaneChangePath(); module_type_->insert_stop_point(module_type_->get_current_lanes(), out.path); out.turn_signal_info = module_type_->get_current_turn_signal_info(); @@ -163,7 +163,7 @@ BehaviorModuleOutput LaneChangeInterface::planWaitingApproval() setObjectsOfInterestData(data.current_obj_pose, data.obj_shape, color); } - path_reference_ = std::make_shared(getPreviousModuleOutput().reference_path); + path_reference_ = std::make_shared(out.reference_path); stop_pose_ = module_type_->getStopPose(); if (!module_type_->isValidPath()) { From c1b84e2106b15b1504ff2429c7cb3ddc13150212 Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Wed, 13 Nov 2024 11:59:34 +0900 Subject: [PATCH 04/21] set lane changing longitudinal accel to zero for terminal lc path Signed-off-by: mohammad alqudah --- .../autoware_behavior_path_lane_change_module/src/scene.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp index 81521749a7e36..3d82d3e231ee6 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp @@ -1496,9 +1496,10 @@ std::optional NormalLaneChange::get_terminal_lane_change_path() }); const auto max_path_velocity = prepare_segment.points.back().point.longitudinal_velocity_mps; + constexpr double lane_changing_lon_accel{0.0}; const auto lane_changing_metrics = calculation::calc_shift_phase_metrics( common_data_ptr_, shift_length, prep_metric.velocity, max_path_velocity, - prep_metric.sampled_lon_accel, max_lane_changing_length); + lane_changing_lon_accel, max_lane_changing_length); const auto sorted_lane_ids = utils::lane_change::get_sorted_lane_ids(common_data_ptr_); From 38a784142a0daa8731c1d20820daaf5f1df050fd Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Wed, 13 Nov 2024 13:20:49 +0900 Subject: [PATCH 05/21] rename function Signed-off-by: mohammad alqudah --- .../autoware/behavior_path_lane_change_module/scene.hpp | 2 +- .../autoware_behavior_path_lane_change_module/src/scene.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp index f3122f74c3d09..b22959daa76aa 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp @@ -154,7 +154,7 @@ class NormalLaneChange : public LaneChangeBase bool check_candidate_path_safety( const LaneChangePath & candidate_path, const lane_change::TargetObjects & target_objects) const; - std::optional get_terminal_lane_change_path() const; + std::optional compute_terminal_lane_change_path() const; bool isValidPath(const PathWithLaneId & path) const override; diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp index 3d82d3e231ee6..b5aabbefa55bb 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp @@ -361,7 +361,7 @@ BehaviorModuleOutput NormalLaneChange::getTerminalLaneChangePath() const return prev_module_output_; } - const auto terminal_lc_path = get_terminal_lane_change_path(); + const auto terminal_lc_path = compute_terminal_lane_change_path(); if (!terminal_lc_path) { RCLCPP_DEBUG(logger_, "Terminal path not found. Returning previous module's path as output."); @@ -1441,7 +1441,7 @@ bool NormalLaneChange::check_candidate_path_safety( return safety_check_with_normal_rss.is_safe; } -std::optional NormalLaneChange::get_terminal_lane_change_path() const +std::optional NormalLaneChange::compute_terminal_lane_change_path() const { const auto & transient_data = common_data_ptr_->transient_data; const auto dist_to_terminal_start = transient_data.dist_to_terminal_start; From aa5bb0734fd2a2640705eae16e9addfa9d9a311e Mon Sep 17 00:00:00 2001 From: "tomoya.kimura" Date: Wed, 27 Nov 2024 17:28:07 +0900 Subject: [PATCH 06/21] chore: rename codeowners file Signed-off-by: tomoya.kimura --- .github/{CODEOWNERS => _CODEOWNERS} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{CODEOWNERS => _CODEOWNERS} (100%) diff --git a/.github/CODEOWNERS b/.github/_CODEOWNERS similarity index 100% rename from .github/CODEOWNERS rename to .github/_CODEOWNERS From d547fcca478c2f18c1dba527c1c8f34ce2b56fae Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Fri, 6 Dec 2024 14:19:22 +0900 Subject: [PATCH 07/21] remove unused member variable prev_approved_path_ Signed-off-by: mohammad alqudah --- .../autoware/behavior_path_lane_change_module/interface.hpp | 2 -- .../behavior_path_lane_change_module/utils/base_class.hpp | 2 -- .../src/interface.cpp | 6 +----- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/interface.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/interface.hpp index 07920f83fd980..78bb69bd4f519 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/interface.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/interface.hpp @@ -129,8 +129,6 @@ class LaneChangeInterface : public SceneModuleInterface mutable MarkerArray virtual_wall_marker_; - std::unique_ptr prev_approved_path_; - void clearAbortApproval() { is_abort_path_approved_ = false; } bool is_abort_path_approved_{false}; diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/base_class.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/base_class.hpp index bdd4878f65c87..a5b5ea6f73872 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/base_class.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/base_class.hpp @@ -290,8 +290,6 @@ class LaneChangeBase std::optional lane_change_stop_pose_{std::nullopt}; mutable std::optional terminal_lane_change_path_{std::nullopt}; - PathWithLaneId prev_approved_path_{}; - int unsafe_hysteresis_count_{0}; bool is_abort_path_approved_{false}; bool is_abort_approval_requested_{false}; diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/interface.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/interface.cpp index 06bf6d0d91823..a22cad97a8cab 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/interface.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/interface.cpp @@ -43,8 +43,7 @@ LaneChangeInterface::LaneChangeInterface( std::unique_ptr && module_type) : SceneModuleInterface{name, node, rtc_interface_ptr_map, objects_of_interest_marker_interface_ptr_map}, // NOLINT parameters_{std::move(parameters)}, - module_type_{std::move(module_type)}, - prev_approved_path_{std::make_unique()} + module_type_{std::move(module_type)} { module_type_->setTimeKeeper(getTimeKeeper()); logger_ = utils::lane_change::getLogger(module_type_->getModuleTypeStr()); @@ -109,7 +108,6 @@ BehaviorModuleOutput LaneChangeInterface::plan() auto output = module_type_->generateOutput(); path_reference_ = std::make_shared(output.reference_path); - *prev_approved_path_ = getPreviousModuleOutput().path; stop_pose_ = module_type_->getStopPose(); @@ -155,8 +153,6 @@ BehaviorModuleOutput LaneChangeInterface::plan() BehaviorModuleOutput LaneChangeInterface::planWaitingApproval() { - *prev_approved_path_ = getPreviousModuleOutput().path; - BehaviorModuleOutput out = module_type_->getTerminalLaneChangePath(); module_type_->insert_stop_point(module_type_->get_current_lanes(), out.path); From 8eb32aea4f2a7a56d52281d2716a6eee3e1d23c4 Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Mon, 9 Dec 2024 08:25:38 +0900 Subject: [PATCH 08/21] refactor stop point insertion for terminal lc path Signed-off-by: mohammad alqudah --- .../scene.hpp | 7 ++- .../utils/base_class.hpp | 2 +- .../utils/calculation.hpp | 3 + .../src/interface.cpp | 2 +- .../src/scene.cpp | 50 ++++++++-------- .../src/utils/calculation.cpp | 58 ++++++++++++++----- 6 files changed, 77 insertions(+), 45 deletions(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp index 21b90d569f307..77d322064759d 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp @@ -70,9 +70,12 @@ class NormalLaneChange : public LaneChangeBase void extendOutputDrivableArea(BehaviorModuleOutput & output) const override; - void insert_stop_point(const lanelet::ConstLanelets & lanelets, PathWithLaneId & path) override; + void insert_stop_point( + const lanelet::ConstLanelets & lanelets, PathWithLaneId & path, + const bool is_waiting_approval = false) override; - void insert_stop_point_on_current_lanes(PathWithLaneId & path); + void insert_stop_point_on_current_lanes( + PathWithLaneId & path, const bool is_waiting_approval = false); PathWithLaneId getReferencePath() const override; diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/base_class.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/base_class.hpp index a5b5ea6f73872..00387f490d60f 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/base_class.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/base_class.hpp @@ -130,7 +130,7 @@ class LaneChangeBase virtual void insert_stop_point( [[maybe_unused]] const lanelet::ConstLanelets & lanelets, - [[maybe_unused]] PathWithLaneId & path) + [[maybe_unused]] PathWithLaneId & path, [[maybe_unused]] const bool is_waiting_approval = false) { } diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/calculation.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/calculation.hpp index 29047c90590b3..88474aba39876 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/calculation.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/calculation.hpp @@ -67,6 +67,9 @@ double calc_dist_to_last_fit_width( const lanelet::ConstLanelets & lanelets, const Pose & src_pose, const BehaviorPathPlannerParameters & bpp_param, const double margin = 0.1); +double calc_dist_to_last_fit_width( + const CommonDataPtr & common_data_ptr, const PathWithLaneId & path, const double margin = 0.1); + /** * @brief Calculates the maximum preparation longitudinal distance for lane change. * diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/interface.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/interface.cpp index a22cad97a8cab..9f3c6c9ef48bf 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/interface.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/interface.cpp @@ -155,7 +155,7 @@ BehaviorModuleOutput LaneChangeInterface::planWaitingApproval() { BehaviorModuleOutput out = module_type_->getTerminalLaneChangePath(); - module_type_->insert_stop_point(module_type_->get_current_lanes(), out.path); + module_type_->insert_stop_point(module_type_->get_current_lanes(), out.path, true); out.turn_signal_info = module_type_->get_current_turn_signal_info(); const auto & lane_change_debug = module_type_->getDebugData(); diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp index 4946a191d05b4..35bb0e21dfd17 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp @@ -479,7 +479,7 @@ void NormalLaneChange::extendOutputDrivableArea(BehaviorModuleOutput & output) c } void NormalLaneChange::insert_stop_point( - const lanelet::ConstLanelets & lanelets, PathWithLaneId & path) + const lanelet::ConstLanelets & lanelets, PathWithLaneId & path, const bool is_waiting_approval) { universe_utils::ScopedTimeTrack st(__func__, *time_keeper_); if (lanelets.empty()) { @@ -504,55 +504,53 @@ void NormalLaneChange::insert_stop_point( return; } - insert_stop_point_on_current_lanes(path); + insert_stop_point_on_current_lanes(path, is_waiting_approval); } -void NormalLaneChange::insert_stop_point_on_current_lanes(PathWithLaneId & path) +void NormalLaneChange::insert_stop_point_on_current_lanes( + PathWithLaneId & path, const bool is_waiting_approval) { const auto & path_front_pose = path.points.front().point.pose; - const auto & center_line = common_data_ptr_->current_lanes_path.points; - const auto get_arc_length_along_lanelet = [&](const geometry_msgs::msg::Pose & target) { - return motion_utils::calcSignedArcLength( - center_line, path_front_pose.position, target.position); - }; + const auto & ego_pose = common_data_ptr_->get_ego_pose(); + const auto dist_from_path_front = + motion_utils::calcSignedArcLength(path.points, path_front_pose.position, ego_pose.position); const auto & transient_data = common_data_ptr_->transient_data; const auto & lanes_ptr = common_data_ptr_->lanes_ptr; const auto & lc_param_ptr = common_data_ptr_->lc_param_ptr; - const auto dist_to_terminal = std::invoke([&]() -> double { - const auto target_pose = (lanes_ptr->current_lane_in_goal_section) - ? common_data_ptr_->route_handler_ptr->getGoalPose() - : center_line.back().point.pose; - return get_arc_length_along_lanelet(target_pose); - }); - - const auto & bpp_param_ptr = common_data_ptr_->bpp_param_ptr; - const auto min_dist_buffer = transient_data.current_dist_buffer.min; const auto dist_to_terminal_start = - dist_to_terminal - min_dist_buffer - calculation::calc_stopping_distance(lc_param_ptr); + transient_data.dist_to_terminal_start - calculation::calc_stopping_distance(lc_param_ptr); - const auto distance_to_last_fit_width = std::invoke([&]() -> double { + const auto & bpp_param_ptr = common_data_ptr_->bpp_param_ptr; + const auto dist_to_terminal_stop = std::invoke([&]() -> double { const auto & curr_lanes_poly = common_data_ptr_->lanes_polygon_ptr->current; - if (utils::isEgoWithinOriginalLane(curr_lanes_poly, getEgoPose(), *bpp_param_ptr)) { - return utils::lane_change::calculation::calc_dist_to_last_fit_width( - lanes_ptr->current, path.points.front().point.pose, *bpp_param_ptr); + if (!utils::isEgoWithinOriginalLane(curr_lanes_poly, getEgoPose(), *bpp_param_ptr)) { + return dist_from_path_front + dist_to_terminal_start; + } + + if (terminal_lane_change_path_ && is_waiting_approval) { + return calculation::calc_dist_to_last_fit_width(common_data_ptr_, path); } - return std::numeric_limits::max(); - }); - const auto dist_to_terminal_stop = std::min(dist_to_terminal_start, distance_to_last_fit_width); + const auto dist_to_last_fit_width = calculation::calc_dist_to_last_fit_width( + lanes_ptr->current, path.points.front().point.pose, *bpp_param_ptr); + + return std::min(dist_from_path_front + dist_to_terminal_start, dist_to_last_fit_width); + }); if (filtered_objects_.current_lane.empty()) { set_stop_pose(dist_to_terminal_stop, path); return; } + const auto & center_line = common_data_ptr_->current_lanes_path.points; const auto dist_to_target_lane_start = std::invoke([&]() -> double { const auto & front_lane = lanes_ptr->target_neighbor.front(); const auto target_front = utils::to_geom_msg_pose(front_lane.centerline2d().front(), front_lane); - return get_arc_length_along_lanelet(target_front); + return motion_utils::calcSignedArcLength( + center_line, path_front_pose.position, target_front.position); }); const auto arc_length_to_current_obj = utils::lane_change::get_min_dist_to_current_lanes_obj( diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/utils/calculation.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/utils/calculation.cpp index 3ba11d62a2be7..4fbc80ced842f 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/utils/calculation.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/utils/calculation.cpp @@ -58,22 +58,10 @@ double calc_stopping_distance(const LCParamPtr & lc_param_ptr) } double calc_dist_to_last_fit_width( - const lanelet::ConstLanelets & lanelets, const Pose & src_pose, + const lanelet::ConstLanelets & lanelets, const lanelet::BasicPolygon2d & lanelet_polygon, + const universe_utils::LineString2d & line_string, const Pose & src_pose, const BehaviorPathPlannerParameters & bpp_param, const double margin) { - if (lanelets.empty()) return 0.0; - - const auto lane_polygon = lanelets.back().polygon2d().basicPolygon(); - const auto center_line = lanelet::utils::generateFineCenterline(lanelets.back(), 1.0); - - if (center_line.size() <= 1) return 0.0; - - universe_utils::LineString2d line_string; - line_string.reserve(center_line.size() - 1); - std::for_each(center_line.begin() + 1, center_line.end(), [&line_string](const auto & point) { - boost::geometry::append(line_string, universe_utils::Point2d(point.x(), point.y())); - }); - const double buffer_distance = 0.5 * bpp_param.vehicle_width + margin; universe_utils::MultiPolygon2d center_line_polygon; namespace strategy = boost::geometry::strategy::buffer; @@ -85,7 +73,7 @@ double calc_dist_to_last_fit_width( if (center_line_polygon.empty()) return 0.0; std::vector intersection_points; - boost::geometry::intersection(lane_polygon, center_line_polygon, intersection_points); + boost::geometry::intersection(lanelet_polygon, center_line_polygon, intersection_points); if (intersection_points.empty()) { return utils::getDistanceToEndOfLane(src_pose, lanelets); @@ -102,6 +90,46 @@ double calc_dist_to_last_fit_width( return std::max(distance - (bpp_param.base_link2front + margin), 0.0); } +double calc_dist_to_last_fit_width( + const CommonDataPtr & common_data_ptr, const PathWithLaneId & path, const double margin) +{ + const auto & current_lanes = common_data_ptr->lanes_ptr->current; + const auto & current_lanes_polygon = common_data_ptr->lanes_polygon_ptr->current; + const auto & bpp_param = *common_data_ptr->bpp_param_ptr; + + universe_utils::LineString2d line_string; + line_string.reserve(path.points.size() - 1); + std::for_each(path.points.begin() + 1, path.points.end(), [&line_string](const auto & point) { + const auto & position = point.point.pose.position; + boost::geometry::append(line_string, universe_utils::Point2d(position.x, position.y)); + }); + + const auto & src_pose = path.points.front().point.pose; + return calc_dist_to_last_fit_width( + current_lanes, current_lanes_polygon, line_string, src_pose, bpp_param, margin); +} + +double calc_dist_to_last_fit_width( + const lanelet::ConstLanelets & lanelets, const Pose & src_pose, + const BehaviorPathPlannerParameters & bpp_param, const double margin) +{ + if (lanelets.empty()) return 0.0; + + const auto lane_polygon = lanelets.back().polygon2d().basicPolygon(); + const auto center_line = lanelet::utils::generateFineCenterline(lanelets.back(), 1.0); + + if (center_line.size() <= 1) return 0.0; + + universe_utils::LineString2d line_string; + line_string.reserve(center_line.size() - 1); + std::for_each(center_line.begin() + 1, center_line.end(), [&line_string](const auto & point) { + boost::geometry::append(line_string, universe_utils::Point2d(point.x(), point.y())); + }); + + return calc_dist_to_last_fit_width( + lanelets, lane_polygon, line_string, src_pose, bpp_param, margin); +} + double calc_maximum_prepare_length(const CommonDataPtr & common_data_ptr) { const auto max_prepare_duration = common_data_ptr->lc_param_ptr->trajectory.max_prepare_duration; From aacb9614d7e35958dd279f2fafe3b3846c18ed01 Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Mon, 9 Dec 2024 08:57:06 +0900 Subject: [PATCH 09/21] add flag to enable/disable terminal path feature Signed-off-by: mohammad alqudah --- .../config/lane_change.param.yaml | 1 + .../behavior_path_lane_change_module/utils/parameters.hpp | 3 +++ .../autoware_behavior_path_lane_change_module/src/manager.cpp | 3 +++ .../autoware_behavior_path_lane_change_module/src/scene.cpp | 4 ++++ 4 files changed, 11 insertions(+) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml index bf892b3058a16..0de2df8c1f2fc 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml @@ -5,6 +5,7 @@ backward_length_buffer_for_end_of_lane: 3.0 # [m] backward_length_buffer_for_blocking_object: 3.0 # [m] backward_length_from_intersection: 5.0 # [m] + enable_terminal_path: true # turn signal min_length_for_turn_signal_activation: 10.0 # [m] diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/parameters.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/parameters.hpp index a76742307e061..3145d9c6b5c12 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/parameters.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/parameters.hpp @@ -173,6 +173,9 @@ struct Parameters // debug marker bool publish_debug_marker{false}; + + // terminal lane change path + bool enable_terminal_path{false}; }; } // namespace autoware::behavior_path_planner::lane_change diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/manager.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/manager.cpp index 1bb41069140e7..eab39828b4c30 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/manager.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/manager.cpp @@ -240,6 +240,9 @@ LCParamPtr LaneChangeModuleManager::set_params(rclcpp::Node * node, const std::s // debug marker p.publish_debug_marker = getOrDeclareParameter(*node, parameter("publish_debug_marker")); + // terminal lane change path + p.enable_terminal_path = getOrDeclareParameter(*node, parameter("enable_terminal_path")); + // validation of safety check parameters // if loose check is not enabled, lane change module will keep on chattering and canceling, and // false positive situation might occur diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp index 35bb0e21dfd17..9ad973549d179 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp @@ -386,6 +386,10 @@ LaneChangePath NormalLaneChange::getLaneChangePath() const BehaviorModuleOutput NormalLaneChange::getTerminalLaneChangePath() const { + if (!lane_change_parameters_->enable_terminal_path) { + return prev_module_output_; + } + const auto & current_lanes = get_current_lanes(); if (current_lanes.empty()) { RCLCPP_DEBUG(logger_, "Current lanes not found. Returning previous module's path as output."); From 5ee503fb8b6fa0916438419eefd5e1eecf932c50 Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Mon, 9 Dec 2024 10:46:30 +0900 Subject: [PATCH 10/21] update README Signed-off-by: mohammad alqudah --- .../autoware_behavior_path_lane_change_module/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md index 86d8d1c0c1413..0a3dfc9368a21 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md @@ -886,6 +886,7 @@ The following parameters are configurable in [lane_change.param.yaml](https://gi | `backward_length_buffer_for_end_of_lane` | [m] | double | The end of lane buffer to ensure ego vehicle has enough distance to start lane change | 3.0 | | `backward_length_buffer_for_blocking_object` | [m] | double | The end of lane buffer to ensure ego vehicle has enough distance to start lane change when there is an object in front | 3.0 | | `backward_length_from_intersection` | [m] | double | Distance threshold from the last intersection to invalidate or cancel the lane change path | 5.0 | +| `enable_terminal_path` | [-] | bool | Flag to enable/disable terminal lane change path feature | true | | `trajectory.max_prepare_duration` | [s] | double | The maximum preparation time for the ego vehicle to be ready to perform lane change. | 4.0 | | `trajectory.min_prepare_duration` | [s] | double | The minimum preparation time for the ego vehicle to be ready to perform lane change. | 2.0 | | `trajectory.lateral_jerk` | [m/s3] | double | Lateral jerk value for lane change path generation | 0.5 | From a2f6eca007171c5313e45b687a89a6fc0826d41b Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Tue, 10 Dec 2024 12:06:04 +0900 Subject: [PATCH 11/21] add parameter to configure stop point placement Signed-off-by: mohammad alqudah --- .../config/lane_change.param.yaml | 6 +++++- .../behavior_path_lane_change_module/utils/parameters.hpp | 1 + .../src/manager.cpp | 4 +++- .../autoware_behavior_path_lane_change_module/src/scene.cpp | 4 +++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml index 0de2df8c1f2fc..6c121e81fe0cc 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml @@ -5,11 +5,15 @@ backward_length_buffer_for_end_of_lane: 3.0 # [m] backward_length_buffer_for_blocking_object: 3.0 # [m] backward_length_from_intersection: 5.0 # [m] - enable_terminal_path: true # turn signal min_length_for_turn_signal_activation: 10.0 # [m] + # terminal path + terminal_path: + enable: true + stop_at_boundary: false + # trajectory generation trajectory: max_prepare_duration: 4.0 diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/parameters.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/parameters.hpp index 3145d9c6b5c12..16df9d26a0f56 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/parameters.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/parameters.hpp @@ -176,6 +176,7 @@ struct Parameters // terminal lane change path bool enable_terminal_path{false}; + bool stop_at_boundary_for_terminal_path{false}; }; } // namespace autoware::behavior_path_planner::lane_change diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/manager.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/manager.cpp index eab39828b4c30..aa927c439fbf6 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/manager.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/manager.cpp @@ -241,7 +241,9 @@ LCParamPtr LaneChangeModuleManager::set_params(rclcpp::Node * node, const std::s p.publish_debug_marker = getOrDeclareParameter(*node, parameter("publish_debug_marker")); // terminal lane change path - p.enable_terminal_path = getOrDeclareParameter(*node, parameter("enable_terminal_path")); + p.enable_terminal_path = getOrDeclareParameter(*node, parameter("terminal_path.enable")); + p.stop_at_boundary_for_terminal_path = + getOrDeclareParameter(*node, parameter("terminal_path.stop_at_boundary")); // validation of safety check parameters // if loose check is not enabled, lane change module will keep on chattering and canceling, and diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp index 9baa5cb83340e..921b09a41e827 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp @@ -533,7 +533,9 @@ void NormalLaneChange::insert_stop_point_on_current_lanes( return dist_from_path_front + dist_to_terminal_start; } - if (terminal_lane_change_path_ && is_waiting_approval) { + if ( + terminal_lane_change_path_ && is_waiting_approval && + lc_param_ptr->stop_at_boundary_for_terminal_path) { return calculation::calc_dist_to_last_fit_width(common_data_ptr_, path); } From 33a68042b6b94d99547323a80f35a20118abb494 Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Fri, 13 Dec 2024 10:33:06 +0900 Subject: [PATCH 12/21] compute terminal path only when near terminal start Signed-off-by: mohammad alqudah --- .../src/scene.cpp | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp index 739112574f4b2..aa68b992c8b07 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp @@ -385,7 +385,9 @@ LaneChangePath NormalLaneChange::getLaneChangePath() const BehaviorModuleOutput NormalLaneChange::getTerminalLaneChangePath() const { - if (!lane_change_parameters_->enable_terminal_path) { + if ( + !lane_change_parameters_->enable_terminal_path || + !common_data_ptr_->transient_data.is_ego_near_current_terminal_start) { return prev_module_output_; } @@ -1401,14 +1403,21 @@ std::optional NormalLaneChange::compute_terminal_lane_change_pat return {}; } - const auto & lane_changing_start_pose = prepare_segment.points.back().point.pose; - // t = 2 * d / (v1 + v2) const auto duration_to_lc_start = 2.0 * dist_to_terminal_start / (current_velocity + min_lc_velocity); - const auto lon_accel = (min_lc_velocity - current_velocity) / duration_to_lc_start; + const auto lon_accel = std::invoke([&]() -> double { + if (duration_to_lc_start < calculation::eps) { + return 0.0; + } + return std::clamp( + (min_lc_velocity - current_velocity) / duration_to_lc_start, + lane_change_parameters_->trajectory.min_longitudinal_acc, + lane_change_parameters_->trajectory.max_longitudinal_acc); + }); + const auto vel_on_prep = current_velocity + lon_accel * duration_to_lc_start; const LaneChangePhaseMetrics prep_metric( - duration_to_lc_start, dist_to_terminal_start, min_lc_velocity, lon_accel, lon_accel, 0.0); + duration_to_lc_start, dist_to_terminal_start, vel_on_prep, lon_accel, lon_accel, 0.0); if (terminal_lane_change_path_) { terminal_lane_change_path_->info.set_prepare(prep_metric); @@ -1422,6 +1431,7 @@ std::optional NormalLaneChange::compute_terminal_lane_change_pat return terminal_lane_change_path_->path; } + const auto & lane_changing_start_pose = prepare_segment.points.back().point.pose; const auto & target_lanes = common_data_ptr_->lanes_ptr->target; const auto shift_length = lanelet::utils::getLateralDistanceToClosestLanelet(target_lanes, lane_changing_start_pose); From 75a6878a0f2dbd4f422251397cf7d0cd3b918293 Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Fri, 20 Dec 2024 16:19:43 +0900 Subject: [PATCH 13/21] add option to disable feature near goal Signed-off-by: mohammad alqudah --- .../README.md | 11 ++++++++++- .../config/lane_change.param.yaml | 3 ++- .../structs/data.hpp | 2 ++ .../structs/parameters.hpp | 12 ++++++++---- .../src/manager.cpp | 6 ++++-- .../src/scene.cpp | 15 +++++++++++++-- 6 files changed, 39 insertions(+), 10 deletions(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md index 0a3dfc9368a21..80c4a28d513bf 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md @@ -886,7 +886,6 @@ The following parameters are configurable in [lane_change.param.yaml](https://gi | `backward_length_buffer_for_end_of_lane` | [m] | double | The end of lane buffer to ensure ego vehicle has enough distance to start lane change | 3.0 | | `backward_length_buffer_for_blocking_object` | [m] | double | The end of lane buffer to ensure ego vehicle has enough distance to start lane change when there is an object in front | 3.0 | | `backward_length_from_intersection` | [m] | double | Distance threshold from the last intersection to invalidate or cancel the lane change path | 5.0 | -| `enable_terminal_path` | [-] | bool | Flag to enable/disable terminal lane change path feature | true | | `trajectory.max_prepare_duration` | [s] | double | The maximum preparation time for the ego vehicle to be ready to perform lane change. | 4.0 | | `trajectory.min_prepare_duration` | [s] | double | The minimum preparation time for the ego vehicle to be ready to perform lane change. | 2.0 | | `trajectory.lateral_jerk` | [m/s3] | double | Lateral jerk value for lane change path generation | 0.5 | @@ -935,6 +934,16 @@ The following parameters are used to judge lane change completion. | `delay_lane_change.min_road_shoulder_width` | [m] | double | Width considered as road shoulder if lane doesn't have road shoulder when checking for parked vehicle | 0.5 | | `delay_lane_change.th_parked_vehicle_shift_ratio` | [-] | double | Stopped vehicles beyond this distance ratio from center line will be considered as parked | 0.6 | +### Terminal Lane Change Path + +The following parameters are used to configure terminal lane change path feature. + +| Name | Unit | Type | Description | Default value | +| :-------------------------------- | ---- | ---- | ------------------------------------------------------------------------- | ------------- | +| `terminal_path.enable` | [-] | bool | Flag to enable/disable terminal path feature | true | +| `terminal_path.disable_near_goal` | [-] | bool | Flag to disable terminal path feature if ego is near goal | true | +| `terminal_path.stop_at_boundary` | [-] | bool | If true, ego will stop at current lane boundary instead of middle of lane | true | + ### Collision checks #### Target Objects diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml index 6c121e81fe0cc..f5edb59adba5a 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml @@ -12,7 +12,8 @@ # terminal path terminal_path: enable: true - stop_at_boundary: false + disable_near_goal: true + stop_at_boundary: true # trajectory generation trajectory: diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/structs/data.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/structs/data.hpp index c6fcc3b1795af..acc30f2526e93 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/structs/data.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/structs/data.hpp @@ -233,6 +233,8 @@ struct TransientData double target_lane_length{std::numeric_limits::min()}; + double dist_to_target_end{std::numeric_limits::max()}; + lanelet::ArcCoordinates current_lanes_ego_arc; // arc coordinates of ego pose along current lanes lanelet::ArcCoordinates target_lanes_ego_arc; // arc coordinates of ego pose along target lanes diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/structs/parameters.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/structs/parameters.hpp index b9baebd952802..28b8a453c9a1a 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/structs/parameters.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/structs/parameters.hpp @@ -137,12 +137,20 @@ struct DelayParameters double th_parked_vehicle_shift_ratio{0.6}; }; +struct TerminalPathParameters +{ + bool enable{false}; + bool disable_near_goal{false}; + bool stop_at_boundary{false}; +}; + struct Parameters { TrajectoryParameters trajectory{}; SafetyParameters safety{}; CancelParameters cancel{}; DelayParameters delay{}; + TerminalPathParameters terminal_path{}; // lane change parameters double backward_lane_length{200.0}; @@ -173,10 +181,6 @@ struct Parameters // debug marker bool publish_debug_marker{false}; - - // terminal lane change path - bool enable_terminal_path{false}; - bool stop_at_boundary_for_terminal_path{false}; }; } // namespace autoware::behavior_path_planner::lane_change diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/manager.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/manager.cpp index aa927c439fbf6..f00e19159450b 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/manager.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/manager.cpp @@ -241,8 +241,10 @@ LCParamPtr LaneChangeModuleManager::set_params(rclcpp::Node * node, const std::s p.publish_debug_marker = getOrDeclareParameter(*node, parameter("publish_debug_marker")); // terminal lane change path - p.enable_terminal_path = getOrDeclareParameter(*node, parameter("terminal_path.enable")); - p.stop_at_boundary_for_terminal_path = + p.terminal_path.enable = getOrDeclareParameter(*node, parameter("terminal_path.enable")); + p.terminal_path.disable_near_goal = + getOrDeclareParameter(*node, parameter("terminal_path.disable_near_goal")); + p.terminal_path.stop_at_boundary = getOrDeclareParameter(*node, parameter("terminal_path.stop_at_boundary")); // validation of safety check parameters diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp index aa68b992c8b07..a776ba683198b 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp @@ -168,6 +168,9 @@ void NormalLaneChange::update_transient_data(const bool is_approved) transient_data.dist_to_terminal_start = transient_data.dist_to_terminal_end - transient_data.current_dist_buffer.min; + transient_data.dist_to_target_end = calculation::calc_dist_from_pose_to_terminal_end( + common_data_ptr_, common_data_ptr_->lanes_ptr->target, common_data_ptr_->get_ego_pose()); + transient_data.max_prepare_length = calculation::calc_maximum_prepare_length(common_data_ptr_); transient_data.target_lane_length = @@ -386,11 +389,19 @@ LaneChangePath NormalLaneChange::getLaneChangePath() const BehaviorModuleOutput NormalLaneChange::getTerminalLaneChangePath() const { if ( - !lane_change_parameters_->enable_terminal_path || + !lane_change_parameters_->terminal_path.enable || !common_data_ptr_->transient_data.is_ego_near_current_terminal_start) { return prev_module_output_; } + const auto is_near_goal = lane_change_parameters_->terminal_path.disable_near_goal && + common_data_ptr_->lanes_ptr->target_lane_in_goal_section && + common_data_ptr_->transient_data.dist_to_target_end < + common_data_ptr_->transient_data.lane_changing_length.max; + if (is_near_goal) { + return prev_module_output_; + } + const auto & current_lanes = get_current_lanes(); if (current_lanes.empty()) { RCLCPP_DEBUG(logger_, "Current lanes not found. Returning previous module's path as output."); @@ -536,7 +547,7 @@ void NormalLaneChange::insert_stop_point_on_current_lanes( if ( terminal_lane_change_path_ && is_waiting_approval && - lc_param_ptr->stop_at_boundary_for_terminal_path) { + lc_param_ptr->terminal_path.stop_at_boundary) { return calculation::calc_dist_to_last_fit_width(common_data_ptr_, path); } From 349850b9535e63ef405792e30fafd2ffaf3633e6 Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Mon, 23 Dec 2024 10:19:41 +0900 Subject: [PATCH 14/21] set default flag value to false Signed-off-by: mohammad alqudah --- .../autoware_behavior_path_lane_change_module/README.md | 2 +- .../config/lane_change.param.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md index 80c4a28d513bf..836766a16cdff 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md @@ -942,7 +942,7 @@ The following parameters are used to configure terminal lane change path feature | :-------------------------------- | ---- | ---- | ------------------------------------------------------------------------- | ------------- | | `terminal_path.enable` | [-] | bool | Flag to enable/disable terminal path feature | true | | `terminal_path.disable_near_goal` | [-] | bool | Flag to disable terminal path feature if ego is near goal | true | -| `terminal_path.stop_at_boundary` | [-] | bool | If true, ego will stop at current lane boundary instead of middle of lane | true | +| `terminal_path.stop_at_boundary` | [-] | bool | If true, ego will stop at current lane boundary instead of middle of lane | false | ### Collision checks diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml index f5edb59adba5a..e1021ad19dc22 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml @@ -13,7 +13,7 @@ terminal_path: enable: true disable_near_goal: true - stop_at_boundary: true + stop_at_boundary: false # trajectory generation trajectory: From a382a0ea24abaf2085f0b14fbbf93ddedb3c40ee Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Mon, 23 Dec 2024 14:29:03 +0900 Subject: [PATCH 15/21] add documentation for terminal lane change path Signed-off-by: mohammad alqudah --- .../README.md | 10 ++++++++++ .../images/lane_change-no_terminal_path.png | Bin 0 -> 52805 bytes .../images/lane_change-terminal_path.png | Bin 0 -> 56568 bytes 3 files changed, 10 insertions(+) create mode 100644 planning/behavior_path_planner/autoware_behavior_path_lane_change_module/images/lane_change-no_terminal_path.png create mode 100644 planning/behavior_path_planner/autoware_behavior_path_lane_change_module/images/lane_change-terminal_path.png diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md index 836766a16cdff..49917b57f6061 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md @@ -874,6 +874,16 @@ endif @enduml ``` +## Terminal Lane Change Path + +Depending on the space configration around the Ego vehicle, it is possible that a valid LC path cannot be generated. If that happens, then Ego will get stuck at `terminal_start` and will be not be able to proceed. Therefore we introduced the terminal LC path feature; when ego gets near to the terminal point (dist to `terminal_start` is less than the maximum lane change length) a terminal lane changing path will be computed starting from the terminal start point on the current lane and connects to the target lane. The terminal path only needs to be computed once in each execution of LC module. If no valid candidate paths are found in the path generation process, then the terminal path will be used as a fallback candidate path, the safety of the terminal path is not ensured and therefore it can only be force approved. The following images illustrate the expected behavior without and with the terminal path feature respectively: + +![no terminal path](./images/lane_change-no_terminal_path.png) + +![terminal path](./images/lane_change-terminal_path.png) + +Additionally if terminal path feature is enabled and path is computed, stop point placement can be configured to be at the edge of the current lane instead of at the `terminal_start` position, as indicated by the dashed red line in the image above. + ## Parameters ### Essential lane change parameters diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/images/lane_change-no_terminal_path.png b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/images/lane_change-no_terminal_path.png new file mode 100644 index 0000000000000000000000000000000000000000..2c7b76e1749974c64c7422aa524ed6839e12059b GIT binary patch literal 52805 zcmeEv2S60p+BO)wF=|X=G`7TG7q^0o4VLXNvuv4}-7%JJY%lIEY_Xux#KhR6V#Thp z61$>^iHTjX_Zme|M2!XP{_pHUL}Tu~|Mz|Oe*e8cDY|p!%qj1A`-cL{XJp^#;`iUuLPINW*`jOBr~)lU^8Q*nK9HONg^eZ9K}e4-!`+= z0DlP$I;)d-9pN)8R1aDpswcqQ>oZ`d+V=>aq5;Ls;fiv^bsrvo10n!I*f#mzqm2fWKobvnEiTta76 zTqinh&M>z#X`z3h+~ajSt%ld`2n1ZJukfA9#RNVJbuR!hhyJK5oehSd(S`;}U29=< z6_?cWUX9ym@TM5tX1l>_a4TFIo!McmcouYxtxBhQwxNff8m8TuUZn}3u{)rm9*Jf% zIB+{upbizVV%e1iJW7bd6 zM{{uTW^3WqGU(rs4UaR;t*aE5x3Em8=x_7wEAAB1+<@VRnyj^Awe+!b&|> z*RC| z?0@zQ{%1(U1eZ<_7N~zjctX{|PpVW@fBTt=+94bN_iFyh6{ujb^uJ+$!K%6_O=`A> zJRmMOm|ipZJ;@ra!It9mn7v_lQ|t74op!J&HtGgfqq7>_&NPR<(#q)#shTt!xGR6O zkYF<#DM!`obU~@c;|lv_sb)WA@42B46Dmt7gH|yFo?heCL@^V>mkfa?-5AB-`oRxi zB&LW^1{vV8w1huR7j&_-LR*?9NHc3ha+;3kOiyO&nfd^mi3Hf`I=e0%QCitj1t(lD zAT}C=bdT17aKv^KO)pA_Ne*z}30;~#h@@$m5=U}S>_d17={hFq5SzIS4S_Rdb`C2= zA@+%R2}VT8XD3@^3^7mUgZnO$M>8vh0ye3Vq#|Z9j59?jw~-En`XxeX2EuPkF|#dN z5#mKuUJH!Zm#pHN^g?5dUM2A)C8D7+olrp2BwAxr6%}pMmG)B->GsEsS;vhn(0|?wp zN#q3eBAbU)vU)%}E}bAW7Ndrc0ZmYkM#c51RdT1;!s?;3;}(4)*GUpKheibVEulFx zHH4g|fq4Th=*bqoALu~QCk<7IjnJk^$E4^R8bd&A2K&i{UPH6Qs9Ew@J(3xJ&`78Y z_idA;s|k7V?fvr2Vo`XG&`&Vgg2A;F#zUVO}@G(ybDL6}E~ zwp?q9LgGb~3AAL3)q_|P(on+aMFXq=qEdSi!pKavi2ZttK%-UA{AePJhA4bKO(HE^ zZl?J)JY5=M5ik(kLIC<@hw6sL^(T(5oMj@%kz(v&?l}=z)-}xW$WdB>XiB&ZJVlX6Zkb1HoMw3@%|%h6P?bk zNJdu#(mg8d(Ero6sba|0aZ@P1s7Qwy7c~T5X%E zZBunluR5pqMy|6uZu$?2o0vUb-yi(9h?^?%kkz)S+BQ|&rfS<%ZJYih_nYW%+9vwH z#fDSGn_Sh#soFSI8>edHRBfF8GmX=mNxgp$<5ZD|tTs;7#;MvkRU4;jKwocX7soFYKTc>L4^q*;+XskDm9sXP7_24A_-#ukqeah)?oN|KGRK6G; zoV8XnFpk*44Uj6-rgM-sI5`5Rg(T@32Ii$KA4#~6;3Qb+1enVSrDQ2R6 zCo3@ABgX{8-wJbZkYp@%Nbslo=-Px zXmXp3L2^-=z$V9Vja-Ryl{A4^!qI6uA+oUy3NN7u3M>R)&eYR57LqAulMd2tuzMwh&F0ZENdysb z6O|aACKt)va2hQM7YXcWQ0La!FqTe8isg2Cq7-wvwLE7)vdR4WEz2hsZf$!1&+97b^(H7 z>>$bzNHj_v6V`&Iut}^7LybexNsdqZY41n7ZJH6YNaGyMz@C1ZRW%D2+IijYqQN(JK z>I6Il6%tekWPU_I+6*|FrWLwuu(nQw zZZc~GYM%}cz{zNw6kMsNO9g6%hR^ZnY&M~m&-crf61R@2^Ai{?sHAiK8iGxzVVxD2 z#}8*O2|VBp=xt~WNheKmOpvZnaRY=>z$FM@P@(X+Nud`dMF?G~#8nDdStZkj$}wvI zwd2V;n!uy8$dhow?@%zrY)r*9>ufG1jyc^joa<2F+!UEjLNnO$L|o)_YitsYh7dC` zCMif}y3$Ann<*E{d4x*LLUCM;VJ5no&PDVLnOkZ1r>QYc49pAU$4S%EF`r(9%C!!3 zuz_%)Bwgm#(O7;Aj)Ex(#4X269=XFMM=hjTO_!K48a%`I%hb5btstP3Z%WY;Iwqks ziIpmS3~mv~Gz>wI;Bkeho#RpRNL1(GYH&M;i3+_YoKLEh7ThNz{7wx)^AiqIt;9&0 zo~F0r47vcf=z=N^au@e=oLyUmX0#~PLl5;QLBlO6!1!=Dw|Bl*DKWyu2_jn zLJd&-q+F~>g2~LW*Owgk~ zC5{J_K{6xfXpClEcBCSY(NclPzAtD`c zYLzCpvXfAi#E%9|iMWDemeJTEB~xOS1x-mhLgEFTA=BA87DR|TNR>;C+u2DY@J;-rQlM?l6S zq|A!Z6g<*|1?5VmjZ8DB^m45d_243tLv59)byhVERq6*z2`gJobFn~PXfm3>MA+GC zTp$MCchWG7?N#ywX(XQD)>#BDDV(M@m^mJ>B{4}%xf;hzb`75&P|($EIh^2@F`Ypo zk&}e*$ryFeq|{ir46R7dMj199>5y2GX`r74E<3@usWFC;1@mT6wi2hC6e!_Mms#`# zNtgtn-z_KsGzm(0dJC>m(!3_UzzTLxl8D>&K1_&{B=`&3AmYXtbcF$v2bByzUq%S5 zm`Ea3+sGus!A;Z(a5>ILZDdfd#2FH|nn1-ehbu|Oz;P_7=b-_P6`>1~6I8?z*o}cV#K1Rfj%(H6TJ?^VpGHEc3(2hm55s4=bS zK^R~IZPs*@k1wQV!2SFG^juZEpkGJ>t#}0!X*S;vqC9LPhyhb0k{y%ViKw? z`k;;<9={&+F)Bchz>66+}6L}g0R;c|sR$O@7S z@NEcsiX1luquA3%eoS+CM_Nh`tx+wE3c1x&fXrb2~u1sO!W zU{`~3yECXzngTMF-ltRAthi9-mQg+$Q|?eRa3;yK`jcp=h_EFDpz^L}XCz#W1;7BQVLZ07rrmCRH-iNk{lH@XK%w zN*8EQJMghcpGJUQRuXKkQlJ;e3A$8I05XFBYzV=YXqh_D_Y#(p#&Kexalv14%9&y& zDzKT9e2h-u0te`F@L5f2%I8GE579C83MoNX;#OOV6hm2Bo1UjpN_=`@52haMGRD)h zbSjfgE|9DB43imbK245s?vZ6HtRsgR5K)J?J(aF7fNF5;tM91~HXDgsZrS0mB`*Lmrbv!w1V5s6)@>sxk0E zHFo)6HB+Akv4K;H<0&McgqVXQA=XgfbUjyr`RK_s4-;|VLOEUF0RK&`qnidxRdSmK z>>x(#K?j*LX<`1jO(50_y)2M_iP9lRl49U9*gR0I!z2F(? zgD6h`{+vD;2Y?0*W)dX@1>o`FbTw{uD`|RxhNpMR>^4Hq<4Tkw9G-Vs6+C>f&Y^cI zMXsRE%yH-luLiKw*QiXbe_BNc(r-Kg*x*G+9%2pvN zGN2RTTm#<;V-WkLd_DvGBDDg8wX_Qor9wJSZ9|o$ikkxb5F{-&8rX5V-U=q1&!Wo- zw!`26x#Xjyo&esm;w(7fZ;^ssQrJOXIE+52&}#-;?L|Z;narVg$q5#n;F%n7A2FyB z(zQ%^5&$s33Hs2dBwWd$Bi)#VYbNOuw?csU3^oa63w(0A zUJS7v$UDx!tZX)BC!HwaOUH2u2Ju*oj)%FmnBK1d-(Eq-*$|hcp;op?VP(0K85|d> zw8=1$Kn?t+AqjdCPUo^f$E8RKL7Lu<$_XLM4?5k1Gf}1<;yscWE>{&{q~yI>rZ`MR0&OXz4mkt^qzI6ySf# z7;1MC%_~#z0Z##Jk=`x`KSytaxYI8S>cNL3({v7m0kM}|Ddg~>9}`YHNH5+HiJU#4Z+*fJW7 zi)Lji1#+HVXmS{=Uie>-qEm?j1n3owz)Htel5~Hmf{{uy7lihKsle!UMTCds!N#2?xF% zoWw*;GP_=+wqiaVAvZ%@o-U&q!AF+Jl}s*AZwEU?=;(5Rk%eP=hL&j}G&GQ-ph*G# z7lB$OiF$!ah=VQz`>8}|R2(4>l1kJEK4Va6cRA%EJpy!R6IMB@g_H#a4n!rl5V%PQG}Y*Y5*%VsyNuGExWt_t1dYP*K)g?b z7*L*~U?%vJL$Q(%-hsVxdQT1E-fI_XziRd63|nsi|^;&_&!%FCgKC;6S_4dr){IZ{m;W&2E5Q3Rm$ND7@2u z2_y-f#RBlOD)?!finlMT9Q%%`Kp&}!NCGH`p{M>=5J~72L;hRaoK^5B|1MINrUC#< z2e6J5Ak1GuXGJlXsiA+MN@b(3gJ*d{D3(M8JR)H9y$#ngF%d9F;Z7yKD0QLNcuXO5 z&5AoAs4u%8fNou;XtUE38*K)T@kFQEGyq-sC+?|8tp9V-YnT;CFqKGql{`>|RRH2M z#1^ly%Ks*K4ZRv!tr}Ua8d2l=mdr4tj3urnRT*Hnb-07fTUE5s-S#0uO_K`aTo zoLZhMP0L{0q>vAQsK!kyofgER3jhKl{f{yQ-K&9&jLuH&cH3xD3*RSY(kQ&a3S7}w z<9dat`Nybveur3SqfjqBkhinxEPonl80j-yC-fVIxPwA8)dC(P;2_eFAU}vI`G9gs zpo2N;=3*#CsxGnlIvXvhNkvMc6FlPYx#GyvshSeIY|9p+Baz$aA#-IY3{ zGBE-0Gg+{HVjqk-jFbvlbg5Ei1Z+)MC&Vx1CBT@))OcXCRL6$)0I!n|_Y!EKew0>f z4AffCDc*oOKu06+lgo5MwLRY7=~<9sad=E8jhd3B}CHKd!wM zv2qN>AJQ<&tydd*f5k>KD4ihB6zc3NNeJO|+Qm$hNg7Bnrc`3i+WjU2>{o|zd;h2n zQk){Hf{N=1{bquW2qDPAx>3llbSaM?kScuCyn(k9;M!^g9-w3(h){e*(Ga8=wWt|l zA;qh{(0eW+I#K*=27abw!vefd;hs|RWC1yWwF5Z-+9trWzW>NKz+04T0Y6!+K>q{=;HiQv=wO|w=fiRX>&HZRYBt5=A!J%=?P2XG*#Q0obYOTk zk*f*6(?cNy)68K_<+PAI0ktCbx6%3!ZKZreqbcYWNWo5O{1g)JA1fdRO z-#nvPETm9~Y2op~{wwe#$Sxl+T0v%%2=G0H-irX=T4B{f^8)^}QZh;LCM6pzkkznm z0(p}vjUZ=$!fT;Woz);4Kw~Ha{tbr8m1-8s(*>z=C}YC@5@5fAEb@SMNq$MU}h}X;i0HaNv#f~7&HQKH}$p_tSAkhVIct@tO{s8i<%A;HUcFmj%S00 zquK-M5Q7GWT`Y3~U^VMNqky&mtwL!MDM$fG5m*mCXfrElC>;$}m3qbxnh~so`0X0v zUqwMbm4BH!-8JOCqB0(Nj%RzU_qZv&1n@Mi$!!A=tBW$GCz#kcV8 z8T5^bfkKEsYzNg~0}?>TQ1(Cu{b96_ESp)t z*VL-W!nzvpjzOQoeWM53Wdr|%Z8L)21$1iA+o)2_09`}bM&NHN6ZEqt~x&W0&4RIR}d_$mTs0@6ZH_ED@oCDIknfnLIIn2ERZ5$*8e8{lx_%^ih zx9GTV92nMd)Nh^`+LEy0{YFaCSTG(+pHO8Kg8J)aLX%BH=$-ly(z+Vzyh1=nC`3jo zKO9Cqr=ATXp$k#)_0S;rd|||S#jCtA=v>M>{gcl}<(eyaoRWAzJ_m^gJ_3FOO(CX* zZIP80vSE~@Q+`+?@UMcx$~Mx&_hsPc$iRMCL;8>67foUU%*SX{2Ppmpbal$t3jnVN zDBnp82>KZ1BT#a|lq$HM5dOCXq2$5$79{yMZTj1F9-wpoVWlZ)0_%Z*HV4a0NiSGn z&@)wM0@nzje^cHeczb~P4xS7(>yQWccVG4*aOg>nyigDR(FSO$Dy^uziG{z01kxhGm> zPe3E8b?|7R^)SYMsAZrdBh0B3KKy^PxA6F(hu3=p*#fGA{idu|#r!Jg&8OxK)D8Lh zV8Q-`$^JIp`13p&mGgX^q6oAMo@ywYNl6ve8-?2+`VNGTiW&_(s_gZ8e?qwbYmYPN z3w=!@h59s97$d&wo{T{g+LL*6+vZ=9c&F3efaaw$V=BKKS0(q(s@SikA^exo|S0am9vzfqmvsLpRx=QrN&>sRMDs5gmTWsF|SivB|xqU!uc zb$-KKo!_7$_t&xl)%gu7OH-ZSc>P<3|5%oxI==xg9aeeg^nWwI!C+V5QCGfM$YlRp zPLeBO=;H!0Iaf14=K{8Tz4N$IIW_)cfYg zmGc|($JT7HxBjLsKdpNib>swgN)XZiL!SB^me^#fYEraF)_Cn5@^Vhjpj-A|Gqg+8 zKOXT;p7MioNnLr3@)xa{XFlcZo5?X=pTqI=xgRY2kmbp`H@rP_=AA%3Gp+2_nf1cc zjpHLCYrgf5yQ=x`MQ+%$@PjcCHR`q<@s>XZ)vP<8)A+3qQ>_kd(so4ISJSvn{;G@Q zhzs|pBpLE=oiV+&{ZOBcWo^53NlGo4S0ot|arVPzJ=?u)C{T8zQ)5p=uIUO_y~VX) z>a^)gm$l3lM2sBWK%bf_2?PQ^mn`AdY%G#S=5uavZa-)-QeV%NFP`4(;b)VwYRvH* z^_;)KuCecZ)9Jgh?h|Zc+lnmq{QiU2GXG-oBSt79YnYbFZ~Y~CE=xxI%>mc#@nPE$ z>79Qa{&woZl-2-&D~Av-srT+D?eFFH91$UqTME;uc5fO2dc5xTIv7$$XP(Ez; ztgO!{DtCyFm^)mDPJClkYedCIwEE%7e0r#hton`PhwK+?-t2D+HRCS3Uxdn+w}YnQ zbo`_@n&NhXruJ)=C_=RaBO+$K6Zr;@j$8~KbeJ}8f9TPn&}!MV%%9(Ar|~!Kp&{g( zc_EHjye1;jdvijoZo=1TR`)9?@A&m(G(-d52W>}O-}ySjYCr1Oy?f?~mBfLuA9m57 z?C0F~WvvF+W@qP3FFm(;&ll~xKNvWF`12?3(^tkE7`&%dhh_7I=d?^et#P~7r%stU z^F~Ht6HD6>X#JkkC<4~~oCoCm^q}&~suFdjX~!Pl`v&;n}IP_lKT+zcZ{vilcL471E46 z{?Vht%z=xGw-)Jt8(r_-{er%;^N+9YN5pw=bzGJeTygE%wY^P*tveyJSmM$NCyzc^ zbavC~XE%OZlB8HSukVXTi`1PBFP=TV_u}cD1I^}TMECUFW(_kRo43;2X>Bpr*O;FiJ7Zb zbv>fqptdlV1LpKc?nLMY5Y)Ai12cP{QTO@ElZROh=D4N$j?dI{{ zhl%RVAJ$=MjZlY+De{fa8v9y@8*83iHK1FwIT<^OZ>cYw-C%BawL>y({k*1IZ%kv+ST-RN+n&Zq5z{We-K!?e}pMv3>~nwu;8 zWoNYUm0V#G^{)g%RV=^ z-uw$=*Y*u*i<__j%kou6AS~~qmsLSLhi6; z_9{EAyZ2jz?t5pktq0E@%kkEn-E-uyvNOW6OBosadY7_poZFn;(t9|eNrS_8IWK>1 zz`x`@T)JkhE~;au`{QV&{QFkfm z589ru@J}h>uCQbAgKEBbekbF=p;5K>{hYmH%I@)>9Jp|G4@hSJq4s(C!7P?}O0W9| znd<__mhR7aujext=lO$UhurUX>$hp@^H11gKc1gso)dlJ_uJd<%-T5d)aw4$)EABm zIVV=pTbZBUST?Wo%gw>hb;ri^jFFu*O&Gd*ox9h>2nB-GavwCR0+ijWd};VLVrEXX z``A+C5V@~;pYHVcrA9Aj{P@1lKX0)xyZ^TI?1dA0{8snIx$Jp!(x2Q&{dU5N*?o4( zGEYBxbaf(9yoUKho9E{)m66B#8AmqCxzT zE7p24`F6{=`9Y^r=h`o`6<}`af9c7_}XApBY8CsgCUw)vSzEG z&!fxlTs?gIheg*GjA=b3aaL^kLH;S`Z6c-ZN+LVv%KbJu-pN0G_0^QQb4%5&_C<@@ ze9S3Zvu)g>uMZ!3kHc&2za07a!V#g+cJ?$~ckSCfZDRXAz1rNkcl5VS4~=S=)@}q^ zW|D_BYY*__cF`w1s8Lnh4{a*qMXWAqQl4{b-TtW0voe0?-`|uWVwe0ldLhC{R1?nmH$ zEZ+Bhms4la>k|KNw7?6XA}XHAQOd>~!?m$epNxOC#d{layJWJ!yrIlJq> z5|+<$-1Hu=m(GQjxu5@7dLnS|*oZpF#GCWd=d^s3lDB2ZvW(ci(R*gQz2b1N7scQIwr1CWogSuff9d86sV|7DyT=!1WDP&Ns=Vb%WK>yJX8-n|#5!30 zTlRF!`NDtYs}ng-pO;@cfPjpGTh;k#X+RJoRZi+BB6 zu&#gWtXr*@jhH>3d3Fc(&V^ZJ*)5I=&UTmgXu0}Dk4}D0NE?o=d#n6r4RfDCIp)&o z$A)(>G%!|NQLH~ikW?|w|%*{hc{bL5O(hOsTLT)B0vpzqV!_oF3 zpJ{4S@MO1sy6C5>Vl`<{WZpZ)Q-0Gn_a8$RGN(y750&fR{RMRPWp#Jsp;=LzKm2I_ zQe^hQ1S>z1_D1Mf)yX-7QW{U20?$JiFS4;r^xW`jcKze1_t? zSLE3PdKc{X$tOiK@10$ly&pp1gX8oKcS`#de!wZZwSLLB(nj3lg>3JIlFWg@ef|k8 zdXqWVi<-vwY)&f+iB9~^E)eKtf8ME1Rq6e?^JQPe^P}H&-`_3vlI_uhgh0-_qchJv zFdx$&-#dBf-UCC*r=N4=T{!TYwVM^ja_=W_gMVC*xMbC;RcCjs?zegR;z)7YV9P-(b#)rQQ z%=IUScU{aKa$x_5U6Lb__3N%(2Y(Tr8#hyZl!qlJXU^MbJ%zRo+@f!+PBpBogf{POnOdCqe%kXQ zP~k_x^U<#C`flU{L&ny{C-a?W*6{}nEiC-z>4ql0Nbmi;}Gja&(_Z8uqW~+6>xs9d3cV;^0G83*OTu!b8*3k zMRPtzR}6SQ;l2+oeKg1Ob9(-jJ5UsRJqqe&UcI;?z0K{_E1tTX*i-s0P7yILr>|pL z?MrK(KkRk>hXuBty$(Fxc*Cv_c46=b7q_!lp$N0*l%Bh?%M~wAQCR^a6fMtE1DaZ*4cMvqcw9} zE2dz>upD#zg`(_?)wr`)sd}yZt5qvi*B43W^>HU@G#l?-=rj7qA*Y5eADB0Me4lfh z?B6eJmQlL-*y{YO-b2bBY`-R-$sCeCKKqCyk6hQ|w}K0`505dHd(LiZQGV{ky?FKS zFAi@xaO9H(C-d60_HkOY4mLr}mrMAn>)4P7g5voJ(mkb%G}+A}bH8p?C)oHKXK}`+ z4kNdm{Xw>U_E~?EZ%WoK+LN+ru40oYNtzL9?Qk`JA;a`_A$7DEh^HzS!EM$d&(X;c2LT z(Syz%_wK$`v(0f^jODF6UD`-dxYwT6@s}Q5I(1;xX5({m_+v+>?%gLAHlDiU`O~CJ zFHU&)n7?hnr8XQWr*d!BUWVv}`?YTI`YK74l9 z!TDzk#<%$9RJZxt(w@#~dVsFW{W?@Kp-nz#jz`OJ zJYa`uc*~Vv?+#~7^TTVO3BK{>{V*mur+k1qH@MN%c<8fw9YmMDta0P;+C@|xf8KX# z*u7ti=FQ&u{P4-ug-3*Krk&ixUbvcMH}2l8+xX>8%9EosV|1uH@eT~kJ#}; zzw+Ga?=L4WUq3Xva0xMR@QDFU!& zuWPe)4CF{!&xpg)Z=IIuKFwLjdl~r-6*y_LfB2fX$kBiE?EV?!A*paei>ThwIX|4u za=c?I_@vWk5J3GPZyz#UZ9yHrLw}d`n#Wh8EBK^OFBgxhJ#Xl5^#afCWMn=pS((u} z*Njj9BJBirbUo31dB50uo^{&n{-Yz}TK69CT}bJTr~~PO)@|iAskSTC;Aj#Iz>X^y za))dly3IB-d3Fqv3bwZ7sZYK%Fv7(H}I6XEC`-DIrUyRSLA{eTj~4b zF1!|Lm)89GgZP0b*DpIy*MD<-xuU~^t3Ryld?~P}6+^fC-DAY`%XObmI+hUf1KL9V zt_)fDf%6Y*ptF>u=KIt>|(u7=#~CaA==<{7`VZb~-PQTLWZ=3xJD%?EuiLYqQ#41hdh4plR>zwx?jM@> zg2rt}y!R{ZXuYaN=B|wDbXTLkQ%Am2d+)@)FFJNezxjDNI_q<_TAlT<_=4}!w4cC4 z?}wDs)xwMWzF(qVpPDtT&G9ey@87SF&l`U4anZaai1Q53e)-mYZ(oO|;BW0Th5fA_ z;DPMota(tss_Ea}^lr})1Gf9uKDcv^nBIM0^ca>iTCp^F@_Vg%22YN3Lh^1+-tD*} zcjDGRSum!}^!yHUuc;p=bSus+zxe%^pExEB9R8Spc9XS3xpZ#lF|C6gBfTkg#-)cM z+R+MF)Hzor*;R9TJ9|?8)uGw32gb*qZ|f;KIQihguD%i0INIo9D-0 z%wivy8pxmbvTOPIA=x2Eb2w;^+0N~3t>%*X7q~Z4eYDlV4BP_ zpFLjl{@k9v7zpj^7HYnY@Hd({smN<~(P+BdWw9v4dKRm$aiHQ#R% zZ)M)jEovQjv|vo~cixk?n@W2}b(rJ&a@0q0=L`_?xUqSC?)}qzxI7O8})s(^I1noe{HoMoU#AH zjL5k)-a(s&gI$QcTRo#M{Hlt2Xglc(D)NTp*AyzM%K74J$DURh+uvsF?2Kn(LNa|Kx$~+g=A&Y&vy%_TilA>)NqTn73|BTfa1BPyDVVs@CCp z2Q$*9KeO+gyL8~5Ib}n(zF07LaJj7D!sPr*ai0*zyD#qUYSf-};;uULE7OS;*!eU| zWDf7wg4$6wD6mD6DHldWm}$iXGhlY8^TX}ZgOT~ zEAg2dXM-BisW-Zp%zS9;y+51(-AB7ie^@kO?3Q2lcIlqiHRtnvOUHkL+>g$EAm2J{ z@1)$;@)`2T4Q=*L{Wh%0JJbjSPSh}UivDQdp@)n8^%L6Sk=;kxy6>+qkH2$bcJl#S zl&uD4&-tuX?C=e-C$D~g>eud<+tXHe`*^$(MXuOS3~M*7->`YdXV?1ZWpPT4kRV`@ z{S|)5Ij9VK*8Kd|ty|rvPOn|(939agxis`X(wsANi|y=&eWUL{SjuYDX+z%! zvdk`2>Ti4b?QPdzPTWxvegCi^Z&TcmRmJDCTXbv|QjoBv1}XKVtYDM)wqMn(o0rk5 zYwUu-^&56a!uq1^ik-{m4b}d5{3_$J8t!jIMcD+@?m0N{J2$)lVNCUeejI^>J#`K{suuso1Sn zvjN4{MQMF*b$}_QcFO)@?ZS0yAjUr6EI(J!wcC*;NzL{-jtN?n=4E_gKjKP1Fuhy* zK`(C}=w7&I-tZ|W`aWw53H2rio?oBs?EEq>`qboc$SGT&Eovrb{$L#^C$WzC;`Xi2 z$~Nb%jwuO1g6AHjefQr2J2$NhF*g6%rkIBF_Ka`SJ+J4JV#@*V_J=3?QQB%@OkCoV zEb;ff`qhq_(k#-;nDE(69+d$Sje}rJJ7;;Vs-{d%4tlm_@V)Z9=Z99&S2bEOx zW(`L@zj$&@|1RHmK&CXhm@yDvR{X9ZZ~Xb`*$0+gD=ZXWv2PYH5{zGw`$d}+{~?Un zH>S;S)A00tOEPDTY5rtullZ60lg@;Qwhy8-bWPuluSJ}ax}Z=}qHiBbOuv1;)6C}0 zk8aM$*jkoT^8V5EhPio9ZgzoiVAJ;A*6BkZ7iKPPWxHK-)@IYE%jUSp&KYts;J&qX z58iL-uFl9M(~KS(ZsEIkn-}yprY1c7yFKr(YwGy8xZOnMrp8R3GpVc)0!M(YEK$dUFfgI-j)t`Mp+gGtw5Yy(jC$o!nqP{PGsFVDH{0 z?KJTUWL;M8 zrt4Y?Q#MSnoZGan(|4KC4JGSy%0E+8z6a-c^ z+F#b4+*QCSS+&3afFW1Mo3_=tS7bf4YBFr+d?ArYu0aZuH_CK@!C)4Giq3=`yM1f- zw95Q(Q`7hk<7SNw`9beTLuT3K?i^y|iX8dKQT(tF?QN(`!xQV-jg=9dgl4qz&eWMid!79E$=YD zQRiDzW_NjB=iN#8YBI{u{KFc>$9p@kjHq_UotLJsOpyhux@!jW+5U ztclMJK3r@#F#RZ`Y;PQ2{yocazQ^+MQ6tdKeW!(3RaJhW2;3L}YR_ui zH?&zT>JN^_@m4*_2~}wimFh!z%De66hDh>F$M}dtBS*eoWkmdINwgi{wm;pUbfqez zP->0`Y&)U-F9jiQLQX*6X?rG2e`8RixlsPWE>VZjntTRLc?P`-N5a1*DXE=|dx}uv zbVN32(jE8YJt(qO9oG@y*G2Vy_fVeO$m7(yTv)rP@Qq5ZdHW;3fu>r1{6>|!pJOnj z<y>Y>+zha z)7RWvQloxVf4ZuIZY&x$^6Q2f?TG%KjVmY2hK43uB z-Qe=Zku{GhMtIQRgr2#AwrY4&W=fPax9;L)5k+$zwDbkX$FHlY9q?s4b;G3t{zQdJ zS-o3-MpTcV&-D(5GJ0?tekC#%{PjsS)i$)pr!CQff*R=!mX?n}0<~ zZuI4T5xGP6xC7b?P4DGT_vs&(6)t?ax?fqJGt(AteOf5F(>|A<_jE0P#j%OOj!mn! z9b)*3Kj!C*SQfeA!?;E-hi~a(ZMY=&-9@FP9Uc8{_1jsx;`>{n{k*qKaPiFe^O4?F z3p_~v@1u`QKFf`mn<`{oc>8Ea?yUPutN5aBhrXY3C~AjyulVEDX(a@*ehs|6(7eT8 zGRwmHHRDFCIUBS-FTVBU%#hVx`uFQzZ~mgz@7#LJfI=Mc=rbV7YR%d{gHm>B`(HX! zf8y#o@7!Ef=!?l~Cta2CbLIr~S76=VqA@BMDt>&89W@UpHfr8^*Q`dKU)GG=Fi*pZ z+^|S9sKZ|{!%k1+z#7G>#DNVLWjY5&OV?x^T|e{4+D*TD%=N44_p0Ip- ztpz3Tnyoh`thlP{_He<0p~a)>uI$rzazxyr38tKIs^k@OSNs~;q^>QZm3VLc1!>ba ztm$T7-NjxydoycdL>z6*M-9Un{wll2p>9Wdn~9c3#GM_~BwpC`x1zt2p5H)aG&Lf~ zU)?9E-I^1wW9q*kMccuk)V^K6G_)^;^?aLlshM%TX(ib*{{D1K2ZUdzH% z5%ozpbYNzm_lEX|-kc@$D4GLK;cu&!*Q?rljrJ)x-oBvOhx@i~^%yR04@6q~`)40p z$Zv3L>zw?#ORT5XT<`fw#gN{bu$W&vvWxfRW%bKL(}zYjT)gdi(Sh-`YVC8r+pR(& zL`p#m_byxvdk1yb@61Un(H~jJU*{+#j~(KF*KEwzZQ{~?__3u+Uo1MiIrb9uvH{ZN z#X$Kdg0PY3)O|j4ZudJUR+4>VpzZvz>aFM8ZuyRR38nG5V|V5y{A#-|;PZ#Du~D!& zyob>)?uU0o+rm`+CIL)FZPoyI{pv>4pgz-kIf>2|vh9_;vB8&)uFKN|UT zS#p!->tY(tVfzOZtU9Oa(K|4{_R*)!J{0fT>RF=UcRXY>Nll$p!Wz@Gk$m_HU|_Mz72+h$v~8w+wz`UemxlkQ$t z?>ZS=vgF%4+b6Pb+s>?WU-xf6!Frq*TX)vki)|0b&YL%@Pw$J`K<^5gf3DtnDkAuJ zLgBq%ayle8BW|faeRAo{Q~m6hx7Q79cK^=#=kDI^_V%0AA!>F~S?BC6_O;8>{3liQ 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 H?gjiG%@K1E literal 0 HcmV?d00001 From 78b118240656e0904aa6ffe5eeaadb655aa2a651 Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Tue, 24 Dec 2024 11:59:28 +0900 Subject: [PATCH 16/21] ensure actual prepare duration is always above minimum prepare duration threshold Signed-off-by: mohammad alqudah --- .../src/utils/calculation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/utils/calculation.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/utils/calculation.cpp index 4fbc80ced842f..a0130fcd27041 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/utils/calculation.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/utils/calculation.cpp @@ -445,7 +445,7 @@ double calc_actual_prepare_duration( if (max_acc < eps) { return params.max_prepare_duration; } - return (min_lc_velocity - current_velocity) / max_acc; + return std::max((min_lc_velocity - current_velocity) / max_acc, params.min_prepare_duration); }); return std::max(params.max_prepare_duration - active_signal_duration, min_prepare_duration); From fb2f9e0178d8befbbd3710f12f13e745a2605b2c Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Tue, 24 Dec 2024 13:44:12 +0900 Subject: [PATCH 17/21] explicitly return std::nullopt Signed-off-by: mohammad alqudah --- .../src/scene.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp index a776ba683198b..32b855eaf90ef 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -1407,11 +1408,11 @@ std::optional NormalLaneChange::compute_terminal_lane_change_pat try { if (!get_prepare_segment(prepare_segment, dist_to_terminal_start)) { RCLCPP_DEBUG(logger_, "failed to get valid prepare segment for terminal LC path"); - return {}; + return std::nullopt; } } catch (const std::exception & e) { RCLCPP_DEBUG(logger_, "failed to get terminal LC path: %s", e.what()); - return {}; + return std::nullopt; } // t = 2 * d / (v1 + v2) @@ -1479,7 +1480,7 @@ std::optional NormalLaneChange::compute_terminal_lane_change_pat return candidate_path.path; } - return {}; + return std::nullopt; } PathSafetyStatus NormalLaneChange::isApprovedPathSafe() const From 2d258d0d59e82fc252bdebe1d9aee076a6add9d0 Mon Sep 17 00:00:00 2001 From: mkquda <168697710+mkquda@users.noreply.github.com> Date: Wed, 25 Dec 2024 14:29:46 +0900 Subject: [PATCH 18/21] Update planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp Co-authored-by: Zulfaqar Azmi <93502286+zulfaqar-azmi-t4@users.noreply.github.com> --- .../autoware_behavior_path_lane_change_module/src/scene.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp index dabaced74f18f..072638d430314 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp @@ -1348,9 +1348,7 @@ std::optional NormalLaneChange::compute_terminal_lane_change_pat const auto max_lane_changing_length = std::invoke([&]() { double max_length = transient_data.dist_to_terminal_end - prep_metric.length; - max_length = - std::min(max_length, dist_lc_start_to_end_of_lanes - transient_data.next_dist_buffer.min); - return max_length; + return std::min(max_length, dist_lc_start_to_end_of_lanes - transient_data.next_dist_buffer.min); }); const auto max_path_velocity = prepare_segment.points.back().point.longitudinal_velocity_mps; From 1a4fd5b54ddc1fb7e148d4bb662ad82681f23709 Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Wed, 25 Dec 2024 16:30:17 +0900 Subject: [PATCH 19/21] fix assignment Signed-off-by: mohammad alqudah --- .../behavior_path_lane_change_module/structs/data.hpp | 8 ++++---- .../src/scene.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/structs/data.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/structs/data.hpp index dba6718dd0413..d5bbfe25fe1e9 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/structs/data.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/structs/data.hpp @@ -139,10 +139,10 @@ struct Info void set_lane_changing(const PhaseMetrics & _lc_metrics) { - longitudinal_acceleration.prepare = _lc_metrics.actual_lon_accel; - velocity.prepare = _lc_metrics.velocity; - duration.prepare = _lc_metrics.duration; - length.prepare = _lc_metrics.length; + longitudinal_acceleration.lane_changing = _lc_metrics.actual_lon_accel; + velocity.lane_changing = _lc_metrics.velocity; + duration.lane_changing = _lc_metrics.duration; + length.lane_changing = _lc_metrics.length; } }; diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp index 072638d430314..48f0bf01b0e36 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp @@ -1362,7 +1362,7 @@ std::optional NormalLaneChange::compute_terminal_lane_change_pat LaneChangePath candidate_path; for (const auto & lc_metric : lane_changing_metrics) { try { - candidate_path = get_candidate_path( + candidate_path = utils::lane_change::get_candidate_path( prep_metric, lc_metric, prepare_segment, sorted_lane_ids, lane_changing_start_pose, shift_length); } catch (const std::exception & e) { From 6514c0e794e05613c32e622bfd6e01067bb749d2 Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Wed, 25 Dec 2024 17:19:09 +0900 Subject: [PATCH 20/21] fix spelling Signed-off-by: mohammad alqudah --- .../autoware_behavior_path_lane_change_module/README.md | 2 +- .../autoware_behavior_path_lane_change_module/src/scene.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md index 49917b57f6061..79a3debed79cc 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/README.md @@ -876,7 +876,7 @@ endif ## Terminal Lane Change Path -Depending on the space configration around the Ego vehicle, it is possible that a valid LC path cannot be generated. If that happens, then Ego will get stuck at `terminal_start` and will be not be able to proceed. Therefore we introduced the terminal LC path feature; when ego gets near to the terminal point (dist to `terminal_start` is less than the maximum lane change length) a terminal lane changing path will be computed starting from the terminal start point on the current lane and connects to the target lane. The terminal path only needs to be computed once in each execution of LC module. If no valid candidate paths are found in the path generation process, then the terminal path will be used as a fallback candidate path, the safety of the terminal path is not ensured and therefore it can only be force approved. The following images illustrate the expected behavior without and with the terminal path feature respectively: +Depending on the space configuration around the Ego vehicle, it is possible that a valid LC path cannot be generated. If that happens, then Ego will get stuck at `terminal_start` and will be not be able to proceed. Therefore we introduced the terminal LC path feature; when ego gets near to the terminal point (dist to `terminal_start` is less than the maximum lane change length) a terminal lane changing path will be computed starting from the terminal start point on the current lane and connects to the target lane. The terminal path only needs to be computed once in each execution of LC module. If no valid candidate paths are found in the path generation process, then the terminal path will be used as a fallback candidate path, the safety of the terminal path is not ensured and therefore it can only be force approved. The following images illustrate the expected behavior without and with the terminal path feature respectively: ![no terminal path](./images/lane_change-no_terminal_path.png) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp index 48f0bf01b0e36..18f5c97c454db 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp @@ -1348,7 +1348,8 @@ std::optional NormalLaneChange::compute_terminal_lane_change_pat const auto max_lane_changing_length = std::invoke([&]() { double max_length = transient_data.dist_to_terminal_end - prep_metric.length; - return std::min(max_length, dist_lc_start_to_end_of_lanes - transient_data.next_dist_buffer.min); + return std::min( + max_length, dist_lc_start_to_end_of_lanes - transient_data.next_dist_buffer.min); }); const auto max_path_velocity = prepare_segment.points.back().point.longitudinal_velocity_mps; From 44f90b8c67f3b2d99ad87864014508937c48c43c Mon Sep 17 00:00:00 2001 From: mohammad alqudah Date: Fri, 27 Dec 2024 15:10:49 +0900 Subject: [PATCH 21/21] fix merge errors Signed-off-by: mohammad alqudah --- .../behavior_path_lane_change_module/base_class.hpp | 2 ++ .../autoware/behavior_path_lane_change_module/scene.hpp | 2 ++ .../behavior_path_lane_change_module/utils/path.hpp | 2 +- .../src/scene.cpp | 8 ++++---- .../src/utils/path.cpp | 4 ++-- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/base_class.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/base_class.hpp index 69194651538d0..43a929591dad1 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/base_class.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/base_class.hpp @@ -104,6 +104,8 @@ class LaneChangeBase virtual LaneChangePath getLaneChangePath() const = 0; + virtual BehaviorModuleOutput getTerminalLaneChangePath() const = 0; + virtual bool isEgoOnPreparePhase() const = 0; virtual bool isRequiredStop(const bool is_trailing_object) = 0; diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp index c06a0feeca308..fe1d18ea6ea0c 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp @@ -65,6 +65,8 @@ class NormalLaneChange : public LaneChangeBase LaneChangePath getLaneChangePath() const override; + BehaviorModuleOutput getTerminalLaneChangePath() const override; + BehaviorModuleOutput generateOutput() override; void extendOutputDrivableArea(BehaviorModuleOutput & output) const override; diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/path.hpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/path.hpp index dcc327b4793e1..77ba8fe68a653 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/path.hpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/path.hpp @@ -48,7 +48,7 @@ using behavior_path_planner::lane_change::CommonDataPtr; */ bool get_prepare_segment( const CommonDataPtr & common_data_ptr, const PathWithLaneId & prev_module_path, - const LaneChangePhaseMetrics prep_metric, PathWithLaneId & prepare_segment); + const double prep_length, PathWithLaneId & prepare_segment); /** * @brief Generates the candidate path for a lane change maneuver. diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp index e3bbb439212f0..cc17603adae21 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp @@ -1174,7 +1174,7 @@ bool NormalLaneChange::get_lane_change_paths(LaneChangePaths & candidate_paths) PathWithLaneId prepare_segment; try { if (!utils::lane_change::get_prepare_segment( - common_data_ptr_, prev_module_output_.path, prep_metric, prepare_segment)) { + common_data_ptr_, prev_module_output_.path, prep_metric.length, prepare_segment)) { debug_print("Reject: failed to get valid prepare segment!"); continue; } @@ -1301,7 +1301,8 @@ std::optional NormalLaneChange::compute_terminal_lane_change_pat PathWithLaneId prepare_segment; try { - if (!get_prepare_segment(prepare_segment, dist_to_terminal_start)) { + if (!utils::lane_change::get_prepare_segment( + common_data_ptr_, prev_module_output_.path, dist_to_terminal_start, prepare_segment)) { RCLCPP_DEBUG(logger_, "failed to get valid prepare segment for terminal LC path"); return std::nullopt; } @@ -1365,8 +1366,7 @@ std::optional NormalLaneChange::compute_terminal_lane_change_pat for (const auto & lc_metric : lane_changing_metrics) { try { candidate_path = utils::lane_change::get_candidate_path( - prep_metric, lc_metric, prepare_segment, sorted_lane_ids, lane_changing_start_pose, - shift_length); + common_data_ptr_, prep_metric, lc_metric, prepare_segment, sorted_lane_ids, shift_length); } catch (const std::exception & e) { continue; } diff --git a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/utils/path.cpp b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/utils/path.cpp index d7303d7d1df2d..44ee1624f0f51 100644 --- a/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/utils/path.cpp +++ b/planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/utils/path.cpp @@ -154,7 +154,7 @@ using behavior_path_planner::lane_change::CommonDataPtr; bool get_prepare_segment( const CommonDataPtr & common_data_ptr, const PathWithLaneId & prev_module_path, - const LaneChangePhaseMetrics prep_metric, PathWithLaneId & prepare_segment) + const double prep_length, PathWithLaneId & prepare_segment) { const auto & current_lanes = common_data_ptr->lanes_ptr->current; const auto & target_lanes = common_data_ptr->lanes_ptr->target; @@ -168,7 +168,7 @@ bool get_prepare_segment( const size_t current_seg_idx = autoware::motion_utils::findFirstNearestSegmentIndexWithSoftConstraints( prepare_segment.points, common_data_ptr->get_ego_pose(), 3.0, 1.0); - utils::clipPathLength(prepare_segment, current_seg_idx, prep_metric.length, backward_path_length); + utils::clipPathLength(prepare_segment, current_seg_idx, prep_length, backward_path_length); if (prepare_segment.points.empty()) return false;