49 const std::string& name,
50 const std::shared_ptr<DependencyInjector>& injector) {
55 return Task::LoadConfig<LaneChangePathConfig>(&config_);
60 UpdateLaneChangeStatus();
61 const auto& status =
injector_->planning_context()
62 ->mutable_planning_status()
63 ->mutable_change_lane()
73 ->mutable_planning_status()
74 ->mutable_change_lane()
76 return Status(ErrorCode::PLANNING_ERROR,
77 "Not satisfy lane change conditions");
79 std::vector<PathBoundary> candidate_path_boundaries;
80 std::vector<PathData> candidate_path_data;
83 if (!DecidePathBounds(&candidate_path_boundaries)) {
84 return Status(ErrorCode::PLANNING_ERROR,
"lane change path bounds failed");
86 if (!OptimizePath(candidate_path_boundaries, &candidate_path_data)) {
87 return Status(ErrorCode::PLANNING_ERROR,
88 "lane change path optimize failed");
90 if (!AssessPath(&candidate_path_data,
92 return Status(ErrorCode::PLANNING_ERROR,
"No valid lane change path");
98bool LaneChangePath::DecidePathBounds(std::vector<PathBoundary>* boundary) {
99 boundary->emplace_back();
100 auto& path_bound = boundary->back();
101 double path_narrowest_width = 0;
105 const std::string msg =
"Failed to initialize path boundaries.";
112 AERROR <<
"Failed to decide a rough boundary based on self lane.";
118 AERROR <<
"Failed to decide a rough boundary based on adc.";
123 GetBoundaryFromLaneChangeForbiddenZone(&path_bound);
125 path_bound.set_label(
"regular/lane_change");
128 std::string blocking_obstacle_id;
134 &blocking_obstacle_id, &path_narrowest_width)) {
135 AERROR <<
"Failed to decide fine tune the boundaries after "
136 "taking into consideration all static obstacles.";
142 while (!blocking_obstacle_id.empty() &&
143 path_bound.size() < temp_path_bound.size() &&
144 counter < FLAGS_num_extra_tail_bound_point) {
145 path_bound.push_back(temp_path_bound[path_bound.size()]);
149 path_bound.set_blocking_obstacle_id(blocking_obstacle_id);
153bool LaneChangePath::OptimizePath(
154 const std::vector<PathBoundary>& path_boundaries,
155 std::vector<PathData>* candidate_path_data) {
158 std::array<double, 3> end_state = {0.0, 0.0, 0.0};
159 for (
const auto& path_boundary : path_boundaries) {
160 size_t path_boundary_size = path_boundary.boundary().size();
161 if (path_boundary_size <= 1U) {
162 AERROR <<
"Get invalid path boundary with size: " << path_boundary_size;
165 std::vector<double> opt_l, opt_dl, opt_ddl;
166 std::vector<std::pair<double, double>> ddl_bounds;
171 std::vector<double> ref_l(path_boundary_size, 0);
172 std::vector<double> weight_ref_l(path_boundary_size, 0);
176 ddl_bounds, jerk_bound, config, &opt_l, &opt_dl, &opt_ddl);
179 opt_l, opt_dl, opt_ddl, path_boundary.delta_s(),
180 path_boundary.start_s());
182 path_data.SetReferenceLine(&reference_line);
183 path_data.SetFrenetPath(std::move(frenet_frame_path));
184 if (FLAGS_use_front_axe_center_in_path_planning) {
185 auto discretized_path = DiscretizedPath(
188 path_data.SetDiscretizedPath(discretized_path);
190 path_data.set_path_label(path_boundary.label());
191 path_data.set_blocking_obstacle_id(path_boundary.blocking_obstacle_id());
192 candidate_path_data->push_back(std::move(path_data));
195 if (candidate_path_data->empty()) {
201bool LaneChangePath::AssessPath(std::vector<PathData>* candidate_path_data,
202 PathData* final_path) {
203 std::vector<PathData> valid_path_data;
204 for (
auto& curr_path_data : *candidate_path_data) {
207 SetPathInfo(&curr_path_data);
209 FLAGS_path_trim_destination_threshold) {
212 if (curr_path_data.Empty()) {
213 AINFO <<
"lane change path is empty after trimed";
216 valid_path_data.push_back(curr_path_data);
219 if (valid_path_data.empty()) {
220 AINFO <<
"All lane change path are not valid";
224 *final_path = valid_path_data[0];
231void LaneChangePath::UpdateLaneChangeStatus() {
232 std::string change_lane_id;
233 auto* prev_status =
injector_->planning_context()
234 ->mutable_planning_status()
235 ->mutable_change_lane();
238 if (!prev_status->has_status()) {
243 if (!has_change_lane) {
246 prev_status->path_id());
252 const auto* history_frame =
injector_->frame_history()->Latest();
253 if (!CheckLastFrameSucceed(history_frame)) {
255 is_exist_lane_change_start_position_ =
false;
260 ADEBUG <<
"change_lane_id" << change_lane_id;
262 if (now - prev_status->timestamp() >
265 ADEBUG <<
"change lane again after failed";
268 }
else if (prev_status->status() ==
270 if (now - prev_status->timestamp() >
273 AINFO <<
"change lane again after success";
276 if (prev_status->path_id() != change_lane_id) {
277 AINFO <<
"change_lane_id" << change_lane_id <<
"prev"
278 << prev_status->path_id();
280 prev_status->path_id());
286bool LaneChangePath::IsClearToChangeLane(
287 ReferenceLineInfo* reference_line_info) {
288 double ego_start_s = reference_line_info->AdcSlBoundary().start_s();
289 double ego_end_s = reference_line_info->AdcSlBoundary().end_s();
291 std::abs(reference_line_info->vehicle_state().linear_velocity());
293 for (
const auto* obstacle :
294 reference_line_info->path_decision()->obstacles().Items()) {
295 if (obstacle->IsVirtual() || obstacle->IsStatic()) {
296 ADEBUG <<
"skip one virtual or static obstacle";
300 double start_s = std::numeric_limits<double>::max();
301 double end_s = -std::numeric_limits<double>::max();
302 double start_l = std::numeric_limits<double>::max();
303 double end_l = -std::numeric_limits<double>::max();
305 for (
const auto& p : obstacle->PerceptionPolygon().points()) {
307 reference_line_info->reference_line().XYToSL(p, &sl_point);
308 start_s = std::fmin(start_s, sl_point.
s());
309 end_s = std::fmax(end_s, sl_point.
s());
311 start_l = std::fmin(start_l, sl_point.
l());
312 end_l = std::fmax(end_l, sl_point.
l());
315 if (reference_line_info->IsChangeLanePath()) {
316 double left_width(0), right_width(0);
317 reference_line_info->mutable_reference_line()->GetLaneWidth(
318 (start_s + end_s) * 0.5, &left_width, &right_width);
319 if (end_l < -right_width || start_l > left_width) {
326 bool same_direction =
true;
327 if (obstacle->HasTrajectory()) {
328 double obstacle_moving_direction =
329 obstacle->Trajectory().trajectory_point(0).path_point().theta();
330 const auto& vehicle_state = reference_line_info->vehicle_state();
331 double vehicle_moving_direction = vehicle_state.heading();
333 vehicle_moving_direction =
337 obstacle_moving_direction - vehicle_moving_direction));
338 same_direction = heading_difference < (M_PI / 2.0);
342 static constexpr double kSafeTimeOnSameDirection = 3.0;
343 static constexpr double kSafeTimeOnOppositeDirection = 5.0;
344 static constexpr double kForwardMinSafeDistanceOnSameDirection = 10.0;
345 static constexpr double kBackwardMinSafeDistanceOnSameDirection = 10.0;
346 static constexpr double kForwardMinSafeDistanceOnOppositeDirection = 50.0;
347 static constexpr double kBackwardMinSafeDistanceOnOppositeDirection = 1.0;
348 static constexpr double kDistanceBuffer = 0.5;
350 double kForwardSafeDistance = 0.0;
351 double kBackwardSafeDistance = 0.0;
352 if (same_direction) {
353 kForwardSafeDistance =
354 std::fmax(kForwardMinSafeDistanceOnSameDirection,
355 (ego_v - obstacle->speed()) * kSafeTimeOnSameDirection);
356 kBackwardSafeDistance =
357 std::fmax(kBackwardMinSafeDistanceOnSameDirection,
358 (obstacle->speed() - ego_v) * kSafeTimeOnSameDirection);
360 kForwardSafeDistance =
361 std::fmax(kForwardMinSafeDistanceOnOppositeDirection,
362 (ego_v + obstacle->speed()) * kSafeTimeOnOppositeDirection);
363 kBackwardSafeDistance = kBackwardMinSafeDistanceOnOppositeDirection;
366 if (HysteresisFilter(ego_start_s - end_s, kBackwardSafeDistance,
367 kDistanceBuffer, obstacle->IsLaneChangeBlocking()) &&
368 HysteresisFilter(start_s - ego_end_s, kForwardSafeDistance,
369 kDistanceBuffer, obstacle->IsLaneChangeBlocking())) {
370 reference_line_info->path_decision()
371 ->Find(obstacle->Id())
372 ->SetLaneChangeBlocking(
true);
373 ADEBUG <<
"Lane Change is blocked by obstacle" << obstacle->Id();
376 reference_line_info->path_decision()
377 ->Find(obstacle->Id())
378 ->SetLaneChangeBlocking(
false);
384void LaneChangePath::GetLaneChangeStartPoint(
385 const ReferenceLine& reference_line,
double adc_frenet_s,
386 common::math::Vec2d* start_xy) {
387 double lane_change_start_s =
389 common::SLPoint lane_change_start_sl;
390 lane_change_start_sl.set_s(lane_change_start_s);
391 lane_change_start_sl.set_l(0.0);
392 reference_line.SLToXY(lane_change_start_sl, start_xy);
395void LaneChangePath::GetBoundaryFromLaneChangeForbiddenZone(
396 PathBoundary*
const path_bound) {
398 CHECK_NOTNULL(path_bound);
400 if (is_clear_to_change_lane_) {
401 is_exist_lane_change_start_position_ =
false;
404 double lane_change_start_s = 0.0;
408 if (is_exist_lane_change_start_position_) {
409 common::SLPoint point_sl;
410 reference_line.
XYToSL(lane_change_start_xy_, &point_sl);
411 lane_change_start_s = point_sl.s();
414 lane_change_start_s =
419 &lane_change_start_xy_);
427 double adc_half_width =
429 for (
size_t i = 0; i < path_bound->size(); ++i) {
430 double curr_s = (*path_bound)[i].s;
431 if (curr_s > lane_change_start_s) {
434 double curr_lane_left_width = 0.0;
435 double curr_lane_right_width = 0.0;
436 double offset_to_map = 0.0;
437 reference_line.GetOffsetToMap(curr_s, &offset_to_map);
438 if (reference_line.GetLaneWidth(curr_s, &curr_lane_left_width,
439 &curr_lane_right_width)) {
440 double offset_to_lane_center = 0.0;
441 reference_line.GetOffsetToMap(curr_s, &offset_to_lane_center);
442 curr_lane_left_width += offset_to_lane_center;
443 curr_lane_right_width -= offset_to_lane_center;
445 curr_lane_left_width -= offset_to_map;
446 curr_lane_right_width += offset_to_map;
448 (*path_bound)[i].l_lower.l =
init_sl_state_.second[0] > curr_lane_left_width
449 ? curr_lane_left_width + adc_half_width
450 : (*path_bound)[i].l_lower.l;
451 (*path_bound)[i].l_lower.l =
452 std::fmin((*path_bound)[i].l_lower.l,
init_sl_state_.second[0] - 0.1);
453 (*path_bound)[i].l_upper.l =
455 ? -curr_lane_right_width - adc_half_width
456 : (*path_bound)[i].l_upper.l;
457 (*path_bound)[i].l_upper.l =
458 std::fmax((*path_bound)[i].l_upper.l,
init_sl_state_.second[0] + 0.1);
462void LaneChangePath::UpdateStatus(
double timestamp,
464 const std::string& path_id) {
465 auto* lane_change_status =
injector_->planning_context()
466 ->mutable_planning_status()
467 ->mutable_change_lane();
468 AINFO <<
"lane change update from" << lane_change_status->DebugString()
470 lane_change_status->set_timestamp(timestamp);
471 lane_change_status->set_path_id(path_id);
472 lane_change_status->set_status(status_code);
473 AINFO << lane_change_status->DebugString();
476bool LaneChangePath::HysteresisFilter(
const double obstacle_distance,
477 const double safe_distance,
478 const double distance_buffer,
479 const bool is_obstacle_blocking) {
480 if (is_obstacle_blocking) {
481 return obstacle_distance < safe_distance + distance_buffer;
483 return obstacle_distance < safe_distance - distance_buffer;
487void LaneChangePath::SetPathInfo(PathData*
const path_data) {
488 std::vector<PathPointDecision> path_decision;
492 const auto& discrete_path = path_data->discretized_path();
493 SLBoundary ego_sl_boundary;
494 for (
size_t i = 0; i < discrete_path.size(); ++i) {
496 ADEBUG <<
"Unable to get SL-boundary of ego-vehicle.";
499 double lane_left_width = 0.0;
500 double lane_right_width = 0.0;
502 (ego_sl_boundary.start_s() + ego_sl_boundary.end_s()) / 2.0;
504 middle_s, &lane_left_width, &lane_right_width)) {
506 double back_to_inlane_extra_buffer = 0.2;
509 if (ego_sl_boundary.start_l() > lane_left_width ||
510 ego_sl_boundary.end_l() < -lane_right_width) {
513 }
else if (ego_sl_boundary.start_l() >
514 -lane_right_width + back_to_inlane_extra_buffer &&
515 ego_sl_boundary.end_l() <
516 lane_left_width - back_to_inlane_extra_buffer) {
521 std::get<1>((path_decision)[i]) =
525 AERROR <<
"reference line not ready when setting path point guide";
529 path_data->SetPathPointDecisionGuide(std::move(path_decision));
532bool LaneChangePath::CheckLastFrameSucceed(
535 for (
const auto& reference_line_info : last_frame->reference_line_info()) {
536 if (!reference_line_info.IsChangeLanePath()) {
539 const auto history_trajectory_type =
540 reference_line_info.trajectory_type();
A general class to denote the return status of an API call.
static Status OK()
generate a success status.
@Brief This is a helper class that can load vehicle configurations.
static const VehicleConfig & GetConfig()
Get the current vehicle configuration.
Rectangular (undirected) bounding box in 2-D.
The class of polygon in 2-D.
Implements a class of 2-dimensional vectors.
a singleton clock that can be used to get the current timestamp.
static double NowInSeconds()
gets the current time in second.
const std::string & Id() const
Frame holds all data for one planning cycle.
const std::list< ReferenceLineInfo > & reference_line_info() const
bool Init(const std::string &config_dir, const std::string &name, const std::shared_ptr< DependencyInjector > &injector) override
static void InitPathPointDecision(const PathData &path_data, const PathData::PathPointType type, std::vector< PathPointDecision > *const path_point_decision)
Init path_point_decision as type
static void TrimTailingOutLanePoints(PathData *const path_data)
Trim the points at tail of path which away from lane
static bool IsValidRegularPath(const ReferenceLineInfo &reference_line_info, const PathData &path_data)
Check if the generated path is valid
static bool GetBoundaryFromSelfLane(const ReferenceLineInfo &reference_line_info, const SLState &init_sl_state, PathBoundary *const path_bound)
generate path bound by self lane and adc position
static bool ExtendBoundaryByADC(const ReferenceLineInfo &reference_line_info, const SLState &init_sl_state, const double extend_buffer, PathBoundary *const path_bound)
extend boundary to include adc
static bool GetBoundaryFromStaticObstacles(const ReferenceLineInfo &reference_line_info, std::vector< SLPolygon > *const sl_polygons, const SLState &init_sl_state, PathBoundary *const path_boundaries, std::string *const blocking_obstacle_id, double *const narrowest_width)
Refine the boundary based on static obstacles.
static void GetSLPolygons(const ReferenceLineInfo &reference_line_info, std::vector< SLPolygon > *polygons, const SLState &init_sl_state)
static bool InitPathBoundary(const ReferenceLineInfo &reference_line_info, PathBoundary *const path_bound, SLState init_sl_state)
Starting from ADC's current position, increment until the path length, and set lateral bounds to be i...
void RecordDebugInfo(const PathBound &path_boundaries, const std::string &debug_name, ReferenceLineInfo *const reference_line_info)
add path_boundary debug info for PnC monitor
bool GetSLBoundary(const PathData &path_data, int point_index, const ReferenceLineInfo *reference_line_info, SLBoundary *const sl_boundary)
get sl boundary of the point on PathData
std::vector< SLPolygon > obs_sl_polygons_
void GetStartPointSLState()
calculate init sl state by planning start point, result will store in init_sl_state_
static std::vector< common::PathPoint > ConvertPathPointRefFromFrontAxeToRearAxe(const PathData &path_data)
static void CalculateAccBound(const PathBoundary &path_boundary, const ReferenceLine &reference_line, std::vector< std::pair< double, double > > *ddl_bounds)
calculate ddl bound by referenceline kappa and adc lat accleration
static FrenetFramePath ToPiecewiseJerkPath(const std::vector< double > &x, const std::vector< double > &dx, const std::vector< double > &ddx, const double delta_s, const double start_s)
Data format tramform from raw data to FrenetFramePath
static double EstimateJerkBoundary(const double vehicle_speed)
Calculation of jerk boundary based on vehicle kinematics model.
static bool OptimizePath(const SLState &init_state, const std::array< double, 3 > &end_state, std::vector< double > l_ref, std::vector< double > l_ref_weight, const PathBoundary &path_boundary, const std::vector< std::pair< double, double > > &ddl_bounds, double dddl_bound, const PiecewiseJerkPathConfig &config, std::vector< double > *x, std::vector< double > *dx, std::vector< double > *ddx)
Piecewise jerk path optimizer.
ReferenceLineInfo holds all data for one reference line.
bool path_reusable() const
std::vector< SLPolygon > * mutable_obs_sl_polygons()
const hdmap::RouteSegments & Lanes() const
const ReferenceLine & reference_line() const
double SDistanceToDestination() const
bool IsChangeLanePath() const
Check if the current reference line is a change lane reference line, i.e., ADC's current position is ...
PathData * mutable_path_data()
bool XYToSL(const common::math::Vec2d &xy_point, common::SLPoint *const sl_point, double warm_start_s=-1.0) const
Transvert Cartesian coordinates to Frenet.
bool GetLaneWidth(const double s, double *const lane_left_width, double *const lane_right_width) const
std::shared_ptr< DependencyInjector > injector_
ReferenceLineInfo * reference_line_info_
virtual bool Init(const std::string &config_dir, const std::string &name, const std::shared_ptr< DependencyInjector > &injector)
Planning module main class.
double NormalizeAngle(const double angle)
Normalize angle to [-PI, PI).
constexpr double kJunctionClearanceDist
constexpr double kIntersectionClearanceDist
std::vector< PathBoundPoint > PathBound
optional VehicleParam vehicle_param
optional double change_lane_success_freeze_time
optional PiecewiseJerkPathConfig path_optimizer_config
optional double extend_adc_buffer
optional double change_lane_fail_freeze_time
optional double lane_change_prepare_length