00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "cartographer/mapping/internal/2d/pose_graph_2d.h"
00018
00019 #include <algorithm>
00020 #include <cmath>
00021 #include <cstdio>
00022 #include <functional>
00023 #include <iomanip>
00024 #include <iostream>
00025 #include <iterator>
00026 #include <limits>
00027 #include <memory>
00028 #include <sstream>
00029 #include <string>
00030
00031 #include "Eigen/Eigenvalues"
00032 #include "absl/memory/memory.h"
00033 #include "cartographer/common/math.h"
00034 #include "cartographer/mapping/internal/2d/overlapping_submaps_trimmer_2d.h"
00035 #include "cartographer/mapping/proto/pose_graph/constraint_builder_options.pb.h"
00036 #include "cartographer/sensor/compressed_point_cloud.h"
00037 #include "cartographer/sensor/internal/voxel_filter.h"
00038 #include "cartographer/transform/transform.h"
00039 #include "glog/logging.h"
00040
00041 namespace cartographer {
00042 namespace mapping {
00043
00044 static auto* kWorkQueueDelayMetric = metrics::Gauge::Null();
00045 static auto* kConstraintsSameTrajectoryMetric = metrics::Gauge::Null();
00046 static auto* kConstraintsDifferentTrajectoryMetric = metrics::Gauge::Null();
00047 static auto* kActiveSubmapsMetric = metrics::Gauge::Null();
00048 static auto* kFrozenSubmapsMetric = metrics::Gauge::Null();
00049 static auto* kDeletedSubmapsMetric = metrics::Gauge::Null();
00050
00051 PoseGraph2D::PoseGraph2D(
00052 const proto::PoseGraphOptions& options,
00053 std::unique_ptr<optimization::OptimizationProblem2D> optimization_problem,
00054 common::ThreadPool* thread_pool)
00055 : options_(options),
00056 optimization_problem_(std::move(optimization_problem)),
00057 constraint_builder_(options_.constraint_builder_options(), thread_pool),
00058 thread_pool_(thread_pool) {
00059 if (options.has_overlapping_submaps_trimmer_2d()) {
00060 const auto& trimmer_options = options.overlapping_submaps_trimmer_2d();
00061 AddTrimmer(absl::make_unique<OverlappingSubmapsTrimmer2D>(
00062 trimmer_options.fresh_submaps_count(),
00063 trimmer_options.min_covered_area(),
00064 trimmer_options.min_added_submaps_count()));
00065 }
00066 }
00067
00068 PoseGraph2D::~PoseGraph2D() {
00069 WaitForAllComputations();
00070 absl::MutexLock locker(&work_queue_mutex_);
00071 CHECK(work_queue_ == nullptr);
00072 }
00073
00074 std::vector<SubmapId> PoseGraph2D::InitializeGlobalSubmapPoses(
00075 const int trajectory_id, const common::Time time,
00076 const std::vector<std::shared_ptr<const Submap2D>>& insertion_submaps) {
00077 CHECK(!insertion_submaps.empty());
00078 const auto& submap_data = optimization_problem_->submap_data();
00079 if (insertion_submaps.size() == 1) {
00080
00081 if (submap_data.SizeOfTrajectoryOrZero(trajectory_id) == 0) {
00082 if (data_.initial_trajectory_poses.count(trajectory_id) > 0) {
00083 data_.trajectory_connectivity_state.Connect(
00084 trajectory_id,
00085 data_.initial_trajectory_poses.at(trajectory_id).to_trajectory_id,
00086 time);
00087 }
00088 optimization_problem_->AddSubmap(
00089 trajectory_id, transform::Project2D(
00090 ComputeLocalToGlobalTransform(
00091 data_.global_submap_poses_2d, trajectory_id) *
00092 insertion_submaps[0]->local_pose()));
00093 }
00094 CHECK_EQ(1, submap_data.SizeOfTrajectoryOrZero(trajectory_id));
00095 const SubmapId submap_id{trajectory_id, 0};
00096 CHECK(data_.submap_data.at(submap_id).submap == insertion_submaps.front());
00097 return {submap_id};
00098 }
00099 CHECK_EQ(2, insertion_submaps.size());
00100 const auto end_it = submap_data.EndOfTrajectory(trajectory_id);
00101 CHECK(submap_data.BeginOfTrajectory(trajectory_id) != end_it);
00102 const SubmapId last_submap_id = std::prev(end_it)->id;
00103 if (data_.submap_data.at(last_submap_id).submap ==
00104 insertion_submaps.front()) {
00105
00106
00107 const auto& first_submap_pose = submap_data.at(last_submap_id).global_pose;
00108 optimization_problem_->AddSubmap(
00109 trajectory_id,
00110 first_submap_pose *
00111 constraints::ComputeSubmapPose(*insertion_submaps[0]).inverse() *
00112 constraints::ComputeSubmapPose(*insertion_submaps[1]));
00113 return {last_submap_id,
00114 SubmapId{trajectory_id, last_submap_id.submap_index + 1}};
00115 }
00116 CHECK(data_.submap_data.at(last_submap_id).submap ==
00117 insertion_submaps.back());
00118 const SubmapId front_submap_id{trajectory_id,
00119 last_submap_id.submap_index - 1};
00120 CHECK(data_.submap_data.at(front_submap_id).submap ==
00121 insertion_submaps.front());
00122 return {front_submap_id, last_submap_id};
00123 }
00124
00125 NodeId PoseGraph2D::AppendNode(
00126 std::shared_ptr<const TrajectoryNode::Data> constant_data,
00127 const int trajectory_id,
00128 const std::vector<std::shared_ptr<const Submap2D>>& insertion_submaps,
00129 const transform::Rigid3d& optimized_pose) {
00130 absl::MutexLock locker(&mutex_);
00131 AddTrajectoryIfNeeded(trajectory_id);
00132 if (!CanAddWorkItemModifying(trajectory_id)) {
00133 LOG(WARNING) << "AddNode was called for finished or deleted trajectory.";
00134 }
00135 const NodeId node_id = data_.trajectory_nodes.Append(
00136 trajectory_id, TrajectoryNode{constant_data, optimized_pose});
00137 ++data_.num_trajectory_nodes;
00138
00139 if (data_.submap_data.SizeOfTrajectoryOrZero(trajectory_id) == 0 ||
00140 std::prev(data_.submap_data.EndOfTrajectory(trajectory_id))
00141 ->data.submap != insertion_submaps.back()) {
00142
00143
00144 const SubmapId submap_id =
00145 data_.submap_data.Append(trajectory_id, InternalSubmapData());
00146 data_.submap_data.at(submap_id).submap = insertion_submaps.back();
00147 LOG(INFO) << "Inserted submap " << submap_id << ".";
00148 kActiveSubmapsMetric->Increment();
00149 }
00150 return node_id;
00151 }
00152
00153 NodeId PoseGraph2D::AddNode(
00154 std::shared_ptr<const TrajectoryNode::Data> constant_data,
00155 const int trajectory_id,
00156 const std::vector<std::shared_ptr<const Submap2D>>& insertion_submaps) {
00157 const transform::Rigid3d optimized_pose(
00158 GetLocalToGlobalTransform(trajectory_id) * constant_data->local_pose);
00159
00160 const NodeId node_id = AppendNode(constant_data, trajectory_id,
00161 insertion_submaps, optimized_pose);
00162
00163
00164 const bool newly_finished_submap =
00165 insertion_submaps.front()->insertion_finished();
00166 AddWorkItem([=]() LOCKS_EXCLUDED(mutex_) {
00167 return ComputeConstraintsForNode(node_id, insertion_submaps,
00168 newly_finished_submap);
00169 });
00170 return node_id;
00171 }
00172
00173 void PoseGraph2D::AddWorkItem(
00174 const std::function<WorkItem::Result()>& work_item) {
00175 absl::MutexLock locker(&work_queue_mutex_);
00176 if (work_queue_ == nullptr) {
00177 work_queue_ = absl::make_unique<WorkQueue>();
00178 auto task = absl::make_unique<common::Task>();
00179 task->SetWorkItem([this]() { DrainWorkQueue(); });
00180 thread_pool_->Schedule(std::move(task));
00181 }
00182 const auto now = std::chrono::steady_clock::now();
00183 work_queue_->push_back({now, work_item});
00184 kWorkQueueDelayMetric->Set(
00185 std::chrono::duration_cast<std::chrono::duration<double>>(
00186 now - work_queue_->front().time)
00187 .count());
00188 }
00189
00190 void PoseGraph2D::AddTrajectoryIfNeeded(const int trajectory_id) {
00191 data_.trajectories_state[trajectory_id];
00192 CHECK(data_.trajectories_state.at(trajectory_id).state !=
00193 TrajectoryState::FINISHED);
00194 CHECK(data_.trajectories_state.at(trajectory_id).state !=
00195 TrajectoryState::DELETED);
00196 CHECK(data_.trajectories_state.at(trajectory_id).deletion_state ==
00197 InternalTrajectoryState::DeletionState::NORMAL);
00198 data_.trajectory_connectivity_state.Add(trajectory_id);
00199
00200 if (!global_localization_samplers_[trajectory_id]) {
00201 global_localization_samplers_[trajectory_id] =
00202 absl::make_unique<common::FixedRatioSampler>(
00203 options_.global_sampling_ratio());
00204 }
00205 }
00206
00207 void PoseGraph2D::AddImuData(const int trajectory_id,
00208 const sensor::ImuData& imu_data) {
00209 AddWorkItem([=]() LOCKS_EXCLUDED(mutex_) {
00210 absl::MutexLock locker(&mutex_);
00211 if (CanAddWorkItemModifying(trajectory_id)) {
00212 optimization_problem_->AddImuData(trajectory_id, imu_data);
00213 }
00214 return WorkItem::Result::kDoNotRunOptimization;
00215 });
00216 }
00217
00218 void PoseGraph2D::AddOdometryData(const int trajectory_id,
00219 const sensor::OdometryData& odometry_data) {
00220 AddWorkItem([=]() LOCKS_EXCLUDED(mutex_) {
00221 absl::MutexLock locker(&mutex_);
00222 if (CanAddWorkItemModifying(trajectory_id)) {
00223 optimization_problem_->AddOdometryData(trajectory_id, odometry_data);
00224 }
00225 return WorkItem::Result::kDoNotRunOptimization;
00226 });
00227 }
00228
00229 void PoseGraph2D::AddFixedFramePoseData(
00230 const int trajectory_id,
00231 const sensor::FixedFramePoseData& fixed_frame_pose_data) {
00232 LOG(FATAL) << "Not yet implemented for 2D.";
00233 }
00234
00235 void PoseGraph2D::AddLandmarkData(int trajectory_id,
00236 const sensor::LandmarkData& landmark_data) {
00237 AddWorkItem([=]() LOCKS_EXCLUDED(mutex_) {
00238 absl::MutexLock locker(&mutex_);
00239 if (CanAddWorkItemModifying(trajectory_id)) {
00240 for (const auto& observation : landmark_data.landmark_observations) {
00241 data_.landmark_nodes[observation.id].landmark_observations.emplace_back(
00242 PoseGraphInterface::LandmarkNode::LandmarkObservation{
00243 trajectory_id, landmark_data.time,
00244 observation.landmark_to_tracking_transform,
00245 observation.translation_weight, observation.rotation_weight});
00246 }
00247 }
00248 return WorkItem::Result::kDoNotRunOptimization;
00249 });
00250 }
00251
00252 void PoseGraph2D::ComputeConstraint(const NodeId& node_id,
00253 const SubmapId& submap_id) {
00254 bool maybe_add_local_constraint = false;
00255 bool maybe_add_global_constraint = false;
00256 const TrajectoryNode::Data* constant_data;
00257 const Submap2D* submap;
00258 {
00259 absl::MutexLock locker(&mutex_);
00260 CHECK(data_.submap_data.at(submap_id).state == SubmapState::kFinished);
00261 if (!data_.submap_data.at(submap_id).submap->insertion_finished()) {
00262
00263
00264 return;
00265 }
00266
00267 const common::Time node_time = GetLatestNodeTime(node_id, submap_id);
00268 const common::Time last_connection_time =
00269 data_.trajectory_connectivity_state.LastConnectionTime(
00270 node_id.trajectory_id, submap_id.trajectory_id);
00271 if (node_id.trajectory_id == submap_id.trajectory_id ||
00272 node_time <
00273 last_connection_time +
00274 common::FromSeconds(
00275 options_.global_constraint_search_after_n_seconds())) {
00276
00277
00278
00279
00280 maybe_add_local_constraint = true;
00281 } else if (global_localization_samplers_[node_id.trajectory_id]->Pulse()) {
00282 maybe_add_global_constraint = true;
00283 }
00284 constant_data = data_.trajectory_nodes.at(node_id).constant_data.get();
00285 submap = static_cast<const Submap2D*>(
00286 data_.submap_data.at(submap_id).submap.get());
00287 }
00288
00289 if (maybe_add_local_constraint) {
00290 const transform::Rigid2d initial_relative_pose =
00291 optimization_problem_->submap_data()
00292 .at(submap_id)
00293 .global_pose.inverse() *
00294 optimization_problem_->node_data().at(node_id).global_pose_2d;
00295 constraint_builder_.MaybeAddConstraint(
00296 submap_id, submap, node_id, constant_data, initial_relative_pose);
00297 } else if (maybe_add_global_constraint) {
00298 constraint_builder_.MaybeAddGlobalConstraint(submap_id, submap, node_id,
00299 constant_data);
00300 }
00301 }
00302
00303 WorkItem::Result PoseGraph2D::ComputeConstraintsForNode(
00304 const NodeId& node_id,
00305 std::vector<std::shared_ptr<const Submap2D>> insertion_submaps,
00306 const bool newly_finished_submap) {
00307 std::vector<SubmapId> submap_ids;
00308 std::vector<SubmapId> finished_submap_ids;
00309 std::set<NodeId> newly_finished_submap_node_ids;
00310 {
00311 absl::MutexLock locker(&mutex_);
00312 const auto& constant_data =
00313 data_.trajectory_nodes.at(node_id).constant_data;
00314 submap_ids = InitializeGlobalSubmapPoses(
00315 node_id.trajectory_id, constant_data->time, insertion_submaps);
00316 CHECK_EQ(submap_ids.size(), insertion_submaps.size());
00317 const SubmapId matching_id = submap_ids.front();
00318 const transform::Rigid2d local_pose_2d =
00319 transform::Project2D(constant_data->local_pose *
00320 transform::Rigid3d::Rotation(
00321 constant_data->gravity_alignment.inverse()));
00322 const transform::Rigid2d global_pose_2d =
00323 optimization_problem_->submap_data().at(matching_id).global_pose *
00324 constraints::ComputeSubmapPose(*insertion_submaps.front()).inverse() *
00325 local_pose_2d;
00326 optimization_problem_->AddTrajectoryNode(
00327 matching_id.trajectory_id,
00328 optimization::NodeSpec2D{constant_data->time, local_pose_2d,
00329 global_pose_2d,
00330 constant_data->gravity_alignment});
00331 for (size_t i = 0; i < insertion_submaps.size(); ++i) {
00332 const SubmapId submap_id = submap_ids[i];
00333
00334
00335 CHECK(data_.submap_data.at(submap_id).state ==
00336 SubmapState::kNoConstraintSearch);
00337 data_.submap_data.at(submap_id).node_ids.emplace(node_id);
00338 const transform::Rigid2d constraint_transform =
00339 constraints::ComputeSubmapPose(*insertion_submaps[i]).inverse() *
00340 local_pose_2d;
00341 data_.constraints.push_back(
00342 Constraint{submap_id,
00343 node_id,
00344 {transform::Embed3D(constraint_transform),
00345 options_.matcher_translation_weight(),
00346 options_.matcher_rotation_weight()},
00347 Constraint::INTRA_SUBMAP});
00348 }
00349
00350
00351
00352
00353
00354 for (const auto& submap_id_data : data_.submap_data) {
00355 if (submap_id_data.data.state == SubmapState::kFinished) {
00356 CHECK_EQ(submap_id_data.data.node_ids.count(node_id), 0);
00357 finished_submap_ids.emplace_back(submap_id_data.id);
00358 }
00359 }
00360 if (newly_finished_submap) {
00361 const SubmapId newly_finished_submap_id = submap_ids.front();
00362 InternalSubmapData& finished_submap_data =
00363 data_.submap_data.at(newly_finished_submap_id);
00364 CHECK(finished_submap_data.state == SubmapState::kNoConstraintSearch);
00365 finished_submap_data.state = SubmapState::kFinished;
00366 newly_finished_submap_node_ids = finished_submap_data.node_ids;
00367 }
00368 }
00369
00370 for (const auto& submap_id : finished_submap_ids) {
00371 ComputeConstraint(node_id, submap_id);
00372 }
00373
00374 if (newly_finished_submap) {
00375 const SubmapId newly_finished_submap_id = submap_ids.front();
00376
00377
00378 for (const auto& node_id_data : optimization_problem_->node_data()) {
00379 const NodeId& node_id = node_id_data.id;
00380 if (newly_finished_submap_node_ids.count(node_id) == 0) {
00381 ComputeConstraint(node_id, newly_finished_submap_id);
00382 }
00383 }
00384 }
00385 constraint_builder_.NotifyEndOfNode();
00386 absl::MutexLock locker(&mutex_);
00387 ++num_nodes_since_last_loop_closure_;
00388 if (options_.optimize_every_n_nodes() > 0 &&
00389 num_nodes_since_last_loop_closure_ > options_.optimize_every_n_nodes()) {
00390 return WorkItem::Result::kRunOptimization;
00391 }
00392 return WorkItem::Result::kDoNotRunOptimization;
00393 }
00394
00395 common::Time PoseGraph2D::GetLatestNodeTime(const NodeId& node_id,
00396 const SubmapId& submap_id) const {
00397 common::Time time = data_.trajectory_nodes.at(node_id).constant_data->time;
00398 const InternalSubmapData& submap_data = data_.submap_data.at(submap_id);
00399 if (!submap_data.node_ids.empty()) {
00400 const NodeId last_submap_node_id =
00401 *data_.submap_data.at(submap_id).node_ids.rbegin();
00402 time = std::max(
00403 time,
00404 data_.trajectory_nodes.at(last_submap_node_id).constant_data->time);
00405 }
00406 return time;
00407 }
00408
00409 void PoseGraph2D::UpdateTrajectoryConnectivity(const Constraint& constraint) {
00410 CHECK_EQ(constraint.tag, Constraint::INTER_SUBMAP);
00411 const common::Time time =
00412 GetLatestNodeTime(constraint.node_id, constraint.submap_id);
00413 data_.trajectory_connectivity_state.Connect(
00414 constraint.node_id.trajectory_id, constraint.submap_id.trajectory_id,
00415 time);
00416 }
00417
00418 void PoseGraph2D::DeleteTrajectoriesIfNeeded() {
00419 TrimmingHandle trimming_handle(this);
00420 for (auto& it : data_.trajectories_state) {
00421 if (it.second.deletion_state ==
00422 InternalTrajectoryState::DeletionState::WAIT_FOR_DELETION) {
00423
00424
00425 auto submap_ids = trimming_handle.GetSubmapIds(it.first);
00426 for (auto& submap_id : submap_ids) {
00427 trimming_handle.TrimSubmap(submap_id);
00428 }
00429 it.second.state = TrajectoryState::DELETED;
00430 it.second.deletion_state = InternalTrajectoryState::DeletionState::NORMAL;
00431 }
00432 }
00433 }
00434
00435 void PoseGraph2D::HandleWorkQueue(
00436 const constraints::ConstraintBuilder2D::Result& result) {
00437 {
00438 absl::MutexLock locker(&mutex_);
00439 data_.constraints.insert(data_.constraints.end(), result.begin(),
00440 result.end());
00441 }
00442 RunOptimization();
00443
00444 if (global_slam_optimization_callback_) {
00445 std::map<int, NodeId> trajectory_id_to_last_optimized_node_id;
00446 std::map<int, SubmapId> trajectory_id_to_last_optimized_submap_id;
00447 {
00448 absl::MutexLock locker(&mutex_);
00449 const auto& submap_data = optimization_problem_->submap_data();
00450 const auto& node_data = optimization_problem_->node_data();
00451 for (const int trajectory_id : node_data.trajectory_ids()) {
00452 if (node_data.SizeOfTrajectoryOrZero(trajectory_id) == 0 ||
00453 submap_data.SizeOfTrajectoryOrZero(trajectory_id) == 0) {
00454 continue;
00455 }
00456 trajectory_id_to_last_optimized_node_id.emplace(
00457 trajectory_id,
00458 std::prev(node_data.EndOfTrajectory(trajectory_id))->id);
00459 trajectory_id_to_last_optimized_submap_id.emplace(
00460 trajectory_id,
00461 std::prev(submap_data.EndOfTrajectory(trajectory_id))->id);
00462 }
00463 }
00464 global_slam_optimization_callback_(
00465 trajectory_id_to_last_optimized_submap_id,
00466 trajectory_id_to_last_optimized_node_id);
00467 }
00468
00469 {
00470 absl::MutexLock locker(&mutex_);
00471 for (const Constraint& constraint : result) {
00472 UpdateTrajectoryConnectivity(constraint);
00473 }
00474 DeleteTrajectoriesIfNeeded();
00475 TrimmingHandle trimming_handle(this);
00476 for (auto& trimmer : trimmers_) {
00477 trimmer->Trim(&trimming_handle);
00478 }
00479 trimmers_.erase(
00480 std::remove_if(trimmers_.begin(), trimmers_.end(),
00481 [](std::unique_ptr<PoseGraphTrimmer>& trimmer) {
00482 return trimmer->IsFinished();
00483 }),
00484 trimmers_.end());
00485
00486 num_nodes_since_last_loop_closure_ = 0;
00487
00488
00489 double inter_constraints_same_trajectory = 0;
00490 double inter_constraints_different_trajectory = 0;
00491 for (const auto& constraint : data_.constraints) {
00492 if (constraint.tag ==
00493 cartographer::mapping::PoseGraph::Constraint::INTRA_SUBMAP) {
00494 continue;
00495 }
00496 if (constraint.node_id.trajectory_id ==
00497 constraint.submap_id.trajectory_id) {
00498 ++inter_constraints_same_trajectory;
00499 } else {
00500 ++inter_constraints_different_trajectory;
00501 }
00502 }
00503 kConstraintsSameTrajectoryMetric->Set(inter_constraints_same_trajectory);
00504 kConstraintsDifferentTrajectoryMetric->Set(
00505 inter_constraints_different_trajectory);
00506 }
00507
00508 DrainWorkQueue();
00509 }
00510
00511 void PoseGraph2D::DrainWorkQueue() {
00512 bool process_work_queue = true;
00513 size_t work_queue_size;
00514 while (process_work_queue) {
00515 std::function<WorkItem::Result()> work_item;
00516 {
00517 absl::MutexLock locker(&work_queue_mutex_);
00518 if (work_queue_->empty()) {
00519 work_queue_.reset();
00520 return;
00521 }
00522 work_item = work_queue_->front().task;
00523 work_queue_->pop_front();
00524 work_queue_size = work_queue_->size();
00525 }
00526 process_work_queue = work_item() == WorkItem::Result::kDoNotRunOptimization;
00527 }
00528 LOG(INFO) << "Remaining work items in queue: " << work_queue_size;
00529
00530 constraint_builder_.WhenDone(
00531 [this](const constraints::ConstraintBuilder2D::Result& result) {
00532 HandleWorkQueue(result);
00533 });
00534 }
00535
00536 void PoseGraph2D::WaitForAllComputations() {
00537 int num_trajectory_nodes;
00538 {
00539 absl::MutexLock locker(&mutex_);
00540 num_trajectory_nodes = data_.num_trajectory_nodes;
00541 }
00542
00543 const int num_finished_nodes_at_start =
00544 constraint_builder_.GetNumFinishedNodes();
00545
00546 auto report_progress = [this, num_trajectory_nodes,
00547 num_finished_nodes_at_start]() {
00548
00549 if (num_trajectory_nodes != num_finished_nodes_at_start) {
00550 std::ostringstream progress_info;
00551 progress_info << "Optimizing: " << std::fixed << std::setprecision(1)
00552 << 100. *
00553 (constraint_builder_.GetNumFinishedNodes() -
00554 num_finished_nodes_at_start) /
00555 (num_trajectory_nodes - num_finished_nodes_at_start)
00556 << "%...";
00557 std::cout << "\r\x1b[K" << progress_info.str() << std::flush;
00558 }
00559 };
00560
00561
00562
00563 {
00564 const auto predicate = [this]()
00565 EXCLUSIVE_LOCKS_REQUIRED(work_queue_mutex_) {
00566 return work_queue_ == nullptr;
00567 };
00568 absl::MutexLock locker(&work_queue_mutex_);
00569 while (!work_queue_mutex_.AwaitWithTimeout(
00570 absl::Condition(&predicate),
00571 absl::FromChrono(common::FromSeconds(1.)))) {
00572 report_progress();
00573 }
00574 }
00575
00576
00577 absl::MutexLock locker(&mutex_);
00578 bool notification = false;
00579 constraint_builder_.WhenDone(
00580 [this,
00581 ¬ification](const constraints::ConstraintBuilder2D::Result& result)
00582 LOCKS_EXCLUDED(mutex_) {
00583 absl::MutexLock locker(&mutex_);
00584 data_.constraints.insert(data_.constraints.end(), result.begin(),
00585 result.end());
00586 notification = true;
00587 });
00588 const auto predicate = [¬ification]() EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
00589 return notification;
00590 };
00591 while (!mutex_.AwaitWithTimeout(absl::Condition(&predicate),
00592 absl::FromChrono(common::FromSeconds(1.)))) {
00593 report_progress();
00594 }
00595 CHECK_EQ(constraint_builder_.GetNumFinishedNodes(), num_trajectory_nodes);
00596 std::cout << "\r\x1b[KOptimizing: Done. " << std::endl;
00597 }
00598
00599 void PoseGraph2D::DeleteTrajectory(const int trajectory_id) {
00600 {
00601 absl::MutexLock locker(&mutex_);
00602 auto it = data_.trajectories_state.find(trajectory_id);
00603 if (it == data_.trajectories_state.end()) {
00604 LOG(WARNING) << "Skipping request to delete non-existing trajectory_id: "
00605 << trajectory_id;
00606 return;
00607 }
00608 it->second.deletion_state =
00609 InternalTrajectoryState::DeletionState::SCHEDULED_FOR_DELETION;
00610 }
00611 AddWorkItem([this, trajectory_id]() LOCKS_EXCLUDED(mutex_) {
00612 absl::MutexLock locker(&mutex_);
00613 CHECK(data_.trajectories_state.at(trajectory_id).state !=
00614 TrajectoryState::ACTIVE);
00615 CHECK(data_.trajectories_state.at(trajectory_id).state !=
00616 TrajectoryState::DELETED);
00617 CHECK(data_.trajectories_state.at(trajectory_id).deletion_state ==
00618 InternalTrajectoryState::DeletionState::SCHEDULED_FOR_DELETION);
00619 data_.trajectories_state.at(trajectory_id).deletion_state =
00620 InternalTrajectoryState::DeletionState::WAIT_FOR_DELETION;
00621 return WorkItem::Result::kDoNotRunOptimization;
00622 });
00623 }
00624
00625 void PoseGraph2D::FinishTrajectory(const int trajectory_id) {
00626 AddWorkItem([this, trajectory_id]() LOCKS_EXCLUDED(mutex_) {
00627 absl::MutexLock locker(&mutex_);
00628 CHECK(!IsTrajectoryFinished(trajectory_id));
00629 data_.trajectories_state[trajectory_id].state = TrajectoryState::FINISHED;
00630
00631 for (const auto& submap : data_.submap_data.trajectory(trajectory_id)) {
00632 data_.submap_data.at(submap.id).state = SubmapState::kFinished;
00633 }
00634 return WorkItem::Result::kRunOptimization;
00635 });
00636 }
00637
00638 bool PoseGraph2D::IsTrajectoryFinished(const int trajectory_id) const {
00639 return data_.trajectories_state.count(trajectory_id) != 0 &&
00640 data_.trajectories_state.at(trajectory_id).state ==
00641 TrajectoryState::FINISHED;
00642 }
00643
00644 void PoseGraph2D::FreezeTrajectory(const int trajectory_id) {
00645 {
00646 absl::MutexLock locker(&mutex_);
00647 data_.trajectory_connectivity_state.Add(trajectory_id);
00648 }
00649 AddWorkItem([this, trajectory_id]() LOCKS_EXCLUDED(mutex_) {
00650 absl::MutexLock locker(&mutex_);
00651 CHECK(!IsTrajectoryFrozen(trajectory_id));
00652 data_.trajectories_state[trajectory_id].state = TrajectoryState::FROZEN;
00653 return WorkItem::Result::kDoNotRunOptimization;
00654 });
00655 }
00656
00657 bool PoseGraph2D::IsTrajectoryFrozen(const int trajectory_id) const {
00658 return data_.trajectories_state.count(trajectory_id) != 0 &&
00659 data_.trajectories_state.at(trajectory_id).state ==
00660 TrajectoryState::FROZEN;
00661 }
00662
00663 void PoseGraph2D::AddSubmapFromProto(
00664 const transform::Rigid3d& global_submap_pose, const proto::Submap& submap) {
00665 if (!submap.has_submap_2d()) {
00666 return;
00667 }
00668
00669 const SubmapId submap_id = {submap.submap_id().trajectory_id(),
00670 submap.submap_id().submap_index()};
00671
00672 const transform::Rigid2d global_submap_pose_2d =
00673 transform::Project2D(global_submap_pose);
00674 {
00675 absl::MutexLock locker(&mutex_);
00676 const std::shared_ptr<const Submap2D> submap_ptr =
00677 std::make_shared<const Submap2D>(submap.submap_2d(),
00678 &conversion_tables_);
00679 AddTrajectoryIfNeeded(submap_id.trajectory_id);
00680 if (!CanAddWorkItemModifying(submap_id.trajectory_id)) return;
00681 data_.submap_data.Insert(submap_id, InternalSubmapData());
00682 data_.submap_data.at(submap_id).submap = submap_ptr;
00683
00684 data_.global_submap_poses_2d.Insert(
00685 submap_id, optimization::SubmapSpec2D{global_submap_pose_2d});
00686 }
00687
00688
00689
00690 if (IsTrajectoryFrozen(submap_id.trajectory_id)) {
00691 kFrozenSubmapsMetric->Increment();
00692 } else {
00693 kActiveSubmapsMetric->Increment();
00694 }
00695
00696 AddWorkItem(
00697 [this, submap_id, global_submap_pose_2d]() LOCKS_EXCLUDED(mutex_) {
00698 absl::MutexLock locker(&mutex_);
00699 data_.submap_data.at(submap_id).state = SubmapState::kFinished;
00700 optimization_problem_->InsertSubmap(submap_id, global_submap_pose_2d);
00701 return WorkItem::Result::kDoNotRunOptimization;
00702 });
00703 }
00704
00705 void PoseGraph2D::AddNodeFromProto(const transform::Rigid3d& global_pose,
00706 const proto::Node& node) {
00707 const NodeId node_id = {node.node_id().trajectory_id(),
00708 node.node_id().node_index()};
00709 std::shared_ptr<const TrajectoryNode::Data> constant_data =
00710 std::make_shared<const TrajectoryNode::Data>(FromProto(node.node_data()));
00711
00712 {
00713 absl::MutexLock locker(&mutex_);
00714 AddTrajectoryIfNeeded(node_id.trajectory_id);
00715 if (!CanAddWorkItemModifying(node_id.trajectory_id)) return;
00716 data_.trajectory_nodes.Insert(node_id,
00717 TrajectoryNode{constant_data, global_pose});
00718 }
00719
00720 AddWorkItem([this, node_id, global_pose]() LOCKS_EXCLUDED(mutex_) {
00721 absl::MutexLock locker(&mutex_);
00722 const auto& constant_data =
00723 data_.trajectory_nodes.at(node_id).constant_data;
00724 const auto gravity_alignment_inverse = transform::Rigid3d::Rotation(
00725 constant_data->gravity_alignment.inverse());
00726 optimization_problem_->InsertTrajectoryNode(
00727 node_id,
00728 optimization::NodeSpec2D{
00729 constant_data->time,
00730 transform::Project2D(constant_data->local_pose *
00731 gravity_alignment_inverse),
00732 transform::Project2D(global_pose * gravity_alignment_inverse),
00733 constant_data->gravity_alignment});
00734 return WorkItem::Result::kDoNotRunOptimization;
00735 });
00736 }
00737
00738 void PoseGraph2D::SetTrajectoryDataFromProto(
00739 const proto::TrajectoryData& data) {
00740 LOG(ERROR) << "not implemented";
00741 }
00742
00743 void PoseGraph2D::AddNodeToSubmap(const NodeId& node_id,
00744 const SubmapId& submap_id) {
00745 AddWorkItem([this, node_id, submap_id]() LOCKS_EXCLUDED(mutex_) {
00746 absl::MutexLock locker(&mutex_);
00747 if (CanAddWorkItemModifying(submap_id.trajectory_id)) {
00748 data_.submap_data.at(submap_id).node_ids.insert(node_id);
00749 }
00750 return WorkItem::Result::kDoNotRunOptimization;
00751 });
00752 }
00753
00754 void PoseGraph2D::AddSerializedConstraints(
00755 const std::vector<Constraint>& constraints) {
00756 AddWorkItem([this, constraints]() LOCKS_EXCLUDED(mutex_) {
00757 absl::MutexLock locker(&mutex_);
00758 for (const auto& constraint : constraints) {
00759 CHECK(data_.trajectory_nodes.Contains(constraint.node_id));
00760 CHECK(data_.submap_data.Contains(constraint.submap_id));
00761 CHECK(data_.trajectory_nodes.at(constraint.node_id).constant_data !=
00762 nullptr);
00763 CHECK(data_.submap_data.at(constraint.submap_id).submap != nullptr);
00764 switch (constraint.tag) {
00765 case Constraint::Tag::INTRA_SUBMAP:
00766 CHECK(data_.submap_data.at(constraint.submap_id)
00767 .node_ids.emplace(constraint.node_id)
00768 .second);
00769 break;
00770 case Constraint::Tag::INTER_SUBMAP:
00771 UpdateTrajectoryConnectivity(constraint);
00772 break;
00773 }
00774 const Constraint::Pose pose = {
00775 constraint.pose.zbar_ij *
00776 transform::Rigid3d::Rotation(
00777 data_.trajectory_nodes.at(constraint.node_id)
00778 .constant_data->gravity_alignment.inverse()),
00779 constraint.pose.translation_weight, constraint.pose.rotation_weight};
00780 data_.constraints.push_back(Constraint{
00781 constraint.submap_id, constraint.node_id, pose, constraint.tag});
00782 }
00783 LOG(INFO) << "Loaded " << constraints.size() << " constraints.";
00784 return WorkItem::Result::kDoNotRunOptimization;
00785 });
00786 }
00787
00788 void PoseGraph2D::AddTrimmer(std::unique_ptr<PoseGraphTrimmer> trimmer) {
00789
00790 PoseGraphTrimmer* const trimmer_ptr = trimmer.release();
00791 AddWorkItem([this, trimmer_ptr]() LOCKS_EXCLUDED(mutex_) {
00792 absl::MutexLock locker(&mutex_);
00793 trimmers_.emplace_back(trimmer_ptr);
00794 return WorkItem::Result::kDoNotRunOptimization;
00795 });
00796 }
00797
00798 void PoseGraph2D::RunFinalOptimization() {
00799 {
00800 AddWorkItem([this]() LOCKS_EXCLUDED(mutex_) {
00801 absl::MutexLock locker(&mutex_);
00802 optimization_problem_->SetMaxNumIterations(
00803 options_.max_num_final_iterations());
00804 return WorkItem::Result::kRunOptimization;
00805 });
00806 AddWorkItem([this]() LOCKS_EXCLUDED(mutex_) {
00807 absl::MutexLock locker(&mutex_);
00808 optimization_problem_->SetMaxNumIterations(
00809 options_.optimization_problem_options()
00810 .ceres_solver_options()
00811 .max_num_iterations());
00812 return WorkItem::Result::kDoNotRunOptimization;
00813 });
00814 }
00815 WaitForAllComputations();
00816 }
00817
00818 void PoseGraph2D::RunOptimization() {
00819 if (optimization_problem_->submap_data().empty()) {
00820 return;
00821 }
00822
00823
00824
00825
00826
00827 optimization_problem_->Solve(data_.constraints, GetTrajectoryStates(),
00828 data_.landmark_nodes);
00829 absl::MutexLock locker(&mutex_);
00830
00831 const auto& submap_data = optimization_problem_->submap_data();
00832 const auto& node_data = optimization_problem_->node_data();
00833 for (const int trajectory_id : node_data.trajectory_ids()) {
00834 for (const auto& node : node_data.trajectory(trajectory_id)) {
00835 auto& mutable_trajectory_node = data_.trajectory_nodes.at(node.id);
00836 mutable_trajectory_node.global_pose =
00837 transform::Embed3D(node.data.global_pose_2d) *
00838 transform::Rigid3d::Rotation(
00839 mutable_trajectory_node.constant_data->gravity_alignment);
00840 }
00841
00842
00843
00844 const auto local_to_new_global =
00845 ComputeLocalToGlobalTransform(submap_data, trajectory_id);
00846 const auto local_to_old_global = ComputeLocalToGlobalTransform(
00847 data_.global_submap_poses_2d, trajectory_id);
00848 const transform::Rigid3d old_global_to_new_global =
00849 local_to_new_global * local_to_old_global.inverse();
00850
00851 const NodeId last_optimized_node_id =
00852 std::prev(node_data.EndOfTrajectory(trajectory_id))->id;
00853 auto node_it =
00854 std::next(data_.trajectory_nodes.find(last_optimized_node_id));
00855 for (; node_it != data_.trajectory_nodes.EndOfTrajectory(trajectory_id);
00856 ++node_it) {
00857 auto& mutable_trajectory_node = data_.trajectory_nodes.at(node_it->id);
00858 mutable_trajectory_node.global_pose =
00859 old_global_to_new_global * mutable_trajectory_node.global_pose;
00860 }
00861 }
00862 for (const auto& landmark : optimization_problem_->landmark_data()) {
00863 data_.landmark_nodes[landmark.first].global_landmark_pose = landmark.second;
00864 }
00865 data_.global_submap_poses_2d = submap_data;
00866 }
00867
00868 bool PoseGraph2D::CanAddWorkItemModifying(int trajectory_id) {
00869 auto it = data_.trajectories_state.find(trajectory_id);
00870 if (it == data_.trajectories_state.end()) {
00871 return true;
00872 }
00873 if (it->second.state == TrajectoryState::FINISHED) {
00874
00875 LOG(FATAL) << "trajectory_id " << trajectory_id
00876 << " has finished "
00877 "but modification is requested, skipping.";
00878 return false;
00879 }
00880 if (it->second.deletion_state !=
00881 InternalTrajectoryState::DeletionState::NORMAL) {
00882 LOG(FATAL) << "trajectory_id " << trajectory_id
00883 << " has been scheduled for deletion "
00884 "but modification is requested, skipping.";
00885 return false;
00886 }
00887 if (it->second.state == TrajectoryState::DELETED) {
00888 LOG(FATAL) << "trajectory_id " << trajectory_id
00889 << " has been deleted "
00890 "but modification is requested, skipping.";
00891 return false;
00892 }
00893 return true;
00894 }
00895
00896 MapById<NodeId, TrajectoryNode> PoseGraph2D::GetTrajectoryNodes() const {
00897 absl::MutexLock locker(&mutex_);
00898 return data_.trajectory_nodes;
00899 }
00900
00901 MapById<NodeId, TrajectoryNodePose> PoseGraph2D::GetTrajectoryNodePoses()
00902 const {
00903 MapById<NodeId, TrajectoryNodePose> node_poses;
00904 absl::MutexLock locker(&mutex_);
00905 for (const auto& node_id_data : data_.trajectory_nodes) {
00906 absl::optional<TrajectoryNodePose::ConstantPoseData> constant_pose_data;
00907 if (node_id_data.data.constant_data != nullptr) {
00908 constant_pose_data = TrajectoryNodePose::ConstantPoseData{
00909 node_id_data.data.constant_data->time,
00910 node_id_data.data.constant_data->local_pose};
00911 }
00912 node_poses.Insert(
00913 node_id_data.id,
00914 TrajectoryNodePose{node_id_data.data.global_pose, constant_pose_data});
00915 }
00916 return node_poses;
00917 }
00918
00919 std::map<int, PoseGraphInterface::TrajectoryState>
00920 PoseGraph2D::GetTrajectoryStates() const {
00921 std::map<int, PoseGraphInterface::TrajectoryState> trajectories_state;
00922 absl::MutexLock locker(&mutex_);
00923 for (const auto& it : data_.trajectories_state) {
00924 trajectories_state[it.first] = it.second.state;
00925 }
00926 return trajectories_state;
00927 }
00928
00929 std::map<std::string, transform::Rigid3d> PoseGraph2D::GetLandmarkPoses()
00930 const {
00931 std::map<std::string, transform::Rigid3d> landmark_poses;
00932 absl::MutexLock locker(&mutex_);
00933 for (const auto& landmark : data_.landmark_nodes) {
00934
00935 if (!landmark.second.global_landmark_pose.has_value()) continue;
00936 landmark_poses[landmark.first] =
00937 landmark.second.global_landmark_pose.value();
00938 }
00939 return landmark_poses;
00940 }
00941
00942 void PoseGraph2D::SetLandmarkPose(const std::string& landmark_id,
00943 const transform::Rigid3d& global_pose,
00944 const bool frozen) {
00945 AddWorkItem([=]() LOCKS_EXCLUDED(mutex_) {
00946 absl::MutexLock locker(&mutex_);
00947 data_.landmark_nodes[landmark_id].global_landmark_pose = global_pose;
00948 data_.landmark_nodes[landmark_id].frozen = frozen;
00949 return WorkItem::Result::kDoNotRunOptimization;
00950 });
00951 }
00952
00953 sensor::MapByTime<sensor::ImuData> PoseGraph2D::GetImuData() const {
00954 absl::MutexLock locker(&mutex_);
00955 return optimization_problem_->imu_data();
00956 }
00957
00958 sensor::MapByTime<sensor::OdometryData> PoseGraph2D::GetOdometryData() const {
00959 absl::MutexLock locker(&mutex_);
00960 return optimization_problem_->odometry_data();
00961 }
00962
00963 std::map<std::string , PoseGraphInterface::LandmarkNode>
00964 PoseGraph2D::GetLandmarkNodes() const {
00965 absl::MutexLock locker(&mutex_);
00966 return data_.landmark_nodes;
00967 }
00968
00969 std::map<int, PoseGraphInterface::TrajectoryData>
00970 PoseGraph2D::GetTrajectoryData() const {
00971
00972 return {};
00973 }
00974
00975 sensor::MapByTime<sensor::FixedFramePoseData>
00976 PoseGraph2D::GetFixedFramePoseData() const {
00977
00978
00979 return {};
00980 }
00981
00982 std::vector<PoseGraphInterface::Constraint> PoseGraph2D::constraints() const {
00983 std::vector<PoseGraphInterface::Constraint> result;
00984 absl::MutexLock locker(&mutex_);
00985 for (const Constraint& constraint : data_.constraints) {
00986 result.push_back(Constraint{
00987 constraint.submap_id, constraint.node_id,
00988 Constraint::Pose{constraint.pose.zbar_ij *
00989 transform::Rigid3d::Rotation(
00990 data_.trajectory_nodes.at(constraint.node_id)
00991 .constant_data->gravity_alignment),
00992 constraint.pose.translation_weight,
00993 constraint.pose.rotation_weight},
00994 constraint.tag});
00995 }
00996 return result;
00997 }
00998
00999 void PoseGraph2D::SetInitialTrajectoryPose(const int from_trajectory_id,
01000 const int to_trajectory_id,
01001 const transform::Rigid3d& pose,
01002 const common::Time time) {
01003 absl::MutexLock locker(&mutex_);
01004 data_.initial_trajectory_poses[from_trajectory_id] =
01005 InitialTrajectoryPose{to_trajectory_id, pose, time};
01006 }
01007
01008 transform::Rigid3d PoseGraph2D::GetInterpolatedGlobalTrajectoryPose(
01009 const int trajectory_id, const common::Time time) const {
01010 CHECK_GT(data_.trajectory_nodes.SizeOfTrajectoryOrZero(trajectory_id), 0);
01011 const auto it = data_.trajectory_nodes.lower_bound(trajectory_id, time);
01012 if (it == data_.trajectory_nodes.BeginOfTrajectory(trajectory_id)) {
01013 return data_.trajectory_nodes.BeginOfTrajectory(trajectory_id)
01014 ->data.global_pose;
01015 }
01016 if (it == data_.trajectory_nodes.EndOfTrajectory(trajectory_id)) {
01017 return std::prev(data_.trajectory_nodes.EndOfTrajectory(trajectory_id))
01018 ->data.global_pose;
01019 }
01020 return transform::Interpolate(
01021 transform::TimestampedTransform{std::prev(it)->data.time(),
01022 std::prev(it)->data.global_pose},
01023 transform::TimestampedTransform{it->data.time(),
01024 it->data.global_pose},
01025 time)
01026 .transform;
01027 }
01028
01029 transform::Rigid3d PoseGraph2D::GetLocalToGlobalTransform(
01030 const int trajectory_id) const {
01031 absl::MutexLock locker(&mutex_);
01032 return ComputeLocalToGlobalTransform(data_.global_submap_poses_2d,
01033 trajectory_id);
01034 }
01035
01036 std::vector<std::vector<int>> PoseGraph2D::GetConnectedTrajectories() const {
01037 absl::MutexLock locker(&mutex_);
01038 return data_.trajectory_connectivity_state.Components();
01039 }
01040
01041 PoseGraphInterface::SubmapData PoseGraph2D::GetSubmapData(
01042 const SubmapId& submap_id) const {
01043 absl::MutexLock locker(&mutex_);
01044 return GetSubmapDataUnderLock(submap_id);
01045 }
01046
01047 MapById<SubmapId, PoseGraphInterface::SubmapData>
01048 PoseGraph2D::GetAllSubmapData() const {
01049 absl::MutexLock locker(&mutex_);
01050 return GetSubmapDataUnderLock();
01051 }
01052
01053 MapById<SubmapId, PoseGraphInterface::SubmapPose>
01054 PoseGraph2D::GetAllSubmapPoses() const {
01055 absl::MutexLock locker(&mutex_);
01056 MapById<SubmapId, SubmapPose> submap_poses;
01057 for (const auto& submap_id_data : data_.submap_data) {
01058 auto submap_data = GetSubmapDataUnderLock(submap_id_data.id);
01059 submap_poses.Insert(
01060 submap_id_data.id,
01061 PoseGraph::SubmapPose{submap_data.submap->num_range_data(),
01062 submap_data.pose});
01063 }
01064 return submap_poses;
01065 }
01066
01067 transform::Rigid3d PoseGraph2D::ComputeLocalToGlobalTransform(
01068 const MapById<SubmapId, optimization::SubmapSpec2D>& global_submap_poses,
01069 const int trajectory_id) const {
01070 auto begin_it = global_submap_poses.BeginOfTrajectory(trajectory_id);
01071 auto end_it = global_submap_poses.EndOfTrajectory(trajectory_id);
01072 if (begin_it == end_it) {
01073 const auto it = data_.initial_trajectory_poses.find(trajectory_id);
01074 if (it != data_.initial_trajectory_poses.end()) {
01075 return GetInterpolatedGlobalTrajectoryPose(it->second.to_trajectory_id,
01076 it->second.time) *
01077 it->second.relative_pose;
01078 } else {
01079 return transform::Rigid3d::Identity();
01080 }
01081 }
01082 const SubmapId last_optimized_submap_id = std::prev(end_it)->id;
01083
01084 return transform::Embed3D(
01085 global_submap_poses.at(last_optimized_submap_id).global_pose) *
01086 data_.submap_data.at(last_optimized_submap_id)
01087 .submap->local_pose()
01088 .inverse();
01089 }
01090
01091 PoseGraphInterface::SubmapData PoseGraph2D::GetSubmapDataUnderLock(
01092 const SubmapId& submap_id) const {
01093 const auto it = data_.submap_data.find(submap_id);
01094 if (it == data_.submap_data.end()) {
01095 return {};
01096 }
01097 auto submap = it->data.submap;
01098 if (data_.global_submap_poses_2d.Contains(submap_id)) {
01099
01100 return {submap,
01101 transform::Embed3D(
01102 data_.global_submap_poses_2d.at(submap_id).global_pose)};
01103 }
01104
01105 return {submap, ComputeLocalToGlobalTransform(data_.global_submap_poses_2d,
01106 submap_id.trajectory_id) *
01107 submap->local_pose()};
01108 }
01109
01110 PoseGraph2D::TrimmingHandle::TrimmingHandle(PoseGraph2D* const parent)
01111 : parent_(parent) {}
01112
01113 int PoseGraph2D::TrimmingHandle::num_submaps(const int trajectory_id) const {
01114 const auto& submap_data = parent_->optimization_problem_->submap_data();
01115 return submap_data.SizeOfTrajectoryOrZero(trajectory_id);
01116 }
01117
01118 MapById<SubmapId, PoseGraphInterface::SubmapData>
01119 PoseGraph2D::TrimmingHandle::GetOptimizedSubmapData() const {
01120 MapById<SubmapId, PoseGraphInterface::SubmapData> submaps;
01121 for (const auto& submap_id_data : parent_->data_.submap_data) {
01122 if (submap_id_data.data.state != SubmapState::kFinished ||
01123 !parent_->data_.global_submap_poses_2d.Contains(submap_id_data.id)) {
01124 continue;
01125 }
01126 submaps.Insert(
01127 submap_id_data.id,
01128 SubmapData{submap_id_data.data.submap,
01129 transform::Embed3D(parent_->data_.global_submap_poses_2d
01130 .at(submap_id_data.id)
01131 .global_pose)});
01132 }
01133 return submaps;
01134 }
01135
01136 std::vector<SubmapId> PoseGraph2D::TrimmingHandle::GetSubmapIds(
01137 int trajectory_id) const {
01138 std::vector<SubmapId> submap_ids;
01139 const auto& submap_data = parent_->optimization_problem_->submap_data();
01140 for (const auto& it : submap_data.trajectory(trajectory_id)) {
01141 submap_ids.push_back(it.id);
01142 }
01143 return submap_ids;
01144 }
01145
01146 const MapById<NodeId, TrajectoryNode>&
01147 PoseGraph2D::TrimmingHandle::GetTrajectoryNodes() const {
01148 return parent_->data_.trajectory_nodes;
01149 }
01150
01151 const std::vector<PoseGraphInterface::Constraint>&
01152 PoseGraph2D::TrimmingHandle::GetConstraints() const {
01153 return parent_->data_.constraints;
01154 }
01155
01156 bool PoseGraph2D::TrimmingHandle::IsFinished(const int trajectory_id) const {
01157 return parent_->IsTrajectoryFinished(trajectory_id);
01158 }
01159
01160 void PoseGraph2D::TrimmingHandle::SetTrajectoryState(int trajectory_id,
01161 TrajectoryState state) {
01162 parent_->data_.trajectories_state[trajectory_id].state = state;
01163 }
01164
01165 void PoseGraph2D::TrimmingHandle::TrimSubmap(const SubmapId& submap_id) {
01166
01167
01168 CHECK(parent_->data_.submap_data.at(submap_id).state ==
01169 SubmapState::kFinished);
01170
01171
01172
01173 std::set<NodeId> nodes_to_retain;
01174 for (const Constraint& constraint : parent_->data_.constraints) {
01175 if (constraint.tag == Constraint::Tag::INTRA_SUBMAP &&
01176 constraint.submap_id != submap_id) {
01177 nodes_to_retain.insert(constraint.node_id);
01178 }
01179 }
01180
01181 std::set<NodeId> nodes_to_remove;
01182 {
01183 std::vector<Constraint> constraints;
01184 for (const Constraint& constraint : parent_->data_.constraints) {
01185 if (constraint.submap_id == submap_id) {
01186 if (constraint.tag == Constraint::Tag::INTRA_SUBMAP &&
01187 nodes_to_retain.count(constraint.node_id) == 0) {
01188
01189
01190 nodes_to_remove.insert(constraint.node_id);
01191 }
01192 } else {
01193 constraints.push_back(constraint);
01194 }
01195 }
01196 parent_->data_.constraints = std::move(constraints);
01197 }
01198
01199 {
01200 std::vector<Constraint> constraints;
01201 for (const Constraint& constraint : parent_->data_.constraints) {
01202 if (nodes_to_remove.count(constraint.node_id) == 0) {
01203 constraints.push_back(constraint);
01204 }
01205 }
01206 parent_->data_.constraints = std::move(constraints);
01207 }
01208
01209
01210 CHECK(parent_->data_.submap_data.at(submap_id).state ==
01211 SubmapState::kFinished);
01212 parent_->data_.submap_data.Trim(submap_id);
01213 parent_->constraint_builder_.DeleteScanMatcher(submap_id);
01214 parent_->optimization_problem_->TrimSubmap(submap_id);
01215
01216
01217 kDeletedSubmapsMetric->Increment();
01218 if (parent_->IsTrajectoryFrozen(submap_id.trajectory_id)) {
01219 kFrozenSubmapsMetric->Decrement();
01220 } else {
01221 kActiveSubmapsMetric->Decrement();
01222 }
01223
01224
01225
01226 for (const NodeId& node_id : nodes_to_remove) {
01227 parent_->data_.trajectory_nodes.Trim(node_id);
01228 parent_->optimization_problem_->TrimTrajectoryNode(node_id);
01229 }
01230 }
01231
01232 MapById<SubmapId, PoseGraphInterface::SubmapData>
01233 PoseGraph2D::GetSubmapDataUnderLock() const {
01234 MapById<SubmapId, PoseGraphInterface::SubmapData> submaps;
01235 for (const auto& submap_id_data : data_.submap_data) {
01236 submaps.Insert(submap_id_data.id,
01237 GetSubmapDataUnderLock(submap_id_data.id));
01238 }
01239 return submaps;
01240 }
01241
01242 void PoseGraph2D::SetGlobalSlamOptimizationCallback(
01243 PoseGraphInterface::GlobalSlamOptimizationCallback callback) {
01244 global_slam_optimization_callback_ = callback;
01245 }
01246
01247 void PoseGraph2D::RegisterMetrics(metrics::FamilyFactory* family_factory) {
01248 auto* latency = family_factory->NewGaugeFamily(
01249 "mapping_2d_pose_graph_work_queue_delay",
01250 "Age of the oldest entry in the work queue in seconds");
01251 kWorkQueueDelayMetric = latency->Add({});
01252 auto* constraints = family_factory->NewGaugeFamily(
01253 "mapping_2d_pose_graph_constraints",
01254 "Current number of constraints in the pose graph");
01255 kConstraintsDifferentTrajectoryMetric =
01256 constraints->Add({{"tag", "inter_submap"}, {"trajectory", "different"}});
01257 kConstraintsSameTrajectoryMetric =
01258 constraints->Add({{"tag", "inter_submap"}, {"trajectory", "same"}});
01259 auto* submaps = family_factory->NewGaugeFamily(
01260 "mapping_2d_pose_graph_submaps", "Number of submaps in the pose graph.");
01261 kActiveSubmapsMetric = submaps->Add({{"state", "active"}});
01262 kFrozenSubmapsMetric = submaps->Add({{"state", "frozen"}});
01263 kDeletedSubmapsMetric = submaps->Add({{"state", "deleted"}});
01264 }
01265
01266 }
01267 }