Apollo 10.0
自动驾驶开放平台
st_boundary_mapper.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2017 The Apollo Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
16
22
23#include <algorithm>
24#include <limits>
25#include <memory>
26#include <utility>
27
28#include "modules/common_msgs/basic_msgs/pnc_point.pb.h"
29#include "modules/common_msgs/planning_msgs/decision.pb.h"
30
31#include "cyber/common/log.h"
41
42namespace apollo {
43namespace planning {
44
51
53 const SpeedBoundsDeciderConfig& config, const ReferenceLine& reference_line,
54 const PathData& path_data, const double planning_distance,
55 const double planning_time,
56 const std::shared_ptr<DependencyInjector>& injector)
57 : speed_bounds_config_(config),
58 reference_line_(reference_line),
59 path_data_(path_data),
60 vehicle_param_(common::VehicleConfigHelper::GetConfig().vehicle_param()),
61 planning_max_distance_(planning_distance),
62 planning_max_time_(planning_time),
63 injector_(injector) {}
64
66 // Sanity checks.
67 CHECK_GT(planning_max_time_, 0.0);
68 if (path_data_.discretized_path().size() < 2) {
69 AERROR << "Fail to get params because of too few path points. path points "
70 "size: "
71 << path_data_.discretized_path().size() << ".";
72 return Status(ErrorCode::PLANNING_ERROR,
73 "Fail to get params because of too few path points");
74 }
75
76 // Go through every obstacle.
77 Obstacle* stop_obstacle = nullptr;
78 ObjectDecisionType stop_decision;
79 double min_stop_s = std::numeric_limits<double>::max();
80 for (const auto* ptr_obstacle_item : path_decision->obstacles().Items()) {
81 Obstacle* ptr_obstacle = path_decision->Find(ptr_obstacle_item->Id());
82 ACHECK(ptr_obstacle != nullptr);
83
84 // If no longitudinal decision has been made, then plot it onto ST-graph.
85 if (!ptr_obstacle->HasLongitudinalDecision()) {
86 ComputeSTBoundary(ptr_obstacle);
87 continue;
88 }
89
90 // If there is a longitudinal decision, then fine-tune boundary.
91 const auto& decision = ptr_obstacle->LongitudinalDecision();
92 if (decision.has_stop()) {
93 // 1. Store the closest stop fence info.
94 // TODO(all): store ref. s value in stop decision; refine the code then.
95 common::SLPoint stop_sl_point;
96 reference_line_.XYToSL(decision.stop().stop_point(), &stop_sl_point);
97 const double stop_s = stop_sl_point.s();
98
99 if (stop_s < min_stop_s) {
100 stop_obstacle = ptr_obstacle;
101 min_stop_s = stop_s;
102 stop_decision = decision;
103 }
104 } else if (decision.has_follow() || decision.has_overtake() ||
105 decision.has_yield()) {
106 // 2. Depending on the longitudinal overtake/yield decision,
107 // fine-tune the upper/lower st-boundary of related obstacles.
108 ComputeSTBoundaryWithDecision(ptr_obstacle, decision);
109 } else if (!decision.has_ignore()) {
110 // 3. Ignore those unrelated obstacles.
111 AWARN << "No mapping for decision: " << decision.DebugString();
112 }
113 }
114 if (stop_obstacle) {
115 bool success = MapStopDecision(stop_obstacle, stop_decision);
116 if (!success) {
117 const std::string msg = "Fail to MapStopDecision.";
118 AERROR << msg;
119 return Status(ErrorCode::PLANNING_ERROR, msg);
120 }
121 }
122
123 return Status::OK();
124}
125
126bool STBoundaryMapper::MapStopDecision(
127 Obstacle* stop_obstacle, const ObjectDecisionType& stop_decision) const {
128 DCHECK(stop_decision.has_stop()) << "Must have stop decision";
129 common::SLPoint stop_sl_point;
130 reference_line_.XYToSL(stop_decision.stop().stop_point(), &stop_sl_point);
131
132 double st_stop_s = 0.0;
133 const double stop_ref_s =
134 stop_sl_point.s() - vehicle_param_.front_edge_to_center();
135
136 if (stop_ref_s > path_data_.frenet_frame_path().back().s()) {
137 st_stop_s = path_data_.discretized_path().back().s() +
138 (stop_ref_s - path_data_.frenet_frame_path().back().s());
139 } else {
140 PathPoint stop_point;
141 if (!path_data_.GetPathPointWithRefS(stop_ref_s, &stop_point)) {
142 return false;
143 }
144 st_stop_s = stop_point.s();
145 }
146
147 const double s_min = std::fmax(0.0, st_stop_s);
148 const double s_max = std::fmax(
149 s_min, std::fmax(planning_max_distance_, reference_line_.Length()));
150
151 std::vector<std::pair<STPoint, STPoint>> point_pairs;
152 point_pairs.emplace_back(STPoint(s_min, 0.0), STPoint(s_max, 0.0));
153 point_pairs.emplace_back(
154 STPoint(s_min, planning_max_time_),
155 STPoint(s_max + speed_bounds_config_.boundary_buffer(),
156 planning_max_time_));
157 auto boundary = STBoundary(point_pairs);
158 boundary.SetBoundaryType(STBoundary::BoundaryType::STOP);
159 boundary.SetCharacteristicLength(speed_bounds_config_.boundary_buffer());
160 boundary.set_id(stop_obstacle->Id());
161 stop_obstacle->set_path_st_boundary(boundary);
162 return true;
163}
164
165void STBoundaryMapper::ComputeSTBoundary(Obstacle* obstacle) const {
166 if (FLAGS_use_st_drivable_boundary) {
167 return;
168 }
169 std::vector<STPoint> lower_points;
170 std::vector<STPoint> upper_points;
171
172 if (!GetOverlapBoundaryPoints(path_data_.discretized_path(), *obstacle,
173 &upper_points, &lower_points)) {
174 return;
175 }
176
177 auto boundary = STBoundary::CreateInstance(lower_points, upper_points);
178 boundary.set_id(obstacle->Id());
179
180 // TODO(all): potential bug here.
181 const auto& prev_st_boundary = obstacle->path_st_boundary();
182 const auto& ref_line_st_boundary = obstacle->reference_line_st_boundary();
183 if (!prev_st_boundary.IsEmpty()) {
184 boundary.SetBoundaryType(prev_st_boundary.boundary_type());
185 } else if (!ref_line_st_boundary.IsEmpty()) {
186 boundary.SetBoundaryType(ref_line_st_boundary.boundary_type());
187 }
188
189 obstacle->set_path_st_boundary(boundary);
190}
191
192bool STBoundaryMapper::GetOverlapBoundaryPoints(
193 const std::vector<PathPoint>& path_points, const Obstacle& obstacle,
194 std::vector<STPoint>* upper_points,
195 std::vector<STPoint>* lower_points) const {
196 // Sanity checks.
197 DCHECK(upper_points->empty());
198 DCHECK(lower_points->empty());
199 if (path_points.empty()) {
200 AERROR << "No points in path_data_.discretized_path().";
201 return false;
202 }
203
204 const auto* planning_status = injector_->planning_context()
205 ->mutable_planning_status()
206 ->mutable_change_lane();
207
208 double l_buffer =
209 planning_status->status() == ChangeLaneStatus::IN_CHANGE_LANE
210 ? speed_bounds_config_.lane_change_obstacle_nudge_l_buffer()
211 : FLAGS_nonstatic_obstacle_nudge_l_buffer;
212
213 // Draw the given obstacle on the ST-graph.
214 const auto& trajectory = obstacle.Trajectory();
215 const double obstacle_length = obstacle.Perception().length();
216 const double obstacle_width = obstacle.Perception().width();
217 if (trajectory.trajectory_point().empty()) {
218 bool box_check_collision = false;
219
220 // For those with no predicted trajectories, just map the obstacle's
221 // current position to ST-graph and always assume it's static.
222 if (!obstacle.IsStatic()) {
223 AWARN << "Non-static obstacle[" << obstacle.Id()
224 << "] has NO prediction trajectory."
225 << obstacle.Perception().ShortDebugString();
226 }
227
228 const Box2d& obs_box = obstacle.PerceptionBoundingBox();
229
230 for (const auto& curr_point_on_path : path_points) {
231 if (curr_point_on_path.s() > planning_max_distance_) {
232 break;
233 }
234 if (CheckOverlap(curr_point_on_path, obs_box, l_buffer)) {
235 box_check_collision = true;
236 break;
237 }
238 }
239
240 if (box_check_collision) {
241 // const double backward_distance =
242 // -vehicle_param_.front_edge_to_center();
243 const double backward_distance = 0;
244 const double forward_distance = obs_box.length();
245
246 for (const auto& curr_point_on_path : path_points) {
247 if (curr_point_on_path.s() > planning_max_distance_) {
248 break;
249 }
250 const Polygon2d& obs_polygon = obstacle.PerceptionPolygon();
251 if (CheckOverlap(curr_point_on_path, obs_polygon, l_buffer)) {
252 // If there is overlapping, then plot it on ST-graph.
253 double low_s =
254 std::fmax(0.0, curr_point_on_path.s() + backward_distance);
255 double high_s = std::fmin(planning_max_distance_,
256 curr_point_on_path.s() + forward_distance);
257 AINFO << "check colllision for obstacle[" << obstacle.Id()
258 << "], at: " << curr_point_on_path.DebugString();
259 // It is an unrotated rectangle appearing on the ST-graph.
260 // TODO(jiacheng): reconsider the backward_distance, it might be
261 // unnecessary, but forward_distance is indeed meaningful though.
262 lower_points->emplace_back(
263 low_s - speed_bounds_config_.point_extension(), 0.0);
264 lower_points->emplace_back(
265 low_s - speed_bounds_config_.point_extension(),
266 planning_max_time_);
267 upper_points->emplace_back(
268 high_s + speed_bounds_config_.point_extension(), 0.0);
269 upper_points->emplace_back(
270 high_s + speed_bounds_config_.point_extension(),
271 planning_max_time_);
272 break;
273 }
274 }
275 }
276 } else {
277 // For those with predicted trajectories (moving obstacles):
278 // 1. Subsample to reduce computation time.
279 const int default_num_point = 50;
280 DiscretizedPath discretized_path;
281 if (path_points.size() > 2 * default_num_point) {
282 const auto ratio = path_points.size() / default_num_point;
283 std::vector<PathPoint> sampled_path_points;
284 for (size_t i = 0; i < path_points.size(); ++i) {
285 if (i % ratio == 0) {
286 sampled_path_points.push_back(path_points[i]);
287 }
288 }
289 discretized_path = DiscretizedPath(std::move(sampled_path_points));
290 } else {
291 discretized_path = DiscretizedPath(path_points);
292 }
293
294 // 2. Go through every point of the predicted obstacle trajectory.
295 double trajectory_time_interval =
296 obstacle.Trajectory().trajectory_point()[1].relative_time();
297 int trajectory_step =
298 std::min(FLAGS_trajectory_check_collision_time_step,
299 std::max(vehicle_param_.width() / obstacle.speed() /
300 trajectory_time_interval,
301 1.0));
302 bool trajectory_point_collision_status = false;
303 int previous_index = 0;
304
305 for (int i = 0; i < trajectory.trajectory_point_size();
306 i = std::min(i + trajectory_step,
307 trajectory.trajectory_point_size() - 1)) {
308 const auto& trajectory_point = trajectory.trajectory_point(i);
309 Polygon2d obstacle_shape =
310 obstacle.GetObstacleTrajectoryPolygon(trajectory_point);
311
312 double trajectory_point_time = trajectory_point.relative_time();
313 static constexpr double kNegtiveTimeThreshold = -1.0;
314 if (trajectory_point_time < kNegtiveTimeThreshold) {
315 continue;
316 }
317 bool collision = CheckOverlapWithTrajectoryPoint(
318 discretized_path, obstacle_shape, upper_points, lower_points,
319 l_buffer, default_num_point, obstacle_length, obstacle_width,
320 trajectory_point_time);
321 if ((trajectory_point_collision_status ^ collision) && i != 0) {
322 // Start retracing track points forward
323 int index = i - 1;
324 while ((trajectory_point_collision_status ^ collision) &&
325 index > previous_index) {
326 const auto& point = trajectory.trajectory_point(index);
327 trajectory_point_time = point.relative_time();
328 obstacle_shape = obstacle.GetObstacleTrajectoryPolygon(point);
329 collision = CheckOverlapWithTrajectoryPoint(
330 discretized_path, obstacle_shape, upper_points, lower_points,
331 l_buffer, default_num_point, obstacle_length, obstacle_width,
332 trajectory_point_time);
333 index--;
334 }
335 trajectory_point_collision_status = !trajectory_point_collision_status;
336 }
337 if (i == trajectory.trajectory_point_size() - 1) break;
338 previous_index = i;
339 }
340 }
341
342 // Sanity checks and return.
343 std::sort(lower_points->begin(), lower_points->end(),
344 [](const STPoint& a, const STPoint& b) { return a.t() < b.t(); });
345 std::sort(upper_points->begin(), upper_points->end(),
346 [](const STPoint& a, const STPoint& b) { return a.t() < b.t(); });
347 DCHECK_EQ(lower_points->size(), upper_points->size());
348 return (lower_points->size() > 1 && upper_points->size() > 1);
349}
350
351bool STBoundaryMapper::CheckOverlapWithTrajectoryPoint(
352 const DiscretizedPath& discretized_path, const Polygon2d& obstacle_shape,
353 std::vector<STPoint>* upper_points, std::vector<STPoint>* lower_points,
354 const double l_buffer, int default_num_point, const double obstacle_length,
355 const double obstacle_width, const double trajectory_point_time) const {
356 const double step_length = vehicle_param_.front_edge_to_center();
357 auto path_len = std::min(speed_bounds_config_.max_trajectory_len(),
358 discretized_path.Length());
359 // Go through every point of the ADC's path.
360 for (double path_s = 0.0; path_s < path_len; path_s += step_length) {
361 const auto curr_adc_path_point =
362 discretized_path.Evaluate(path_s + discretized_path.front().s());
363 if (CheckOverlap(curr_adc_path_point, obstacle_shape, l_buffer)) {
364 // Found overlap, start searching with higher resolution
365 // const double backward_distance = -step_length;
366 const double backward_distance = 0.0;
367 const double forward_distance = vehicle_param_.length() +
368 vehicle_param_.width() + obstacle_length +
369 obstacle_width;
370 const double default_min_step = 0.1; // in meters
371 const double fine_tuning_step_length = std::fmin(
372 default_min_step, discretized_path.Length() / default_num_point);
373
374 bool find_low = false;
375 bool find_high = false;
376 double low_s = std::fmax(0.0, path_s + backward_distance);
377 double high_s =
378 std::fmin(discretized_path.Length(), path_s + forward_distance);
379
380 // Keep shrinking by the resolution bidirectionally until finally
381 // locating the tight upper and lower bounds.
382 while (low_s < high_s) {
383 if (find_low && find_high) {
384 break;
385 }
386 if (!find_low) {
387 const auto& point_low =
388 discretized_path.Evaluate(low_s + discretized_path.front().s());
389 if (!CheckOverlap(point_low, obstacle_shape, l_buffer)) {
390 low_s += fine_tuning_step_length;
391 } else {
392 find_low = true;
393 }
394 }
395 if (!find_high) {
396 const auto& point_high =
397 discretized_path.Evaluate(high_s + discretized_path.front().s());
398 if (!CheckOverlap(point_high, obstacle_shape, l_buffer)) {
399 high_s -= fine_tuning_step_length;
400 } else {
401 find_high = true;
402 }
403 }
404 }
405 if (find_high && find_low) {
406 lower_points->emplace_back(
407 low_s - speed_bounds_config_.point_extension(),
408 trajectory_point_time);
409 upper_points->emplace_back(
410 high_s + speed_bounds_config_.point_extension(),
411 trajectory_point_time);
412 }
413 return true;
414 }
415 }
416 return false;
417}
418
419void STBoundaryMapper::ComputeSTBoundaryWithDecision(
420 Obstacle* obstacle, const ObjectDecisionType& decision) const {
421 DCHECK(decision.has_follow() || decision.has_yield() ||
422 decision.has_overtake())
423 << "decision is " << decision.DebugString()
424 << ", but it must be follow or yield or overtake.";
425
426 std::vector<STPoint> lower_points;
427 std::vector<STPoint> upper_points;
428
429 if (FLAGS_use_st_drivable_boundary &&
430 obstacle->is_path_st_boundary_initialized()) {
431 const auto& path_st_boundary = obstacle->path_st_boundary();
432 lower_points = path_st_boundary.lower_points();
433 upper_points = path_st_boundary.upper_points();
434 } else {
435 if (!GetOverlapBoundaryPoints(path_data_.discretized_path(), *obstacle,
436 &upper_points, &lower_points)) {
437 return;
438 }
439 }
440
441 auto boundary = STBoundary::CreateInstance(lower_points, upper_points);
442
443 // get characteristic_length and boundary_type.
445 double characteristic_length = 0.0;
446 if (decision.has_follow()) {
447 characteristic_length = std::fabs(decision.follow().distance_s());
448 AINFO << "characteristic_length: " << characteristic_length;
449 boundary = STBoundary::CreateInstance(lower_points, upper_points)
450 .ExpandByS(characteristic_length);
452 } else if (decision.has_yield()) {
453 characteristic_length = std::fabs(decision.yield().distance_s());
454 boundary = STBoundary::CreateInstance(lower_points, upper_points)
455 .ExpandByS(characteristic_length);
457 } else if (decision.has_overtake()) {
458 characteristic_length = std::fabs(decision.overtake().distance_s());
460 } else {
461 DCHECK(false) << "Obj decision should be either yield or overtake: "
462 << decision.DebugString();
463 }
464 boundary.SetBoundaryType(b_type);
465 boundary.set_id(obstacle->Id());
466 boundary.SetCharacteristicLength(characteristic_length);
467 obstacle->set_path_st_boundary(boundary);
468}
469
470bool STBoundaryMapper::CheckOverlap(const PathPoint& path_point,
471 const Box2d& obs_box,
472 const double l_buffer) const {
473 // Convert reference point from center of rear axis to center of ADC.
474 Vec2d ego_center_map_frame((vehicle_param_.front_edge_to_center() -
475 vehicle_param_.back_edge_to_center()) *
476 0.5,
477 (vehicle_param_.left_edge_to_center() -
478 vehicle_param_.right_edge_to_center()) *
479 0.5);
480 ego_center_map_frame.SelfRotate(path_point.theta());
481 ego_center_map_frame.set_x(ego_center_map_frame.x() + path_point.x());
482 ego_center_map_frame.set_y(ego_center_map_frame.y() + path_point.y());
483
484 // Compute the ADC bounding box.
485 Box2d adc_box(ego_center_map_frame, path_point.theta(),
486 vehicle_param_.length(), vehicle_param_.width() + l_buffer * 2);
487
488 // Check whether ADC bounding box overlaps with obstacle bounding box.
489 return obs_box.HasOverlap(adc_box);
490}
491
492bool STBoundaryMapper::CheckOverlap(const PathPoint& path_point,
493 const Polygon2d& obs_polygon,
494 const double l_buffer) const {
495 // Convert reference point from center of rear axis to center of ADC.
496 Vec2d ego_center_map_frame((vehicle_param_.front_edge_to_center() -
497 vehicle_param_.back_edge_to_center()) *
498 0.5,
499 (vehicle_param_.left_edge_to_center() -
500 vehicle_param_.right_edge_to_center()) *
501 0.5);
502 ego_center_map_frame.SelfRotate(path_point.theta());
503 ego_center_map_frame.set_x(ego_center_map_frame.x() + path_point.x());
504 ego_center_map_frame.set_y(ego_center_map_frame.y() + path_point.y());
505
506 // Compute the ADC bounding box.
507 Box2d adc_box(ego_center_map_frame, path_point.theta(),
508 vehicle_param_.length(), vehicle_param_.width() + l_buffer * 2);
509
510 // Check whether ADC polygon overlaps with obstacle polygon.
511 Polygon2d adc_polygon(adc_box);
512 return obs_polygon.HasOverlap(adc_polygon);
513}
514
515} // namespace planning
516} // namespace apollo
A general class to denote the return status of an API call.
Definition status.h:43
static Status OK()
generate a success status.
Definition status.h:60
@Brief This is a helper class that can load vehicle configurations.
Rectangular (undirected) bounding box in 2-D.
Definition box2d.h:52
The class of polygon in 2-D.
Definition polygon2d.h:42
Implements a class of 2-dimensional vectors.
Definition vec2d.h:42
const std::vector< const T * > & Items() const
List all the items in the container.
This is the class that associates an Obstacle with its path properties.
Definition obstacle.h:62
bool HasLongitudinalDecision() const
Definition obstacle.cc:636
const ObjectDecisionType & LongitudinalDecision() const
return the merged longitudinal decision Longitudinal decision is one of {Stop, Yield,...
Definition obstacle.cc:577
const std::string & Id() const
Definition obstacle.h:75
void set_path_st_boundary(const STBoundary &boundary)
Definition obstacle.cc:698
const FrenetFramePath & frenet_frame_path() const
Definition path_data.cc:94
bool GetPathPointWithRefS(const double ref_s, common::PathPoint *const path_point) const
Definition path_data.cc:116
const DiscretizedPath & discretized_path() const
Definition path_data.cc:90
PathDecision represents all obstacle decisions on one path.
const Obstacle * Find(const std::string &object_id) const
const IndexedList< std::string, Obstacle > & obstacles() const
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.
common::Status ComputeSTBoundary(PathDecision *path_decision) const
STBoundaryMapper(const SpeedBoundsDeciderConfig &config, const ReferenceLine &reference_line, const PathData &path_data, const double planning_distance, const double planning_time, const std::shared_ptr< DependencyInjector > &injector)
static STBoundary CreateInstance(const std::vector< STPoint > &lower_points, const std::vector< STPoint > &upper_points)
Wrapper of the constructor (old).
STBoundary ExpandByS(const double s) const
Planning module main class.
Define the LineSegment2d class.
#define ACHECK(cond)
Definition log.h:80
#define AERROR
Definition log.h:44
#define AINFO
Definition log.h:42
#define AWARN
Definition log.h:43
Some util functions.
class register implement
Definition arena_queue.h:37
Some string util functions.
Defines the Vec2d class.