Skip to content

Commit a7e68a1

Browse files
fix(lane_change): change stopping logic (RT0-33761) (#1581)
* RT0-33761 fix lane change stopping logic Signed-off-by: Zulfaqar Azmi <zulfaqar.azmi@tier4.jp> * copied from awf main tested implementation Signed-off-by: Zulfaqar Azmi <zulfaqar.azmi@tier4.jp> * doxygen comment Signed-off-by: Zulfaqar Azmi <zulfaqar.azmi@tier4.jp> * Update planning/behavior_path_lane_change_module/src/utils/utils.cpp Co-authored-by: mkquda <168697710+mkquda@users.noreply.github.com> --------- Signed-off-by: Zulfaqar Azmi <zulfaqar.azmi@tier4.jp> Co-authored-by: mkquda <168697710+mkquda@users.noreply.github.com>
1 parent b25fd6b commit a7e68a1

File tree

4 files changed

+238
-115
lines changed

4 files changed

+238
-115
lines changed

planning/behavior_path_lane_change_module/include/behavior_path_lane_change_module/scene.hpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class NormalLaneChange : public LaneChangeBase
6060

6161
void insertStopPoint(const lanelet::ConstLanelets & lanelets, PathWithLaneId & path) override;
6262

63+
void insert_stop_point_on_current_lanes(PathWithLaneId & path);
64+
6365
PathWithLaneId getReferencePath() const override;
6466

6567
std::optional<PathWithLaneId> extendPath() override;
@@ -178,7 +180,7 @@ class NormalLaneChange : public LaneChangeBase
178180

179181
std::pair<double, double> calcCurrentMinMaxAcceleration() const;
180182

181-
void setStopPose(const Pose & stop_pose);
183+
void set_stop_pose(const double arc_length_to_stop_pose, PathWithLaneId & path);
182184

183185
void updateStopTime();
184186

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

+49
Original file line numberDiff line numberDiff line change
@@ -289,5 +289,54 @@ bool isWithinTurnDirectionLanes(const lanelet::ConstLanelet & lanelet, const Pol
289289
double calcPhaseLength(
290290
const double velocity, const double maximum_velocity, const double acceleration,
291291
const double time);
292+
293+
/**
294+
* @brief Calculates the minimum distance to a stationary object in the current lanes.
295+
*
296+
* This function determines the closest distance from the ego vehicle to a stationary object
297+
* in the current lanes. It checks if the object is within the stopping criteria based on its
298+
* velocity and calculates the distance while accounting for the object's size. Only objects
299+
* positioned after the specified distance to the target lane's start are considered.
300+
*
301+
* @param filtered_objects A collection of lane change target objects, including those in the
302+
* current lane.
303+
* @param bpp_param Parameters for the behavior path planner, such as vehicle dimensions.
304+
* @param lc_param Parameters for the lane change process, including the velocity threshold for
305+
* stopping.
306+
* @param dist_to_target_lane_start The distance from the ego vehicle to the start of the target
307+
* lane.
308+
* @param ego_pose The current pose of the ego vehicle.
309+
* @param path The current path of the ego vehicle, containing path points and lane information.
310+
* @return The minimum distance to a stationary object in the current lanes. If no valid object is
311+
* found, returns the maximum possible double value.
312+
*/
313+
double get_min_dist_to_current_lanes_obj(
314+
const LaneChangeTargetObjects & filtered_objects, const BehaviorPathPlannerParameters & bpp_param,
315+
const LaneChangeParameters & lc_param, const double dist_to_target_lane_start, const Pose & pose,
316+
const PathWithLaneId & path);
317+
318+
/**
319+
* @brief Checks if there is any stationary object in the target lanes that would affect the lane
320+
* change stop decision.
321+
*
322+
* This function determines whether there are any stationary objects in the target lanes that could
323+
* impact the decision to insert a stop point for the ego vehicle. It checks each object's velocity,
324+
* position relative to the ego vehicle, and overlap with the target lanes to identify if any object
325+
* meets the criteria for being a blocking obstacle.
326+
*
327+
* @param target_lanes A collection of lanelets representing the target lanes for the lane change.
328+
* @param filtered_objects A collection of lane change target objects, including those in the target
329+
* lanes.
330+
* @param lc_param Parameters for the lane change process, such as the stop velocity threshold.
331+
* @param stop_arc_length The arc length at which the ego vehicle is expected to stop.
332+
* @param ego_pose The current pose of the ego vehicle.
333+
* @param path The current path of the ego vehicle, containing path points and lane information.
334+
* @return true if there is a stationary object in the target lanes that meets the criteria for
335+
* being a blocking object; otherwise, false.
336+
*/
337+
bool has_blocking_target_object(
338+
const lanelet::ConstLanelets & target_lanes, const LaneChangeTargetObjects & filtered_objects,
339+
const LaneChangeParameters & lc_param, const double stop_arc_length, const Pose & ego_pose,
340+
const PathWithLaneId & path);
292341
} // namespace behavior_path_planner::utils::lane_change
293342
#endif // BEHAVIOR_PATH_LANE_CHANGE_MODULE__UTILS__UTILS_HPP_

planning/behavior_path_lane_change_module/src/scene.cpp

+100-114
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,7 @@ BehaviorModuleOutput NormalLaneChange::generateOutput()
165165
output.path.points, output.path.points.front().point.pose.position, getEgoPosition());
166166
const auto stop_dist =
167167
-(current_velocity * current_velocity / (2.0 * planner_data_->parameters.min_acc));
168-
const auto stop_point = utils::insertStopPoint(stop_dist + current_dist, output.path);
169-
setStopPose(stop_point.point.pose);
168+
set_stop_pose(stop_dist + current_dist, output.path);
170169
} else {
171170
insertStopPoint(status_.target_lanes, output.path);
172171
}
@@ -204,7 +203,7 @@ void NormalLaneChange::extendOutputDrivableArea(BehaviorModuleOutput & output)
204203
void NormalLaneChange::insertStopPoint(
205204
const lanelet::ConstLanelets & lanelets, PathWithLaneId & path)
206205
{
207-
if (lanelets.empty()) {
206+
if (lanelets.empty() || status_.current_lanes.empty()) {
208207
return;
209208
}
210209

@@ -214,133 +213,119 @@ void NormalLaneChange::insertStopPoint(
214213
return;
215214
}
216215

217-
const auto shift_intervals = route_handler->getLateralIntervalsToPreferredLane(lanelets.back());
218-
const double lane_change_buffer =
219-
utils::lane_change::calcMinimumLaneChangeLength(*lane_change_parameters_, shift_intervals, 0.0);
216+
const auto & current_lanes = status_.current_lanes;
217+
const auto is_current_lane = lanelets.front().id() == current_lanes.front().id() &&
218+
lanelets.back().id() == current_lanes.back().id();
219+
220+
// if input is not current lane, we can just insert the points at terminal.
221+
if (!is_current_lane) {
222+
const auto shift_intervals = route_handler->getLateralIntervalsToPreferredLane(lanelets.back());
223+
const auto min_dist_buffer = utils::lane_change::calcMinimumLaneChangeLength(
224+
*lane_change_parameters_, shift_intervals, 0.0);
225+
const auto backward_length_buffer =
226+
lane_change_parameters_->backward_length_buffer_for_end_of_lane;
227+
const auto arc_length_to_stop_pose =
228+
motion_utils::calcArcLength(path.points) - backward_length_buffer + min_dist_buffer;
229+
set_stop_pose(arc_length_to_stop_pose, path);
230+
return;
231+
}
220232

221-
const auto getDistanceAlongLanelet = [&](const geometry_msgs::msg::Pose & target) {
222-
return utils::getSignedDistance(path.points.front().point.pose, target, lanelets);
223-
};
233+
insert_stop_point_on_current_lanes(path);
234+
}
224235

225-
// If lanelets.back() is in goal route section, get distance to goal.
226-
// Otherwise, get distance to end of lane.
227-
double distance_to_terminal = 0.0;
228-
if (route_handler->isInGoalRouteSection(lanelets.back())) {
229-
const auto goal = route_handler->getGoalPose();
230-
distance_to_terminal = getDistanceAlongLanelet(goal);
231-
} else {
232-
distance_to_terminal = utils::getDistanceToEndOfLane(path.points.front().point.pose, lanelets);
233-
}
236+
void NormalLaneChange::insert_stop_point_on_current_lanes(PathWithLaneId & path)
237+
{
238+
const auto & path_front_pose = path.points.front().point.pose;
239+
const auto & route_handler_ptr = getRouteHandler();
240+
const auto & current_lanes = status_.current_lanes;
241+
const auto current_path =
242+
route_handler_ptr->getCenterLinePath(current_lanes, 0.0, std::numeric_limits<double>::max());
243+
const auto & center_line = current_path.points;
244+
const auto get_arc_length_along_lanelet = [&](const geometry_msgs::msg::Pose & target) {
245+
return motion_utils::calcSignedArcLength(
246+
center_line, path_front_pose.position, target.position);
247+
};
234248

235-
const double stop_point_buffer = lane_change_parameters_->backward_length_buffer_for_end_of_lane;
236-
const auto target_objects = getTargetObjects(status_.current_lanes, status_.target_lanes);
237-
double stopping_distance = distance_to_terminal - lane_change_buffer - stop_point_buffer;
238-
239-
const auto is_valid_start_point = std::invoke([&]() -> bool {
240-
auto lc_start_point = lanelet::utils::conversion::toLaneletPoint(
241-
status_.lane_change_path.info.lane_changing_start.position);
242-
const auto target_neighbor_preferred_lane_poly_2d =
243-
utils::lane_change::getTargetNeighborLanesPolygon(
244-
*route_handler, status_.current_lanes, type_);
245-
return boost::geometry::covered_by(
246-
lanelet::traits::to2D(lc_start_point), target_neighbor_preferred_lane_poly_2d);
249+
const auto dist_to_terminal = std::invoke([&]() -> double {
250+
const auto target_pose = route_handler_ptr->isInGoalRouteSection(current_lanes.back())
251+
? route_handler_ptr->getGoalPose()
252+
: center_line.back().point.pose;
253+
return get_arc_length_along_lanelet(target_pose);
247254
});
248255

249-
if (!is_valid_start_point) {
250-
const auto stop_point = utils::insertStopPoint(stopping_distance, path);
251-
setStopPose(stop_point.point.pose);
256+
const auto shift_intervals =
257+
route_handler_ptr->getLateralIntervalsToPreferredLane(current_lanes.back());
258+
const auto min_dist_buffer =
259+
utils::lane_change::calcMinimumLaneChangeLength(*lane_change_parameters_, shift_intervals, 0.0);
260+
const auto backward_length_buffer =
261+
lane_change_parameters_->backward_length_buffer_for_end_of_lane;
262+
const auto dist_to_terminal_start = dist_to_terminal - min_dist_buffer - backward_length_buffer;
252263

264+
const auto filtered_objects = getTargetObjects(status_.current_lanes, status_.target_lanes);
265+
if (filtered_objects.current_lane.empty()) {
266+
set_stop_pose(dist_to_terminal_start, path);
253267
return;
254268
}
255269

256-
// calculate minimum distance from path front to the stationary object on the ego lane.
257-
const auto distance_to_ego_lane_obj = [&]() -> double {
258-
double distance_to_obj = distance_to_terminal;
259-
const double distance_to_ego = getDistanceAlongLanelet(getEgoPose());
260-
261-
for (const auto & object : target_objects.current_lane) {
262-
// check if stationary
263-
const auto obj_v = std::abs(object.initial_twist.twist.linear.x);
264-
if (obj_v > lane_change_parameters_->stop_velocity_threshold) {
265-
continue;
266-
}
267-
268-
// calculate distance from path front to the stationary object polygon on the ego lane.
269-
const auto polygon =
270-
tier4_autoware_utils::toPolygon2d(object.initial_pose.pose, object.shape).outer();
271-
for (const auto & polygon_p : polygon) {
272-
const auto p_fp = tier4_autoware_utils::toMsg(polygon_p.to_3d());
273-
const auto lateral_fp = motion_utils::calcLateralOffset(path.points, p_fp);
270+
const auto dist_to_target_lane_start = std::invoke([&]() -> double {
271+
const auto & front_lane = status_.target_lanes.front();
272+
const auto & ll_front_pt = front_lane.centerline2d().front();
273+
const auto front_pt = lanelet::utils::conversion::toGeomMsgPt(ll_front_pt);
274+
const auto yaw = lanelet::utils::getLaneletAngle(front_lane, front_pt);
275+
Pose target_front;
276+
target_front.position = front_pt;
277+
tf2::Quaternion tf_quat;
278+
tf_quat.setRPY(0, 0, yaw);
279+
target_front.orientation = tf2::toMsg(tf_quat);
274280

275-
// ignore if the point is around the ego path
276-
if (std::abs(lateral_fp) > planner_data_->parameters.vehicle_width) {
277-
continue;
278-
}
281+
return get_arc_length_along_lanelet(target_front);
282+
});
279283

280-
const double current_distance_to_obj = calcSignedArcLength(path.points, 0, p_fp);
284+
const auto arc_length_to_current_obj = utils::lane_change::get_min_dist_to_current_lanes_obj(
285+
filtered_objects, getCommonParam(), *lane_change_parameters_, dist_to_target_lane_start,
286+
getEgoPose(), path);
281287

282-
// ignore backward object
283-
if (current_distance_to_obj < distance_to_ego) {
284-
continue;
285-
}
286-
distance_to_obj = std::min(distance_to_obj, current_distance_to_obj);
287-
}
288-
}
289-
return distance_to_obj;
290-
}();
291-
292-
// Need to stop before blocking obstacle
293-
if (distance_to_ego_lane_obj < distance_to_terminal) {
294-
// consider rss distance when the LC need to avoid obstacles
295-
const auto rss_dist = calcRssDistance(
296-
0.0, lane_change_parameters_->minimum_lane_changing_velocity,
297-
lane_change_parameters_->rss_params);
298-
const double lane_change_buffer_for_blocking_object =
299-
utils::lane_change::calcMinimumLaneChangeLength(*lane_change_parameters_, shift_intervals);
300-
301-
const auto stopping_distance_for_obj =
302-
distance_to_ego_lane_obj - lane_change_buffer_for_blocking_object -
303-
lane_change_parameters_->backward_length_buffer_for_blocking_object - rss_dist -
304-
getCommonParam().base_link2front;
305-
306-
// If the target lane in the lane change section is blocked by a stationary obstacle, there
307-
// is no reason for stopping with a lane change margin. Instead, stop right behind the
308-
// obstacle.
309-
// ----------------------------------------------------------
310-
// [obj]>
311-
// ----------------------------------------------------------
312-
// [ego]> | <--- lane change margin ---> [obj]>
313-
// ----------------------------------------------------------
314-
const bool has_blocking_target_lane_obj = std::any_of(
315-
target_objects.target_lane.begin(), target_objects.target_lane.end(), [&](const auto & o) {
316-
const auto v = std::abs(o.initial_twist.twist.linear.x);
317-
if (v > lane_change_parameters_->stop_velocity_threshold) {
318-
return false;
319-
}
288+
// margin with leading vehicle
289+
// consider rss distance when the LC need to avoid obstacles
290+
const auto rss_dist = calcRssDistance(
291+
0.0, lane_change_parameters_->minimum_lane_changing_velocity,
292+
lane_change_parameters_->rss_params);
320293

321-
// target_objects includes objects out of target lanes, so filter them out
322-
if (!boost::geometry::intersects(
323-
tier4_autoware_utils::toPolygon2d(o.initial_pose.pose, o.shape).outer(),
324-
lanelet::utils::combineLaneletsShape(status_.target_lanes)
325-
.polygon2d()
326-
.basicPolygon())) {
327-
return false;
328-
}
294+
const auto min_single_lc_length = utils::lane_change::calcMinimumLaneChangeLength(
295+
*lane_change_parameters_, {shift_intervals.front()}, 0.0);
296+
const auto stop_margin = min_single_lc_length +
297+
lane_change_parameters_->backward_length_buffer_for_blocking_object +
298+
rss_dist + getCommonParam().base_link2front;
299+
const auto stop_arc_length_behind_obj = arc_length_to_current_obj - stop_margin;
329300

330-
const double distance_to_target_lane_obj = getDistanceAlongLanelet(o.initial_pose.pose);
331-
return stopping_distance_for_obj < distance_to_target_lane_obj &&
332-
distance_to_target_lane_obj < distance_to_ego_lane_obj;
333-
});
301+
if (stop_arc_length_behind_obj < dist_to_target_lane_start) {
302+
set_stop_pose(dist_to_target_lane_start, path);
303+
return;
304+
}
334305

335-
if (!has_blocking_target_lane_obj) {
336-
stopping_distance = stopping_distance_for_obj;
337-
}
306+
if (stop_arc_length_behind_obj > dist_to_terminal_start) {
307+
set_stop_pose(dist_to_terminal_start, path);
308+
return;
338309
}
339310

340-
if (stopping_distance > 0.0) {
341-
const auto stop_point = utils::insertStopPoint(stopping_distance, path);
342-
setStopPose(stop_point.point.pose);
311+
// If the target lane in the lane change section is blocked by a stationary obstacle, there
312+
// is no reason for stopping with a lane change margin. Instead, stop right behind the
313+
// obstacle.
314+
// ----------------------------------------------------------
315+
// [obj]>
316+
// ----------------------------------------------------------
317+
// [ego]> | <--- stop margin ---> [obj]>
318+
// ----------------------------------------------------------
319+
const auto has_blocking_target_lane_obj = utils::lane_change::has_blocking_target_object(
320+
status_.target_lanes, filtered_objects, *lane_change_parameters_, stop_arc_length_behind_obj,
321+
getEgoPose(), path);
322+
323+
if (has_blocking_target_lane_obj || stop_arc_length_behind_obj <= 0.0) {
324+
set_stop_pose(dist_to_terminal_start, path);
325+
return;
343326
}
327+
328+
set_stop_pose(stop_arc_length_behind_obj, path);
344329
}
345330

346331
PathWithLaneId NormalLaneChange::getReferencePath() const
@@ -1842,9 +1827,10 @@ bool NormalLaneChange::isVehicleStuck(const lanelet::ConstLanelets & current_lan
18421827
return isVehicleStuck(current_lanes, detection_distance);
18431828
}
18441829

1845-
void NormalLaneChange::setStopPose(const Pose & stop_pose)
1830+
void NormalLaneChange::set_stop_pose(const double arc_length_to_stop_pose, PathWithLaneId & path)
18461831
{
1847-
lane_change_stop_pose_ = stop_pose;
1832+
const auto stop_point = utils::insertStopPoint(arc_length_to_stop_pose, path);
1833+
lane_change_stop_pose_ = stop_point.point.pose;
18481834
}
18491835

18501836
void NormalLaneChange::updateStopTime()

0 commit comments

Comments
 (0)