Skip to content

Ego permanently stops after failed lane change and scenario fallback near a stop sign #15806

Description

@RomainLettuce

System Info

Apollo side

  • OS: Ubuntu 20.04.1 LTS
  • GPU: NVIDIA GeForce RTX 2080 Ti
  • Apollo version: 7.0, with a self-patched modification to the Pull Over feature (fixes an issue where the vehicle fails to merge into the rightmost lane and instead stops in the current lane)
  • Modules enabled: Localization, Perception, Transform, Routing, Prediction, Planning, Traffic Light, Control, Recorder
  • Prediction module modification: Perception input replaced with 3D ground truth (gt_perception)
  • Map: SanFrancisco_correct, with a correction applied to several lanes whose width was incorrectly set to 3.5m instead of the standard 3.7m

Simulator side

Bug Description

In this scenario, the Ego vehicle is required to change from the left lane to the right lane in order to enter a stop sign scenario. However, an NPC vehicle is stopped at the stop line in the right lane, preventing the Ego vehicle from completing the lane change before reaching the stop line. The Ego vehicle first enters the STOP_SIGN_UNPROTECTED scenario and stops as expected, then begins to creep forward. At some point, the scenario switches to the default LANE_FOLLOW scenario, and the vehicle comes to a permanent stop.

From the attached video, the PRE_STOP, STOP, and CREEP stages can be seen repeating twice before the scenario switches to LANE_FOLLOW.

To Reproduce

Our Scenario

  • Ego initial pose: (55.58, 10.2, -53.46)
  • Ego goal pose: (50.05, 10.2, -3.51)
  • NPC initial pose: (40.56, 10.2, -33.48)
  • NPC maneuver: Stops at the stop line. In our case the NPC is stationary at the stop line, but any NPC behavior that prevents the Ego vehicle from completing its own lane change in time should be sufficient to reproduce this issue.

Video and rosbag are attached below.

Expected Behavior

After stopping near the stop line, the Ego vehicle should reroute to first turn left, then come back around, complete the right lane change, and re-attempt the right turn at the stop sign, rather than switching to LANE_FOLLOW and stopping indefinitely.

Suspected Root Cause

(Note: given the complexity of ADS planning logic, this may not be the sole factor. We are sharing our analysis in case it is useful, but a full fix likely requires closer review from maintainers familiar with this code path.)

This appears to be related to a similar underlying pattern we described in #15805 (rerouting causing an unexpected scenario/stage transition), though the mechanism here is somewhat different.

const double adc_distance_to_stop_sign =
stop_sign_overlap.start_s - adc_front_edge_s;
ADEBUG << "adc_distance_to_stop_sign[" << adc_distance_to_stop_sign
<< "] stop_sign[" << stop_sign_overlap.object_id
<< "] stop_sign_overlap_start_s[" << stop_sign_overlap.start_s << "]";
const bool stop_sign_scenario =
(adc_distance_to_stop_sign > 0.0 &&
adc_distance_to_stop_sign <=
scenario_config.start_stop_sign_scenario_distance());
const bool stop_sign_all_way = false; // TODO(all)

In modules/planning/scenarios/scenario_manager.cc (SelectStopSignScenario, lines 444-454), the distance from the Ego vehicle to the stop sign is computed using the front edge of the vehicle body (adc_front_edge_s), and the STOP_SIGN_UNPROTECTED scenario is only selected when this distance is strictly positive (adc_distance_to_stop_sign > 0.0).

When the lane change fails and a rerouting occurs, the scenario manager re-evaluates which scenario to select. On the first rerouting, the Ego vehicle is still behind the stop sign, so STOP_SIGN_UNPROTECTED is correctly re-selected. However, by the time a second rerouting occurs, the vehicle has already crept slightly forward during the CREEP stage, causing the front edge of the vehicle to pass the stop sign's start_s. This makes adc_distance_to_stop_sign negative, so the condition above no longer holds, and the scenario manager falls back to the default LANE_FOLLOW scenario instead.

// build stop decision
ADEBUG << "BuildStopDecision: stop_sign[" << stop_sign_overlap.object_id
<< "] start_s[" << stop_sign_overlap.start_s << "]";
const std::string virtual_obstacle_id =
STOP_SIGN_VO_ID_PREFIX + stop_sign_overlap.object_id;
const std::vector<std::string> wait_for_obstacle_ids(
stop_sign_status.wait_for_obstacle_id().begin(),
stop_sign_status.wait_for_obstacle_id().end());
util::BuildStopDecision(virtual_obstacle_id, stop_sign_overlap.start_s,
config_.stop_sign().stop_distance(),
StopReasonCode::STOP_REASON_STOP_SIGN,
wait_for_obstacle_ids,
TrafficRuleConfig::RuleId_Name(config_.rule_id()),
frame, reference_line_info);

Separately, in modules/planning/traffic_rules/stop_sign.cc (lines 71-84), the stop decision created for the stop sign is only maintained through the STOP and CREEP stages, and is not carried over once the scenario falls back to LANE_FOLLOW. Since the default LANE_FOLLOW scenario has no mechanism to clear or supersede this now-orphaned stop decision, the vehicle remains permanently stopped.

Suggested Fix Direction

In our tests, using the rear edge of the vehicle body (adc_back_edge_s) instead of the front edge when computing the distance to the stop sign resolved this specific case. However, we believe the more fundamental issue is that certain scenario/planning context is not preserved across rerouting, and a more complete fix would involve maintaining relevant stop-sign scenario context through a rerouting event rather than relying purely on distance-based re-entry conditions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions