Skip to content

Commit 95d08d4

Browse files
rej55zulfaqar-azmi-t4pre-commit-ci[bot]
authored
feat(lane_change): consider deceleration in safety check for cancel (#7943)
* feat(lane_change): consider deceleration in safety check for cancel Signed-off-by: Fumiya Watanabe <rej55.g@gmail.com> * docs(lane_change): fix document Signed-off-by: Fumiya Watanabe <rej55.g@gmail.com> * fix conflicts and refactor Signed-off-by: Muhammad Zulfaqar Azmi <zulfaqar.azmi@tier4.jp> * fix conflict Signed-off-by: Muhammad Zulfaqar Azmi <zulfaqar.azmi@tier4.jp> * style(pre-commit): autofix --------- Signed-off-by: Fumiya Watanabe <rej55.g@gmail.com> Signed-off-by: Muhammad Zulfaqar Azmi <zulfaqar.azmi@tier4.jp> Co-authored-by: Muhammad Zulfaqar Azmi <zulfaqar.azmi@tier4.jp> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 556816d commit 95d08d4

File tree

8 files changed

+150
-71
lines changed

8 files changed

+150
-71
lines changed

Diff for: planning/behavior_path_planner/autoware_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
@@ -823,7 +825,8 @@ The following parameters are configurable in `lane_change.param.yaml`.
823825
| `cancel.duration` | [s] | double | The time taken to complete returning to the center line. | 3.0 |
824826
| `cancel.max_lateral_jerk` | [m/sss] | double | The maximum lateral jerk for abort path | 1000.0 |
825827
| `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 |
826-
| `unsafe_hysteresis_threshold` | [-] | int | threshold that helps prevent frequent switching between safe and unsafe decisions | 10 |
828+
| `cancel.unsafe_hysteresis_threshold` | [-] | int | threshold that helps prevent frequent switching between safe and unsafe decisions | 10 |
829+
| `cancel.deceleration_sampling_num` | [-] | int | Number of deceleration patterns to check safety to cancel lane change | 5 |
827830

828831
### Debug
829832

Diff for: planning/behavior_path_planner/autoware_behavior_path_lane_change_module/config/lane_change.param.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
max_lateral_jerk: 1000.0 # [m/s3]
121121
overhang_tolerance: 0.0 # [m]
122122
unsafe_hysteresis_threshold: 10 # [/]
123+
deceleration_sampling_num: 5 # [/]
123124

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

Diff for: planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/scene.hpp

+12
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ using geometry_msgs::msg::Twist;
3737
using lane_change::LanesPolygon;
3838
using tier4_planning_msgs::msg::PathWithLaneId;
3939
using utils::path_safety_checker::ExtendedPredictedObjects;
40+
using utils::path_safety_checker::RSSparams;
4041

4142
class NormalLaneChange : public LaneChangeBase
4243
{
@@ -171,8 +172,18 @@ class NormalLaneChange : public LaneChangeBase
171172
const LaneChangePath & lane_change_path,
172173
const lane_change::TargetObjects & collision_check_objects,
173174
const utils::path_safety_checker::RSSparams & rss_params,
175+
const size_t deceleration_sampling_num, CollisionCheckDebugMap & debug_data) const;
176+
177+
bool has_collision_with_decel_patterns(
178+
const LaneChangePath & lane_change_path, const ExtendedPredictedObjects & objects,
179+
const size_t deceleration_sampling_num, const RSSparams & rss_param,
174180
CollisionCheckDebugMap & debug_data) const;
175181

182+
bool is_collided(
183+
const PathWithLaneId & lane_change_path, const ExtendedPredictedObject & obj,
184+
const std::vector<PoseWithVelocityStamped> & ego_predicted_path,
185+
const RSSparams & selected_rss_param, CollisionCheckDebugMap & debug_data) const;
186+
176187
//! @brief Check if the ego vehicle is in stuck by a stationary obstacle.
177188
//! @param obstacle_check_distance Distance to check ahead for any objects that might be
178189
//! obstructing ego path. It makes sense to use values like the maximum lane change distance.
@@ -220,6 +231,7 @@ class NormalLaneChange : public LaneChangeBase
220231
}
221232

222233
double stop_time_{0.0};
234+
static constexpr double floating_err_th{1e-3};
223235
};
224236
} // namespace autoware::behavior_path_planner
225237
#endif // AUTOWARE__BEHAVIOR_PATH_LANE_CHANGE_MODULE__SCENE_HPP_

Diff for: planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/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

Diff for: planning/behavior_path_planner/autoware_behavior_path_lane_change_module/include/autoware/behavior_path_lane_change_module/utils/utils.hpp

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

161161
std::vector<PoseWithVelocityStamped> convertToPredictedPath(
162162
const LaneChangePath & lane_change_path, const Twist & vehicle_twist, const Pose & pose,
163-
const BehaviorPathPlannerParameters & common_parameters,
163+
const double lane_changing_acceleration, const BehaviorPathPlannerParameters & common_parameters,
164164
const LaneChangeParameters & lane_change_parameters, const double resolution);
165165

166166
bool isParkedObject(

Diff for: planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/manager.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ void LaneChangeModuleManager::initParams(rclcpp::Node * node)
238238
getOrDeclareParameter<double>(*node, parameter("cancel.overhang_tolerance"));
239239
p.cancel.unsafe_hysteresis_threshold =
240240
getOrDeclareParameter<int>(*node, parameter("cancel.unsafe_hysteresis_threshold"));
241+
p.cancel.deceleration_sampling_num =
242+
getOrDeclareParameter<int>(*node, parameter("cancel.deceleration_sampling_num"));
241243

242244
// finish judge parameters
243245
p.lane_change_finish_judge_buffer =

Diff for: planning/behavior_path_planner/autoware_behavior_path_lane_change_module/src/scene.cpp

+124-64
Original file line numberDiff line numberDiff line change
@@ -1685,14 +1685,15 @@ bool NormalLaneChange::getLaneChangePaths(
16851685
}
16861686

16871687
const auto is_safe = std::invoke([&]() {
1688+
constexpr size_t decel_sampling_num = 1;
16881689
const auto safety_check_with_normal_rss = isLaneChangePathSafe(
16891690
*candidate_path, target_objects, common_data_ptr_->lc_param_ptr->rss_params,
1690-
lane_change_debug_.collision_check_objects);
1691+
decel_sampling_num, lane_change_debug_.collision_check_objects);
16911692

16921693
if (!safety_check_with_normal_rss.is_safe && is_stuck) {
16931694
const auto safety_check_with_stuck_rss = isLaneChangePathSafe(
16941695
*candidate_path, target_objects, common_data_ptr_->lc_param_ptr->rss_params_for_stuck,
1695-
lane_change_debug_.collision_check_objects);
1696+
decel_sampling_num, lane_change_debug_.collision_check_objects);
16961697
return safety_check_with_stuck_rss.is_safe;
16971698
}
16981699

@@ -1881,7 +1882,8 @@ PathSafetyStatus NormalLaneChange::isApprovedPathSafe() const
18811882
}
18821883

18831884
const auto safety_status = isLaneChangePathSafe(
1884-
path, target_objects, lane_change_parameters_->rss_params_for_abort, debug_data);
1885+
path, target_objects, lane_change_parameters_->rss_params_for_abort,
1886+
static_cast<size_t>(lane_change_parameters_->cancel.deceleration_sampling_num), debug_data);
18851887
{
18861888
// only for debug purpose
18871889
lane_change_debug_.collision_check_objects.clear();
@@ -2119,7 +2121,7 @@ bool NormalLaneChange::calcAbortPath()
21192121
PathSafetyStatus NormalLaneChange::isLaneChangePathSafe(
21202122
const LaneChangePath & lane_change_path,
21212123
const lane_change::TargetObjects & collision_check_objects,
2122-
const utils::path_safety_checker::RSSparams & rss_params,
2124+
const utils::path_safety_checker::RSSparams & rss_params, const size_t deceleration_sampling_num,
21232125
CollisionCheckDebugMap & debug_data) const
21242126
{
21252127
universe_utils::ScopedTimeTrack st(__func__, *time_keeper_);
@@ -2131,82 +2133,140 @@ PathSafetyStatus NormalLaneChange::isLaneChangePathSafe(
21312133
return {is_safe, !is_object_behind_ego};
21322134
}
21332135

2136+
const auto all_decel_pattern_has_collision =
2137+
[&](const utils::path_safety_checker::ExtendedPredictedObjects & objects) -> bool {
2138+
return has_collision_with_decel_patterns(
2139+
lane_change_path, objects, deceleration_sampling_num, rss_params, debug_data);
2140+
};
2141+
2142+
if (all_decel_pattern_has_collision(collision_check_objects.trailing)) {
2143+
return {!is_safe, is_object_behind_ego};
2144+
}
2145+
2146+
if (all_decel_pattern_has_collision(collision_check_objects.leading)) {
2147+
return {!is_safe, !is_object_behind_ego};
2148+
}
2149+
2150+
return {is_safe, !is_object_behind_ego};
2151+
}
2152+
2153+
bool NormalLaneChange::has_collision_with_decel_patterns(
2154+
const LaneChangePath & lane_change_path, const ExtendedPredictedObjects & objects,
2155+
const size_t deceleration_sampling_num, const RSSparams & rss_param,
2156+
CollisionCheckDebugMap & debug_data) const
2157+
{
2158+
if (objects.empty()) {
2159+
return false;
2160+
}
2161+
21342162
const auto & path = lane_change_path.path;
2135-
const auto & common_parameters = planner_data_->parameters;
2136-
const auto current_pose = getEgoPose();
2137-
const auto current_twist = getEgoTwist();
21382163

21392164
if (path.points.empty()) {
2140-
return {!is_safe, !is_object_behind_ego};
2165+
return false;
21412166
}
21422167

2168+
const auto current_pose = common_data_ptr_->get_ego_pose();
2169+
const auto current_twist = common_data_ptr_->get_ego_twist();
2170+
const auto bpp_param = *common_data_ptr_->bpp_param_ptr;
2171+
const auto global_min_acc = bpp_param.min_acc;
2172+
const auto lane_changing_acc = lane_change_path.info.longitudinal_acceleration.lane_changing;
2173+
2174+
const auto min_acc = std::min(lane_changing_acc, global_min_acc);
2175+
const auto sampling_num =
2176+
std::abs(min_acc - lane_changing_acc) > floating_err_th ? deceleration_sampling_num : 1;
2177+
const auto acc_resolution = (min_acc - lane_changing_acc) / static_cast<double>(sampling_num);
2178+
2179+
std::vector<double> acceleration_values(sampling_num);
2180+
std::iota(acceleration_values.begin(), acceleration_values.end(), 0);
2181+
2182+
std::transform(
2183+
acceleration_values.begin(), acceleration_values.end(), acceleration_values.begin(),
2184+
[&](double n) { return lane_changing_acc + n * acc_resolution; });
2185+
21432186
const auto time_resolution = lane_change_parameters_->prediction_time_resolution;
21442187

2145-
const auto ego_predicted_path = utils::lane_change::convertToPredictedPath(
2146-
lane_change_path, current_twist, current_pose, common_parameters, *lane_change_parameters_,
2147-
time_resolution);
2148-
const auto debug_predicted_path =
2149-
utils::path_safety_checker::convertToPredictedPath(ego_predicted_path, time_resolution);
2150-
2151-
const auto & current_lanes_polygon = common_data_ptr_->lanes_polygon_ptr->current;
2152-
const auto & expanded_target_polygon = common_data_ptr_->lanes_polygon_ptr->expanded_target;
2153-
2154-
constexpr double collision_check_yaw_diff_threshold{M_PI};
2155-
2156-
const auto check_collision = [&](const ExtendedPredictedObject & obj) {
2157-
auto current_debug_data = utils::path_safety_checker::createObjectDebug(obj);
2158-
const auto obj_predicted_paths = utils::path_safety_checker::getPredictedPathFromObj(
2159-
obj, lane_change_parameters_->use_all_predicted_path);
2160-
auto is_safe = true;
2161-
const auto selected_rss_param = (obj.initial_twist.twist.linear.x <=
2162-
lane_change_parameters_->stopped_object_velocity_threshold)
2163-
? lane_change_parameters_->rss_params_for_parked
2164-
: rss_params;
2165-
for (const auto & obj_path : obj_predicted_paths) {
2166-
const auto collided_polygons = utils::path_safety_checker::getCollidedPolygons(
2167-
path, ego_predicted_path, obj, obj_path, common_parameters, selected_rss_param, 1.0,
2168-
get_max_velocity_for_safety_check(), collision_check_yaw_diff_threshold,
2169-
current_debug_data.second);
2170-
2171-
if (collided_polygons.empty()) {
2172-
utils::path_safety_checker::updateCollisionCheckDebugMap(
2173-
debug_data, current_debug_data, is_safe);
2174-
continue;
2175-
}
2188+
const auto all_collided = std::all_of(
2189+
acceleration_values.begin(), acceleration_values.end(), [&](const auto acceleration) {
2190+
const auto ego_predicted_path = utils::lane_change::convertToPredictedPath(
2191+
lane_change_path, current_twist, current_pose, acceleration, bpp_param,
2192+
*lane_change_parameters_, time_resolution);
2193+
const auto debug_predicted_path =
2194+
utils::path_safety_checker::convertToPredictedPath(ego_predicted_path, time_resolution);
2195+
2196+
return std::any_of(objects.begin(), objects.end(), [&](const auto & obj) {
2197+
const auto selected_rss_param = (obj.initial_twist.twist.linear.x <=
2198+
lane_change_parameters_->stopped_object_velocity_threshold)
2199+
? lane_change_parameters_->rss_params_for_parked
2200+
: rss_param;
2201+
return is_collided(
2202+
lane_change_path.path, obj, ego_predicted_path, selected_rss_param, debug_data);
2203+
});
2204+
});
21762205

2177-
const auto collision_in_current_lanes =
2178-
utils::lane_change::isCollidedPolygonsInLanelet(collided_polygons, current_lanes_polygon);
2179-
const auto collision_in_target_lanes =
2180-
utils::lane_change::isCollidedPolygonsInLanelet(collided_polygons, expanded_target_polygon);
2206+
return all_collided;
2207+
}
21812208

2182-
if (!collision_in_current_lanes && !collision_in_target_lanes) {
2183-
utils::path_safety_checker::updateCollisionCheckDebugMap(
2184-
debug_data, current_debug_data, is_safe);
2185-
continue;
2186-
}
2209+
bool NormalLaneChange::is_collided(
2210+
const PathWithLaneId & lane_change_path, const ExtendedPredictedObject & obj,
2211+
const std::vector<PoseWithVelocityStamped> & ego_predicted_path,
2212+
const RSSparams & selected_rss_param, CollisionCheckDebugMap & debug_data) const
2213+
{
2214+
constexpr auto is_collided{true};
2215+
2216+
if (lane_change_path.points.empty()) {
2217+
return !is_collided;
2218+
}
2219+
2220+
if (ego_predicted_path.empty()) {
2221+
return !is_collided;
2222+
}
2223+
2224+
const auto & lanes_polygon_ptr = common_data_ptr_->lanes_polygon_ptr;
2225+
const auto & current_polygon = lanes_polygon_ptr->current;
2226+
const auto & expanded_target_polygon = lanes_polygon_ptr->target;
2227+
2228+
if (!current_polygon.has_value() || !expanded_target_polygon.has_value()) {
2229+
return !is_collided;
2230+
}
2231+
2232+
constexpr auto is_safe{true};
2233+
auto current_debug_data = utils::path_safety_checker::createObjectDebug(obj);
2234+
constexpr auto collision_check_yaw_diff_threshold{M_PI};
2235+
constexpr auto hysteresis_factor{1.0};
2236+
const auto obj_predicted_paths = utils::path_safety_checker::getPredictedPathFromObj(
2237+
obj, lane_change_parameters_->use_all_predicted_path);
2238+
const auto safety_check_max_vel = get_max_velocity_for_safety_check();
2239+
const auto & bpp_param = *common_data_ptr_->bpp_param_ptr;
21872240

2241+
for (const auto & obj_path : obj_predicted_paths) {
2242+
const auto collided_polygons = utils::path_safety_checker::getCollidedPolygons(
2243+
lane_change_path, ego_predicted_path, obj, obj_path, bpp_param, selected_rss_param,
2244+
hysteresis_factor, safety_check_max_vel, collision_check_yaw_diff_threshold,
2245+
current_debug_data.second);
2246+
2247+
if (collided_polygons.empty()) {
21882248
utils::path_safety_checker::updateCollisionCheckDebugMap(
2189-
debug_data, current_debug_data, !is_safe);
2190-
return !is_safe;
2249+
debug_data, current_debug_data, is_safe);
2250+
continue;
21912251
}
2192-
utils::path_safety_checker::updateCollisionCheckDebugMap(
2193-
debug_data, current_debug_data, is_safe);
2194-
return is_safe;
2195-
};
21962252

2197-
for (const auto & obj : collision_check_objects.trailing) {
2198-
if (!check_collision(obj)) {
2199-
return {!is_safe, is_object_behind_ego};
2200-
}
2201-
}
2253+
const auto collision_in_current_lanes =
2254+
utils::lane_change::isCollidedPolygonsInLanelet(collided_polygons, current_polygon);
2255+
const auto collision_in_target_lanes =
2256+
utils::lane_change::isCollidedPolygonsInLanelet(collided_polygons, expanded_target_polygon);
22022257

2203-
for (const auto & obj : collision_check_objects.leading) {
2204-
if (!check_collision(obj)) {
2205-
return {!is_safe, !is_object_behind_ego};
2258+
if (!collision_in_current_lanes && !collision_in_target_lanes) {
2259+
utils::path_safety_checker::updateCollisionCheckDebugMap(
2260+
debug_data, current_debug_data, is_safe);
2261+
continue;
22062262
}
2207-
}
22082263

2209-
return {is_safe, !is_object_behind_ego};
2264+
utils::path_safety_checker::updateCollisionCheckDebugMap(
2265+
debug_data, current_debug_data, !is_safe);
2266+
return is_collided;
2267+
}
2268+
utils::path_safety_checker::updateCollisionCheckDebugMap(debug_data, current_debug_data, is_safe);
2269+
return !is_collided;
22102270
}
22112271

22122272
// Check if the ego vehicle is in stuck by a stationary obstacle or by the terminal of current lanes

Diff for: planning/behavior_path_planner/autoware_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 =
@@ -820,9 +819,9 @@ std::vector<PoseWithVelocityStamped> convertToPredictedPath(
820819
initial_velocity * prepare_time + 0.5 * prepare_acc * prepare_time * prepare_time;
821820
for (double t = prepare_time; t < duration; t += resolution) {
822821
const double delta_t = t - prepare_time;
823-
const double velocity = lane_changing_velocity + lane_changing_acc * delta_t;
824-
const double length =
825-
lane_changing_velocity * delta_t + 0.5 * lane_changing_acc * delta_t * delta_t + offset;
822+
const double velocity = lane_changing_velocity + lane_changing_acceleration * delta_t;
823+
const double length = lane_changing_velocity * delta_t +
824+
0.5 * lane_changing_acceleration * delta_t * delta_t + offset;
826825
const auto pose = autoware::motion_utils::calcInterpolatedPose(
827826
path.points, vehicle_pose_frenet.length + length);
828827
predicted_path.emplace_back(t, pose, velocity);

0 commit comments

Comments
 (0)