Apollo 11.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 PrintCurves print_path_collision;
218 if (trajectory.trajectory_point().empty()) {
219 bool box_check_collision = false;
220
221 // For those with no predicted trajectories, just map the obstacle's
222 // current position to ST-graph and always assume it's static.
223 if (!obstacle.IsStatic()) {
224 AWARN << "Non-static obstacle[" << obstacle.Id()
225 << "] has NO prediction trajectory."
226 << obstacle.Perception().ShortDebugString();
227 }
228
229 const Box2d& obs_box = obstacle.PerceptionBoundingBox();
230
231 for (const auto& curr_point_on_path : path_points) {
232 if (curr_point_on_path.s() > planning_max_distance_) {
233 break;
234 }
235 if (CheckOverlap(curr_point_on_path, obs_box, l_buffer)) {
236 box_check_collision = true;
237 break;
238 }
239 }
240
241 if (box_check_collision) {
242 // const double backward_distance =
243 // -vehicle_param_.front_edge_to_center();
244 const double backward_distance = 0;
245 const double forward_distance = obs_box.length();
246
247 for (const auto& curr_point_on_path : path_points) {
248 if (curr_point_on_path.s() > planning_max_distance_) {
249 break;
250 }
251 const Polygon2d& obs_polygon = obstacle.PerceptionPolygon();
252 Polygon2d ego_collision_polygon;
253 if (CheckOverlap(curr_point_on_path, obs_polygon, l_buffer, &ego_collision_polygon)) {
254 // If there is overlapping, then plot it on ST-graph.
255 double low_s =
256 std::fmax(0.0, curr_point_on_path.s() + backward_distance);
257 double high_s = std::fmin(planning_max_distance_,
258 curr_point_on_path.s() + forward_distance);
259 AINFO << "check colllision for obstacle[" << obstacle.Id()
260 << "], at: " << curr_point_on_path.DebugString();
261 for (const auto& point : ego_collision_polygon.points()) {
262 print_path_collision.AddPoint(obstacle.Id() + "_collision_path_point",
263 point.x(), point.y());
264 }
265 print_path_collision.AddPoint(obstacle.Id() + "_collision_path_point", ego_collision_polygon.points().front().x(), ego_collision_polygon.points().front().y());
266 // It is an unrotated rectangle appearing on the ST-graph.
267 // TODO(jiacheng): reconsider the backward_distance, it might be
268 // unnecessary, but forward_distance is indeed meaningful though.
269 lower_points->emplace_back(low_s - speed_bounds_config_.point_extension(), 0.0);
270 lower_points->emplace_back(low_s - speed_bounds_config_.point_extension(), planning_max_time_);
271 upper_points->emplace_back(high_s + speed_bounds_config_.point_extension(), 0.0);
272 upper_points->emplace_back(high_s + speed_bounds_config_.point_extension(), planning_max_time_);
273 break;
274 }
275 }
276 print_path_collision.PrintToLog();
277 }
278 } else {
279 // For those with predicted trajectories (moving obstacles):
280 // 1. Subsample to reduce computation time.
281 const int default_num_point = 50;
282 DiscretizedPath discretized_path;
283 if (path_points.size() > 2 * default_num_point) {
284 const auto ratio = path_points.size() / default_num_point;
285 std::vector<PathPoint> sampled_path_points;
286 for (size_t i = 0; i < path_points.size(); ++i) {
287 if (i % ratio == 0) {
288 sampled_path_points.push_back(path_points[i]);
289 }
290 }
291 discretized_path = DiscretizedPath(std::move(sampled_path_points));
292 } else {
293 discretized_path = DiscretizedPath(path_points);
294 }
295
296 // 2. Go through every point of the predicted obstacle trajectory.
297 double trajectory_time_interval =
298 obstacle.Trajectory().trajectory_point()[1].relative_time();
299 int trajectory_step =
300 std::min(FLAGS_trajectory_check_collision_time_step,
301 std::max(vehicle_param_.width() / obstacle.speed() /
302 trajectory_time_interval,
303 1.0));
304 bool trajectory_point_collision_status = false;
305 int previous_index = 0;
306
307 for (int i = 0; i < trajectory.trajectory_point_size();
308 i = std::min(i + trajectory_step,
309 trajectory.trajectory_point_size() - 1)) {
310 const auto& trajectory_point = trajectory.trajectory_point(i);
311 Polygon2d obstacle_shape =
312 obstacle.GetObstacleTrajectoryPolygon(trajectory_point);
313
314 double trajectory_point_time = trajectory_point.relative_time();
315 static constexpr double kNegtiveTimeThreshold = -1.0;
316 if (trajectory_point_time < kNegtiveTimeThreshold) {
317 continue;
318 }
319 bool collision = CheckOverlapWithTrajectoryPoint(
320 discretized_path, obstacle_shape, upper_points, lower_points,
321 l_buffer, default_num_point, obstacle_length, obstacle_width,
322 trajectory_point_time);
323 if ((trajectory_point_collision_status ^ collision) && i != 0) {
324 // Start retracing track points forward
325 int index = i - 1;
326 while ((trajectory_point_collision_status ^ collision) &&
327 index > previous_index) {
328 const auto& point = trajectory.trajectory_point(index);
329 trajectory_point_time = point.relative_time();
330 obstacle_shape = obstacle.GetObstacleTrajectoryPolygon(point);
331 collision = CheckOverlapWithTrajectoryPoint(
332 discretized_path, obstacle_shape, upper_points, lower_points,
333 l_buffer, default_num_point, obstacle_length, obstacle_width,
334 trajectory_point_time);
335 index--;
336 }
337 trajectory_point_collision_status = !trajectory_point_collision_status;
338 }
339 if (i == trajectory.trajectory_point_size() - 1) break;
340 previous_index = i;
341 }
342 }
343
344 // Sanity checks and return.
345 std::sort(lower_points->begin(), lower_points->end(),
346 [](const STPoint& a, const STPoint& b) { return a.t() < b.t(); });
347 std::sort(upper_points->begin(), upper_points->end(),
348 [](const STPoint& a, const STPoint& b) { return a.t() < b.t(); });
349 DCHECK_EQ(lower_points->size(), upper_points->size());
350 return (lower_points->size() > 1 && upper_points->size() > 1);
351}
352
353bool STBoundaryMapper::CheckOverlapWithTrajectoryPoint(
354 const DiscretizedPath& discretized_path, const Polygon2d& obstacle_shape,
355 std::vector<STPoint>* upper_points, std::vector<STPoint>* lower_points,
356 const double l_buffer, int default_num_point, const double obstacle_length,
357 const double obstacle_width, const double trajectory_point_time) const {
358 const double step_length = vehicle_param_.front_edge_to_center();
359 auto path_len = std::min(speed_bounds_config_.max_trajectory_len(),
360 discretized_path.Length());
361 // Go through every point of the ADC's path.
362 for (double path_s = 0.0; path_s < path_len; path_s += step_length) {
363 const auto curr_adc_path_point =
364 discretized_path.Evaluate(path_s + discretized_path.front().s());
365 if (CheckOverlap(curr_adc_path_point, obstacle_shape, l_buffer)) {
366 // Found overlap, start searching with higher resolution
367 // const double backward_distance = -step_length;
368 const double backward_distance = 0.0;
369 const double forward_distance = vehicle_param_.length() +
370 vehicle_param_.width() + obstacle_length +
371 obstacle_width;
372 const double default_min_step = 0.1; // in meters
373 const double fine_tuning_step_length = std::fmin(
374 default_min_step, discretized_path.Length() / default_num_point);
375
376 bool find_low = false;
377 bool find_high = false;
378 double low_s = std::fmax(0.0, path_s + backward_distance);
379 double high_s =
380 std::fmin(discretized_path.Length(), path_s + forward_distance);
381
382 // Keep shrinking by the resolution bidirectionally until finally
383 // locating the tight upper and lower bounds.
384 while (low_s < high_s) {
385 if (find_low && find_high) {
386 break;
387 }
388 if (!find_low) {
389 const auto& point_low =
390 discretized_path.Evaluate(low_s + discretized_path.front().s());
391 if (!CheckOverlap(point_low, obstacle_shape, l_buffer)) {
392 low_s += fine_tuning_step_length;
393 } else {
394 find_low = true;
395 }
396 }
397 if (!find_high) {
398 const auto& point_high =
399 discretized_path.Evaluate(high_s + discretized_path.front().s());
400 if (!CheckOverlap(point_high, obstacle_shape, l_buffer)) {
401 high_s -= fine_tuning_step_length;
402 } else {
403 find_high = true;
404 }
405 }
406 }
407 if (find_high && find_low) {
408 lower_points->emplace_back(
409 low_s - speed_bounds_config_.point_extension(),
410 trajectory_point_time);
411 upper_points->emplace_back(
412 high_s + speed_bounds_config_.point_extension(),
413 trajectory_point_time);
414 }
415 return true;
416 }
417 }
418 return false;
419}
420
421void STBoundaryMapper::ComputeSTBoundaryWithDecision(
422 Obstacle* obstacle, const ObjectDecisionType& decision) const {
423 DCHECK(decision.has_follow() || decision.has_yield() ||
424 decision.has_overtake())
425 << "decision is " << decision.DebugString()
426 << ", but it must be follow or yield or overtake.";
427
428 std::vector<STPoint> lower_points;
429 std::vector<STPoint> upper_points;
430
431 if (FLAGS_use_st_drivable_boundary &&
432 obstacle->is_path_st_boundary_initialized()) {
433 const auto& path_st_boundary = obstacle->path_st_boundary();
434 lower_points = path_st_boundary.lower_points();
435 upper_points = path_st_boundary.upper_points();
436 } else {
437 if (!GetOverlapBoundaryPoints(path_data_.discretized_path(), *obstacle,
438 &upper_points, &lower_points)) {
439 return;
440 }
441 }
442
443 auto boundary = STBoundary::CreateInstance(lower_points, upper_points);
444
445 // get characteristic_length and boundary_type.
447 double characteristic_length = 0.0;
448 if (decision.has_follow()) {
449 characteristic_length = std::fabs(decision.follow().distance_s());
450 AINFO << "characteristic_length: " << characteristic_length;
451 boundary = STBoundary::CreateInstance(lower_points, upper_points)
452 .ExpandByS(characteristic_length);
454 } else if (decision.has_yield()) {
455 characteristic_length = std::fabs(decision.yield().distance_s());
456 boundary = STBoundary::CreateInstance(lower_points, upper_points)
457 .ExpandByS(characteristic_length);
459 } else if (decision.has_overtake()) {
460 characteristic_length = std::fabs(decision.overtake().distance_s());
462 } else {
463 DCHECK(false) << "Obj decision should be either yield or overtake: "
464 << decision.DebugString();
465 }
466 boundary.SetBoundaryType(b_type);
467 boundary.set_id(obstacle->Id());
468 boundary.SetCharacteristicLength(characteristic_length);
469 obstacle->set_path_st_boundary(boundary);
470}
471
472bool STBoundaryMapper::CheckOverlap(const PathPoint& path_point,
473 const Box2d& obs_box,
474 const double l_buffer) const {
475 // Convert reference point from center of rear axis to center of ADC.
476 Vec2d ego_center_map_frame((vehicle_param_.front_edge_to_center() -
477 vehicle_param_.back_edge_to_center()) *
478 0.5,
479 (vehicle_param_.left_edge_to_center() -
480 vehicle_param_.right_edge_to_center()) *
481 0.5);
482 ego_center_map_frame.SelfRotate(path_point.theta());
483 ego_center_map_frame.set_x(ego_center_map_frame.x() + path_point.x());
484 ego_center_map_frame.set_y(ego_center_map_frame.y() + path_point.y());
485
486 // Compute the ADC bounding box.
487 Box2d adc_box(ego_center_map_frame, path_point.theta(),
488 vehicle_param_.length(), vehicle_param_.width() + l_buffer * 2);
489
490 // Check whether ADC bounding box overlaps with obstacle bounding box.
491 return obs_box.HasOverlap(adc_box);
492}
493
494bool STBoundaryMapper::CheckOverlap(const PathPoint& path_point,
495 const Polygon2d& obs_polygon,
496 const double l_buffer,
497 Polygon2d* collision_ego_polygon) const {
498 // Convert reference point from center of rear axis to center of ADC.
499 Vec2d ego_center_map_frame((vehicle_param_.front_edge_to_center() -
500 vehicle_param_.back_edge_to_center()) *
501 0.5,
502 (vehicle_param_.left_edge_to_center() -
503 vehicle_param_.right_edge_to_center()) *
504 0.5);
505 ego_center_map_frame.SelfRotate(path_point.theta());
506 ego_center_map_frame.set_x(ego_center_map_frame.x() + path_point.x());
507 ego_center_map_frame.set_y(ego_center_map_frame.y() + path_point.y());
508
509 // Compute the ADC bounding box.
510 Box2d adc_box(ego_center_map_frame, path_point.theta(),
511 vehicle_param_.length(), vehicle_param_.width() + l_buffer * 2);
512
513
514 // Check whether ADC polygon overlaps with obstacle polygon.
515 Polygon2d adc_polygon(adc_box);
516 if (obs_polygon.HasOverlap(adc_polygon)) {
517 if (collision_ego_polygon != nullptr) {
518 *collision_ego_polygon = adc_polygon;
519 }
520 return true;
521 } else {
522 return false;
523 }
524}
525
526} // namespace planning
527} // 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.