Skip to content

Commit 060c656

Browse files
rej55zulfaqar-azmi-t4
authored andcommitted
feat(lane_change): consider deceleration in safety check for cancel
Signed-off-by: Fumiya Watanabe <rej55.g@gmail.com>
1 parent 45a5eb7 commit 060c656

File tree

8 files changed

+59
-30
lines changed

8 files changed

+59
-30
lines changed

planning/behavior_path_lane_change_module/README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,8 @@ detach
573573
@enduml
574574
```
575575

576+
During a lane change, a safety check is made in consideration of the deceleration of the ego vehicle, and a safety check is made for `cancel.deceleration_sampling_num` deceleration patterns, and the lane change will be canceled if the abort condition is satisfied for all deceleration patterns.
577+
576578
To preventive measure for lane change path oscillations caused by alternating safe and unsafe conditions, an additional hysteresis count check is implemented before executing an abort or cancel maneuver. If unsafe, the `unsafe_hysteresis_count_` is incremented and compared against `unsafe_hysteresis_threshold`; exceeding it prompts an abort condition check, ensuring decisions are made with consideration to recent safety assessments as shown in flow chart above. This mechanism stabilizes decision-making, preventing abrupt changes due to transient unsafe conditions.
577579

578580
```plantuml
@@ -822,7 +824,8 @@ The following parameters are configurable in `lane_change.param.yaml`.
822824
| `cancel.duration` | [s] | double | The time taken to complete returning to the center line. | 3.0 |
823825
| `cancel.max_lateral_jerk` | [m/sss] | double | The maximum lateral jerk for abort path | 1000.0 |
824826
| `cancel.overhang_tolerance` | [m] | double | Lane change cancel is prohibited if the vehicle head exceeds the lane boundary more than this tolerance distance | 0.0 |
825-
| `unsafe_hysteresis_threshold` | [-] | int | threshold that helps prevent frequent switching between safe and unsafe decisions | 10 |
827+
| `cancel.unsafe_hysteresis_threshold` | [-] | int | threshold that helps prevent frequent switching between safe and unsafe decisions | 10 |
828+
| `cancel.deceleration_sampling_num` | [-] | int | Number of deceleration patterns to check safety to cancel lane change | 5 |
826829

827830
### Debug
828831

planning/behavior_path_lane_change_module/config/lane_change.param.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
max_lateral_jerk: 1000.0 # [m/s3]
120120
overhang_tolerance: 0.0 # [m]
121121
unsafe_hysteresis_threshold: 10 # [/]
122+
deceleration_sampling_num: 5 # [/]
122123

123124
lane_change_finish_judge_buffer: 2.0 # [m]
124125
finish_judge_lateral_threshold: 0.1 # [m]

planning/behavior_path_lane_change_module/include/behavior_path_lane_change_module/scene.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ class NormalLaneChange : public LaneChangeBase
168168
const LaneChangePath & lane_change_path,
169169
const lane_change::TargetObjects & collision_check_objects,
170170
const utils::path_safety_checker::RSSparams & rss_params,
171-
CollisionCheckDebugMap & debug_data) const;
171+
const size_t deceleration_sampling_num, CollisionCheckDebugMap & debug_data) const;
172172

173173
//! @brief Check if the ego vehicle is in stuck by a stationary obstacle.
174174
//! @param obstacle_check_distance Distance to check ahead for any objects that might be

planning/behavior_path_lane_change_module/include/behavior_path_lane_change_module/utils/data_structs.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ struct CancelParameters
9494
// number of unsafe exceeds unsafe_hysteresis_threshold, the lane change will be cancelled or
9595
// aborted.
9696
int unsafe_hysteresis_threshold{2};
97+
98+
int deceleration_sampling_num{5};
9799
};
98100

99101
struct Parameters

planning/behavior_path_lane_change_module/include/behavior_path_lane_change_module/utils/utils.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ std::optional<lanelet::ConstLanelet> getLaneChangeTargetLane(
158158

159159
std::vector<PoseWithVelocityStamped> convertToPredictedPath(
160160
const LaneChangePath & lane_change_path, const Twist & vehicle_twist, const Pose & pose,
161-
const BehaviorPathPlannerParameters & common_parameters,
161+
const double lane_changing_acceleration, const BehaviorPathPlannerParameters & common_parameters,
162162
const LaneChangeParameters & lane_change_parameters, const double resolution);
163163

164164
bool isParkedObject(

planning/behavior_path_lane_change_module/src/manager.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,8 @@ void LaneChangeModuleManager::initParams(rclcpp::Node * node)
236236
getOrDeclareParameter<double>(*node, parameter("cancel.overhang_tolerance"));
237237
p.cancel.unsafe_hysteresis_threshold =
238238
getOrDeclareParameter<int>(*node, parameter("cancel.unsafe_hysteresis_threshold"));
239+
p.cancel.deceleration_sampling_num =
240+
getOrDeclareParameter<int>(*node, parameter("cancel.deceleration_sampling_num"));
239241

240242
// finish judge parameters
241243
p.lane_change_finish_judge_buffer =

planning/behavior_path_lane_change_module/src/scene.cpp

+44-22
Original file line numberDiff line numberDiff line change
@@ -1679,13 +1679,13 @@ bool NormalLaneChange::getLaneChangePaths(
16791679

16801680
const auto is_safe = std::invoke([&]() {
16811681
const auto safety_check_with_normal_rss = isLaneChangePathSafe(
1682-
*candidate_path, target_objects, common_data_ptr_->lc_param_ptr->rss_params,
1682+
*candidate_path, target_objects, common_data_ptr_->lc_param_ptr->rss_params, 1,
16831683
lane_change_debug_.collision_check_objects);
16841684

16851685
if (!safety_check_with_normal_rss.is_safe && is_stuck) {
16861686
const auto safety_check_with_stuck_rss = isLaneChangePathSafe(
16871687
*candidate_path, target_objects, common_data_ptr_->lc_param_ptr->rss_params_for_stuck,
1688-
lane_change_debug_.collision_check_objects);
1688+
1, lane_change_debug_.collision_check_objects);
16891689
return safety_check_with_stuck_rss.is_safe;
16901690
}
16911691

@@ -1875,7 +1875,8 @@ PathSafetyStatus NormalLaneChange::isApprovedPathSafe() const
18751875
}
18761876

18771877
const auto safety_status = isLaneChangePathSafe(
1878-
path, target_objects, lane_change_parameters_->rss_params_for_abort, debug_data);
1878+
path, target_objects, lane_change_parameters_->rss_params_for_abort,
1879+
static_cast<size_t>(lane_change_parameters_->cancel.deceleration_sampling_num), debug_data);
18791880
{
18801881
// only for debug purpose
18811882
lane_change_debug_.collision_check_objects.clear();
@@ -2109,7 +2110,7 @@ bool NormalLaneChange::calcAbortPath()
21092110
PathSafetyStatus NormalLaneChange::isLaneChangePathSafe(
21102111
const LaneChangePath & lane_change_path,
21112112
const lane_change::TargetObjects & collision_check_objects,
2112-
const utils::path_safety_checker::RSSparams & rss_params,
2113+
const utils::path_safety_checker::RSSparams & rss_params, const size_t deceleration_sampling_num,
21132114
CollisionCheckDebugMap & debug_data) const
21142115
{
21152116
constexpr auto is_safe = true;
@@ -2129,22 +2130,31 @@ PathSafetyStatus NormalLaneChange::isLaneChangePathSafe(
21292130
return {!is_safe, !is_object_behind_ego};
21302131
}
21312132

2132-
const auto time_resolution = lane_change_parameters_->prediction_time_resolution;
2133+
const double current_acc = lane_change_path.info.longitudinal_acceleration.lane_changing;
2134+
const double min_acc = std::min(current_acc, common_parameters.min_acc);
2135+
const auto sampling_num =
2136+
std::abs(min_acc - current_acc) > 1.0E-03 ? deceleration_sampling_num : 1;
2137+
const double acc_resolution = (min_acc - current_acc) / static_cast<double>(sampling_num);
2138+
2139+
std::vector<double> acceleration_values(sampling_num);
2140+
std::iota(acceleration_values.begin(), acceleration_values.end(), 0);
21332141

2134-
const auto ego_predicted_path = utils::lane_change::convertToPredictedPath(
2135-
lane_change_path, current_twist, current_pose, common_parameters, *lane_change_parameters_,
2136-
time_resolution);
2137-
const auto debug_predicted_path =
2138-
utils::path_safety_checker::convertToPredictedPath(ego_predicted_path, time_resolution);
2142+
std::transform(
2143+
acceleration_values.begin(), acceleration_values.end(), acceleration_values.begin(),
2144+
[&](double n) { return current_acc + n * acc_resolution; });
2145+
2146+
const auto time_resolution = lane_change_parameters_->prediction_time_resolution;
21392147

21402148
const auto & current_lanes_polygon = common_data_ptr_->lanes_polygon_ptr->current;
21412149
const auto & expanded_target_polygon = common_data_ptr_->lanes_polygon_ptr->expanded_target;
21422150

2143-
const auto check_collision = [&](const ExtendedPredictedObject & obj) {
2151+
const auto has_collision = [&](
2152+
const ExtendedPredictedObject & obj,
2153+
const std::vector<PoseWithVelocityStamped> & ego_predicted_path) {
2154+
constexpr bool is_collided = true;
21442155
auto current_debug_data = utils::path_safety_checker::createObjectDebug(obj);
21452156
const auto obj_predicted_paths = utils::path_safety_checker::getPredictedPathFromObj(
21462157
obj, lane_change_parameters_->use_all_predicted_path);
2147-
auto is_safe = true;
21482158
const auto selected_rss_param =
21492159
(obj.initial_twist.twist.linear.x <=
21502160
lane_change_parameters_->prepare_segment_ignore_object_velocity_thresh)
@@ -2174,23 +2184,35 @@ PathSafetyStatus NormalLaneChange::isLaneChangePathSafe(
21742184

21752185
utils::path_safety_checker::updateCollisionCheckDebugMap(
21762186
debug_data, current_debug_data, !is_safe);
2177-
return !is_safe;
2187+
return is_collided;
21782188
}
21792189
utils::path_safety_checker::updateCollisionCheckDebugMap(
21802190
debug_data, current_debug_data, is_safe);
2181-
return is_safe;
2191+
return !is_collided;
21822192
};
21832193

2184-
for (const auto & obj : collision_check_objects.trailing) {
2185-
if (!check_collision(obj)) {
2186-
return {!is_safe, is_object_behind_ego};
2187-
}
2194+
const auto has_collision_with_decel_pattern =
2195+
[&](const utils::path_safety_checker::ExtendedPredictedObjects & objects) {
2196+
return std::all_of(
2197+
acceleration_values.begin(), acceleration_values.end(), [&](const auto acceleration) {
2198+
const auto ego_predicted_path = utils::lane_change::convertToPredictedPath(
2199+
lane_change_path, current_twist, current_pose, acceleration, common_parameters,
2200+
*lane_change_parameters_, time_resolution);
2201+
const auto debug_predicted_path =
2202+
utils::path_safety_checker::convertToPredictedPath(ego_predicted_path, time_resolution);
2203+
2204+
return std::any_of(objects.begin(), objects.end(), [&](const auto & obj) {
2205+
return has_collision(obj, ego_predicted_path);
2206+
});
2207+
});
2208+
};
2209+
2210+
if (has_collision_with_decel_pattern(collision_check_objects.trailing)) {
2211+
return {!is_safe, is_object_behind_ego};
21882212
}
21892213

2190-
for (const auto & obj : collision_check_objects.leading) {
2191-
if (!check_collision(obj)) {
2192-
return {!is_safe, !is_object_behind_ego};
2193-
}
2214+
if (has_collision_with_decel_pattern(collision_check_objects.leading)) {
2215+
return {!is_safe, !is_object_behind_ego};
21942216
}
21952217

21962218
return {is_safe, !is_object_behind_ego};

planning/behavior_path_lane_change_module/src/utils/utils.cpp

+4-5
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,7 @@ std::optional<lanelet::ConstLanelet> getLaneChangeTargetLane(
778778

779779
std::vector<PoseWithVelocityStamped> convertToPredictedPath(
780780
const LaneChangePath & lane_change_path, const Twist & vehicle_twist, const Pose & vehicle_pose,
781-
const BehaviorPathPlannerParameters & common_parameters,
781+
const double lane_changing_acceleration, const BehaviorPathPlannerParameters & common_parameters,
782782
const LaneChangeParameters & lane_change_parameters, const double resolution)
783783
{
784784
if (lane_change_path.path.points.empty()) {
@@ -787,7 +787,6 @@ std::vector<PoseWithVelocityStamped> convertToPredictedPath(
787787

788788
const auto & path = lane_change_path.path;
789789
const auto prepare_acc = lane_change_path.info.longitudinal_acceleration.prepare;
790-
const auto lane_changing_acc = lane_change_path.info.longitudinal_acceleration.lane_changing;
791790
const auto duration = lane_change_path.info.duration.sum();
792791
const auto prepare_time = lane_change_path.info.duration.prepare;
793792
const auto & minimum_lane_changing_velocity =
@@ -819,9 +818,9 @@ std::vector<PoseWithVelocityStamped> convertToPredictedPath(
819818
initial_velocity * prepare_time + 0.5 * prepare_acc * prepare_time * prepare_time;
820819
for (double t = prepare_time; t < duration; t += resolution) {
821820
const double delta_t = t - prepare_time;
822-
const double velocity = lane_changing_velocity + lane_changing_acc * delta_t;
823-
const double length =
824-
lane_changing_velocity * delta_t + 0.5 * lane_changing_acc * delta_t * delta_t + offset;
821+
const double velocity = lane_changing_velocity + lane_changing_acceleration * delta_t;
822+
const double length = lane_changing_velocity * delta_t +
823+
0.5 * lane_changing_acceleration * delta_t * delta_t + offset;
825824
const auto pose =
826825
motion_utils::calcInterpolatedPose(path.points, vehicle_pose_frenet.length + length);
827826
predicted_path.emplace_back(t, pose, velocity);

0 commit comments

Comments
 (0)