pose_graph_2d.cc
Go to the documentation of this file.
00001 /*
00002  * Copyright 2016 The Cartographer Authors
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *      http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
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     // If we don't already have an entry for the first submap, add one.
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     // In this case, 'last_submap_id' is the ID of
00106     // 'insertions_submaps.front()' and 'insertions_submaps.back()' is new.
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   // Test if the 'insertion_submap.back()' is one we never saw before.
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     // We grow 'data_.submap_data' as needed. This code assumes that the first
00143     // time we see a new submap is as 'insertion_submaps.back()'.
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   // We have to check this here, because it might have changed by the time we
00163   // execute the lambda.
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   // Make sure we have a sampler for this trajectory.
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       // Uplink server only receives grids when they are finished, so skip
00263       // constraint search before that.
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       // If the node and the submap belong to the same trajectory or if there
00277       // has been a recent global constraint that ties that node's trajectory to
00278       // the submap's trajectory, it suffices to do a match constrained to a
00279       // local search window.
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       // Even if this was the last node added to 'submap_id', the submap will
00334       // only be marked as finished in 'data_.submap_data' further below.
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     // TODO(gaschler): Consider not searching for constraints against
00351     // trajectories scheduled for deletion.
00352     // TODO(danielsievers): Add a member variable and avoid having to copy
00353     // them out here.
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     // We have a new completed submap, so we look into adding constraints for
00377     // old nodes.
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       // TODO(gaschler): Consider directly deleting from data_, which may be
00424       // more complete.
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     // Update the gauges that count the current number of constraints.
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   // We have to optimize again.
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     // Log progress on nodes only when we are actually processing nodes.
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   // First wait for the work queue to drain so that it's safe to schedule
00562   // a WhenDone() callback.
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   // Now wait for any pending constraint computations to finish.
00577   absl::MutexLock locker(&mutex_);
00578   bool notification = false;
00579   constraint_builder_.WhenDone(
00580       [this,
00581        &notification](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 = [&notification]() 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     // Immediately show the submap at the 'global_submap_pose'.
00684     data_.global_submap_poses_2d.Insert(
00685         submap_id, optimization::SubmapSpec2D{global_submap_pose_2d});
00686   }
00687 
00688   // TODO(MichaelGrupp): MapBuilder does freezing before deserializing submaps,
00689   // so this should be fine.
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   // C++11 does not allow us to move a unique_ptr into a lambda.
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   // No other thread is accessing the optimization_problem_,
00824   // data_.constraints, data_.frozen_trajectories and data_.landmark_nodes
00825   // when executing the Solve. Solve is time consuming, so not taking the mutex
00826   // before Solve to avoid blocking foreground processing.
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     // Extrapolate all point cloud poses that were not included in the
00843     // 'optimization_problem_' yet.
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     // TODO(gaschler): Replace all FATAL to WARNING after some testing.
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     // Landmark without value has not been optimized yet.
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 /* landmark ID */, 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   // The 2D optimization problem does not have any 'TrajectoryData'.
00972   return {};
00973 }
00974 
00975 sensor::MapByTime<sensor::FixedFramePoseData>
00976 PoseGraph2D::GetFixedFramePoseData() const {
00977   // FixedFramePoseData is not yet implemented for 2D. We need to return empty
00978   // so serialization works.
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   // Accessing 'local_pose' in Submap is okay, since the member is const.
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     // We already have an optimized pose.
01100     return {submap,
01101             transform::Embed3D(
01102                 data_.global_submap_poses_2d.at(submap_id).global_pose)};
01103   }
01104   // We have to extrapolate.
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   // TODO(hrapp): We have to make sure that the trajectory has been finished
01167   // if we want to delete the last submaps.
01168   CHECK(parent_->data_.submap_data.at(submap_id).state ==
01169         SubmapState::kFinished);
01170 
01171   // Compile all nodes that are still INTRA_SUBMAP constrained once the submap
01172   // with 'submap_id' is gone.
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   // Remove all 'data_.constraints' related to 'submap_id'.
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           // This node will no longer be INTRA_SUBMAP contrained and has to be
01189           // removed.
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   // Remove all 'data_.constraints' related to 'nodes_to_remove'.
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   // Mark the submap with 'submap_id' as trimmed and remove its data.
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   // We have one submap less, update the gauge metrics.
01217   kDeletedSubmapsMetric->Increment();
01218   if (parent_->IsTrajectoryFrozen(submap_id.trajectory_id)) {
01219     kFrozenSubmapsMetric->Decrement();
01220   } else {
01221     kActiveSubmapsMetric->Decrement();
01222   }
01223 
01224   // Remove the 'nodes_to_remove' from the pose graph and the optimization
01225   // problem.
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 }  // namespace mapping
01267 }  // namespace cartographer


cartographer
Author(s): The Cartographer Authors
autogenerated on Thu May 9 2019 02:27:35