Apollo 10.0
自动驾驶开放平台
vectornet_evaluator.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2021 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
18
19#include <limits>
20#include <omp.h>
21
22#include "Eigen/Dense"
23
24#include "cyber/common/file.h"
30
31namespace apollo {
32namespace prediction {
33
37
42
44
46 Obstacle* obstacle_ptr,
47 ObstaclesContainer* obstacles_container,
48 torch::Tensor* ptr_target_obs_pos,
49 torch::Tensor* ptr_target_obs_pos_step,
50 torch::Tensor* ptr_vector_mask,
51 torch::Tensor* ptr_obstacle_data,
52 torch::Tensor* ptr_all_obs_p_id) {
53 // Extract features of pos_history
54 std::vector<std::pair<double, double>> target_pos_history(20, {0.0, 0.0});
55 std::vector<std::pair<double, double>> all_obs_length;
56 std::vector<std::vector<std::pair<double, double>>> all_obs_pos_history;
57
58 if (!ExtractObstaclesHistory(obstacle_ptr, obstacles_container,
59 &target_pos_history, &all_obs_length,
60 &all_obs_pos_history, ptr_vector_mask)) {
61 ADEBUG << "Obstacle [" << obstacle_ptr->id()
62 << "] failed to extract obstacle history";
63 return false;
64 }
65
66 for (int j = 0; j < 20; ++j) {
67 ptr_target_obs_pos->index_put_({19 - j, 0}, target_pos_history[j].first);
68 ptr_target_obs_pos->index_put_({19 - j, 1}, target_pos_history[j].second);
69 if (j == 19 || (j > 0 && target_pos_history[j + 1].first == 0.0)) {
70 break;
71 }
72 ptr_target_obs_pos_step->index_put_(
73 {19 - j, 0},
74 target_pos_history[j].first - target_pos_history[j + 1].first);
75 ptr_target_obs_pos_step->index_put_(
76 {19 - j, 1},
77 target_pos_history[j].second - target_pos_history[j + 1].second);
78 }
79
80 int obs_num =
81 obstacles_container->curr_frame_considered_obstacle_ids().size();
82 torch::Tensor all_obstacle_pos = torch::zeros({obs_num, 20, 2});
83 torch::Tensor obs_length_data = torch::zeros({obs_num, 2});
84 auto opts = torch::TensorOptions().dtype(torch::kDouble);
85
86 for (int i = 0; i < obs_num; ++i) {
87 std::vector<double> obs_p_id{std::numeric_limits<float>::max(),
88 std::numeric_limits<float>::max()};
89 for (int j = 0; j < 20; ++j) {
90 // Process obs pid
91 if (obs_p_id[0] > all_obs_pos_history[i][j].first) {
92 obs_p_id[0] = all_obs_pos_history[i][j].first;
93 }
94 if (obs_p_id[1] > all_obs_pos_history[i][j].second) {
95 obs_p_id[1] = all_obs_pos_history[i][j].second;
96 }
97 // Process obs pos history
98 all_obstacle_pos.index_put_(
99 {i, 19 - j, 0},
100 all_obs_pos_history[i][j].first);
101 all_obstacle_pos.index_put_(
102 {i, 19 - j, 1},
103 all_obs_pos_history[i][j].second);
104 }
105
106 ptr_all_obs_p_id->index_put_({i},
107 torch::from_blob(obs_p_id.data(), {2}, opts));
108 obs_length_data.index_put_({i, 0}, all_obs_length[i].first);
109 obs_length_data.index_put_({i, 1}, all_obs_length[i].second);
110 }
111
112 // Extend obs data to specific dimension
113 torch::Tensor obs_pos_data = torch::cat(
114 {all_obstacle_pos.index(
115 {torch::indexing::Slice(),
116 torch::indexing::Slice(torch::indexing::None, -1),
117 torch::indexing::Slice()}),
118 all_obstacle_pos.index({torch::indexing::Slice(),
119 torch::indexing::Slice(1, torch::indexing::None),
120 torch::indexing::Slice()})},
121 2);
122
123 // Add obs attribute
124 torch::Tensor obs_attr_agent =
125 torch::tensor({11.0, 4.0}).unsqueeze(0).unsqueeze(0).repeat({1, 19, 1});
126 torch::Tensor obs_attr_other =
127 torch::tensor({10.0, 4.0}).unsqueeze(0).unsqueeze(0).repeat(
128 {(obs_num - 1), 19, 1});
129 torch::Tensor obs_attr = torch::cat({obs_attr_agent, obs_attr_other}, 0);
130 // ADD obs id
131 // add 500 to avoid same id as in map_info
132 torch::Tensor obs_id =
133 torch::arange(500,
134 obs_num + 500).unsqueeze(1).repeat({1, 19}).unsqueeze(2);
135 // Process obs data
136 obs_length_data = obs_length_data.unsqueeze(1).repeat({1, 19, 1});
137 torch::Tensor obs_data_with_len =
138 torch::cat({obs_pos_data, obs_length_data}, 2);
139
140 torch::Tensor obs_data_with_attr =
141 torch::cat({obs_data_with_len, obs_attr}, 2);
142
143 torch::Tensor obs_data_with_id = torch::cat({obs_data_with_attr, obs_id}, 2);
144 *ptr_obstacle_data =
145 torch::cat({torch::zeros({obs_num, (50 - 19), 9}), obs_data_with_id}, 1);
146
147 return true;
148}
149
150bool VectornetEvaluator::VectornetProcessMapData(
151 FeatureVector* map_feature,
152 PidVector* map_p_id,
153 const int obs_num,
154 torch::Tensor* ptr_map_data,
155 torch::Tensor* ptr_all_map_p_id,
156 torch::Tensor* ptr_vector_mask) {
157 int map_polyline_num = map_feature->size();
158
159 for (int i = 0; i < map_polyline_num && obs_num + i < 450; ++i) {
160 size_t one_polyline_vector_size = map_feature->at(i).size();
161 if (one_polyline_vector_size < 50) {
162 ptr_vector_mask->index_put_({obs_num + i,
163 torch::indexing::Slice(
164 one_polyline_vector_size,
165 torch::indexing::None)},
166 1);
167 }
168 }
169
170 auto opts = torch::TensorOptions().dtype(torch::kDouble);
171
172 for (int i = 0; i < map_polyline_num && i + obs_num < 450; ++i) {
173 ptr_all_map_p_id->index_put_({i}, torch::from_blob(map_p_id->at(i).data(),
174 {2},
175 opts));
176
177 int one_polyline_vector_size = map_feature->at(i).size();
178 for (int j = 0; j < one_polyline_vector_size && j < 50; ++j) {
179 ptr_map_data->index_put_({i, j},
180 torch::from_blob(map_feature->at(i)[j].data(),
181 {9},
182 opts));
183 }
184 }
185 *ptr_map_data = ptr_map_data->toType(at::kFloat);
186 *ptr_all_map_p_id = ptr_all_map_p_id->toType(at::kFloat);
187
188 return true;
189}
190
191bool VectornetEvaluator::Evaluate(Obstacle* obstacle_ptr,
192 ObstaclesContainer* obstacles_container) {
193 omp_set_num_threads(1);
194
195 obstacle_ptr->SetEvaluatorType(evaluator_type_);
196
197 Clear();
198 CHECK_NOTNULL(obstacle_ptr);
199 int id = obstacle_ptr->id();
200 if (!obstacle_ptr->latest_feature().IsInitialized()) {
201 AERROR << "Obstacle [" << id << "] has no latest feature.";
202 return false;
203 }
204
205 int obs_num =
206 obstacles_container->curr_frame_considered_obstacle_ids().size();
207
208 auto start_time_obs = std::chrono::system_clock::now();
209
210 torch::Tensor target_obstacle_pos = torch::zeros({20, 2});
211 torch::Tensor target_obstacle_pos_step = torch::zeros({20, 2});
212 torch::Tensor vector_mask = torch::zeros({450, 50});
213 torch::Tensor obstacle_data = torch::zeros({obs_num, 20, 2});
214 torch::Tensor all_obs_p_id = torch::zeros({obs_num, 2});
215 // torch::Tensor obs_length_tmp = torch::zeros({obs_num, 2});
216
217 if (!VectornetProcessObstaclePosition(obstacle_ptr,
218 obstacles_container,
219 &target_obstacle_pos,
220 &target_obstacle_pos_step,
221 &vector_mask,
222 &obstacle_data,
223 &all_obs_p_id)) {
224 AERROR << "Obstacle [" << id << "] processing obstacle position fails.";
225 return false;
226 }
227
228 auto end_time_obs = std::chrono::system_clock::now();
229 std::chrono::duration<double> diff_obs = end_time_obs - start_time_obs;
230 AINFO << "obstacle vectors used time: " << diff_obs.count() * 1000 << " ms.";
231
232 Feature* latest_feature_ptr = obstacle_ptr->mutable_latest_feature();
233 CHECK_NOTNULL(latest_feature_ptr);
234
235 // Query the map data
236 FeatureVector map_feature;
237 PidVector map_p_id;
238 const double pos_x = latest_feature_ptr->position().x();
239 const double pos_y = latest_feature_ptr->position().y();
240 common::PointENU center_point
242 const double heading = latest_feature_ptr->velocity_heading();
243
244 auto start_time_query = std::chrono::system_clock::now();
245
246 if (!vector_net_.query(center_point, heading, &map_feature, &map_p_id)) {
247 return false;
248 }
249
250 auto end_time_query = std::chrono::system_clock::now();
251 std::chrono::duration<double> diff_query = end_time_query - start_time_query;
252 AINFO << "vectors query used time: " << diff_query.count() * 1000 << " ms.";
253
254 // process map data & map p id & v_mask for map polyline
255 int map_polyline_num = map_feature.size();
256 int data_length =
257 ((obs_num + map_polyline_num) < 450) ? (obs_num + map_polyline_num) : 450;
258
259 // Process input tensor
260 auto start_time_data_prep = std::chrono::system_clock::now();
261 torch::Tensor map_data = torch::zeros({map_polyline_num, 50, 9});
262 torch::Tensor all_map_p_id = torch::zeros({map_polyline_num, 2});
263
264 if (!VectornetProcessMapData(&map_feature,
265 &map_p_id,
266 obs_num,
267 &map_data,
268 &all_map_p_id,
269 &vector_mask)) {
270 AERROR << "Obstacle [" << id << "] processing map data fails.";
271 return false;
272 }
273
274 // process p mask
275 torch::Tensor polyline_mask = torch::zeros({450});
276 if (data_length < 450) {
277 polyline_mask.index_put_(
278 {torch::indexing::Slice(data_length, torch::indexing::None)}, 1);
279 }
280
281
282 // Extend data & pid to specific demension
283 torch::Tensor data_tmp = torch::cat({obstacle_data, map_data}, 0);
284 torch::Tensor p_id_tmp = torch::cat({all_obs_p_id, all_map_p_id}, 0);
285 torch::Tensor vector_data;
286 torch::Tensor polyline_id;
287 if (data_length < 450) {
288 torch::Tensor data_zeros = torch::zeros({(450 - data_length), 50, 9});
289 torch::Tensor p_id_zeros = torch::zeros({(450 - data_length), 2});
290 vector_data = torch::cat({data_tmp, data_zeros}, 0);
291 polyline_id = torch::cat({p_id_tmp, p_id_zeros}, 0);
292 } else {
293 vector_data = data_tmp.index({torch::indexing::Slice(0, 450)});
294 polyline_id = p_id_tmp.index({torch::indexing::Slice(0, 450)});
295 }
296
297 // Empty rand mask as placeholder
298 auto rand_mask = torch::zeros({450}).toType(at::kBool);
299 // Change mask type to bool
300 auto bool_vector_mask = vector_mask.toType(at::kBool);
301 auto bool_polyline_mask = polyline_mask.toType(at::kBool);
302 // Build input features for torch
303 std::vector<torch::jit::IValue> torch_inputs;
304
305 torch_inputs.push_back(c10::ivalue::Tuple::create(
306 {std::move(target_obstacle_pos.unsqueeze(0).to(device_)),
307 std::move(target_obstacle_pos_step.unsqueeze(0).to(device_)),
308 std::move(vector_data.unsqueeze(0).to(device_)),
309 std::move(bool_vector_mask.unsqueeze(0).to(device_)),
310 std::move(bool_polyline_mask.unsqueeze(0).to(device_)),
311 std::move(rand_mask.unsqueeze(0).to(device_)),
312 std::move(polyline_id.unsqueeze(0).to(device_))}));
313
314 auto end_time_data_prep = std::chrono::system_clock::now();
315 std::chrono::duration<double> diff_data_prep =
316 end_time_data_prep - start_time_data_prep;
317 AINFO << "vectornet input tensor preparation used time: "
318 << diff_data_prep.count() * 1000 << " ms.";
319
320 auto start_time_inference = std::chrono::system_clock::now();
321
322 at::Tensor torch_output_tensor = torch_default_output_tensor_;
323 torch_output_tensor =
324 torch_vehicle_model_.forward(torch_inputs).toTensor().to(torch::kCPU);
325
326 auto end_time_inference = std::chrono::system_clock::now();
327 std::chrono::duration<double> diff_inference =
328 end_time_inference - start_time_inference;
329 AINFO << "vectornet inference used time: " << diff_inference.count() * 1000
330 << " ms.";
331
332 // Get the trajectory
333 auto start_time_output_process = std::chrono::system_clock::now();
334
335 auto torch_output = torch_output_tensor.accessor<float, 3>();
336 Trajectory* trajectory = latest_feature_ptr->add_predicted_trajectory();
337 trajectory->set_probability(1.0);
338
339 for (int i = 0; i < 30; ++i) {
340 double prev_x = pos_x;
341 double prev_y = pos_y;
342 if (i > 0) {
343 const auto& last_point = trajectory->trajectory_point(i - 1).path_point();
344 prev_x = last_point.x();
345 prev_y = last_point.y();
346 }
347 TrajectoryPoint* point = trajectory->add_trajectory_point();
348 double dx = static_cast<double>(torch_output[0][i][0]);
349 double dy = static_cast<double>(torch_output[0][i][1]);
350
351 double heading = latest_feature_ptr->velocity_heading();
352 Vec2d offset(dx, dy);
353 Vec2d rotated_offset = offset.rotate(heading - (M_PI / 2));
354 double point_x = pos_x + rotated_offset.x();
355 double point_y = pos_y + rotated_offset.y();
356 point->mutable_path_point()->set_x(point_x);
357 point->mutable_path_point()->set_y(point_y);
358
359 if (i < 10) { // use origin heading for the first second
360 point->mutable_path_point()->set_theta(
361 latest_feature_ptr->velocity_heading());
362 } else {
363 point->mutable_path_point()->set_theta(
364 std::atan2(trajectory->trajectory_point(i).path_point().y() -
365 trajectory->trajectory_point(i - 1).path_point().y(),
366 trajectory->trajectory_point(i).path_point().x() -
367 trajectory->trajectory_point(i - 1).path_point().x()));
368 }
369 point->set_relative_time(static_cast<double>(i) *
370 FLAGS_prediction_trajectory_time_resolution);
371 if (i == 0) {
372 point->set_v(latest_feature_ptr->speed());
373 } else {
374 double diff_x = point_x - prev_x;
375 double diff_y = point_y - prev_y;
376 point->set_v(std::hypot(diff_x, diff_y) /
377 FLAGS_prediction_trajectory_time_resolution);
378 }
379 }
380
381 auto end_time_output_process = std::chrono::system_clock::now();
382 std::chrono::duration<double> diff_output_process =
383 end_time_output_process - start_time_output_process;
384 AINFO << "vectornet output process used time: "
385 << diff_output_process.count() * 1000 << " ms.";
386 return true;
387}
388
389bool VectornetEvaluator::ExtractObstaclesHistory(
390 Obstacle* obstacle_ptr, ObstaclesContainer* obstacles_container,
391 std::vector<std::pair<double, double>>* target_pos_history,
392 std::vector<std::pair<double, double>>* all_obs_length,
393 std::vector<std::vector<std::pair<double, double>>>* all_obs_pos_history,
394 torch::Tensor* vector_mask) {
395 const Feature& obs_curr_feature = obstacle_ptr->latest_feature();
396 double obs_curr_heading = obs_curr_feature.velocity_heading();
397 std::pair<double, double> obs_curr_pos = std::make_pair(
398 obs_curr_feature.position().x(), obs_curr_feature.position().y());
399 // Extract target obstacle history
400 for (std::size_t i = 0; i < obstacle_ptr->history_size() && i < 20; ++i) {
401 const Feature& target_feature = obstacle_ptr->feature(i);
402 if (!target_feature.IsInitialized()) {
403 break;
404 }
405 target_pos_history->at(i) =
406 WorldCoordToObjCoordNorth(
407 std::make_pair(target_feature.position().x(),
408 target_feature.position().y()),
409 obs_curr_pos, obs_curr_heading);
410 }
411 all_obs_length->emplace_back(
412 std::make_pair(obs_curr_feature.length(), obs_curr_feature.width()));
413 all_obs_pos_history->emplace_back(*target_pos_history);
414
415 // Extract other obstacles & convert pos to traget obstacle relative coord
416 std::vector<std::pair<double, double>> pos_history(20, {0.0, 0.0});
417 for (int id : obstacles_container->curr_frame_considered_obstacle_ids()) {
418 Obstacle* obstacle = obstacles_container->GetObstacle(id);
419 if (!obstacle) {
420 continue;
421 }
422 int target_id = obstacle_ptr->id();
423 if (id == target_id) {
424 continue;
425 }
426 const Feature& other_obs_curr_feature = obstacle->latest_feature();
427 all_obs_length->emplace_back(std::make_pair(
428 other_obs_curr_feature.length(), other_obs_curr_feature.width()));
429
430 size_t obs_his_size = obstacle->history_size();
431 obs_his_size = obs_his_size <= 20 ? obs_his_size : 20;
432 int cur_idx = all_obs_pos_history->size();
433 // if cur_dix >= 450, index_put_ discards it automatically.
434 if (obs_his_size > 1) {
435 vector_mask->index_put_({cur_idx,
436 torch::indexing::Slice(torch::indexing::None,
437 -(obs_his_size - 1))},
438 1);
439 } else {
440 vector_mask->index_put_({cur_idx,
441 torch::indexing::Slice(torch::indexing::None,
442 -1)},
443 1);
444 }
445
446 for (size_t i = 0; i < obs_his_size; ++i) {
447 const Feature& feature = obstacle->feature(i);
448 if (!feature.IsInitialized()) {
449 break;
450 }
451 pos_history[i] = WorldCoordToObjCoordNorth(
452 std::make_pair(feature.position().x(), feature.position().y()),
453 obs_curr_pos, obs_curr_heading);
454 }
455 all_obs_pos_history->emplace_back(pos_history);
456 }
457 return true;
458}
459
460void VectornetEvaluator::LoadModel() {
461 if (FLAGS_use_cuda && torch::cuda::is_available()) {
462 AINFO << "CUDA is available";
463 device_ = torch::Device(torch::kCUDA);
464 torch_vehicle_model_ =
465 torch::jit::load(FLAGS_torch_vehicle_vectornet_file, device_);
466 } else {
467 torch_vehicle_model_ =
468 torch::jit::load(FLAGS_torch_vehicle_vectornet_cpu_file, device_);
469 }
470 torch::set_num_threads(1);
471
472 // Fake intput for the first frame
473 torch::Tensor target_obstacle_pos = torch::randn({1, 20, 2});
474 torch::Tensor target_obstacle_pos_step = torch::randn({1, 20, 2});
475 torch::Tensor vector_data = torch::randn({1, 450, 50, 9});
476 torch::Tensor vector_mask = torch::randn({1, 450, 50}) > 0.9;
477 torch::Tensor polyline_mask = torch::randn({1, 450}) > 0.9;
478 torch::Tensor rand_mask = torch::zeros({1, 450});
479 torch::Tensor polyline_id = torch::randn({1, 450, 2});
480 std::vector<torch::jit::IValue> torch_inputs;
481 torch_inputs.push_back(c10::ivalue::Tuple::create(
482 {std::move(target_obstacle_pos.to(device_)),
483 std::move(target_obstacle_pos_step.to(device_)),
484 std::move(vector_data.to(device_)), std::move(vector_mask.to(device_)),
485 std::move(polyline_mask.to(device_)), std::move(rand_mask.to(device_)),
486 std::move(polyline_id.to(device_))}));
487 // warm up to avoid very slow first inference later
488 WarmUp(torch_inputs, &torch_vehicle_model_, &torch_default_output_tensor_);
489}
490
491} // namespace prediction
492} // namespace apollo
Implements a class of 2-dimensional vectors.
Definition vec2d.h:42
double y() const
Getter for y component
Definition vec2d.h:57
Vec2d rotate(const double angle) const
rotate the vector by angle.
Definition vec2d.cc:65
double x() const
Getter for x component
Definition vec2d.h:54
static PointENU ToPointENU(const double x, const double y, const double z=0)
ObstacleConf::EvaluatorType evaluator_type_
Definition evaluator.h:156
Prediction obstacle.
Definition obstacle.h:52
Feature * mutable_latest_feature()
Get a pointer to the latest feature.
Definition obstacle.cc:88
int id() const
Get the obstacle's ID.
Definition obstacle.cc:60
size_t history_size() const
Get the number of historical features.
Definition obstacle.cc:93
const Feature & feature(const size_t i) const
Get the ith feature from latest to earliest.
Definition obstacle.cc:67
void SetEvaluatorType(const ObstacleConf::EvaluatorType &evaluator_type)
Definition obstacle.cc:1484
const Feature & latest_feature() const
Get the latest feature.
Definition obstacle.cc:78
const std::vector< int > & curr_frame_considered_obstacle_ids()
Get non-ignore obstacle IDs in the current frame
Obstacle * GetObstacle(const int id)
Get obstacle pointer
void Clear()
Clear obstacle feature map
bool ExtractObstaclesHistory(Obstacle *obstacle_ptr, ObstaclesContainer *obstacles_container, std::vector< std::pair< double, double > > *curr_pos_history, std::vector< std::pair< double, double > > *all_obs_length, std::vector< std::vector< std::pair< double, double > > > *all_obs_pos_history, torch::Tensor *vector_mask)
Extract all obstacles history
bool VectornetProcessObstaclePosition(Obstacle *obstacle_ptr, ObstaclesContainer *obstacles_container, torch::Tensor *ptr_target_obs_pos, torch::Tensor *ptr_target_obs_pos_step, torch::Tensor *ptr_vector_mask, torch::Tensor *ptr_obstacle_data, torch::Tensor *ptr_all_obs_p_id)
Process obstacle position to vector
#define ADEBUG
Definition log.h:41
#define AERROR
Definition log.h:44
#define AINFO
Definition log.h:42
void WarmUp(const std::vector< torch::jit::IValue > &torch_inputs, torch::jit::script::Module *model, at::Tensor *default_output_ptr)
warm up function to avoid slowly inference of torch model
Definition warm_up.cc:28
std::vector< std::vector< double > > PidVector
Definition vector_net.h:34
std::vector< std::vector< std::vector< double > > > FeatureVector
Definition vector_net.h:33
class register implement
Definition arena_queue.h:37
optional double velocity_heading
Definition feature.proto:93
optional apollo::common::Point3D position
Definition feature.proto:88
warm-up function for torch model to avoid first multiple slowly inference