Apollo 10.0
自动驾驶开放平台
lane_aggregating_evaluator.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2019 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 <algorithm>
20#include <utility>
21
28
29namespace apollo {
30namespace prediction {
31
36
37void LaneAggregatingEvaluator::LoadModel() {
38 torch::set_num_threads(1);
39 torch_obstacle_encoding_ = torch::jit::load(
40 FLAGS_torch_lane_aggregating_obstacle_encoding_file, device_);
41 torch_lane_encoding_ = torch::jit::load(
42 FLAGS_torch_lane_aggregating_lane_encoding_file, device_);
43 torch_prediction_layer_ = torch::jit::load(
44 FLAGS_torch_lane_aggregating_prediction_layer_file, device_);
45}
46
48 Obstacle* obstacle_ptr, ObstaclesContainer* obstacles_container) {
49 // Sanity checks.
50 CHECK_NOTNULL(obstacle_ptr);
51
52 obstacle_ptr->SetEvaluatorType(evaluator_type_);
53
54 int id = obstacle_ptr->id();
55 if (!obstacle_ptr->latest_feature().IsInitialized()) {
56 AERROR << "Obstacle [" << id << "] has no latest feature.";
57 return false;
58 }
59 Feature* latest_feature_ptr = obstacle_ptr->mutable_latest_feature();
60 CHECK_NOTNULL(latest_feature_ptr);
61 if (!latest_feature_ptr->has_lane() ||
62 !latest_feature_ptr->lane().has_lane_graph_ordered()) {
63 AERROR << "Obstacle [" << id << "] has no lane graph.";
64 return false;
65 }
66 LaneGraph* lane_graph_ptr =
67 latest_feature_ptr->mutable_lane()->mutable_lane_graph_ordered();
68 CHECK_NOTNULL(lane_graph_ptr);
69 if (lane_graph_ptr->lane_sequence().empty()) {
70 AERROR << "Obstacle [" << id << "] has no lane sequences.";
71 return false;
72 }
73 ADEBUG << "There are " << lane_graph_ptr->lane_sequence_size()
74 << " lane sequences to scan.";
75
76 // Extract features, and do model inferencing.
77 // 1. Encode the obstacle features.
78 std::vector<double> obstacle_feature_values;
79 if (!ExtractObstacleFeatures(obstacle_ptr, &obstacle_feature_values)) {
80 ADEBUG << "Failed to extract obstacle features for obs_id = " << id;
81 }
82 if (obstacle_feature_values.size() != OBSTACLE_FEATURE_SIZE) {
83 ADEBUG << "Obstacle [" << id << "] has fewer than "
84 << "expected obstacle feature_values "
85 << obstacle_feature_values.size() << ".";
86 return false;
87 }
88 ADEBUG << "Obstacle feature size = " << obstacle_feature_values.size();
89 std::vector<torch::jit::IValue> obstacle_encoding_inputs;
90 torch::Tensor obstacle_encoding_inputs_tensor =
91 torch::zeros({1, static_cast<int>(obstacle_feature_values.size())});
92 for (size_t i = 0; i < obstacle_feature_values.size(); ++i) {
93 obstacle_encoding_inputs_tensor[0][i] =
94 static_cast<float>(obstacle_feature_values[i]);
95 }
96 obstacle_encoding_inputs.push_back(
97 std::move(obstacle_encoding_inputs_tensor));
98 torch::Tensor obstalce_encoding =
99 torch_obstacle_encoding_.forward(obstacle_encoding_inputs)
100 .toTensor()
101 .to(torch::kCPU);
102 // 2. Encode the lane features.
103 std::vector<std::vector<double>> lane_feature_values;
104 std::vector<int> lane_sequence_idx_to_remove;
105 if (!ExtractStaticEnvFeatures(obstacle_ptr, lane_graph_ptr,
106 &lane_feature_values,
107 &lane_sequence_idx_to_remove)) {
108 AERROR << "Failed to extract static environmental features around obs_id = "
109 << id;
110 }
111 std::vector<torch::Tensor> lane_encoding_list;
112 for (const auto& single_lane_feature_values : lane_feature_values) {
113 if (single_lane_feature_values.size() !=
114 SINGLE_LANE_FEATURE_SIZE *
115 (LANE_POINTS_SIZE + BACKWARD_LANE_POINTS_SIZE)) {
116 AERROR << "Obstacle [" << id << "] has incorrect lane feature size.";
117 return false;
118 }
119 std::vector<torch::jit::IValue> single_lane_encoding_inputs;
120 torch::Tensor single_lane_encoding_inputs_tensor =
121 torch::zeros({1, SINGLE_LANE_FEATURE_SIZE * LANE_POINTS_SIZE});
122 for (size_t i = 0; i < SINGLE_LANE_FEATURE_SIZE * LANE_POINTS_SIZE; ++i) {
123 single_lane_encoding_inputs_tensor[0][i] = static_cast<float>(
124 single_lane_feature_values[i + SINGLE_LANE_FEATURE_SIZE *
125 BACKWARD_LANE_POINTS_SIZE]);
126 }
127 single_lane_encoding_inputs.push_back(
128 std::move(single_lane_encoding_inputs_tensor));
129 torch::Tensor single_lane_encoding =
130 torch_lane_encoding_.forward(single_lane_encoding_inputs)
131 .toTensor()
132 .to(torch::kCPU);
133 lane_encoding_list.push_back(std::move(single_lane_encoding));
134 }
135 // 3. Aggregate the lane features.
136 // torch::Tensor aggregated_lane_encoding =
137 // AggregateLaneEncodings(lane_encoding_list);
138 // 4. Make prediction.
139 std::vector<double> prediction_scores;
140 for (size_t i = 0; i < lane_encoding_list.size(); ++i) {
141 std::vector<torch::jit::IValue> prediction_layer_inputs;
142 torch::Tensor prediction_layer_inputs_tensor = torch::zeros(
143 {1, OBSTACLE_ENCODING_SIZE + SINGLE_LANE_ENCODING_SIZE + 1});
144 for (size_t j = 0; j < OBSTACLE_ENCODING_SIZE; ++j) {
145 prediction_layer_inputs_tensor[0][j] = obstalce_encoding[0][j];
146 }
147 for (size_t j = 0; j + 1 < SINGLE_LANE_ENCODING_SIZE; ++j) {
148 prediction_layer_inputs_tensor[0][OBSTACLE_ENCODING_SIZE + j] =
149 lane_encoding_list[i][0][j];
150 }
151 prediction_layer_inputs_tensor[0][OBSTACLE_ENCODING_SIZE +
152 SINGLE_LANE_ENCODING_SIZE - 1] =
153 static_cast<float>(lane_feature_values[i][SINGLE_LANE_FEATURE_SIZE *
154 BACKWARD_LANE_POINTS_SIZE]);
155 prediction_layer_inputs_tensor[0][OBSTACLE_ENCODING_SIZE +
156 SINGLE_LANE_ENCODING_SIZE] = 0.0;
157 // for (size_t j = 0; j < AGGREGATED_ENCODING_SIZE; ++j) {
158 // prediction_layer_inputs_tensor[0][OBSTACLE_ENCODING_SIZE +
159 // SINGLE_LANE_ENCODING_SIZE + j] =
160 // aggregated_lane_encoding[0][j];
161 // }
162 prediction_layer_inputs.push_back(
163 std::move(prediction_layer_inputs_tensor));
164 torch::Tensor prediction_layer_output =
165 torch_prediction_layer_.forward(prediction_layer_inputs)
166 .toTensor()
167 .to(torch::kCPU);
168 auto prediction_score = prediction_layer_output.accessor<float, 2>();
169 prediction_scores.push_back(static_cast<double>(prediction_score[0][0]));
170 }
171
172 // Pass the predicted result to the predictor to plot trajectory.
173 std::vector<double> output_probabilities = StableSoftmax(prediction_scores);
174 for (int i = 0; i < lane_graph_ptr->lane_sequence_size(); ++i) {
175 lane_graph_ptr->mutable_lane_sequence(i)->set_probability(
176 output_probabilities[i]);
177 ADEBUG << output_probabilities[i];
178 }
179
180 *(latest_feature_ptr->mutable_lane()->mutable_lane_graph()) = *lane_graph_ptr;
181 return true;
182}
183
184bool LaneAggregatingEvaluator::ExtractObstacleFeatures(
185 const Obstacle* obstacle_ptr, std::vector<double>* feature_values) {
186 // Sanity checks.
187 CHECK_NOTNULL(obstacle_ptr);
188 feature_values->clear();
189 FLAGS_cruise_historical_frame_length = 20;
190 std::vector<double> has_history(FLAGS_cruise_historical_frame_length, 1.0);
191 std::vector<std::pair<double, double>> pos_history(
192 FLAGS_cruise_historical_frame_length, std::make_pair(0.0, 0.0));
193 std::vector<std::pair<double, double>> vel_history(
194 FLAGS_cruise_historical_frame_length, std::make_pair(0.0, 0.0));
195 std::vector<std::pair<double, double>> acc_history(
196 FLAGS_cruise_historical_frame_length, std::make_pair(0.0, 0.0));
197 std::vector<double> vel_heading_history(FLAGS_cruise_historical_frame_length,
198 0.0);
199 std::vector<double> vel_heading_changing_rate_history(
200 FLAGS_cruise_historical_frame_length, 0.0);
201
202 // Get obstacle's current position to set up the relative coord. system.
203 const Feature& obs_curr_feature = obstacle_ptr->latest_feature();
204 double obs_curr_heading = obs_curr_feature.velocity_heading();
205 std::pair<double, double> obs_curr_pos = std::make_pair(
206 obs_curr_feature.position().x(), obs_curr_feature.position().y());
207 double prev_timestamp = obs_curr_feature.timestamp();
208
209 // Starting from the most recent timestamp and going backward.
210 ADEBUG << "Obstacle has " << obstacle_ptr->history_size()
211 << " history timestamps.";
212 for (std::size_t i = 0; i < std::min(obstacle_ptr->history_size(),
213 FLAGS_cruise_historical_frame_length);
214 ++i) {
215 const Feature& feature = obstacle_ptr->feature(i);
216 if (!feature.IsInitialized()) {
217 has_history[i] = 0.0;
218 continue;
219 }
220 if (i != 0 && has_history[i - 1] == 0.0) {
221 has_history[i] = 0.0;
222 continue;
223 }
224 // Extract normalized position info.
225 if (feature.has_position()) {
226 pos_history[i] = WorldCoordToObjCoord(
227 std::make_pair(feature.position().x(), feature.position().y()),
228 obs_curr_pos, obs_curr_heading);
229 } else {
230 has_history[i] = 0.0;
231 }
232 // Extract normalized velocity info.
233 if (feature.has_velocity()) {
234 auto vel_end = WorldCoordToObjCoord(
235 std::make_pair(feature.velocity().x(), feature.velocity().y()),
236 obs_curr_pos, obs_curr_heading);
237 auto vel_begin = WorldCoordToObjCoord(std::make_pair(0.0, 0.0),
238 obs_curr_pos, obs_curr_heading);
239 vel_history[i] = std::make_pair(vel_end.first - vel_begin.first,
240 vel_end.second - vel_begin.second);
241 } else {
242 has_history[i] = 0.0;
243 }
244 // Extract normalized acceleration info.
245 if (feature.has_acceleration()) {
246 auto acc_end =
247 WorldCoordToObjCoord(std::make_pair(feature.acceleration().x(),
248 feature.acceleration().y()),
249 obs_curr_pos, obs_curr_heading);
250 auto acc_begin = WorldCoordToObjCoord(std::make_pair(0.0, 0.0),
251 obs_curr_pos, obs_curr_heading);
252 acc_history[i] = std::make_pair(acc_end.first - acc_begin.first,
253 acc_end.second - acc_begin.second);
254 } else {
255 has_history[i] = 0.0;
256 }
257 // Extract velocity heading info.
258 if (feature.has_velocity_heading()) {
259 vel_heading_history[i] =
260 WorldAngleToObjAngle(feature.velocity_heading(), obs_curr_heading);
261 if (i != 0) {
262 vel_heading_changing_rate_history[i] =
263 (vel_heading_history[i] - vel_heading_history[i - 1]) /
264 (feature.timestamp() - prev_timestamp + FLAGS_double_precision);
265 prev_timestamp = feature.timestamp();
266 }
267 } else {
268 has_history[i] = 0.0;
269 }
270 }
271
272 for (std::size_t i = obstacle_ptr->history_size();
273 i < FLAGS_cruise_historical_frame_length; ++i) {
274 has_history[i] = 0.0;
275 }
276
277 // Update the extracted features into the feature_values vector.
278 for (std::size_t i = 0; i < FLAGS_cruise_historical_frame_length; i++) {
279 feature_values->push_back(has_history[i]);
280 feature_values->push_back(pos_history[i].first);
281 feature_values->push_back(pos_history[i].second);
282 feature_values->push_back(vel_history[i].first);
283 feature_values->push_back(vel_history[i].second);
284 feature_values->push_back(acc_history[i].first);
285 feature_values->push_back(acc_history[i].second);
286 feature_values->push_back(vel_heading_history[i]);
287 feature_values->push_back(vel_heading_changing_rate_history[i]);
288 }
289
290 return true;
291}
292
293bool LaneAggregatingEvaluator::ExtractStaticEnvFeatures(
294 const Obstacle* obstacle_ptr, const LaneGraph* lane_graph_ptr,
295 std::vector<std::vector<double>>* feature_values,
296 std::vector<int>* lane_sequence_idx_to_remove) {
297 // Sanity checks.
298 CHECK_NOTNULL(lane_graph_ptr);
299 feature_values->clear();
300
301 // Get obstacle's current position to set up the relative coord. system.
302 const Feature& obs_curr_feature = obstacle_ptr->latest_feature();
303 double obs_curr_heading = obs_curr_feature.velocity_heading();
304 std::pair<double, double> obs_curr_pos = std::make_pair(
305 obs_curr_feature.position().x(), obs_curr_feature.position().y());
306
307 // Go through every lane-sequence (ordered from left to right) and
308 // extract needed features.
309 for (int i = 0; i < lane_graph_ptr->lane_sequence_size(); ++i) {
310 // Get all the properties of the current lane-sequence.
311 // Go through all the lane-points to fill up the feature_values.
312 const LaneSequence& lane_sequence = lane_graph_ptr->lane_sequence(i);
313 std::vector<double> curr_feature_values;
314
315 // Extract features from backward lane-points.
316 size_t count = 0;
317 std::vector<double> backward_feature_values;
318 for (int j = lane_sequence.adc_lane_segment_idx(); j >= 0; --j) {
319 if (count >= SINGLE_LANE_FEATURE_SIZE * BACKWARD_LANE_POINTS_SIZE) {
320 break;
321 }
322 const LaneSegment& lane_segment = lane_sequence.lane_segment(j);
323 int k_starting_idx = lane_segment.lane_point_size() - 1;
324 if (j == lane_sequence.adc_lane_segment_idx()) {
325 k_starting_idx = std::min(lane_segment.adc_lane_point_idx(),
326 lane_segment.lane_point_size() - 1);
327 }
328 for (int k = k_starting_idx; k >= 0; --k) {
329 if (count >= SINGLE_LANE_FEATURE_SIZE * BACKWARD_LANE_POINTS_SIZE) {
330 break;
331 }
332 const LanePoint& lane_point = lane_segment.lane_point(k);
333 std::pair<double, double> relative_s_l =
334 WorldCoordToObjCoord(std::make_pair(lane_point.position().x(),
335 lane_point.position().y()),
336 obs_curr_pos, obs_curr_heading);
337 double relative_ang =
338 WorldAngleToObjAngle(lane_point.heading(), obs_curr_heading);
339
340 backward_feature_values.push_back(lane_point.kappa());
341 backward_feature_values.push_back(relative_ang);
342 backward_feature_values.push_back(relative_s_l.first);
343 backward_feature_values.push_back(relative_s_l.second);
344
345 count += 4;
346 }
347 }
348 // If lane-points are not enough, then extrapolate linearly.
349 while (count >= SINGLE_LANE_FEATURE_SIZE * 2 &&
350 count < SINGLE_LANE_FEATURE_SIZE * BACKWARD_LANE_POINTS_SIZE) {
351 std::size_t s = backward_feature_values.size();
352 double relative_l_new =
353 2 * backward_feature_values[s - 1] - backward_feature_values[s - 5];
354 double relative_s_new =
355 2 * backward_feature_values[s - 2] - backward_feature_values[s - 6];
356 double relative_ang_new = backward_feature_values[s - 3];
357
358 backward_feature_values.push_back(0.0);
359 backward_feature_values.push_back(relative_ang_new);
360 backward_feature_values.push_back(relative_s_new);
361 backward_feature_values.push_back(relative_l_new);
362
363 count += 4;
364 }
365
366 for (int j = static_cast<int>(backward_feature_values.size()) - 1; j >= 0;
367 --j) {
368 curr_feature_values.push_back(backward_feature_values[j]);
369 }
370
371 // Extract features from forward lane-points.
372 count = 0;
373 for (int j = lane_sequence.adc_lane_segment_idx();
374 j < lane_sequence.lane_segment_size(); ++j) {
375 if (count >= SINGLE_LANE_FEATURE_SIZE * LANE_POINTS_SIZE) {
376 break;
377 }
378 const LaneSegment& lane_segment = lane_sequence.lane_segment(j);
379 int k_starting_idx = 0;
380 if (j == lane_sequence.adc_lane_segment_idx()) {
381 k_starting_idx = std::min(lane_segment.adc_lane_point_idx(),
382 lane_segment.lane_point_size() - 1);
383 }
384 for (int k = k_starting_idx; k < lane_segment.lane_point_size(); ++k) {
385 if (count >= SINGLE_LANE_FEATURE_SIZE * LANE_POINTS_SIZE) {
386 break;
387 }
388 const LanePoint& lane_point = lane_segment.lane_point(k);
389 std::pair<double, double> relative_s_l =
390 WorldCoordToObjCoord(std::make_pair(lane_point.position().x(),
391 lane_point.position().y()),
392 obs_curr_pos, obs_curr_heading);
393 double relative_ang =
394 WorldAngleToObjAngle(lane_point.heading(), obs_curr_heading);
395
396 curr_feature_values.push_back(relative_s_l.second);
397 curr_feature_values.push_back(relative_s_l.first);
398 curr_feature_values.push_back(relative_ang);
399 curr_feature_values.push_back(lane_point.kappa());
400 count += 4;
401 }
402 }
403 // If lane-points are not enough, then extrapolate linearly.
404 while (count >= SINGLE_LANE_FEATURE_SIZE * 2 &&
405 count < SINGLE_LANE_FEATURE_SIZE * LANE_POINTS_SIZE) {
406 std::size_t s = curr_feature_values.size();
407 double relative_l_new =
408 2 * curr_feature_values[s - 4] - curr_feature_values[s - 8];
409 double relative_s_new =
410 2 * curr_feature_values[s - 3] - curr_feature_values[s - 7];
411 double relative_ang_new = curr_feature_values[s - 2];
412
413 curr_feature_values.push_back(relative_l_new);
414 curr_feature_values.push_back(relative_s_new);
415 curr_feature_values.push_back(relative_ang_new);
416 curr_feature_values.push_back(0.0);
417 count += 4;
418 }
419
420 feature_values->push_back(curr_feature_values);
421 }
422
423 return true;
424}
425
426torch::Tensor LaneAggregatingEvaluator::AggregateLaneEncodings(
427 const std::vector<torch::Tensor>& lane_encoding_list) {
428 torch::Tensor output_tensor =
429 torch::zeros({1, SINGLE_LANE_ENCODING_SIZE * 2});
430 torch::Tensor max_pooling_tensor = LaneEncodingMaxPooling(lane_encoding_list);
431 torch::Tensor avg_pooling_tensor = LaneEncodingAvgPooling(lane_encoding_list);
432 for (size_t i = 0; i < SINGLE_LANE_ENCODING_SIZE; ++i) {
433 output_tensor[0][i] = max_pooling_tensor[0][i];
434 }
435 for (size_t i = 0; i < SINGLE_LANE_ENCODING_SIZE; ++i) {
436 output_tensor[0][SINGLE_LANE_ENCODING_SIZE + i] = avg_pooling_tensor[0][i];
437 }
438 return output_tensor;
439}
440
441torch::Tensor LaneAggregatingEvaluator::LaneEncodingMaxPooling(
442 const std::vector<torch::Tensor>& lane_encoding_list) {
443 ACHECK(!lane_encoding_list.empty());
444 torch::Tensor output_tensor = lane_encoding_list[0];
445
446 for (size_t i = 1; i < lane_encoding_list.size(); ++i) {
447 for (size_t j = 0; j < SINGLE_LANE_ENCODING_SIZE; ++j) {
448 output_tensor[0][j] =
449 torch::max(output_tensor[0][j], lane_encoding_list[i][0][j]);
450 }
451 }
452 return output_tensor;
453}
454
455torch::Tensor LaneAggregatingEvaluator::LaneEncodingAvgPooling(
456 const std::vector<torch::Tensor>& lane_encoding_list) {
457 ACHECK(!lane_encoding_list.empty());
458 torch::Tensor output_tensor = lane_encoding_list[0];
459
460 for (size_t i = 1; i < lane_encoding_list.size(); ++i) {
461 for (size_t j = 0; j < SINGLE_LANE_ENCODING_SIZE; ++j) {
462 output_tensor[0][j] += lane_encoding_list[i][0][j];
463 }
464 }
465
466 torch::Tensor lane_encoding_list_size = torch::zeros({1});
467 lane_encoding_list_size[0] = static_cast<float>(lane_encoding_list.size());
468 for (size_t i = 0; i < SINGLE_LANE_ENCODING_SIZE; ++i) {
469 output_tensor[0][i] /= lane_encoding_list_size[0];
470 }
471 return output_tensor;
472}
473
474std::vector<double> LaneAggregatingEvaluator::StableSoftmax(
475 const std::vector<double>& prediction_scores) {
476 double max_score = prediction_scores[0];
477 for (const auto& prediction_score : prediction_scores) {
478 max_score = std::fmax(max_score, prediction_score);
479 }
480 std::vector<double> output_probabilities;
481 for (const auto& prediction_score : prediction_scores) {
482 output_probabilities.push_back(std::exp(prediction_score - max_score));
483 }
484 double denominator = 0.0;
485 for (const auto& element : output_probabilities) {
486 denominator += element;
487 }
488 for (auto& element : output_probabilities) {
489 element = element / denominator;
490 }
491 return output_probabilities;
492}
493
494} // namespace prediction
495} // namespace apollo
double WorldAngleToObjAngle(double input_world_angle, double obj_world_angle)
Definition evaluator.h:116
std::pair< double, double > WorldCoordToObjCoord(std::pair< double, double > input_world_coord, std::pair< double, double > obj_world_coord, double obj_world_angle)
Definition evaluator.h:94
ObstacleConf::EvaluatorType evaluator_type_
Definition evaluator.h:156
bool Evaluate(Obstacle *obstacle_ptr, ObstaclesContainer *obstacles_container) override
Override Evaluate
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
Use container manager to manage all containers
Define the lane aggregating evaluator class
#define ACHECK(cond)
Definition log.h:80
#define ADEBUG
Definition log.h:41
#define AERROR
Definition log.h:44
class register implement
Definition arena_queue.h:37
Obstacles container
optional apollo::common::Point3D velocity
Definition feature.proto:90
optional double velocity_heading
Definition feature.proto:93
optional apollo::common::Point3D position
Definition feature.proto:88
optional apollo::common::Point3D acceleration
Definition feature.proto:92
repeated LaneSequence lane_sequence
Defines the Vec2d class.