Skip to content

Commit 21d6d79

Browse files
zulfaqar-azmi-t4satoshi-ota
authored andcommitted
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 4111909 commit 21d6d79

File tree

8 files changed

+70
-3
lines changed

8 files changed

+70
-3
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.
@@ -423,6 +446,7 @@ The following parameters are configurable in `lane_change.param.yaml`.
423446
| `cancel.duration` | [s] | double | The time taken to complete returning to the center line. | 3.0 |
424447
| `cancel.max_lateral_jerk` | [m/sss] | double | The maximum lateral jerk for abort path | 1000.0 |
425448
| `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 |
449+
| `unsafe_hysteresis_threshold` | [-] | int | threshold that helps prevent frequent switching between safe and unsafe decisions | 10 |
426450

427451
### Debug
428452

planning/behavior_path_lane_change_module/config/lane_change.param.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
duration: 5.0 # [s]
104104
max_lateral_jerk: 1000.0 # [m/s3]
105105
overhang_tolerance: 0.0 # [m]
106+
unsafe_hysteresis_threshold: 10 # [/]
106107

107108
finish_judge_lateral_threshold: 0.2 # [m]
108109

planning/behavior_path_lane_change_module/include/behavior_path_lane_change_module/scene.hpp

+4
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>
@@ -73,6 +74,9 @@ class NormalLaneChange : public LaneChangeBase
7374

7475
PathSafetyStatus isApprovedPathSafe() const override;
7576

77+
PathSafetyStatus evaluateApprovedPathWithUnsafeHysteresis(
78+
PathSafetyStatus approved_path_safety_status) override;
79+
7680
bool isRequiredStop(const bool is_object_coming_from_rear) override;
7781

7882
bool isNearEndOfCurrentLanes(

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
@@ -97,6 +97,9 @@ class LaneChangeBase
9797

9898
virtual PathSafetyStatus isApprovedPathSafe() const = 0;
9999

100+
virtual PathSafetyStatus evaluateApprovedPathWithUnsafeHysteresis(
101+
PathSafetyStatus approve_path_safety_status) = 0;
102+
100103
virtual bool isNearEndOfCurrentLanes(
101104
const lanelet::ConstLanelets & current_lanes, const lanelet::ConstLanelets & target_lanes,
102105
const double threshold) const = 0;
@@ -242,6 +245,7 @@ class LaneChangeBase
242245

243246
PathWithLaneId prev_approved_path_{};
244247

248+
int unsafe_hysteresis_count_{0};
245249
bool is_abort_path_approved_{false};
246250
bool is_abort_approval_requested_{false};
247251
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
@@ -77,6 +77,11 @@ struct LaneChangeCancelParameters
7777
double duration{5.0};
7878
double max_lateral_jerk{10.0};
7979
double overhang_tolerance{0.0};
80+
81+
// unsafe_hysteresis_threshold will be compare with the number of detected unsafe instance. If the
82+
// number of unsafe exceeds unsafe_hysteresis_threshold, the lane change will be cancelled or
83+
// aborted.
84+
int unsafe_hysteresis_threshold{2};
8085
};
8186

8287
struct LaneChangeParameters

planning/behavior_path_lane_change_module/src/interface.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ void LaneChangeInterface::processOnExit()
5959
{
6060
module_type_->resetParameters();
6161
debug_marker_.markers.clear();
62+
post_process_safety_status_ = {};
6263
resetPathCandidate();
6364
}
6465

@@ -95,9 +96,10 @@ void LaneChangeInterface::updateData()
9596

9697
void LaneChangeInterface::postProcess()
9798
{
98-
RCLCPP_DEBUG(logger_, "post processing");
99-
if (!isWaitingApproval()) {
100-
post_process_safety_status_ = module_type_->isApprovedPathSafe();
99+
if (getCurrentStatus() == ModuleStatus::RUNNING) {
100+
const auto safety_status = module_type_->isApprovedPathSafe();
101+
post_process_safety_status_ =
102+
module_type_->evaluateApprovedPathWithUnsafeHysteresis(safety_status);
101103
}
102104
}
103105

planning/behavior_path_lane_change_module/src/manager.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ void LaneChangeModuleManager::initParams(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"));
@@ -376,6 +378,8 @@ void LaneChangeModuleManager::updateModuleParams(const std::vector<rclcpp::Param
376378
updateParam<double>(parameters, ns + "duration", p->cancel.duration);
377379
updateParam<double>(parameters, ns + "max_lateral_jerk", p->cancel.max_lateral_jerk);
378380
updateParam<double>(parameters, ns + "overhang_tolerance", p->cancel.overhang_tolerance);
381+
updateParam<int>(
382+
parameters, ns + "unsafe_hysteresis_threshold", p->cancel.unsafe_hysteresis_threshold);
379383
}
380384
std::for_each(observers_.begin(), observers_.end(), [&p](const auto & observer) {
381385
if (!observer.expired()) observer.lock()->updateModuleParams(p);

planning/behavior_path_lane_change_module/src/scene.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,7 @@ void NormalLaneChange::resetParameters()
499499
current_lane_change_state_ = LaneChangeStates::Normal;
500500
abort_path_ = nullptr;
501501
status_ = {};
502+
unsafe_hysteresis_count_ = 0;
502503
lane_change_debug_.reset();
503504

504505
RCLCPP_DEBUG(logger_, "reset all flags and debug information.");
@@ -1730,6 +1731,28 @@ PathSafetyStatus NormalLaneChange::isApprovedPathSafe() const
17301731
return safety_status;
17311732
}
17321733

1734+
PathSafetyStatus NormalLaneChange::evaluateApprovedPathWithUnsafeHysteresis(
1735+
PathSafetyStatus approved_path_safety_status)
1736+
{
1737+
if (!approved_path_safety_status.is_safe) {
1738+
++unsafe_hysteresis_count_;
1739+
RCLCPP_DEBUG(
1740+
logger_, "%s: Increasing hysteresis count to %d.", __func__, unsafe_hysteresis_count_);
1741+
} else {
1742+
if (unsafe_hysteresis_count_ > 0) {
1743+
RCLCPP_DEBUG(logger_, "%s: Lane change is now SAFE. Resetting hysteresis count.", __func__);
1744+
}
1745+
unsafe_hysteresis_count_ = 0;
1746+
}
1747+
if (unsafe_hysteresis_count_ > lane_change_parameters_->cancel.unsafe_hysteresis_threshold) {
1748+
RCLCPP_DEBUG(
1749+
logger_, "%s: hysteresis count exceed threshold. lane change is now %s", __func__,
1750+
(approved_path_safety_status.is_safe ? "safe" : "UNSAFE"));
1751+
return approved_path_safety_status;
1752+
}
1753+
return {};
1754+
}
1755+
17331756
TurnSignalInfo NormalLaneChange::calcTurnSignalInfo() const
17341757
{
17351758
const auto get_blinker_pose = [](const PathWithLaneId & path, const double length) {

0 commit comments

Comments
 (0)