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
parent
31f77a01d2
commit
7434e96369
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -371,6 +371,36 @@ void SparsePoseGraph::WaitForAllComputations() {
|
||||||
locker.Await([¬ification]() { return notification; });
|
locker.Await([¬ification]() { 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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue