First rough implementation of loading a map. (#382)

This implements loading of serialized maps in 2D:
We insert all submaps at the serialized optimized pose into
the pose graph and remove their poses from the optimization.

Related to #197 and #315.
master
Wolfgang Hess 2017-07-04 17:20:13 +02:00 committed by GitHub
parent 31f77a01d2
commit 7434e96369
10 changed files with 108 additions and 5 deletions

View File

@ -84,5 +84,7 @@ bool ProtoStreamReader::Read(string* decompressed_data) {
return true; return true;
} }
bool ProtoStreamReader::ok() const { return !in_.fail(); }
} // namespace io } // namespace io
} // namespace cartographer } // namespace cartographer

View File

@ -71,6 +71,8 @@ class ProtoStreamReader {
proto->ParseFromString(decompressed_data); proto->ParseFromString(decompressed_data);
} }
bool ok() const;
private: private:
bool Read(string* decompressed_data); bool Read(string* decompressed_data);

View File

@ -157,6 +157,43 @@ void MapBuilder::SerializeState(io::ProtoStreamWriter* const writer) {
// TODO(whess): Serialize additional sensor data: IMU, odometry. // TODO(whess): Serialize additional sensor data: IMU, odometry.
} }
void MapBuilder::LoadMap(io::ProtoStreamReader* const reader) {
proto::SparsePoseGraph pose_graph;
CHECK(reader->ReadProto(&pose_graph));
// TODO(whess): Not all trajectories should be builders, i.e. support should
// be added for trajectories without latest pose, options, etc. Appease the
// trajectory builder for now.
proto::TrajectoryBuilderOptions unused_options;
unused_options.mutable_trajectory_builder_2d_options()
->mutable_submaps_options()
->set_resolution(0.05);
unused_options.mutable_trajectory_builder_2d_options()
->set_num_odometry_states(1);
const std::unordered_set<string> unused_sensor_ids;
const int map_trajectory_id =
AddTrajectoryBuilder(unused_sensor_ids, unused_options);
FinishTrajectory(map_trajectory_id);
sparse_pose_graph_->FreezeTrajectory(map_trajectory_id);
for (;;) {
proto::SerializedData proto;
if (!reader->ReadProto(&proto)) {
break;
}
if (proto.has_submap()) {
const transform::Rigid3d submap_pose = transform::ToRigid3(
pose_graph.trajectory(proto.submap().submap_id().trajectory_id())
.submap(proto.submap().submap_id().submap_index())
.pose());
sparse_pose_graph_->AddSubmapFromProto(map_trajectory_id, submap_pose,
proto.submap());
}
}
CHECK(reader->ok());
}
int MapBuilder::num_trajectory_builders() const { int MapBuilder::num_trajectory_builders() const {
return trajectory_builders_.size(); return trajectory_builders_.size();
} }

View File

@ -81,6 +81,9 @@ class MapBuilder {
// Serializes the current state to a proto stream. // Serializes the current state to a proto stream.
void SerializeState(io::ProtoStreamWriter* writer); void SerializeState(io::ProtoStreamWriter* writer);
// Loads submaps from a proto stream into a new frozen trajectory.
void LoadMap(io::ProtoStreamReader* reader);
int num_trajectory_builders() const; int num_trajectory_builders() const;
mapping::SparsePoseGraph* sparse_pose_graph(); mapping::SparsePoseGraph* sparse_pose_graph();

View File

@ -26,6 +26,7 @@
#include "cartographer/common/lua_parameter_dictionary.h" #include "cartographer/common/lua_parameter_dictionary.h"
#include "cartographer/mapping/id.h" #include "cartographer/mapping/id.h"
#include "cartographer/mapping/pose_graph_trimmer.h" #include "cartographer/mapping/pose_graph_trimmer.h"
#include "cartographer/mapping/proto/serialization.pb.h"
#include "cartographer/mapping/proto/sparse_pose_graph.pb.h" #include "cartographer/mapping/proto/sparse_pose_graph.pb.h"
#include "cartographer/mapping/proto/sparse_pose_graph_options.pb.h" #include "cartographer/mapping/proto/sparse_pose_graph_options.pb.h"
#include "cartographer/mapping/submaps.h" #include "cartographer/mapping/submaps.h"
@ -73,6 +74,15 @@ class SparsePoseGraph {
SparsePoseGraph(const SparsePoseGraph&) = delete; SparsePoseGraph(const SparsePoseGraph&) = delete;
SparsePoseGraph& operator=(const SparsePoseGraph&) = delete; SparsePoseGraph& operator=(const SparsePoseGraph&) = delete;
// Freezes a trajectory. Poses in this trajectory will not be optimized.
virtual void FreezeTrajectory(int trajectory_id) = 0;
// Adds a 'submap' from a proto with the given 'initial_pose' to the frozen
// trajectory with 'trajectory_id'.
virtual void AddSubmapFromProto(int trajectory_id,
const transform::Rigid3d& initial_pose,
const proto::Submap& submap) = 0;
// Adds a 'trimmer'. It will be used after all data added before it has been // Adds a 'trimmer'. It will be used after all data added before it has been
// included in the pose graph. // included in the pose graph.
virtual void AddTrimmer(std::unique_ptr<PoseGraphTrimmer> trimmer) = 0; virtual void AddTrimmer(std::unique_ptr<PoseGraphTrimmer> trimmer) = 0;

View File

@ -371,6 +371,36 @@ void SparsePoseGraph::WaitForAllComputations() {
locker.Await([&notification]() { return notification; }); locker.Await([&notification]() { return notification; });
} }
void SparsePoseGraph::FreezeTrajectory(const int trajectory_id) {
common::MutexLocker locker(&mutex_);
AddWorkItem([this, trajectory_id]() REQUIRES(mutex_) {
CHECK_EQ(frozen_trajectories_.count(trajectory_id), 0);
frozen_trajectories_.insert(trajectory_id);
});
}
void SparsePoseGraph::AddSubmapFromProto(const int trajectory_id,
const transform::Rigid3d& initial_pose,
const mapping::proto::Submap& submap) {
if (!submap.has_submap_2d()) {
return;
}
std::shared_ptr<const Submap> submap_ptr =
std::make_shared<const Submap>(submap.submap_2d());
common::MutexLocker locker(&mutex_);
const mapping::SubmapId submap_id =
submap_data_.Append(trajectory_id, SubmapData());
submap_data_.at(submap_id).submap = submap_ptr;
AddWorkItem([this, submap_id, initial_pose]() REQUIRES(mutex_) {
CHECK_EQ(frozen_trajectories_.count(submap_id.trajectory_id), 1);
submap_data_.at(submap_id).state = SubmapState::kFinished;
optimization_problem_.AddSubmap(submap_id.trajectory_id,
transform::Project2D(initial_pose));
});
}
void SparsePoseGraph::AddTrimmer( void SparsePoseGraph::AddTrimmer(
std::unique_ptr<mapping::PoseGraphTrimmer> trimmer) { std::unique_ptr<mapping::PoseGraphTrimmer> trimmer) {
common::MutexLocker locker(&mutex_); common::MutexLocker locker(&mutex_);
@ -395,7 +425,7 @@ void SparsePoseGraph::RunOptimization() {
if (optimization_problem_.submap_data().empty()) { if (optimization_problem_.submap_data().empty()) {
return; return;
} }
optimization_problem_.Solve(constraints_); optimization_problem_.Solve(constraints_, frozen_trajectories_);
common::MutexLocker locker(&mutex_); common::MutexLocker locker(&mutex_);
std::vector<int> num_trimmed_submaps; std::vector<int> num_trimmed_submaps;

View File

@ -80,6 +80,10 @@ class SparsePoseGraph : public mapping::SparsePoseGraph {
const Eigen::Vector3d& linear_acceleration, const Eigen::Vector3d& linear_acceleration,
const Eigen::Vector3d& angular_velocity); const Eigen::Vector3d& angular_velocity);
void FreezeTrajectory(int trajectory_id) override;
void AddSubmapFromProto(int trajectory_id,
const transform::Rigid3d& initial_pose,
const mapping::proto::Submap& submap) override;
void AddTrimmer(std::unique_ptr<mapping::PoseGraphTrimmer> trimmer) override; void AddTrimmer(std::unique_ptr<mapping::PoseGraphTrimmer> trimmer) override;
void RunFinalOptimization() override; void RunFinalOptimization() override;
std::vector<std::vector<int>> GetConnectedTrajectories() override; std::vector<std::vector<int>> GetConnectedTrajectories() override;
@ -208,6 +212,9 @@ class SparsePoseGraph : public mapping::SparsePoseGraph {
std::vector<std::unique_ptr<mapping::PoseGraphTrimmer>> trimmers_ std::vector<std::unique_ptr<mapping::PoseGraphTrimmer>> trimmers_
GUARDED_BY(mutex_); GUARDED_BY(mutex_);
// Set of all frozen trajectories not being optimized.
std::set<int> frozen_trajectories_ GUARDED_BY(mutex_);
// Allows querying and manipulating the pose graph by the 'trimmers_'. The // Allows querying and manipulating the pose graph by the 'trimmers_'. The
// 'mutex_' of the pose graph is held while this class is used. // 'mutex_' of the pose graph is held while this class is used.
class TrimmingHandle : public mapping::Trimmable { class TrimmingHandle : public mapping::Trimmable {

View File

@ -123,7 +123,8 @@ void OptimizationProblem::SetMaxNumIterations(const int32 max_num_iterations) {
max_num_iterations); max_num_iterations);
} }
void OptimizationProblem::Solve(const std::vector<Constraint>& constraints) { void OptimizationProblem::Solve(const std::vector<Constraint>& constraints,
const std::set<int>& frozen_trajectories) {
if (node_data_.empty()) { if (node_data_.empty()) {
// Nothing to optimize. // Nothing to optimize.
return; return;
@ -140,15 +141,17 @@ void OptimizationProblem::Solve(const std::vector<Constraint>& constraints) {
bool first_submap = true; bool first_submap = true;
for (size_t trajectory_id = 0; trajectory_id != submap_data_.size(); for (size_t trajectory_id = 0; trajectory_id != submap_data_.size();
++trajectory_id) { ++trajectory_id) {
const bool frozen = frozen_trajectories.count(trajectory_id);
// Reserve guarantees that data does not move, so the pointers for Ceres // Reserve guarantees that data does not move, so the pointers for Ceres
// stay valid. // stay valid.
C_submaps[trajectory_id].reserve(submap_data_[trajectory_id].size()); C_submaps[trajectory_id].reserve(submap_data_[trajectory_id].size());
for (const SubmapData& submap_data : submap_data_[trajectory_id]) { for (const SubmapData& submap_data : submap_data_[trajectory_id]) {
C_submaps[trajectory_id].push_back(FromPose(submap_data.pose)); C_submaps[trajectory_id].push_back(FromPose(submap_data.pose));
problem.AddParameterBlock(C_submaps[trajectory_id].back().data(), 3); problem.AddParameterBlock(C_submaps[trajectory_id].back().data(), 3);
if (first_submap) { if (first_submap || frozen) {
first_submap = false; first_submap = false;
// Fix the pose of the first submap. // Fix the pose of the first submap or all submaps of a frozen
// trajectory.
problem.SetParameterBlockConstant( problem.SetParameterBlockConstant(
C_submaps[trajectory_id].back().data()); C_submaps[trajectory_id].back().data());
} }

View File

@ -70,7 +70,8 @@ class OptimizationProblem {
void SetMaxNumIterations(int32 max_num_iterations); void SetMaxNumIterations(int32 max_num_iterations);
// Computes the optimized poses. // Computes the optimized poses.
void Solve(const std::vector<Constraint>& constraints); void Solve(const std::vector<Constraint>& constraints,
const std::set<int>& frozen_trajectories);
const std::vector<std::deque<NodeData>>& node_data() const; const std::vector<std::deque<NodeData>>& node_data() const;
const std::vector<std::deque<SubmapData>>& submap_data() const; const std::vector<std::deque<SubmapData>>& submap_data() const;

View File

@ -77,6 +77,14 @@ class SparsePoseGraph : public mapping::SparsePoseGraph {
const Eigen::Vector3d& linear_acceleration, const Eigen::Vector3d& linear_acceleration,
const Eigen::Vector3d& angular_velocity); const Eigen::Vector3d& angular_velocity);
void FreezeTrajectory(const int trajectory_id) override {
LOG(FATAL) << "Not yet implemented for 3D.";
}
void AddSubmapFromProto(const int trajectory_id,
const transform::Rigid3d& initial_pose,
const mapping::proto::Submap& submap) override {
LOG(FATAL) << "Not yet implemented for 3D";
}
void AddTrimmer(std::unique_ptr<mapping::PoseGraphTrimmer> trimmer) override { void AddTrimmer(std::unique_ptr<mapping::PoseGraphTrimmer> trimmer) override {
LOG(FATAL) << "Not yet implemented for 3D."; LOG(FATAL) << "Not yet implemented for 3D.";
} }