Skip to content

Commit 411d5d7

Browse files
feat(lane_change): cancel hysteresis (autowarefoundation#6288)
* feat(lane_change): cancel hysteresis Signed-off-by: Muhammad Zulfaqar Azmi <zulfaqar.azmi@tier4.jp> * Update documentation Signed-off-by: Muhammad Zulfaqar Azmi <zulfaqar.azmi@tier4.jp> * fix the explanation of the hysteresis count Signed-off-by: Muhammad Zulfaqar Azmi <zulfaqar.azmi@tier4.jp> --------- Signed-off-by: Muhammad Zulfaqar Azmi <zulfaqar.azmi@tier4.jp>
1 parent f874652 commit 411d5d7

File tree

8 files changed

+64
-1
lines changed

8 files changed

+64
-1
lines changed

planning/behavior_path_lane_change_module/README.md

+24
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,29 @@ detach
284284
@enduml
285285
```
286286

287+
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.
288+
289+
```plantuml
290+
@startuml
291+
skinparam defaultTextAlignment center
292+
skinparam backgroundColor #WHITE
293+
294+
title Abort Lane Change
295+
296+
if (Perform collision check?) then (<color:green><b>SAFE</b></color>)
297+
:Reset unsafe_hysteresis_count_;
298+
else (<color:red><b>UNSAFE</b></color>)
299+
:Increase unsafe_hysteresis_count_;
300+
if (unsafe_hysteresis_count_ > unsafe_hysteresis_threshold?) then (<color:green><b>FALSE</b></color>)
301+
else (<color:red><b>TRUE</b></color>)
302+
#LightPink:Check abort condition;
303+
stop
304+
endif
305+
endif
306+
:Continue lane changing;
307+
@enduml
308+
```
309+
287310
#### Cancel
288311

289312
Suppose the lane change trajectory is evaluated as unsafe. In that case, if the ego vehicle has not departed from the current lane yet, the trajectory will be reset, and the ego vehicle will resume the lane following the maneuver.
@@ -433,6 +456,7 @@ The following parameters are configurable in `lane_change.param.yaml`.
433456
| `cancel.duration` | [s] | double | The time taken to complete returning to the center line. | 3.0 |
434457
| `cancel.max_lateral_jerk` | [m/sss] | double | The maximum lateral jerk for abort path | 1000.0 |
435458
| `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 |
459+
| `unsafe_hysteresis_threshold` | [-] | int | threshold that helps prevent frequent switching between safe and unsafe decisions | 10 |
436460

437461
### Debug
438462

planning/behavior_path_lane_change_module/config/lane_change.param.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
duration: 5.0 # [s]
107107
max_lateral_jerk: 1000.0 # [m/s3]
108108
overhang_tolerance: 0.0 # [m]
109+
unsafe_hysteresis_threshold: 10 # [/]
109110

110111
finish_judge_lateral_threshold: 0.2 # [m]
111112

planning/behavior_path_lane_change_module/include/behavior_path_lane_change_module/scene.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define BEHAVIOR_PATH_LANE_CHANGE_MODULE__SCENE_HPP_
1616

1717
#include "behavior_path_lane_change_module/utils/base_class.hpp"
18+
#include "behavior_path_lane_change_module/utils/data_structs.hpp"
1819

1920
#include <memory>
2021
#include <utility>
@@ -72,6 +73,8 @@ class NormalLaneChange : public LaneChangeBase
7273
PathSafetyStatus isApprovedPathSafe() const override;
7374

7475
bool isRequiredStop(const bool is_object_coming_from_rear) const override;
76+
PathSafetyStatus evaluateApprovedPathWithUnsafeHysteresis(
77+
PathSafetyStatus approved_path_safety_status) override;
7578

7679
bool isNearEndOfCurrentLanes(
7780
const lanelet::ConstLanelets & current_lanes, const lanelet::ConstLanelets & target_lanes,

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

+4
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ class LaneChangeBase
9494

9595
virtual PathSafetyStatus isApprovedPathSafe() const = 0;
9696

97+
virtual PathSafetyStatus evaluateApprovedPathWithUnsafeHysteresis(
98+
PathSafetyStatus approve_path_safety_status) = 0;
99+
97100
virtual bool isNearEndOfCurrentLanes(
98101
const lanelet::ConstLanelets & current_lanes, const lanelet::ConstLanelets & target_lanes,
99102
const double threshold) const = 0;
@@ -248,6 +251,7 @@ class LaneChangeBase
248251

249252
PathWithLaneId prev_approved_path_{};
250253

254+
int unsafe_hysteresis_count_{0};
251255
bool is_abort_path_approved_{false};
252256
bool is_abort_approval_requested_{false};
253257
bool is_activated_{false};

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

+5
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ struct LaneChangeCancelParameters
7575
double duration{5.0};
7676
double max_lateral_jerk{10.0};
7777
double overhang_tolerance{0.0};
78+
79+
// unsafe_hysteresis_threshold will be compare with the number of detected unsafe instance. If the
80+
// number of unsafe exceeds unsafe_hysteresis_threshold, the lane change will be cancelled or
81+
// aborted.
82+
int unsafe_hysteresis_threshold{2};
7883
};
7984

8085
struct LaneChangeParameters

planning/behavior_path_lane_change_module/src/interface.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ ModuleStatus LaneChangeInterface::updateState()
116116
return isWaitingApproval() ? ModuleStatus::RUNNING : ModuleStatus::SUCCESS;
117117
}
118118

119-
const auto [is_safe, is_object_coming_from_rear] = module_type_->isApprovedPathSafe();
119+
const auto [is_safe, is_object_coming_from_rear] =
120+
module_type_->evaluateApprovedPathWithUnsafeHysteresis(module_type_->isApprovedPathSafe());
120121

121122
setObjectDebugVisualization();
122123
if (is_safe) {

planning/behavior_path_lane_change_module/src/manager.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ void LaneChangeModuleManager::init(rclcpp::Node * node)
215215
getOrDeclareParameter<double>(*node, parameter("cancel.max_lateral_jerk"));
216216
p.cancel.overhang_tolerance =
217217
getOrDeclareParameter<double>(*node, parameter("cancel.overhang_tolerance"));
218+
p.cancel.unsafe_hysteresis_threshold =
219+
getOrDeclareParameter<int>(*node, parameter("cancel.unsafe_hysteresis_threshold"));
218220

219221
p.finish_judge_lateral_threshold =
220222
getOrDeclareParameter<double>(*node, parameter("finish_judge_lateral_threshold"));

planning/behavior_path_lane_change_module/src/scene.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ void NormalLaneChange::resetParameters()
420420
is_abort_approval_requested_ = false;
421421
current_lane_change_state_ = LaneChangeStates::Normal;
422422
abort_path_ = nullptr;
423+
unsafe_hysteresis_count_ = 0;
423424

424425
object_debug_.clear();
425426
}
@@ -1451,6 +1452,28 @@ PathSafetyStatus NormalLaneChange::isApprovedPathSafe() const
14511452
return safety_status;
14521453
}
14531454

1455+
PathSafetyStatus NormalLaneChange::evaluateApprovedPathWithUnsafeHysteresis(
1456+
PathSafetyStatus approved_path_safety_status)
1457+
{
1458+
if (!approved_path_safety_status.is_safe) {
1459+
++unsafe_hysteresis_count_;
1460+
RCLCPP_DEBUG(
1461+
logger_, "%s: Increasing hysteresis count to %d.", __func__, unsafe_hysteresis_count_);
1462+
} else {
1463+
if (unsafe_hysteresis_count_ > 0) {
1464+
RCLCPP_DEBUG(logger_, "%s: Lane change is now SAFE. Resetting hysteresis count.", __func__);
1465+
}
1466+
unsafe_hysteresis_count_ = 0;
1467+
}
1468+
if (unsafe_hysteresis_count_ > lane_change_parameters_->cancel.unsafe_hysteresis_threshold) {
1469+
RCLCPP_DEBUG(
1470+
logger_, "%s: hysteresis count exceed threshold. lane change is now %s", __func__,
1471+
(approved_path_safety_status.is_safe ? "safe" : "UNSAFE"));
1472+
return approved_path_safety_status;
1473+
}
1474+
return {};
1475+
}
1476+
14541477
bool NormalLaneChange::isValidPath(const PathWithLaneId & path) const
14551478
{
14561479
const auto & route_handler = planner_data_->route_handler;

0 commit comments

Comments
 (0)