30 #include "Eigen/Eigenvalues" 33 #include "cartographer/mapping/proto/pose_graph/constraint_builder_options.pb.h" 37 #include "glog/logging.h" 43 const proto::PoseGraphOptions& options,
44 std::unique_ptr<optimization::OptimizationProblem3D> optimization_problem,
53 CHECK(work_queue_ ==
nullptr);
58 const std::vector<std::shared_ptr<const Submap3D>>& insertion_submaps) {
59 CHECK(!insertion_submaps.empty());
61 if (insertion_submaps.size() == 1) {
63 if (submap_data.SizeOfTrajectoryOrZero(trajectory_id) == 0) {
64 if (initial_trajectory_poses_.count(trajectory_id) > 0) {
67 initial_trajectory_poses_.at(trajectory_id).to_trajectory_id,
time);
72 insertion_submaps[0]->local_pose());
74 CHECK_EQ(1, submap_data.SizeOfTrajectoryOrZero(trajectory_id));
75 const SubmapId submap_id{trajectory_id, 0};
76 CHECK(submap_data_.at(submap_id).submap == insertion_submaps.front());
79 CHECK_EQ(2, insertion_submaps.size());
80 const auto end_it = submap_data.EndOfTrajectory(trajectory_id);
81 CHECK(submap_data.BeginOfTrajectory(trajectory_id) != end_it);
82 const SubmapId last_submap_id = std::prev(end_it)->id;
83 if (submap_data_.at(last_submap_id).submap == insertion_submaps.front()) {
86 const auto& first_submap_pose = submap_data.at(last_submap_id).global_pose;
88 trajectory_id, first_submap_pose *
89 insertion_submaps[0]->local_pose().inverse() *
90 insertion_submaps[1]->local_pose());
91 return {last_submap_id,
94 CHECK(submap_data_.at(last_submap_id).submap == insertion_submaps.back());
95 const SubmapId front_submap_id{trajectory_id,
97 CHECK(submap_data_.at(front_submap_id).submap == insertion_submaps.front());
98 return {front_submap_id, last_submap_id};
102 std::shared_ptr<const TrajectoryNode::Data> constant_data,
103 const int trajectory_id,
104 const std::vector<std::shared_ptr<const Submap3D>>& insertion_submaps) {
110 const NodeId node_id = trajectory_nodes_.Append(
112 ++num_trajectory_nodes_;
115 if (submap_data_.SizeOfTrajectoryOrZero(trajectory_id) == 0 ||
116 std::prev(submap_data_.EndOfTrajectory(trajectory_id))->data.submap !=
117 insertion_submaps.back()) {
122 submap_data_.at(submap_id).submap = insertion_submaps.back();
127 const bool newly_finished_submap = insertion_submaps.front()->finished();
130 newly_finished_submap);
136 if (work_queue_ ==
nullptr) {
139 work_queue_->push_back(work_item);
146 if (!global_localization_samplers_[trajectory_id]) {
147 global_localization_samplers_[trajectory_id] =
148 common::make_unique<common::FixedRatioSampler>(
170 const int trajectory_id,
175 fixed_frame_pose_data);
184 for (
const auto& observation : landmark_data.landmark_observations) {
185 landmark_nodes_[observation.id].landmark_observations.emplace_back(
186 PoseGraphInterface::LandmarkNode::LandmarkObservation{
187 trajectory_id, landmark_data.time,
188 observation.landmark_to_tracking_transform,
189 observation.translation_weight, observation.rotation_weight});
207 std::vector<TrajectoryNode> submap_nodes;
208 for (
const NodeId& submap_node_id : submap_data_.at(submap_id).node_ids) {
209 submap_nodes.push_back(
211 global_submap_pose_inverse *
212 trajectory_nodes_.at(submap_node_id).global_pose});
221 last_connection_time +
223 options_.global_constraint_search_after_n_seconds())) {
229 submap_id, submap_data_.at(submap_id).submap.get(), node_id,
230 trajectory_nodes_.at(node_id).constant_data.get(), submap_nodes,
231 global_node_pose, global_submap_pose);
232 }
else if (global_localization_samplers_[node_id.
trajectory_id]->Pulse()) {
239 submap_id, submap_data_.at(submap_id).submap.get(), node_id,
240 trajectory_nodes_.at(node_id).constant_data.get(), submap_nodes,
241 global_node_pose.rotation(), global_submap_pose.
rotation());
246 const auto& submap_data = submap_data_.at(submap_id);
248 const NodeId& node_id = node_id_data.id;
249 if (submap_data.node_ids.count(node_id) == 0) {
257 std::vector<std::shared_ptr<const Submap3D>> insertion_submaps,
258 const bool newly_finished_submap) {
259 const auto& constant_data = trajectory_nodes_.at(node_id).constant_data;
261 node_id.
trajectory_id, constant_data->time, insertion_submaps);
262 CHECK_EQ(submap_ids.size(), insertion_submaps.size());
263 const SubmapId matching_id = submap_ids.front();
267 insertion_submaps.front()->local_pose().inverse() * local_pose;
269 matching_id.trajectory_id,
271 for (
size_t i = 0; i < insertion_submaps.size(); ++i) {
272 const SubmapId submap_id = submap_ids[i];
276 submap_data_.at(submap_id).node_ids.emplace(node_id);
278 insertion_submaps[i]->local_pose().
inverse() * local_pose;
279 constraints_.push_back(
282 {constraint_transform,
options_.matcher_translation_weight(),
283 options_.matcher_rotation_weight()},
287 for (
const auto& submap_id_data : submap_data_) {
289 CHECK_EQ(submap_id_data.data.node_ids.count(node_id), 0);
294 if (newly_finished_submap) {
295 const SubmapId finished_submap_id = submap_ids.front();
297 submap_data_.at(finished_submap_id);
305 ++num_nodes_since_last_loop_closure_;
306 CHECK(!run_loop_closure_);
307 if (
options_.optimize_every_n_nodes() > 0 &&
308 num_nodes_since_last_loop_closure_ >
options_.optimize_every_n_nodes()) {
314 run_loop_closure_ =
true;
316 if (work_queue_ ==
nullptr) {
317 work_queue_ = common::make_unique<std::deque<std::function<void()>>>();
327 if (!submap_data.
node_ids.empty()) {
328 const NodeId last_submap_node_id =
329 *submap_data_.at(submap_id).node_ids.rbegin();
331 time, trajectory_nodes_.at(last_submap_node_id).constant_data->time);
349 constraints_.insert(constraints_.end(), result.begin(), result.end());
354 std::map<int, NodeId> trajectory_id_to_last_optimized_node_id;
355 std::map<int, SubmapId> trajectory_id_to_last_optimized_submap_id;
360 for (
const int trajectory_id : node_data.trajectory_ids()) {
361 trajectory_id_to_last_optimized_node_id[trajectory_id] =
362 std::prev(node_data.EndOfTrajectory(trajectory_id))->
id;
363 trajectory_id_to_last_optimized_submap_id[trajectory_id] =
364 std::prev(submap_data.EndOfTrajectory(trajectory_id))->
id;
368 trajectory_id_to_last_optimized_submap_id,
369 trajectory_id_to_last_optimized_node_id);
377 for (
auto& trimmer : trimmers_) {
378 trimmer->Trim(&trimming_handle);
381 std::remove_if(trimmers_.begin(), trimmers_.end(),
382 [](std::unique_ptr<PoseGraphTrimmer>& trimmer) {
383 return trimmer->IsFinished();
387 num_nodes_since_last_loop_closure_ = 0;
388 run_loop_closure_ =
false;
389 while (!run_loop_closure_) {
390 if (work_queue_->empty()) {
394 work_queue_->front()();
395 work_queue_->pop_front();
397 LOG(INFO) <<
"Remaining work items in queue: " << work_queue_->size();
404 bool notification =
false;
406 const int num_finished_nodes_at_start =
408 while (!locker.AwaitWithTimeout(
411 num_trajectory_nodes_) &&
415 std::ostringstream progress_info;
416 progress_info <<
"Optimizing: " << std::fixed << std::setprecision(1)
419 num_finished_nodes_at_start) /
420 (num_trajectory_nodes_ - num_finished_nodes_at_start)
422 std::cout <<
"\r\x1b[K" << progress_info.str() << std::flush;
424 std::cout <<
"\r\x1b[KOptimizing: Done. " << std::endl;
429 constraints_.insert(constraints_.end(), result.begin(), result.end());
432 locker.Await([¬ification]() {
return notification; });
438 CHECK_EQ(finished_trajectories_.count(trajectory_id), 0);
439 finished_trajectories_.insert(trajectory_id);
441 for (
const auto& submap : submap_data_.trajectory(trajectory_id)) {
444 CHECK(!run_loop_closure_);
450 return finished_trajectories_.count(trajectory_id) > 0;
457 CHECK_EQ(frozen_trajectories_.count(trajectory_id), 0);
458 frozen_trajectories_.insert(trajectory_id);
463 return frozen_trajectories_.count(trajectory_id) > 0;
468 if (!submap.has_submap_3d()) {
473 submap.submap_id().submap_index()};
474 std::shared_ptr<const Submap3D> submap_ptr =
475 std::make_shared<const Submap3D>(submap.submap_3d());
480 submap_data_.at(submap_id).submap = submap_ptr;
482 global_submap_poses_.Insert(submap_id,
491 const proto::Node& node) {
493 node.node_id().node_index()};
494 std::shared_ptr<const TrajectoryNode::Data> constant_data =
495 std::make_shared<const TrajectoryNode::Data>(
FromProto(node.node_data()));
499 trajectory_nodes_.Insert(node_id,
TrajectoryNode{constant_data, global_pose});
502 const auto& constant_data = trajectory_nodes_.at(node_id).constant_data;
511 const proto::TrajectoryData& data) {
515 {data.imu_calibration().w(), data.imu_calibration().x(),
516 data.imu_calibration().y(), data.imu_calibration().z()}};
517 if (data.has_fixed_frame_origin_in_map()) {
522 const int trajectory_id = data.trajectory_id();
533 submap_data_.at(submap_id).node_ids.insert(node_id);
541 for (
const auto& constraint : constraints) {
542 CHECK(trajectory_nodes_.Contains(constraint.node_id));
543 CHECK(submap_data_.Contains(constraint.submap_id));
544 CHECK(trajectory_nodes_.at(constraint.node_id).constant_data !=
nullptr);
545 CHECK(submap_data_.at(constraint.submap_id).submap !=
nullptr);
546 switch (constraint.tag) {
547 case Constraint::Tag::INTRA_SUBMAP:
548 CHECK(submap_data_.at(constraint.submap_id)
549 .node_ids.emplace(constraint.node_id)
552 case Constraint::Tag::INTER_SUBMAP:
556 constraints_.push_back(constraint);
558 LOG(INFO) <<
"Loaded " << constraints.size() <<
" constraints.";
575 options_.max_num_final_iterations());
580 options_.optimization_problem_options()
581 .ceres_solver_options()
582 .max_num_iterations());
591 for (
const Constraint& constraint : constraints_) {
592 if (constraint.tag == Constraint::Tag::INTRA_SUBMAP) {
594 trajectory_nodes_.at(constraint.node_id).global_pose;
596 constraint.pose.zbar_ij;
598 global_submap_poses_.at(constraint.submap_id).global_pose;
600 optimized_submap_to_map.
inverse() * optimized_node_to_map;
602 node_to_submap_constraint.
inverse() * optimized_node_to_submap;
603 rotational_residual.
Add(
608 LOG(INFO) <<
"Translational residuals histogram:\n" 609 << translational_residual.
ToString(10);
610 LOG(INFO) <<
"Rotational residuals histogram:\n" 611 << rotational_residual.
ToString(10);
629 for (
const int trajectory_id : node_data.trajectory_ids()) {
630 for (
const auto& node : node_data.trajectory(trajectory_id)) {
631 trajectory_nodes_.at(node.id).global_pose = node.data.global_pose;
636 const auto local_to_new_global =
638 const auto local_to_old_global =
641 local_to_new_global * local_to_old_global.
inverse();
643 const NodeId last_optimized_node_id =
644 std::prev(node_data.EndOfTrajectory(trajectory_id))->
id;
645 auto node_it = std::next(trajectory_nodes_.find(last_optimized_node_id));
646 for (; node_it != trajectory_nodes_.EndOfTrajectory(trajectory_id);
648 auto& mutable_trajectory_node = trajectory_nodes_.at(node_it->id);
649 mutable_trajectory_node.global_pose =
650 old_global_to_new_global * mutable_trajectory_node.global_pose;
654 landmark_nodes_[landmark.first].global_landmark_pose = landmark.second;
656 global_submap_poses_ = submap_data;
659 if (
options_.log_residual_histograms()) {
666 return trajectory_nodes_;
673 for (
const auto& node_id_data : trajectory_nodes_) {
675 if (node_id_data.data.constant_data !=
nullptr) {
677 node_id_data.data.constant_data->
time,
678 node_id_data.data.constant_data->local_pose};
689 std::map<std::string, transform::Rigid3d> landmark_poses;
691 for (
const auto& landmark : landmark_nodes_) {
693 if (!landmark.second.global_landmark_pose.has_value())
continue;
694 landmark_poses[landmark.first] =
695 landmark.second.global_landmark_pose.value();
697 return landmark_poses;
704 landmark_nodes_[landmark_id].global_landmark_pose = global_pose;
727 return landmark_nodes_;
730 std::map<int, PoseGraphInterface::TrajectoryData>
742 const int to_trajectory_id,
746 initial_trajectory_poses_[from_trajectory_id] =
752 CHECK_GT(trajectory_nodes_.SizeOfTrajectoryOrZero(trajectory_id), 0);
753 const auto it = trajectory_nodes_.lower_bound(trajectory_id, time);
754 if (it == trajectory_nodes_.BeginOfTrajectory(trajectory_id)) {
755 return trajectory_nodes_.BeginOfTrajectory(trajectory_id)->data.global_pose;
757 if (it == trajectory_nodes_.EndOfTrajectory(trajectory_id)) {
758 return std::prev(trajectory_nodes_.EndOfTrajectory(trajectory_id))
763 std::prev(it)->data.global_pose},
765 it->data.global_pose},
771 const int trajectory_id)
const {
796 for (
const auto& submap_id_data : submap_data_) {
808 const int trajectory_id)
const {
811 if (begin_it == end_it) {
812 const auto it = initial_trajectory_poses_.find(trajectory_id);
813 if (it != initial_trajectory_poses_.end()) {
816 it->second.relative_pose;
821 const SubmapId last_optimized_submap_id = std::prev(end_it)->id;
823 return global_submap_poses.
at(last_optimized_submap_id).global_pose *
824 submap_data_.at(last_optimized_submap_id)
825 .submap->local_pose()
831 const auto it = submap_data_.find(submap_id);
832 if (it == submap_data_.end()) {
835 auto submap = it->data.submap;
836 if (global_submap_poses_.Contains(submap_id)) {
838 return {submap, global_submap_poses_.at(submap_id).global_pose};
843 submap->local_pose()};
851 return submap_data.SizeOfTrajectoryOrZero(trajectory_id);
855 int trajectory_id)
const {
856 std::vector<SubmapId> submap_ids;
858 for (
const auto& it : submap_data.trajectory(trajectory_id)) {
859 submap_ids.push_back(it.id);
866 for (
const auto& submap_id_data :
parent_->submap_data_) {
868 !
parent_->global_submap_poses_.Contains(submap_id_data.id)) {
874 submap_id_data.data.submap,
875 parent_->global_submap_poses_.at(submap_id_data.id).global_pose});
882 return parent_->trajectory_nodes_;
885 const std::vector<PoseGraphInterface::Constraint>&
902 std::set<NodeId> nodes_to_retain;
904 if (constraint.
tag == Constraint::Tag::INTRA_SUBMAP &&
906 nodes_to_retain.insert(constraint.
node_id);
910 std::set<NodeId> nodes_to_remove;
915 if (constraint.
tag == Constraint::Tag::INTRA_SUBMAP &&
916 nodes_to_retain.count(constraint.
node_id) == 0) {
919 nodes_to_remove.insert(constraint.
node_id);
922 constraints.push_back(constraint);
925 parent_->constraints_ = std::move(constraints);
931 if (nodes_to_remove.count(constraint.
node_id) == 0) {
932 constraints.push_back(constraint);
935 parent_->constraints_ = std::move(constraints);
940 parent_->submap_data_.Trim(submap_id);
941 parent_->constraint_builder_.DeleteScanMatcher(submap_id);
946 for (
const NodeId& node_id : nodes_to_remove) {
947 parent_->trajectory_nodes_.Trim(node_id);
955 for (
const auto& submap_id_data : submap_data_) {
956 submaps.
Insert(submap_id_data.id,
MapById< SubmapId, SubmapData > GetSubmapDataUnderLock() const REQUIRES(mutex_)
PoseGraph::Constraint::Tag FromProto(const proto::PoseGraph::Constraint::Tag &proto)
void WaitForAllComputations() EXCLUDES(mutex_)
void FinishTrajectory(int trajectory_id) override
std::vector< SubmapId > InitializeGlobalSubmapPoses(int trajectory_id, const common::Time time, const std::vector< std::shared_ptr< const Submap3D >> &insertion_submaps) REQUIRES(mutex_)
void SetGlobalSlamOptimizationCallback(PoseGraphInterface::GlobalSlamOptimizationCallback callback) override
void AddTrajectoryIfNeeded(int trajectory_id) REQUIRES(mutex_)
std::unique_ptr< ConstraintBuilder2D > constraint_builder_
void Connect(int trajectory_id_a, int trajectory_id_b, common::Time time)
TrimmingHandle(PoseGraph3D *parent)
std::vector< Constraint > Result
enum cartographer::mapping::PoseGraphInterface::Constraint::Tag tag
void AddWorkItem(const std::function< void()> &work_item) REQUIRES(mutex_)
std::shared_ptr< const Data > constant_data
const DataType & at(const IdType &id) const
common::optional< transform::Rigid3d > fixed_frame_origin_in_map
transform::Rigid3d GetInterpolatedGlobalTrajectoryPose(int trajectory_id, const common::Time time) const REQUIRES(mutex_)
const proto::PoseGraphOptions options_
sensor::MapByTime< sensor::ImuData > GetImuData() const override EXCLUDES(mutex_)
std::function< void(const std::map< int, SubmapId > &, const std::map< int, NodeId > &)> GlobalSlamOptimizationCallback
std::vector< SubmapId > GetSubmapIds(int trajectory_id) const override
void Insert(const IdType &id, const DataType &data)
void SetInitialTrajectoryPose(int from_trajectory_id, int to_trajectory_id, const transform::Rigid3d &pose, const common::Time time) override EXCLUDES(mutex_)
void AddTrimmer(std::unique_ptr< PoseGraphTrimmer > trimmer) override
UniversalTimeScaleClock::time_point Time
void FreezeTrajectory(int trajectory_id) override
void AddLandmarkData(int trajectory_id, const sensor::LandmarkData &landmark_data) override EXCLUDES(mutex_)
void AddImuData(int trajectory_id, const sensor::ImuData &imu_data) override EXCLUDES(mutex_)
void ComputeConstraintsForOldNodes(const SubmapId &submap_id) REQUIRES(mutex_)
ConstIterator EndOfTrajectory(const int trajectory_id) const
void SetLandmarkPose(const std::string &landmark_id, const transform::Rigid3d &global_pose) override EXCLUDES(mutex_)
std::array< double, 4 > imu_calibration
const std::vector< Constraint > & GetConstraints() const override REQUIRES(parent_ -> mutex_)
Duration FromSeconds(const double seconds)
common::Time LastConnectionTime(int trajectory_id_a, int trajectory_id_b)
OptimizationProblem3D optimization_problem_
void Add(int trajectory_id)
std::string ToString(int buckets) const
proto::ProbabilityGridRangeDataInserterOptions2D options_
void AddSubmapFromProto(const transform::Rigid3d &global_submap_pose, const proto::Submap &submap) override
void AddNodeFromProto(const transform::Rigid3d &global_pose, const proto::Node &node) override
MapById< NodeId, TrajectoryNode > GetTrajectoryNodes() const override EXCLUDES(mutex_)
std::map< std::string, transform::Rigid3d > GetLandmarkPoses() const override EXCLUDES(mutex_)
void HandleWorkQueue(const constraints::ConstraintBuilder3D::Result &result) REQUIRES(mutex_)
std::set< NodeId > node_ids
transform::Rigid3d GetLocalToGlobalTransform(int trajectory_id) const EXCLUDES(mutex_) override
bool IsTrajectoryFrozen(int trajectory_id) const override REQUIRES(mutex_)
void ComputeConstraint(const NodeId &node_id, const SubmapId &submap_id) REQUIRES(mutex_)
std::map< int, TrajectoryData > GetTrajectoryData() const override
void UpdateTrajectoryConnectivity(const Constraint &constraint) REQUIRES(mutex_)
PoseGraph::SubmapData GetSubmapData(const SubmapId &submap_id) const EXCLUDES(mutex_) override
void MarkSubmapAsTrimmed(const SubmapId &submap_id) REQUIRES(parent_ -> mutex_) override
PoseGraph3D *const parent_
void ComputeConstraintsForNode(const NodeId &node_id, std::vector< std::shared_ptr< const Submap3D >> insertion_submaps, bool newly_finished_submap) REQUIRES(mutex_)
void AddSerializedConstraints(const std::vector< Constraint > &constraints) override
NodeId AddNode(std::shared_ptr< const TrajectoryNode::Data > constant_data, int trajectory_id, const std::vector< std::shared_ptr< const Submap3D >> &insertion_submaps) EXCLUDES(mutex_)
const MapById< NodeId, TrajectoryNode > & GetTrajectoryNodes() const override REQUIRES(parent_ -> mutex_)
sensor::MapByTime< sensor::FixedFramePoseData > GetFixedFramePoseData() const override EXCLUDES(mutex_)
void AddFixedFramePoseData(int trajectory_id, const sensor::FixedFramePoseData &fixed_frame_pose_data) override EXCLUDES(mutex_)
void AddNodeToSubmap(const NodeId &node_id, const SubmapId &submap_id) override
void AddOdometryData(int trajectory_id, const sensor::OdometryData &odometry_data) override EXCLUDES(mutex_)
void DispatchOptimization() REQUIRES(mutex_)
MapById< SubmapId, SubmapData > GetAllSubmapData() const EXCLUDES(mutex_) override
std::unique_ptr< optimization::OptimizationProblem3D > optimization_problem_
void LogResidualHistograms() const REQUIRES(mutex_)
MapById< SubmapId, SubmapData > GetOptimizedSubmapData() const override REQUIRES(parent_ -> mutex_)
sensor::MapByTime< sensor::OdometryData > GetOdometryData() const override EXCLUDES(mutex_)
PoseGraph3D(const proto::PoseGraphOptions &options, std::unique_ptr< optimization::OptimizationProblem3D > optimization_problem, common::ThreadPool *thread_pool)
void RunOptimization() EXCLUDES(mutex_)
bool IsFinished(int trajectory_id) const override REQUIRES(parent_ -> mutex_)
std::vector< Constraint > constraints() const override EXCLUDES(mutex_)
transform::Rigid3d ComputeLocalToGlobalTransform(const MapById< SubmapId, optimization::SubmapSpec3D > &global_submap_poses, int trajectory_id) const REQUIRES(mutex_)
T NormalizeAngleDifference(T difference)
std::map< std::string, PoseGraph::LandmarkNode > GetLandmarkNodes() const override EXCLUDES(mutex_)
int num_submaps(int trajectory_id) const override
std::vector< std::vector< int > > GetConnectedTrajectories() const override
Mutex::Locker MutexLocker
ConstIterator BeginOfTrajectory(const int trajectory_id) const
bool IsTrajectoryFinished(int trajectory_id) const override REQUIRES(mutex_)
MapById< NodeId, TrajectoryNodePose > GetTrajectoryNodePoses() const override EXCLUDES(mutex_)
GlobalSlamOptimizationCallback global_slam_optimization_callback_
MapById< SubmapId, SubmapPose > GetAllSubmapPoses() const EXCLUDES(mutex_) override
std::vector< std::vector< int > > Components() const
void RunFinalOptimization() override
common::Time GetLatestNodeTime(const NodeId &node_id, const SubmapId &submap_id) const REQUIRES(mutex_)
void SetTrajectoryDataFromProto(const proto::TrajectoryData &data) override
TrajectoryConnectivityState trajectory_connectivity_state_