这一场景的难点在于多静态障碍物的处理。
首先看看规划失败的场景,大佬已经给出示例,不再自己录制了,参考:自动驾驶player
由于设置的起点和终点在一条扯到线上,所以只生成了一条ReferenceLine。(若不在一条车道可以完成规划)
考虑两种方案:1. 生成多条参考线然后走lane change 2. 保持一条参考线走lane borrow
首先看看参考线生成:
…\apollo-r7.0.0\modules\planning\reference_line\reference_line_provider.cc
bool ReferenceLineProvider::GetReferenceLines(
std::list<ReferenceLine> *reference_lines,
std::list<hdmap::RouteSegments> *segments) {
CHECK_NOTNULL(reference_lines);
CHECK_NOTNULL(segments);
if (FLAGS_use_navigation_mode) {
double start_time = Clock::NowInSeconds();
bool result = GetReferenceLinesFromRelativeMap(reference_lines, segments);
if (!result) {
AERROR << "Failed to get reference line from relative map";
}
double end_time = Clock::NowInSeconds();
last_calculation_time_ = end_time - start_time;
return result;
}
if (FLAGS_enable_reference_line_provider_thread) {// 1. 如果有单独的线程,则直接赋值
std::lock_guard<std::mutex> lock(reference_lines_mutex_);
if (!reference_lines_.empty()) {
reference_lines->assign(reference_lines_.begin(), reference_lines_.end());
segments->assign(route_segments_.begin(), route_segments_.end());
return true;
}
} else {// 2. 否则,创建并且更新参考线
double start_time = Clock::NowInSeconds();
if (CreateReferenceLine(reference_lines, segments)) {
UpdateReferenceLine(*reference_lines, *segments);
double end_time = Clock::NowInSeconds();
last_calculation_time_ = end_time - start_time;
return true;
}
}
AWARN << "Reference line is NOT ready.";
if (reference_line_history_.empty()) {
AERROR << "Failed to use reference line latest history";
return false;
}
// 3. 如果失败,则采用上一次的规划轨迹
reference_lines->assign(reference_line_history_.back().begin(),
reference_line_history_.back().end());
segments->assign(route_segments_history_.back().begin(),
route_segments_history_.back().end());
AWARN << "Use reference line from history!";
return true;
}
关键在于这个CreateReferenceLine()
bool ReferenceLineProvider::CreateReferenceLine(
std::list<ReferenceLine> *reference_lines,
std::list<hdmap::RouteSegments> *segments) {
CHECK_NOTNULL(reference_lines);
CHECK_NOTNULL(segments);
// 1. 获取车辆状态
common::VehicleState vehicle_state;
{
std::lock_guard<std::mutex> lock(vehicle_state_mutex_);
vehicle_state = vehicle_state_;
}
// 2. 获取routing
routing::RoutingResponse routing;
{
std::lock_guard<std::mutex> lock(routing_mutex_);
routing = routing_;
}
bool is_new_routing = false;
{
// Update routing in pnc_map// 2.1 如果是新routing,那么更新routing
std::lock_guard<std::mutex> lock(pnc_map_mutex_);
if (pnc_map_->IsNewRouting(routing)) {
is_new_routing = true;
if (!pnc_map_->UpdateRoutingResponse(routing)) {
AERROR << "Failed to update routing in pnc map";
return false;
}
}
}
// 3. 创建routing segment
if (!CreateRouteSegments(vehicle_state, segments)) {
AERROR << "Failed to create reference line from routing";
return false;
}
if (is_new_routing || !FLAGS_enable_reference_line_stitching) {
for (auto iter = segments->begin(); iter != segments->end();) {
reference_lines->emplace_back();
// 4.1.1 平滑routing segment
if (!SmoothRouteSegment(*iter, &reference_lines->back())) {
AERROR << "Failed to create reference line from route segments";
reference_lines->pop_back();
iter = segments->erase(iter);
} else {
common::SLPoint sl;
if (!reference_lines->back().XYToSL(vehicle_state, &sl)) {
AWARN << "Failed to project point: {" << vehicle_state.x() << ","
<< vehicle_state.y() << "} to stitched reference line";
}
// 4.1.2 收缩参考线
Shrink(sl, &reference_lines->back(), &(*iter));
++iter;
}
}
return true;
} else { // stitching reference line
// 4.2 根据routing segment扩展参考线
for (auto iter = segments->begin(); iter != segments->end();) {
reference_lines->emplace_back();
if (!ExtendReferenceLine(vehicle_state, &(*iter),
&reference_lines->back())) {
AERROR << "Failed to extend reference line";
reference_lines->pop_back();
iter = segments->erase(iter);
} else {
++iter;
}
}
}
return true;
}
这里的reference_lines是读取的route segment,ok,pnc_map的活,我就不看了。
第二种思路
通过log看出,由于障碍物并没有阻挡参考线,所以不生成借道决策。
而且在生成借道路径时,回归原车道并不需要输出反向借道的逻辑。
感觉借道的时候,车辆倾向于回归参考线上而不是继续向另一边借道,或者说是借道借一半,并没有规划出完整的施工绕行轨迹。
看看在生成左借道之后怎么生成的pathBound:
bool PathBoundsDecider::GetBoundaryFromLanesAndADC(
const ReferenceLineInfo& reference_line_info,
const LaneBorrowInfo& lane_borrow_info, double ADC_buffer,
PathBound* const path_bound, std::string* const borrow_lane_type,
bool is_fallback_lanechange) {
// Sanity checks.
CHECK_NOTNULL(path_bound);
ACHECK(!path_bound->empty());
const ReferenceLine& reference_line = reference_line_info.reference_line();
bool is_left_lane_boundary = true;
bool is_right_lane_boundary = true;
const double boundary_buffer = 0.05; // meter
// Go through every point, update the boundary based on lane info and ADC's position.
//在每一个点上,根据车道和车辆信息更新边界
double past_lane_left_width = adc_lane_width_ / 2.0;
double past_lane_right_width = adc_lane_width_ / 2.0;
int path_blocked_idx = -1;
bool borrowing_reverse_lane = false;
for (size_t i = 0; i < path_bound->size(); ++i) {
double curr_s = std::get<0>((*path_bound)[i]);
// 1. Get the current lane width at current point.
// 当前车道的宽度
double curr_lane_left_width = 0.0;
double curr_lane_right_width = 0.0;
double offset_to_lane_center = 0.0;
if (!reference_line.GetLaneWidth(curr_s, &curr_lane_left_width,
&curr_lane_right_width)) {
AWARN << "Failed to get lane width at s = " << curr_s;
curr_lane_left_width = past_lane_left_width;
curr_lane_right_width = past_lane_right_width;
} else {
// 检查车道边界是否也是道路边界
double curr_road_left_width = 0.0;
double curr_road_right_width = 0.0;
if (reference_line.GetRoadWidth(curr_s, &curr_road_left_width,
&curr_road_right_width)) {
is_left_lane_boundary =
(std::abs(curr_road_left_width - curr_lane_left_width) >
boundary_buffer);
is_right_lane_boundary =
(std::abs(curr_road_right_width - curr_lane_right_width) >
boundary_buffer);
}
reference_line.GetOffsetToMap(curr_s, &offset_to_lane_center);
curr_lane_left_width += offset_to_lane_center;
curr_lane_right_width -= offset_to_lane_center;
past_lane_left_width = curr_lane_left_width;
past_lane_right_width = curr_lane_right_width;
}
// 2. Get the neighbor lane widths at the current point.
// 获取相邻车道的宽度。
double curr_neighbor_lane_width = 0.0;
if (CheckLaneBoundaryType(reference_line_info, curr_s, lane_borrow_info)) {
hdmap::Id neighbor_lane_id;
if (lane_borrow_info == LaneBorrowInfo::LEFT_BORROW) {
// Borrowing left neighbor lane.
if (reference_line_info.GetNeighborLaneInfo(
ReferenceLineInfo::LaneType::LeftForward, curr_s,
&neighbor_lane_id, &curr_neighbor_lane_width)) {
ADEBUG << "Borrow left forward neighbor lane.";
}
else if (reference_line_info.GetNeighborLaneInfo(
ReferenceLineInfo::LaneType::LeftReverse, curr_s,
&neighbor_lane_id, &curr_neighbor_lane_width)) {
borrowing_reverse_lane = true;
ADEBUG << "Borrow left reverse neighbor lane.";
} else {
ADEBUG << "There is no left neighbor lane.";
}
} else if (lane_borrow_info == LaneBorrowInfo::RIGHT_BORROW) {
// Borrowing right neighbor lane.
if (reference_line_info.GetNeighborLaneInfo(
ReferenceLineInfo::LaneType::RightForward, curr_s,
&neighbor_lane_id, &curr_neighbor_lane_width)) {
ADEBUG << "Borrow right forward neighbor lane.";
} else if (reference_line_info.GetNeighborLaneInfo(
ReferenceLineInfo::LaneType::RightReverse, curr_s,
&neighbor_lane_id, &curr_neighbor_lane_width)) {
borrowing_reverse_lane = true;
ADEBUG << "Borrow right reverse neighbor lane.";
} else {
ADEBUG << "There is no right neighbor lane.";
}
}
}
// 3. Calculate the proper boundary based on lane-width, ADC's position,
// and ADC's velocity.
// 根据车道宽度、ADC位置和ADC速度计算适当的边界
static constexpr double kMaxLateralAccelerations = 1.5;
double offset_to_map = 0.0;
reference_line.GetOffsetToMap(curr_s, &offset_to_map);
double ADC_speed_buffer = (adc_frenet_ld_ > 0 ? 1.0 : -1.0) *
adc_frenet_ld_ * adc_frenet_ld_ /
kMaxLateralAccelerations / 2.0;
double curr_left_bound_lane =
curr_lane_left_width + (lane_borrow_info == LaneBorrowInfo::LEFT_BORROW
? curr_neighbor_lane_width
: 0.0);
double curr_right_bound_lane =
-curr_lane_right_width -
(lane_borrow_info == LaneBorrowInfo::RIGHT_BORROW
? curr_neighbor_lane_width
: 0.0);
double curr_left_bound = 0.0;
double curr_right_bound = 0.0;
if (config_.path_bounds_decider_config()
.is_extend_lane_bounds_to_include_adc() ||
is_fallback_lanechange) {
// extend path bounds to include ADC in fallback or change lane path
// bounds.
double curr_left_bound_adc =
std::fmax(adc_l_to_lane_center_,
adc_l_to_lane_center_ + ADC_speed_buffer) +
GetBufferBetweenADCCenterAndEdge() + ADC_buffer;
curr_left_bound =
std::fmax(curr_left_bound_lane, curr_left_bound_adc) - offset_to_map;
double curr_right_bound_adc =
std::fmin(adc_l_to_lane_center_,
adc_l_to_lane_center_ + ADC_speed_buffer) -
GetBufferBetweenADCCenterAndEdge() - ADC_buffer;
curr_right_bound =
std::fmin(curr_right_bound_lane, curr_right_bound_adc) -
offset_to_map;
} else {
curr_left_bound = curr_left_bound_lane - offset_to_map;
curr_right_bound = curr_right_bound_lane - offset_to_map;
}
ADEBUG << "At s = " << curr_s
<< ", left_lane_bound = " << curr_lane_left_width
<< ", right_lane_bound = " << curr_lane_right_width
<< ", offset = " << offset_to_map;
// 4. Update the boundary.
if (!UpdatePathBoundaryWithBuffer(i, curr_left_bound, curr_right_bound,
path_bound, is_left_lane_boundary,
is_right_lane_boundary)) {
path_blocked_idx = static_cast<int>(i);
}
if (path_blocked_idx != -1) {
break;
}
}
在这里可以看出,向左右借道只考虑了相邻车道的宽度,导致多障碍物场景下不能借道至预期位置,
修改curr_left_bound_lane以及curr_right_bound_lane,扩大l_min与l_max的距离。
此时通过log可以看出,在借道过程中一直给出的label是regular/self,表示沿规划的借道路线行使。此刻不会再生成新的借道路线,如果ego从障碍物中间穿过可能是由于roadwork的距离过远,没有将roadwork的结束位置考虑到规划中,此时不会生成预期路径。也就是说借道必须有恢复原车道才可以生成规划,不是先按别的路径出发再改变规划。(也可能有别的思路和我不同)
结果:自动驾驶player
我跟这位大佬的思路应该是不同的,我的车辆如果规划出进入障碍物之间就不会再向后规划了。但我的思路不用写很多代码,主要还是调参,比较方便。
如果对您有参考价值。希望不吝点赞:)。