Skip to content

PullOverPath 靠边停车路径

源码位置:modules/planning/tasks/pull_over_path/

模块定位

PullOverPath 是靠边停车场景的路径生成任务。当 Scenario 决定执行靠边停车时,本任务负责搜索合适的停车位置并生成从当前位置到停车点的平滑路径。

继承关系:

Task → PathGeneration → PullOverPath

Process 主流程

cpp
Status PullOverPath::Process(Frame* frame, ReferenceLineInfo* rli) {
  // 跳过条件:已有路径 或 换道参考线
  if (!rli->path_data().Empty() || rli->IsChangeLanePath()) return OK;

  GetStartPointSLState();           // 获取起点 SL 状态
  DecidePathBounds(&boundaries);    // 计算路径边界
  OptimizePath(boundaries, &candidates);  // QP 优化路径
  AssessPath(&candidates, final_path);    // 评估选择最优路径
}

DecidePathBounds — 路径边界决策

cpp
bool PullOverPath::DecidePathBounds(vector<PathBoundary>* boundary) {
  // 1. 检查 PlanningContext 中是否标记了 plan_pull_over_path
  if (!plan_pull_over_path) return false;

  // 2. 初始化路径边界为道路宽度
  PathBoundsDeciderUtil::InitPathBoundary(rli, &path_bound, init_sl_state_);
  GetBoundaryFromRoads(rli, &path_bound);

  // 3. 确定靠边方向(左/右)
  if (config == BOTH_SIDE)
    is_pull_over_right = (adc_to_left > adc_to_right);
  else if (config == LEFT_SIDE)
    is_pull_over_right = false;
  else
    is_pull_over_right = true;

  // 4. 用车道边界更新靠边侧的路径边界
  UpdatePullOverBoundaryByLaneBoundary(is_pull_over_right, &path_bound);

  // 5. 考虑静态障碍物收缩边界
  PathBoundsDeciderUtil::GetBoundaryFromStaticObstacles(...);

  // 6. 搜索或验证停车位置
  if (pull_over_status.has_position()) {
    // 验证已有位置是否仍在边界内
    curr_idx = IsPointWithinPathBound(x, y, path_bound);
  }
  if (curr_idx < 0) {
    // 搜索新的停车位置
    SearchPullOverPosition(path_bound, &pull_over_configuration);
    // 更新 PlanningContext
    pull_over_status.set_position(x, y);
    pull_over_status.set_theta(theta);
  }

  // 7. 裁剪路径边界到停车点之后
  TrimPathBound(curr_idx + extra_tail_points);
}

SearchPullOverPosition — 停车位搜索

搜索策略由配置决定:

模式搜索方向目标 s
NEAREST_POSITION前向搜索adc_s + 2 * min_turn_radius * factor
DESTINATION后向搜索路由终点 s
cpp
bool PullOverPath::SearchPullOverPosition(path_bound, &config) {
  // 确定搜索起点 pull_over_s
  if (NEAREST_POSITION) FindNearestPullOverS(&pull_over_s);
  else if (DESTINATION) FindDestinationPullOverS(path_bound, &pull_over_s);

  // 滑动窗口搜索可行停车区域
  while (idx in valid_range) {
    // 跳过交叉口区域
    if (point_in_junction) continue;

    // 检查窗口内所有点是否满足:
    //   - 靠近道路边缘(距边缘 < pull_over_space_width)
    //   - 窗口长度 >= 1.5 * vehicle_length
    //   - 不在交叉口内
    if (feasible_window_found) {
      // 取窗口中心作为停车点
      pull_over_x, pull_over_y = SLToXY(center_s, center_l);
      pull_over_theta = reference_line.GetHeading(center_s);
      return true;
    }
  }
}

FindNearestPullOverS

cpp
bool PullOverPath::FindNearestPullOverS(double* pull_over_s) {
  double pull_over_distance = min_turn_radius * 2 * adjust_factor;
  *pull_over_s = adc_end_s + pull_over_distance;
}

基于车辆最小转弯半径计算最近可停车距离。

FindDestinationPullOverS

cpp
bool PullOverPath::FindDestinationPullOverS(path_bound, double* pull_over_s) {
  // 将路由终点投影到参考线获取 destination_s
  reference_line.XYToSL(routing_end.pose(), &destination_sl);
  // 检查距离 ADC 足够远
  if (destination_s - adc_end_s < buffer) return false;
  // 检查在路径边界范围内
  if (destination_s + buffer >= path_bound.back().s) return false;
  *pull_over_s = destination_s;
}

OptimizePath — QP 路径优化

cpp
bool PullOverPath::OptimizePath(boundaries, &candidate_path_data) {
  for (each path_boundary) {
    // 终点状态:停车位置的横向偏移
    end_state = {pull_over_sl.l(), 0.0, 0.0};

    // 参考线设为停车位置的 l 值,权重使用 pull_over_weight
    ref_l.assign(size, pull_over_sl.l());
    weight_ref_l.assign(size, config_.pull_over_weight());

    // 调用 PiecewiseJerk 路径优化
    PathOptimizerUtil::OptimizePath(
        init_sl_state_, end_state, ref_l, weight_ref_l,
        path_boundary, ddl_bounds, jerk_bound, config, ...);
  }
}

AssessPath — 路径评估

cpp
bool PullOverPath::AssessPath(candidates, &final_path) {
  // 验证路径合法性(无碰撞、曲率合理等)
  PathAssessmentDeciderUtil::IsValidRegularPath(rli, curr_path);

  // 标记路径点决策类型为 IN_LANE
  PathAssessmentDeciderUtil::InitPathPointDecision(
      curr_path, IN_LANE, &path_decision);

  // 输出 debug 信息(停车位置、朝向、车辆尺寸)
  pull_over_debug->set_position(x, y);
  pull_over_debug->set_theta(theta);
}

UpdatePullOverBoundaryByLaneBoundary

根据靠边方向,用车道边界替换路径边界的一侧:

cpp
void PullOverPath::UpdatePullOverBoundaryByLaneBoundary(
    bool is_pull_over_right, PathBoundary* path_bound) {
  for (each point in path_bound) {
    GetLaneWidth(curr_s, &left_width, &right_width);
    if (is_pull_over_right)
      path_bound[i].l_upper = left_width;   // 左侧用车道边界
    else
      path_bound[i].l_lower = right_width;  // 右侧用车道边界
  }
}
  • 靠右停车:左边界收紧到车道线(不允许跨车道)
  • 靠左停车:右边界收紧到车道线

关键配置参数

参数说明
pull_over_direction停车方向:RIGHT_SIDE / LEFT_SIDE / BOTH_SIDE
pull_over_position停车位置策略:NEAREST_POSITION / DESTINATION
pull_over_weightQP 优化中停车位置参考权重
pull_over_approach_lon_distance_adjust_factor最近停车距离调整因子
pull_over_destination_to_adc_buffer目的地到 ADC 最小距离
pull_over_destination_to_pathend_buffer目的地到路径末端缓冲

贡献者

页面历史