Introduces mapping::MapById in the 2D pose graph for submaps. (#578)

PAIR=cschuet
master
Wolfgang Hess 2017-10-12 11:58:59 +02:00 committed by GitHub
parent ea55e837d2
commit cb41777b9e
7 changed files with 107 additions and 74 deletions

View File

@ -19,11 +19,13 @@
#include <algorithm> #include <algorithm>
#include <iterator> #include <iterator>
#include <limits>
#include <map> #include <map>
#include <ostream> #include <ostream>
#include <tuple> #include <tuple>
#include <vector> #include <vector>
#include "cartographer/common/port.h"
#include "glog/logging.h" #include "glog/logging.h"
namespace cartographer { namespace cartographer {
@ -109,7 +111,8 @@ class NestedVectorsById {
std::vector<std::vector<ValueType>> data_; std::vector<std::vector<ValueType>> data_;
}; };
// Like std::map, but indexed by 'IdType' which can be 'NodeId' or 'SubmapId'. // Reminiscent of std::map, but indexed by 'IdType' which can be 'NodeId' or
// 'SubmapId'.
template <typename IdType, typename DataType> template <typename IdType, typename DataType>
class MapById { class MapById {
private: private:
@ -121,25 +124,24 @@ class MapById {
const DataType& data; const DataType& data;
}; };
class ConstIterator class ConstIterator {
: public std::iterator<std::forward_iterator_tag, IdDataReference> {
public: public:
explicit ConstIterator(const MapById& map_by_id) using iterator_category = std::bidirectional_iterator_tag;
: current_trajectory_(map_by_id.trajectories_.begin()), using value_type = IdDataReference;
using difference_type = int64;
using pointer = const IdDataReference*;
using reference = const IdDataReference&;
explicit ConstIterator(const MapById& map_by_id, const int trajectory_id)
: current_trajectory_(
map_by_id.trajectories_.lower_bound(trajectory_id)),
end_trajectory_(map_by_id.trajectories_.end()) { end_trajectory_(map_by_id.trajectories_.end()) {
if (current_trajectory_ != end_trajectory_) { if (current_trajectory_ != end_trajectory_) {
current_data_ = current_trajectory_->second.data_.begin(); current_data_ = current_trajectory_->second.data_.begin();
end_data_ = current_trajectory_->second.data_.end();
AdvanceToValidDataIterator(); AdvanceToValidDataIterator();
} }
} }
static ConstIterator EndIterator(const MapById& map_by_id) {
auto it = ConstIterator(map_by_id);
it.current_trajectory_ = it.end_trajectory_;
return it;
}
IdDataReference operator*() const { IdDataReference operator*() const {
CHECK(current_trajectory_ != end_trajectory_); CHECK(current_trajectory_ != end_trajectory_);
return IdDataReference{ return IdDataReference{
@ -154,6 +156,16 @@ class MapById {
return *this; return *this;
} }
ConstIterator& operator--() {
while (current_trajectory_ == end_trajectory_ ||
current_data_ == current_trajectory_->second.data_.begin()) {
--current_trajectory_;
current_data_ = current_trajectory_->second.data_.end();
}
--current_data_;
return *this;
}
bool operator==(const ConstIterator& it) const { bool operator==(const ConstIterator& it) const {
if (current_trajectory_ == end_trajectory_ || if (current_trajectory_ == end_trajectory_ ||
it.current_trajectory_ == it.end_trajectory_) { it.current_trajectory_ == it.end_trajectory_) {
@ -168,20 +180,18 @@ class MapById {
private: private:
void AdvanceToValidDataIterator() { void AdvanceToValidDataIterator() {
CHECK(current_trajectory_ != end_trajectory_); CHECK(current_trajectory_ != end_trajectory_);
while (current_data_ == end_data_) { while (current_data_ == current_trajectory_->second.data_.end()) {
++current_trajectory_; ++current_trajectory_;
if (current_trajectory_ == end_trajectory_) { if (current_trajectory_ == end_trajectory_) {
return; return;
} }
current_data_ = current_trajectory_->second.data_.begin(); current_data_ = current_trajectory_->second.data_.begin();
end_data_ = current_trajectory_->second.data_.end();
} }
} }
typename std::map<int, MapByIndex>::const_iterator current_trajectory_; typename std::map<int, MapByIndex>::const_iterator current_trajectory_;
typename std::map<int, MapByIndex>::const_iterator end_trajectory_; typename std::map<int, MapByIndex>::const_iterator end_trajectory_;
typename std::map<int, DataType>::const_iterator current_data_; typename std::map<int, DataType>::const_iterator current_data_;
typename std::map<int, DataType>::const_iterator end_data_;
}; };
// Appends data to a 'trajectory_id', creating trajectories as needed. // Appends data to a 'trajectory_id', creating trajectories as needed.
@ -217,6 +227,11 @@ class MapById {
trajectory.data_.erase(it); trajectory.data_.erase(it);
} }
bool Contains(const IdType& id) const {
return trajectories_.count(id.trajectory_id) != 0 &&
trajectories_.at(id.trajectory_id).data_.count(GetIndex(id)) != 0;
}
const DataType& at(const IdType& id) const { const DataType& at(const IdType& id) const {
return trajectories_.at(id.trajectory_id).data_.at(GetIndex(id)); return trajectories_.at(id.trajectory_id).data_.at(GetIndex(id));
} }
@ -225,8 +240,25 @@ class MapById {
return trajectories_.at(id.trajectory_id).data_.at(GetIndex(id)); return trajectories_.at(id.trajectory_id).data_.at(GetIndex(id));
} }
ConstIterator begin() const { return ConstIterator(*this); } // Support querying by trajectory.
ConstIterator end() const { return ConstIterator::EndIterator(*this); } ConstIterator BeginOfTrajectory(const int trajectory_id) const {
return ConstIterator(*this, trajectory_id);
}
ConstIterator EndOfTrajectory(const int trajectory_id) const {
return BeginOfTrajectory(trajectory_id + 1);
}
// Returns 0 if 'trajectory_id' does not exist.
size_t SizeOfTrajectoryOrZero(const int trajectory_id) const {
return trajectories_.count(trajectory_id)
? trajectories_.at(trajectory_id).data_.size()
: 0;
}
ConstIterator begin() const { return BeginOfTrajectory(0); }
ConstIterator end() const {
return BeginOfTrajectory(std::numeric_limits<int>::max());
}
bool empty() const { return begin() == end(); } bool empty() const { return begin() == end(); }

View File

@ -17,6 +17,7 @@
#include "cartographer/mapping/id.h" #include "cartographer/mapping/id.h"
#include <deque> #include <deque>
#include <iterator>
#include <utility> #include <utility>
#include "gtest/gtest.h" #include "gtest/gtest.h"
@ -40,6 +41,9 @@ TEST(IdTest, MapByIdIterator) {
map_by_id.Append(42, 3); map_by_id.Append(42, 3);
map_by_id.Append(0, 0); map_by_id.Append(0, 0);
map_by_id.Append(0, 1); map_by_id.Append(0, 1);
EXPECT_EQ(2, (*map_by_id.BeginOfTrajectory(7)).data);
EXPECT_TRUE(std::next(map_by_id.BeginOfTrajectory(7)) ==
map_by_id.EndOfTrajectory(7));
std::deque<std::pair<NodeId, int>> expected_id_data = { std::deque<std::pair<NodeId, int>> expected_id_data = {
{NodeId{0, 0}, 0}, {NodeId{0, 0}, 0},
{NodeId{0, 1}, 1}, {NodeId{0, 1}, 1},
@ -55,6 +59,23 @@ TEST(IdTest, MapByIdIterator) {
EXPECT_TRUE(expected_id_data.empty()); EXPECT_TRUE(expected_id_data.empty());
} }
TEST(IdTest, MapByIdPrevIterator) {
MapById<NodeId, int> map_by_id;
map_by_id.Append(42, 42);
auto it = map_by_id.end();
ASSERT_TRUE(it != map_by_id.begin());
std::advance(it, -1);
EXPECT_TRUE(it == map_by_id.begin());
}
TEST(IdTest, InsertIntoMapById) {
MapById<NodeId, int> map_by_id;
EXPECT_EQ(0, map_by_id.SizeOfTrajectoryOrZero(42));
map_by_id.Append(42, 42);
map_by_id.Insert(NodeId{42, 5}, 42);
EXPECT_EQ(2, map_by_id.SizeOfTrajectoryOrZero(42));
}
} // namespace } // namespace
} // namespace mapping } // namespace mapping
} // namespace cartographer } // namespace cartographer

View File

@ -22,6 +22,7 @@
#include <functional> #include <functional>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <iterator>
#include <limits> #include <limits>
#include <memory> #include <memory>
#include <sstream> #include <sstream>
@ -55,30 +56,27 @@ std::vector<mapping::SubmapId> SparsePoseGraph::GrowSubmapTransformsAsNeeded(
const int trajectory_id, const int trajectory_id,
const std::vector<std::shared_ptr<const Submap>>& insertion_submaps) { const std::vector<std::shared_ptr<const Submap>>& insertion_submaps) {
CHECK(!insertion_submaps.empty()); CHECK(!insertion_submaps.empty());
auto submap_data = optimization_problem_.submap_data(); const auto& submap_data = optimization_problem_.submap_data();
if (insertion_submaps.size() == 1) { if (insertion_submaps.size() == 1) {
// If we don't already have an entry for the first submap, add one. // If we don't already have an entry for the first submap, add one.
if (static_cast<size_t>(trajectory_id) >= submap_data.size() || if (submap_data.SizeOfTrajectoryOrZero(trajectory_id) == 0) {
submap_data[trajectory_id].empty()) {
optimization_problem_.AddSubmap( optimization_problem_.AddSubmap(
trajectory_id, trajectory_id,
sparse_pose_graph::ComputeSubmapPose(*insertion_submaps[0])); sparse_pose_graph::ComputeSubmapPose(*insertion_submaps[0]));
submap_data = optimization_problem_.submap_data();
} }
CHECK_EQ(submap_data[trajectory_id].size(), 1); CHECK_EQ(1, submap_data.SizeOfTrajectoryOrZero(trajectory_id));
const mapping::SubmapId submap_id{trajectory_id, 0}; const mapping::SubmapId submap_id{trajectory_id, 0};
CHECK(submap_data_.at(submap_id).submap == insertion_submaps.front()); CHECK(submap_data_.at(submap_id).submap == insertion_submaps.front());
return {submap_id}; return {submap_id};
} }
CHECK_EQ(2, insertion_submaps.size()); CHECK_EQ(2, insertion_submaps.size());
CHECK(!submap_data.at(trajectory_id).empty()); const auto end_it = submap_data.EndOfTrajectory(trajectory_id);
const mapping::SubmapId last_submap_id{ CHECK(submap_data.BeginOfTrajectory(trajectory_id) != end_it);
trajectory_id, submap_data.at(trajectory_id).rbegin()->first}; const mapping::SubmapId last_submap_id = (*std::prev(end_it)).id;
if (submap_data_.at(last_submap_id).submap == insertion_submaps.front()) { if (submap_data_.at(last_submap_id).submap == insertion_submaps.front()) {
// In this case, 'last_submap_id' is the ID of 'insertions_submaps.front()' // In this case, 'last_submap_id' is the ID of 'insertions_submaps.front()'
// and 'insertions_submaps.back()' is new. // and 'insertions_submaps.back()' is new.
const auto& first_submap_pose = const auto& first_submap_pose = submap_data.at(last_submap_id).pose;
submap_data.at(trajectory_id).at(last_submap_id.submap_index).pose;
optimization_problem_.AddSubmap( optimization_problem_.AddSubmap(
trajectory_id, trajectory_id,
first_submap_pose * first_submap_pose *
@ -189,10 +187,7 @@ void SparsePoseGraph::ComputeConstraint(const mapping::NodeId& node_id,
// submap's trajectory, it suffices to do a match constrained to a local // submap's trajectory, it suffices to do a match constrained to a local
// search window. // search window.
const transform::Rigid2d initial_relative_pose = const transform::Rigid2d initial_relative_pose =
optimization_problem_.submap_data() optimization_problem_.submap_data().at(submap_id).pose.inverse() *
.at(submap_id.trajectory_id)
.at(submap_id.submap_index)
.pose.inverse() *
optimization_problem_.node_data() optimization_problem_.node_data()
.at(node_id.trajectory_id) .at(node_id.trajectory_id)
.at(node_id.node_index) .at(node_id.node_index)
@ -250,10 +245,7 @@ void SparsePoseGraph::ComputeConstraintsForScan(
constant_data->initial_pose * constant_data->initial_pose *
transform::Rigid3d::Rotation(constant_data->gravity_alignment.inverse())); transform::Rigid3d::Rotation(constant_data->gravity_alignment.inverse()));
const transform::Rigid2d optimized_pose = const transform::Rigid2d optimized_pose =
optimization_problem_.submap_data() optimization_problem_.submap_data().at(matching_id).pose *
.at(matching_id.trajectory_id)
.at(matching_id.submap_index)
.pose *
sparse_pose_graph::ComputeSubmapPose(*insertion_submaps.front()) sparse_pose_graph::ComputeSubmapPose(*insertion_submaps.front())
.inverse() * .inverse() *
pose; pose;
@ -428,14 +420,9 @@ void SparsePoseGraph::AddSubmapFromProto(const int trajectory_id,
submap_data_.Append(trajectory_id, SubmapData()); submap_data_.Append(trajectory_id, SubmapData());
submap_data_.at(submap_id).submap = submap_ptr; submap_data_.at(submap_id).submap = submap_ptr;
// Immediately show the submap at the optimized pose. // Immediately show the submap at the optimized pose.
CHECK_GE(static_cast<size_t>(submap_data_.num_trajectories()), CHECK_EQ(submap_id,
optimized_submap_transforms_.size()); optimized_submap_transforms_.Append(
optimized_submap_transforms_.resize(submap_data_.num_trajectories()); trajectory_id, sparse_pose_graph::SubmapData{initial_pose_2d}));
CHECK_EQ(optimized_submap_transforms_.at(trajectory_id).size(),
submap_id.submap_index);
optimized_submap_transforms_.at(trajectory_id)
.emplace(submap_id.submap_index,
sparse_pose_graph::SubmapData{initial_pose_2d});
AddWorkItem([this, submap_id, initial_pose_2d]() REQUIRES(mutex_) { AddWorkItem([this, submap_id, initial_pose_2d]() REQUIRES(mutex_) {
CHECK_EQ(frozen_trajectories_.count(submap_id.trajectory_id), 1); CHECK_EQ(frozen_trajectories_.count(submap_id.trajectory_id), 1);
submap_data_.at(submap_id).state = SubmapState::kFinished; submap_data_.at(submap_id).state = SubmapState::kFinished;
@ -500,7 +487,7 @@ void SparsePoseGraph::RunOptimization() {
optimization_problem_.Solve(constraints_, frozen_trajectories_); optimization_problem_.Solve(constraints_, frozen_trajectories_);
common::MutexLocker locker(&mutex_); common::MutexLocker locker(&mutex_);
const auto submap_data = optimization_problem_.submap_data(); const auto& submap_data = optimization_problem_.submap_data();
const auto& node_data = optimization_problem_.node_data(); const auto& node_data = optimization_problem_.node_data();
for (int trajectory_id = 0; for (int trajectory_id = 0;
trajectory_id != static_cast<int>(node_data.size()); ++trajectory_id) { trajectory_id != static_cast<int>(node_data.size()); ++trajectory_id) {
@ -601,19 +588,18 @@ SparsePoseGraph::GetAllSubmapData() {
} }
transform::Rigid3d SparsePoseGraph::ComputeLocalToGlobalTransform( transform::Rigid3d SparsePoseGraph::ComputeLocalToGlobalTransform(
const std::vector<std::map<int, sparse_pose_graph::SubmapData>>& const mapping::MapById<mapping::SubmapId, sparse_pose_graph::SubmapData>&
submap_transforms, submap_transforms,
const int trajectory_id) const { const int trajectory_id) const {
if (trajectory_id >= static_cast<int>(submap_transforms.size()) || auto begin_it = submap_transforms.BeginOfTrajectory(trajectory_id);
submap_transforms.at(trajectory_id).empty()) { auto end_it = submap_transforms.EndOfTrajectory(trajectory_id);
if (begin_it == end_it) {
return transform::Rigid3d::Identity(); return transform::Rigid3d::Identity();
} }
const mapping::SubmapId last_optimized_submap_id = (*std::prev(end_it)).id;
const int submap_index = submap_transforms.at(trajectory_id).rbegin()->first;
const mapping::SubmapId last_optimized_submap_id{trajectory_id, submap_index};
// Accessing 'local_pose' in Submap is okay, since the member is const. // Accessing 'local_pose' in Submap is okay, since the member is const.
return transform::Embed3D( return transform::Embed3D(
submap_transforms.at(trajectory_id).at(submap_index).pose) * submap_transforms.at(last_optimized_submap_id).pose) *
submap_data_.at(last_optimized_submap_id) submap_data_.at(last_optimized_submap_id)
.submap->local_pose() .submap->local_pose()
.inverse(); .inverse();
@ -625,16 +611,10 @@ mapping::SparsePoseGraph::SubmapData SparsePoseGraph::GetSubmapDataUnderLock(
return {}; return {};
} }
auto submap = submap_data_.at(submap_id).submap; auto submap = submap_data_.at(submap_id).submap;
if (submap_id.trajectory_id < if (optimized_submap_transforms_.Contains(submap_id)) {
static_cast<int>(optimized_submap_transforms_.size()) &&
submap_id.submap_index < static_cast<int>(optimized_submap_transforms_
.at(submap_id.trajectory_id)
.size())) {
// We already have an optimized pose. // We already have an optimized pose.
return {submap, transform::Embed3D( return {submap, transform::Embed3D(
optimized_submap_transforms_.at(submap_id.trajectory_id) optimized_submap_transforms_.at(submap_id).pose)};
.at(submap_id.submap_index)
.pose)};
} }
// We have to extrapolate. // We have to extrapolate.
return {submap, ComputeLocalToGlobalTransform(optimized_submap_transforms_, return {submap, ComputeLocalToGlobalTransform(optimized_submap_transforms_,
@ -647,7 +627,8 @@ SparsePoseGraph::TrimmingHandle::TrimmingHandle(SparsePoseGraph* const parent)
int SparsePoseGraph::TrimmingHandle::num_submaps( int SparsePoseGraph::TrimmingHandle::num_submaps(
const int trajectory_id) const { const int trajectory_id) const {
return parent_->optimization_problem_.submap_data().at(trajectory_id).size(); const auto& submap_data = parent_->optimization_problem_.submap_data();
return submap_data.SizeOfTrajectoryOrZero(trajectory_id);
} }
void SparsePoseGraph::TrimmingHandle::MarkSubmapAsTrimmed( void SparsePoseGraph::TrimmingHandle::MarkSubmapAsTrimmed(

View File

@ -161,7 +161,7 @@ class SparsePoseGraph : public mapping::SparsePoseGraph {
// Computes the local to global frame transform based on the given optimized // Computes the local to global frame transform based on the given optimized
// 'submap_transforms'. // 'submap_transforms'.
transform::Rigid3d ComputeLocalToGlobalTransform( transform::Rigid3d ComputeLocalToGlobalTransform(
const std::vector<std::map<int, sparse_pose_graph::SubmapData>>& const mapping::MapById<mapping::SubmapId, sparse_pose_graph::SubmapData>&
submap_transforms, submap_transforms,
int trajectory_id) const REQUIRES(mutex_); int trajectory_id) const REQUIRES(mutex_);
@ -214,7 +214,7 @@ class SparsePoseGraph : public mapping::SparsePoseGraph {
int num_trajectory_nodes_ GUARDED_BY(mutex_) = 0; int num_trajectory_nodes_ GUARDED_BY(mutex_) = 0;
// Current submap transforms used for displaying data. // Current submap transforms used for displaying data.
std::vector<std::map<int, sparse_pose_graph::SubmapData>> mapping::MapById<mapping::SubmapId, sparse_pose_graph::SubmapData>
optimized_submap_transforms_ GUARDED_BY(mutex_); optimized_submap_transforms_ GUARDED_BY(mutex_);
// List of all trimmers to consult when optimizations finish. // List of all trimmers to consult when optimizations finish.

View File

@ -255,15 +255,9 @@ const std::vector<std::map<int, NodeData>>& OptimizationProblem::node_data()
return node_data_; return node_data_;
} }
std::vector<std::map<int, SubmapData>> OptimizationProblem::submap_data() const mapping::MapById<mapping::SubmapId, SubmapData>&
const { OptimizationProblem::submap_data() const {
std::vector<std::map<int, SubmapData>> result; return submap_data_;
for (const auto& submap_id_data : submap_data_) {
result.resize(submap_id_data.id.trajectory_id + 1);
result[submap_id_data.id.trajectory_id].emplace(
submap_id_data.id.submap_index, submap_id_data.data);
}
return result;
} }
} // namespace sparse_pose_graph } // namespace sparse_pose_graph

View File

@ -80,7 +80,7 @@ class OptimizationProblem {
const std::set<int>& frozen_trajectories); const std::set<int>& frozen_trajectories);
const std::vector<std::map<int, NodeData>>& node_data() const; const std::vector<std::map<int, NodeData>>& node_data() const;
std::vector<std::map<int, SubmapData>> submap_data() const; const mapping::MapById<mapping::SubmapId, SubmapData>& submap_data() const;
private: private:
struct TrajectoryData { struct TrajectoryData {

View File

@ -58,9 +58,14 @@ class CompressedPointCloud {
}; };
// Forward iterator for compressed point clouds. // Forward iterator for compressed point clouds.
class CompressedPointCloud::ConstIterator class CompressedPointCloud::ConstIterator {
: public std::iterator<std::forward_iterator_tag, Eigen::Vector3f> {
public: public:
using iterator_category = std::forward_iterator_tag;
using value_type = Eigen::Vector3f;
using difference_type = int64;
using pointer = const Eigen::Vector3f*;
using reference = const Eigen::Vector3f&;
// Creates begin iterator. // Creates begin iterator.
explicit ConstIterator(const CompressedPointCloud* compressed_point_cloud); explicit ConstIterator(const CompressedPointCloud* compressed_point_cloud);