2016-08-02 15:07:31 +08:00
|
|
|
/*
|
|
|
|
* Copyright 2016 The Cartographer Authors
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "cartographer/mapping_2d/sparse_pose_graph.h"
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <cmath>
|
|
|
|
#include <cstdio>
|
|
|
|
#include <functional>
|
|
|
|
#include <iomanip>
|
|
|
|
#include <iostream>
|
|
|
|
#include <limits>
|
|
|
|
#include <memory>
|
|
|
|
#include <sstream>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
#include "Eigen/Eigenvalues"
|
|
|
|
#include "cartographer/common/make_unique.h"
|
|
|
|
#include "cartographer/common/math.h"
|
2016-09-22 17:35:47 +08:00
|
|
|
#include "cartographer/mapping/sparse_pose_graph/proto/constraint_builder_options.pb.h"
|
2016-08-02 15:07:31 +08:00
|
|
|
#include "cartographer/sensor/compressed_point_cloud.h"
|
|
|
|
#include "cartographer/sensor/voxel_filter.h"
|
|
|
|
#include "glog/logging.h"
|
|
|
|
|
|
|
|
namespace cartographer {
|
|
|
|
namespace mapping_2d {
|
|
|
|
|
|
|
|
SparsePoseGraph::SparsePoseGraph(
|
|
|
|
const mapping::proto::SparsePoseGraphOptions& options,
|
2017-05-08 23:10:07 +08:00
|
|
|
common::ThreadPool* thread_pool)
|
2016-08-02 15:07:31 +08:00
|
|
|
: options_(options),
|
|
|
|
optimization_problem_(options_.optimization_problem_options()),
|
2017-05-08 23:10:07 +08:00
|
|
|
constraint_builder_(options_.constraint_builder_options(), thread_pool) {}
|
2016-08-02 15:07:31 +08:00
|
|
|
|
|
|
|
SparsePoseGraph::~SparsePoseGraph() {
|
|
|
|
WaitForAllComputations();
|
|
|
|
common::MutexLocker locker(&mutex_);
|
|
|
|
CHECK(scan_queue_ == nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SparsePoseGraph::GrowSubmapTransformsAsNeeded(
|
2017-05-09 23:24:48 +08:00
|
|
|
const std::vector<const mapping::Submap*>& insertion_submaps) {
|
|
|
|
CHECK(!insertion_submaps.empty());
|
2017-05-10 22:28:58 +08:00
|
|
|
const mapping::SubmapId first_submap_id = GetSubmapId(insertion_submaps[0]);
|
|
|
|
const int trajectory_id = first_submap_id.trajectory_id;
|
|
|
|
CHECK_GE(trajectory_id, 0);
|
|
|
|
const auto& submap_data = optimization_problem_.submap_data();
|
2017-05-09 23:24:48 +08:00
|
|
|
if (insertion_submaps.size() == 1) {
|
2017-05-10 22:28:58 +08:00
|
|
|
// If we don't already have an entry for the first submap, add one.
|
|
|
|
CHECK_EQ(first_submap_id.submap_index, 0);
|
|
|
|
if (static_cast<size_t>(trajectory_id) >= submap_data.size() ||
|
|
|
|
submap_data[trajectory_id].empty()) {
|
|
|
|
optimization_problem_.AddSubmap(trajectory_id,
|
|
|
|
transform::Rigid2d::Identity());
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2017-05-09 23:24:48 +08:00
|
|
|
CHECK_EQ(2, insertion_submaps.size());
|
2017-05-10 22:28:58 +08:00
|
|
|
const int next_submap_index = submap_data.at(trajectory_id).size();
|
2016-08-02 15:07:31 +08:00
|
|
|
// CHECK that we have a index for the second submap.
|
2017-05-10 22:28:58 +08:00
|
|
|
const mapping::SubmapId second_submap_id = GetSubmapId(insertion_submaps[1]);
|
|
|
|
CHECK_EQ(second_submap_id.trajectory_id, trajectory_id);
|
|
|
|
CHECK_LE(second_submap_id.submap_index, next_submap_index);
|
2016-08-02 15:07:31 +08:00
|
|
|
// Extrapolate if necessary.
|
2017-05-10 22:28:58 +08:00
|
|
|
if (second_submap_id.submap_index == next_submap_index) {
|
2017-05-09 23:24:48 +08:00
|
|
|
const auto& first_submap_pose =
|
2017-05-10 22:28:58 +08:00
|
|
|
submap_data.at(trajectory_id).at(first_submap_id.submap_index).pose;
|
2017-05-09 23:24:48 +08:00
|
|
|
optimization_problem_.AddSubmap(
|
2017-05-10 22:28:58 +08:00
|
|
|
trajectory_id,
|
2017-05-09 23:24:48 +08:00
|
|
|
first_submap_pose *
|
|
|
|
sparse_pose_graph::ComputeSubmapPose(*insertion_submaps[0])
|
|
|
|
.inverse() *
|
|
|
|
sparse_pose_graph::ComputeSubmapPose(*insertion_submaps[1]));
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SparsePoseGraph::AddScan(
|
|
|
|
common::Time time, const transform::Rigid3d& tracking_to_pose,
|
2017-03-23 21:56:18 +08:00
|
|
|
const sensor::RangeData& range_data_in_pose, const transform::Rigid2d& pose,
|
2016-08-02 15:07:31 +08:00
|
|
|
const kalman_filter::Pose2DCovariance& covariance,
|
2017-05-09 21:21:30 +08:00
|
|
|
const mapping::Submaps* const trajectory,
|
2016-08-02 15:07:31 +08:00
|
|
|
const mapping::Submap* const matching_submap,
|
|
|
|
const std::vector<const mapping::Submap*>& insertion_submaps) {
|
2017-05-09 21:21:30 +08:00
|
|
|
const transform::Rigid3d optimized_pose(
|
|
|
|
GetLocalToGlobalTransform(trajectory) * transform::Embed3D(pose));
|
2016-08-02 15:07:31 +08:00
|
|
|
|
|
|
|
common::MutexLocker locker(&mutex_);
|
2017-05-10 14:23:57 +08:00
|
|
|
trajectory_ids_.emplace(trajectory, trajectory_ids_.size());
|
2017-05-11 19:36:12 +08:00
|
|
|
const int trajectory_id = trajectory_ids_.at(trajectory);
|
2017-05-11 23:17:12 +08:00
|
|
|
const int flat_scan_index = trajectory_nodes_.size();
|
|
|
|
CHECK_LT(flat_scan_index, std::numeric_limits<int>::max());
|
2016-08-02 15:07:31 +08:00
|
|
|
|
2017-05-08 23:10:07 +08:00
|
|
|
constant_node_data_.push_back(mapping::TrajectoryNode::ConstantData{
|
2017-03-23 21:56:18 +08:00
|
|
|
time, range_data_in_pose,
|
2017-05-09 21:21:30 +08:00
|
|
|
Compress(sensor::RangeData{Eigen::Vector3f::Zero(), {}, {}}), trajectory,
|
2016-11-11 21:33:06 +08:00
|
|
|
tracking_to_pose});
|
2016-08-02 15:07:31 +08:00
|
|
|
trajectory_nodes_.push_back(mapping::TrajectoryNode{
|
2017-05-08 23:10:07 +08:00
|
|
|
&constant_node_data_.back(), optimized_pose,
|
2016-08-02 15:07:31 +08:00
|
|
|
});
|
2017-05-11 19:36:12 +08:00
|
|
|
trajectory_connectivity_.Add(trajectory_id);
|
2016-08-02 15:07:31 +08:00
|
|
|
|
2017-05-15 18:12:30 +08:00
|
|
|
if (submap_ids_.count(insertion_submaps.back()) == 0) {
|
|
|
|
submap_states_.resize(
|
|
|
|
std::max<size_t>(submap_states_.size(), trajectory_id + 1));
|
|
|
|
auto& trajectory_submap_states = submap_states_.at(trajectory_id);
|
|
|
|
submap_ids_.emplace(
|
|
|
|
insertion_submaps.back(),
|
|
|
|
mapping::SubmapId{trajectory_id,
|
|
|
|
static_cast<int>(trajectory_submap_states.size())});
|
|
|
|
trajectory_submap_states.emplace_back();
|
|
|
|
trajectory_submap_states.back().submap = insertion_submaps.back();
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
|
|
|
const mapping::Submap* const finished_submap =
|
|
|
|
insertion_submaps.front()->finished_probability_grid != nullptr
|
|
|
|
? insertion_submaps.front()
|
|
|
|
: nullptr;
|
|
|
|
|
|
|
|
// Make sure we have a sampler for this trajectory.
|
2017-05-11 21:08:27 +08:00
|
|
|
if (!global_localization_samplers_[trajectory_id]) {
|
|
|
|
global_localization_samplers_[trajectory_id] =
|
2016-08-02 15:07:31 +08:00
|
|
|
common::make_unique<common::FixedRatioSampler>(
|
|
|
|
options_.global_sampling_ratio());
|
|
|
|
}
|
2016-11-11 21:33:06 +08:00
|
|
|
|
|
|
|
AddWorkItem([=]() REQUIRES(mutex_) {
|
2017-05-15 18:12:30 +08:00
|
|
|
ComputeConstraintsForScan(flat_scan_index, matching_submap,
|
|
|
|
insertion_submaps, finished_submap, pose,
|
|
|
|
covariance);
|
2016-11-11 21:33:06 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void SparsePoseGraph::AddWorkItem(std::function<void()> work_item) {
|
|
|
|
if (scan_queue_ == nullptr) {
|
|
|
|
work_item();
|
|
|
|
} else {
|
|
|
|
scan_queue_->push_back(work_item);
|
|
|
|
}
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
|
|
|
|
2016-12-20 00:57:12 +08:00
|
|
|
void SparsePoseGraph::AddImuData(const mapping::Submaps* trajectory,
|
|
|
|
common::Time time,
|
|
|
|
const Eigen::Vector3d& linear_acceleration,
|
|
|
|
const Eigen::Vector3d& angular_velocity) {
|
|
|
|
common::MutexLocker locker(&mutex_);
|
2017-05-10 23:12:38 +08:00
|
|
|
trajectory_ids_.emplace(trajectory, trajectory_ids_.size());
|
2016-12-20 00:57:12 +08:00
|
|
|
AddWorkItem([=]() REQUIRES(mutex_) {
|
2017-05-10 23:12:38 +08:00
|
|
|
optimization_problem_.AddImuData(trajectory_ids_.at(trajectory), time,
|
|
|
|
linear_acceleration, angular_velocity);
|
2016-12-20 00:57:12 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-11-18 17:36:30 +08:00
|
|
|
void SparsePoseGraph::ComputeConstraint(const int scan_index,
|
2017-05-15 18:12:30 +08:00
|
|
|
const mapping::SubmapId& submap_id) {
|
2017-05-15 20:09:45 +08:00
|
|
|
const mapping::NodeId node_id = scan_index_to_node_id_.at(scan_index);
|
|
|
|
const transform::Rigid2d relative_pose = optimization_problem_.submap_data()
|
|
|
|
.at(submap_id.trajectory_id)
|
|
|
|
.at(submap_id.submap_index)
|
|
|
|
.pose.inverse() *
|
|
|
|
optimization_problem_.node_data()
|
|
|
|
.at(node_id.trajectory_id)
|
|
|
|
.at(node_id.node_index)
|
|
|
|
.point_cloud_pose;
|
2016-11-18 17:36:30 +08:00
|
|
|
|
|
|
|
const mapping::Submaps* const scan_trajectory =
|
|
|
|
trajectory_nodes_[scan_index].constant_data->trajectory;
|
2017-05-11 19:36:12 +08:00
|
|
|
const int scan_trajectory_id = trajectory_ids_.at(scan_trajectory);
|
2017-05-10 18:26:58 +08:00
|
|
|
|
2016-11-18 17:36:30 +08:00
|
|
|
// Only globally match against submaps not in this trajectory.
|
2017-05-15 18:12:30 +08:00
|
|
|
if (scan_trajectory_id != submap_id.trajectory_id &&
|
2017-05-11 21:08:27 +08:00
|
|
|
global_localization_samplers_[scan_trajectory_id]->Pulse()) {
|
2016-11-18 17:36:30 +08:00
|
|
|
constraint_builder_.MaybeAddGlobalConstraint(
|
2017-05-15 18:12:30 +08:00
|
|
|
submap_id,
|
|
|
|
submap_states_.at(submap_id.trajectory_id)
|
|
|
|
.at(submap_id.submap_index)
|
|
|
|
.submap,
|
2017-05-15 20:09:45 +08:00
|
|
|
node_id, scan_index, &trajectory_connectivity_,
|
2017-03-23 21:56:18 +08:00
|
|
|
&trajectory_nodes_[scan_index].constant_data->range_data_2d.returns);
|
2016-11-18 17:36:30 +08:00
|
|
|
} else {
|
|
|
|
const bool scan_and_submap_trajectories_connected =
|
2017-05-11 19:36:12 +08:00
|
|
|
reverse_connected_components_.count(scan_trajectory_id) > 0 &&
|
2017-05-15 18:12:30 +08:00
|
|
|
reverse_connected_components_.count(submap_id.trajectory_id) > 0 &&
|
2017-05-11 19:36:12 +08:00
|
|
|
reverse_connected_components_.at(scan_trajectory_id) ==
|
2017-05-15 18:12:30 +08:00
|
|
|
reverse_connected_components_.at(submap_id.trajectory_id);
|
|
|
|
if (scan_trajectory_id == submap_id.trajectory_id ||
|
2016-11-18 17:36:30 +08:00
|
|
|
scan_and_submap_trajectories_connected) {
|
|
|
|
constraint_builder_.MaybeAddConstraint(
|
2017-05-15 18:12:30 +08:00
|
|
|
submap_id,
|
|
|
|
submap_states_.at(submap_id.trajectory_id)
|
|
|
|
.at(submap_id.submap_index)
|
|
|
|
.submap,
|
2017-05-15 20:09:45 +08:00
|
|
|
node_id, scan_index,
|
2017-03-23 21:56:18 +08:00
|
|
|
&trajectory_nodes_[scan_index].constant_data->range_data_2d.returns,
|
2016-11-18 17:36:30 +08:00
|
|
|
relative_pose);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-02 15:07:31 +08:00
|
|
|
void SparsePoseGraph::ComputeConstraintsForOldScans(
|
|
|
|
const mapping::Submap* submap) {
|
2017-05-15 18:12:30 +08:00
|
|
|
const auto submap_id = GetSubmapId(submap);
|
2017-05-15 20:09:45 +08:00
|
|
|
const auto& submap_state =
|
|
|
|
submap_states_.at(submap_id.trajectory_id).at(submap_id.submap_index);
|
|
|
|
const int num_nodes = scan_index_to_node_id_.size();
|
2016-08-02 15:07:31 +08:00
|
|
|
for (int scan_index = 0; scan_index < num_nodes; ++scan_index) {
|
2017-05-15 22:31:59 +08:00
|
|
|
if (submap_state.node_ids.count(scan_index_to_node_id_[scan_index]) == 0) {
|
2017-05-15 18:12:30 +08:00
|
|
|
ComputeConstraint(scan_index, submap_id);
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SparsePoseGraph::ComputeConstraintsForScan(
|
2016-12-20 23:43:58 +08:00
|
|
|
const int scan_index, const mapping::Submap* matching_submap,
|
2016-08-02 15:07:31 +08:00
|
|
|
std::vector<const mapping::Submap*> insertion_submaps,
|
|
|
|
const mapping::Submap* finished_submap, const transform::Rigid2d& pose,
|
|
|
|
const kalman_filter::Pose2DCovariance& covariance) {
|
|
|
|
GrowSubmapTransformsAsNeeded(insertion_submaps);
|
2017-05-10 22:28:58 +08:00
|
|
|
const mapping::SubmapId matching_id = GetSubmapId(matching_submap);
|
2016-08-02 15:07:31 +08:00
|
|
|
const transform::Rigid2d optimized_pose =
|
2017-05-10 22:28:58 +08:00
|
|
|
optimization_problem_.submap_data()
|
|
|
|
.at(matching_id.trajectory_id)
|
|
|
|
.at(matching_id.submap_index)
|
|
|
|
.pose *
|
2016-08-02 15:07:31 +08:00
|
|
|
sparse_pose_graph::ComputeSubmapPose(*matching_submap).inverse() * pose;
|
2017-05-15 20:09:45 +08:00
|
|
|
CHECK_EQ(scan_index, scan_index_to_node_id_.size());
|
2017-05-15 22:31:59 +08:00
|
|
|
const mapping::NodeId node_id{
|
|
|
|
matching_id.trajectory_id,
|
|
|
|
num_nodes_in_trajectory_[matching_id.trajectory_id]};
|
|
|
|
scan_index_to_node_id_.push_back(node_id);
|
2017-05-15 20:09:45 +08:00
|
|
|
++num_nodes_in_trajectory_[matching_id.trajectory_id];
|
2016-12-20 23:43:58 +08:00
|
|
|
const mapping::TrajectoryNode::ConstantData* const scan_data =
|
|
|
|
trajectory_nodes_[scan_index].constant_data;
|
2017-05-15 20:09:45 +08:00
|
|
|
CHECK_EQ(trajectory_ids_.at(scan_data->trajectory),
|
|
|
|
matching_id.trajectory_id);
|
2016-12-20 23:43:58 +08:00
|
|
|
optimization_problem_.AddTrajectoryNode(
|
2017-05-15 20:09:45 +08:00
|
|
|
matching_id.trajectory_id, scan_data->time, pose, optimized_pose);
|
2016-08-02 15:07:31 +08:00
|
|
|
for (const mapping::Submap* submap : insertion_submaps) {
|
2017-05-15 18:12:30 +08:00
|
|
|
const mapping::SubmapId submap_id = GetSubmapId(submap);
|
|
|
|
CHECK(!submap_states_.at(submap_id.trajectory_id)
|
|
|
|
.at(submap_id.submap_index)
|
|
|
|
.finished);
|
|
|
|
submap_states_.at(submap_id.trajectory_id)
|
|
|
|
.at(submap_id.submap_index)
|
2017-05-15 22:31:59 +08:00
|
|
|
.node_ids.emplace(node_id);
|
2016-08-02 15:07:31 +08:00
|
|
|
// Unchanged covariance as (submap <- map) is a translation.
|
|
|
|
const transform::Rigid2d constraint_transform =
|
|
|
|
sparse_pose_graph::ComputeSubmapPose(*submap).inverse() * pose;
|
2016-12-20 00:57:12 +08:00
|
|
|
constexpr double kFakePositionCovariance = 1e-6;
|
|
|
|
constexpr double kFakeOrientationCovariance = 1e-6;
|
2016-11-08 21:47:03 +08:00
|
|
|
constraints_.push_back(Constraint{
|
2017-05-15 18:12:30 +08:00
|
|
|
submap_id,
|
2017-05-15 22:31:59 +08:00
|
|
|
node_id,
|
2016-11-08 21:47:03 +08:00
|
|
|
{transform::Embed3D(constraint_transform),
|
2016-09-22 17:35:47 +08:00
|
|
|
common::ComputeSpdMatrixSqrtInverse(
|
2016-11-08 21:47:03 +08:00
|
|
|
kalman_filter::Embed3D(covariance, kFakePositionCovariance,
|
|
|
|
kFakeOrientationCovariance),
|
|
|
|
options_.constraint_builder_options()
|
|
|
|
.lower_covariance_eigenvalue_bound())},
|
|
|
|
Constraint::INTRA_SUBMAP});
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
|
|
|
|
2017-05-15 18:12:30 +08:00
|
|
|
for (size_t trajectory_id = 0; trajectory_id < submap_states_.size();
|
|
|
|
++trajectory_id) {
|
|
|
|
for (size_t submap_index = 0;
|
|
|
|
submap_index < submap_states_.at(trajectory_id).size();
|
|
|
|
++submap_index) {
|
|
|
|
if (submap_states_.at(trajectory_id).at(submap_index).finished) {
|
|
|
|
CHECK_EQ(submap_states_.at(trajectory_id)
|
|
|
|
.at(submap_index)
|
2017-05-15 22:31:59 +08:00
|
|
|
.node_ids.count(node_id),
|
2017-05-15 18:12:30 +08:00
|
|
|
0);
|
|
|
|
ComputeConstraint(scan_index,
|
|
|
|
mapping::SubmapId{static_cast<int>(trajectory_id),
|
|
|
|
static_cast<int>(submap_index)});
|
|
|
|
}
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (finished_submap != nullptr) {
|
2017-05-15 18:12:30 +08:00
|
|
|
const mapping::SubmapId finished_submap_id = GetSubmapId(finished_submap);
|
|
|
|
SubmapState& finished_submap_state =
|
|
|
|
submap_states_.at(finished_submap_id.trajectory_id)
|
|
|
|
.at(finished_submap_id.submap_index);
|
2016-08-02 15:07:31 +08:00
|
|
|
CHECK(!finished_submap_state.finished);
|
2016-09-30 22:00:10 +08:00
|
|
|
// We have a new completed submap, so we look into adding constraints for
|
|
|
|
// old scans.
|
|
|
|
ComputeConstraintsForOldScans(finished_submap);
|
2016-08-02 15:07:31 +08:00
|
|
|
finished_submap_state.finished = true;
|
|
|
|
}
|
|
|
|
constraint_builder_.NotifyEndOfScan(scan_index);
|
|
|
|
++num_scans_since_last_loop_closure_;
|
|
|
|
if (options_.optimize_every_n_scans() > 0 &&
|
|
|
|
num_scans_since_last_loop_closure_ > options_.optimize_every_n_scans()) {
|
|
|
|
CHECK(!run_loop_closure_);
|
|
|
|
run_loop_closure_ = true;
|
|
|
|
// If there is a 'scan_queue_' already, some other thread will take care.
|
|
|
|
if (scan_queue_ == nullptr) {
|
|
|
|
scan_queue_ = common::make_unique<std::deque<std::function<void()>>>();
|
|
|
|
HandleScanQueue();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SparsePoseGraph::HandleScanQueue() {
|
|
|
|
constraint_builder_.WhenDone(
|
|
|
|
[this](const sparse_pose_graph::ConstraintBuilder::Result& result) {
|
|
|
|
constraints_.insert(constraints_.end(), result.begin(), result.end());
|
|
|
|
RunOptimization();
|
|
|
|
|
|
|
|
common::MutexLocker locker(&mutex_);
|
|
|
|
num_scans_since_last_loop_closure_ = 0;
|
|
|
|
run_loop_closure_ = false;
|
|
|
|
while (!run_loop_closure_) {
|
|
|
|
if (scan_queue_->empty()) {
|
|
|
|
LOG(INFO) << "We caught up. Hooray!";
|
|
|
|
scan_queue_.reset();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
scan_queue_->front()();
|
|
|
|
scan_queue_->pop_front();
|
|
|
|
}
|
|
|
|
// We have to optimize again.
|
|
|
|
HandleScanQueue();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void SparsePoseGraph::WaitForAllComputations() {
|
|
|
|
bool notification = false;
|
|
|
|
common::MutexLocker locker(&mutex_);
|
|
|
|
const int num_finished_scans_at_start =
|
|
|
|
constraint_builder_.GetNumFinishedScans();
|
2017-05-03 19:44:15 +08:00
|
|
|
while (!locker.AwaitWithTimeout(
|
|
|
|
[this]() REQUIRES(mutex_) {
|
|
|
|
return static_cast<size_t>(constraint_builder_.GetNumFinishedScans()) ==
|
|
|
|
trajectory_nodes_.size();
|
|
|
|
},
|
|
|
|
common::FromSeconds(1.))) {
|
2016-08-02 15:07:31 +08:00
|
|
|
std::ostringstream progress_info;
|
|
|
|
progress_info << "Optimizing: " << std::fixed << std::setprecision(1)
|
2017-05-03 19:44:15 +08:00
|
|
|
<< 100. *
|
|
|
|
(constraint_builder_.GetNumFinishedScans() -
|
|
|
|
num_finished_scans_at_start) /
|
2016-08-02 15:07:31 +08:00
|
|
|
(trajectory_nodes_.size() -
|
2017-05-03 19:44:15 +08:00
|
|
|
num_finished_scans_at_start)
|
|
|
|
<< "%...";
|
2016-08-02 15:07:31 +08:00
|
|
|
std::cout << "\r\x1b[K" << progress_info.str() << std::flush;
|
|
|
|
}
|
|
|
|
std::cout << "\r\x1b[KOptimizing: Done. " << std::endl;
|
2017-05-03 19:44:15 +08:00
|
|
|
constraint_builder_.WhenDone(
|
|
|
|
[this, ¬ification](
|
|
|
|
const sparse_pose_graph::ConstraintBuilder::Result& result) {
|
|
|
|
constraints_.insert(constraints_.end(), result.begin(), result.end());
|
|
|
|
common::MutexLocker locker(&mutex_);
|
|
|
|
notification = true;
|
|
|
|
});
|
2016-08-02 15:07:31 +08:00
|
|
|
locker.Await([¬ification]() { return notification; });
|
|
|
|
}
|
|
|
|
|
|
|
|
void SparsePoseGraph::RunFinalOptimization() {
|
|
|
|
WaitForAllComputations();
|
|
|
|
optimization_problem_.SetMaxNumIterations(
|
|
|
|
options_.max_num_final_iterations());
|
|
|
|
RunOptimization();
|
2016-10-27 21:25:57 +08:00
|
|
|
optimization_problem_.SetMaxNumIterations(
|
|
|
|
options_.optimization_problem_options()
|
|
|
|
.ceres_solver_options()
|
|
|
|
.max_num_iterations());
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void SparsePoseGraph::RunOptimization() {
|
2017-05-09 23:24:48 +08:00
|
|
|
if (optimization_problem_.submap_data().empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
optimization_problem_.Solve(constraints_);
|
|
|
|
common::MutexLocker locker(&mutex_);
|
2016-12-20 00:57:12 +08:00
|
|
|
|
2017-05-09 23:24:48 +08:00
|
|
|
const auto& node_data = optimization_problem_.node_data();
|
2017-05-15 20:09:45 +08:00
|
|
|
const size_t num_optimized_poses = scan_index_to_node_id_.size();
|
2017-05-09 23:24:48 +08:00
|
|
|
for (size_t i = 0; i != num_optimized_poses; ++i) {
|
2017-05-15 22:31:59 +08:00
|
|
|
const mapping::NodeId node_id = scan_index_to_node_id_[i];
|
2017-05-09 23:24:48 +08:00
|
|
|
trajectory_nodes_[i].pose =
|
2017-05-15 22:31:59 +08:00
|
|
|
transform::Embed3D(node_data.at(node_id.trajectory_id)
|
|
|
|
.at(node_id.node_index)
|
2017-05-15 20:09:45 +08:00
|
|
|
.point_cloud_pose);
|
2017-05-09 23:24:48 +08:00
|
|
|
}
|
|
|
|
// Extrapolate all point cloud poses that were added later.
|
|
|
|
std::unordered_map<const mapping::Submaps*, transform::Rigid3d>
|
|
|
|
extrapolation_transforms;
|
|
|
|
for (size_t i = num_optimized_poses; i != trajectory_nodes_.size(); ++i) {
|
|
|
|
const mapping::Submaps* trajectory =
|
|
|
|
trajectory_nodes_[i].constant_data->trajectory;
|
|
|
|
if (extrapolation_transforms.count(trajectory) == 0) {
|
2017-05-10 22:28:58 +08:00
|
|
|
const auto new_submap_transforms = ExtrapolateSubmapTransforms(
|
|
|
|
optimization_problem_.submap_data(), trajectory);
|
2017-05-09 23:24:48 +08:00
|
|
|
const auto old_submap_transforms =
|
|
|
|
ExtrapolateSubmapTransforms(optimized_submap_transforms_, trajectory);
|
|
|
|
CHECK_EQ(new_submap_transforms.size(), old_submap_transforms.size());
|
|
|
|
extrapolation_transforms[trajectory] =
|
|
|
|
transform::Rigid3d(new_submap_transforms.back() *
|
|
|
|
old_submap_transforms.back().inverse());
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
2017-05-09 23:24:48 +08:00
|
|
|
trajectory_nodes_[i].pose =
|
|
|
|
extrapolation_transforms[trajectory] * trajectory_nodes_[i].pose;
|
|
|
|
}
|
2017-05-10 22:28:58 +08:00
|
|
|
optimized_submap_transforms_ = optimization_problem_.submap_data();
|
2017-05-09 23:24:48 +08:00
|
|
|
connected_components_ = trajectory_connectivity_.ConnectedComponents();
|
|
|
|
reverse_connected_components_.clear();
|
|
|
|
for (size_t i = 0; i != connected_components_.size(); ++i) {
|
2017-05-11 19:36:12 +08:00
|
|
|
for (const int trajectory_id : connected_components_[i]) {
|
|
|
|
reverse_connected_components_.emplace(trajectory_id, i);
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-16 16:51:08 +08:00
|
|
|
std::vector<std::vector<mapping::TrajectoryNode>>
|
|
|
|
SparsePoseGraph::GetTrajectoryNodes() {
|
2016-08-02 15:07:31 +08:00
|
|
|
common::MutexLocker locker(&mutex_);
|
2017-05-16 16:51:08 +08:00
|
|
|
std::vector<std::vector<mapping::TrajectoryNode>> result(
|
|
|
|
trajectory_ids_.size());
|
|
|
|
for (const auto& node : trajectory_nodes_) {
|
|
|
|
result.at(trajectory_ids_.at(node.constant_data->trajectory))
|
|
|
|
.push_back(node);
|
|
|
|
}
|
|
|
|
return result;
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
|
|
|
|
2016-11-08 21:47:03 +08:00
|
|
|
std::vector<SparsePoseGraph::Constraint> SparsePoseGraph::constraints() {
|
2017-05-10 14:23:57 +08:00
|
|
|
common::MutexLocker locker(&mutex_);
|
2016-08-02 15:07:31 +08:00
|
|
|
return constraints_;
|
|
|
|
}
|
|
|
|
|
2016-08-10 18:09:52 +08:00
|
|
|
transform::Rigid3d SparsePoseGraph::GetLocalToGlobalTransform(
|
2017-05-09 21:21:30 +08:00
|
|
|
const mapping::Submaps* const submaps) {
|
|
|
|
const auto extrapolated_submap_transforms = GetSubmapTransforms(submaps);
|
|
|
|
return extrapolated_submap_transforms.back() *
|
|
|
|
submaps->Get(extrapolated_submap_transforms.size() - 1)
|
|
|
|
->local_pose()
|
|
|
|
.inverse();
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
|
|
|
|
2017-05-11 19:36:12 +08:00
|
|
|
std::vector<std::vector<int>> SparsePoseGraph::GetConnectedTrajectories() {
|
2016-08-02 15:07:31 +08:00
|
|
|
common::MutexLocker locker(&mutex_);
|
|
|
|
return connected_components_;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<transform::Rigid3d> SparsePoseGraph::GetSubmapTransforms(
|
2017-05-09 21:21:30 +08:00
|
|
|
const mapping::Submaps* const trajectory) {
|
2016-08-02 15:07:31 +08:00
|
|
|
common::MutexLocker locker(&mutex_);
|
2017-05-09 21:21:30 +08:00
|
|
|
return ExtrapolateSubmapTransforms(optimized_submap_transforms_, trajectory);
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<transform::Rigid3d> SparsePoseGraph::ExtrapolateSubmapTransforms(
|
2017-05-10 22:28:58 +08:00
|
|
|
const std::vector<std::vector<sparse_pose_graph::SubmapData>>&
|
|
|
|
submap_transforms,
|
2017-05-09 21:21:30 +08:00
|
|
|
const mapping::Submaps* const trajectory) const {
|
2017-05-11 19:36:12 +08:00
|
|
|
if (trajectory_ids_.count(trajectory) == 0) {
|
|
|
|
return {transform::Rigid3d::Identity()};
|
|
|
|
}
|
2017-05-15 18:12:30 +08:00
|
|
|
const size_t trajectory_id = trajectory_ids_.at(trajectory);
|
|
|
|
if (trajectory_id >= submap_states_.size()) {
|
|
|
|
return {transform::Rigid3d::Identity()};
|
|
|
|
}
|
2017-05-09 21:21:30 +08:00
|
|
|
|
2016-08-02 15:07:31 +08:00
|
|
|
// Submaps for which we have optimized poses.
|
2017-05-11 19:36:12 +08:00
|
|
|
std::vector<transform::Rigid3d> result;
|
2017-05-15 18:12:30 +08:00
|
|
|
for (const auto& state : submap_states_.at(trajectory_id)) {
|
|
|
|
if (trajectory_id < submap_transforms.size() &&
|
|
|
|
result.size() < submap_transforms.at(trajectory_id).size()) {
|
|
|
|
// Submaps for which we have optimized poses.
|
|
|
|
result.push_back(
|
|
|
|
Embed3D(submap_transforms.at(trajectory_id).at(result.size()).pose));
|
|
|
|
} else if (result.empty()) {
|
2017-05-09 21:21:30 +08:00
|
|
|
result.push_back(transform::Rigid3d::Identity());
|
|
|
|
} else {
|
2017-05-15 20:09:45 +08:00
|
|
|
// Extrapolate to the remaining submaps. Accessing local_pose() in Submaps
|
|
|
|
// is okay, since the member is const.
|
2017-05-09 21:21:30 +08:00
|
|
|
result.push_back(result.back() *
|
2017-05-15 18:12:30 +08:00
|
|
|
submap_states_.at(trajectory_id)
|
|
|
|
.at(result.size() - 1)
|
2017-05-09 21:21:30 +08:00
|
|
|
.submap->local_pose()
|
|
|
|
.inverse() *
|
|
|
|
state.submap->local_pose());
|
|
|
|
}
|
|
|
|
}
|
2017-05-15 18:12:30 +08:00
|
|
|
|
2017-05-09 21:21:30 +08:00
|
|
|
if (result.empty()) {
|
|
|
|
result.push_back(transform::Rigid3d::Identity());
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace mapping_2d
|
|
|
|
} // namespace cartographer
|