DeleteTrajectory (#1205)
[RFC=0023](https://github.com/googlecartographer/rfcs/blob/master/text/0023-delete-load.md)master
parent
ecaa95f3b0
commit
54041d76eb
|
@ -135,6 +135,10 @@ void PoseGraphStub::SetLandmarkPose(const std::string& landmark_id,
|
||||||
CHECK(client.Write(request));
|
CHECK(client.Write(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PoseGraphStub::DeleteTrajectory(int trajectory_id) {
|
||||||
|
LOG(FATAL) << "not implemented";
|
||||||
|
}
|
||||||
|
|
||||||
bool PoseGraphStub::IsTrajectoryFinished(int trajectory_id) const {
|
bool PoseGraphStub::IsTrajectoryFinished(int trajectory_id) const {
|
||||||
proto::IsTrajectoryFinishedRequest request;
|
proto::IsTrajectoryFinishedRequest request;
|
||||||
request.set_trajectory_id(trajectory_id);
|
request.set_trajectory_id(trajectory_id);
|
||||||
|
|
|
@ -45,6 +45,7 @@ class PoseGraphStub : public ::cartographer::mapping::PoseGraphInterface {
|
||||||
std::map<std::string, transform::Rigid3d> GetLandmarkPoses() const override;
|
std::map<std::string, transform::Rigid3d> GetLandmarkPoses() const override;
|
||||||
void SetLandmarkPose(const std::string& landmark_id,
|
void SetLandmarkPose(const std::string& landmark_id,
|
||||||
const transform::Rigid3d& global_pose) override;
|
const transform::Rigid3d& global_pose) override;
|
||||||
|
void DeleteTrajectory(int trajectory_id) override;
|
||||||
bool IsTrajectoryFinished(int trajectory_id) const override;
|
bool IsTrajectoryFinished(int trajectory_id) const override;
|
||||||
bool IsTrajectoryFrozen(int trajectory_id) const override;
|
bool IsTrajectoryFrozen(int trajectory_id) const override;
|
||||||
std::map<int, mapping::PoseGraphInterface::TrajectoryData> GetTrajectoryData()
|
std::map<int, mapping::PoseGraphInterface::TrajectoryData> GetTrajectoryData()
|
||||||
|
|
|
@ -113,6 +113,9 @@ NodeId PoseGraph2D::AddNode(
|
||||||
|
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
AddTrajectoryIfNeeded(trajectory_id);
|
AddTrajectoryIfNeeded(trajectory_id);
|
||||||
|
if (!CanAddWorkItemModifying(trajectory_id)) {
|
||||||
|
LOG(WARNING) << "AddNode was called for finished or deleted trajectory.";
|
||||||
|
}
|
||||||
const NodeId node_id = data_.trajectory_nodes.Append(
|
const NodeId node_id = data_.trajectory_nodes.Append(
|
||||||
trajectory_id, TrajectoryNode{constant_data, optimized_pose});
|
trajectory_id, TrajectoryNode{constant_data, optimized_pose});
|
||||||
++data_.num_trajectory_nodes;
|
++data_.num_trajectory_nodes;
|
||||||
|
@ -152,6 +155,8 @@ void PoseGraph2D::AddTrajectoryIfNeeded(const int trajectory_id) {
|
||||||
TrajectoryState::FINISHED);
|
TrajectoryState::FINISHED);
|
||||||
CHECK(data_.trajectories_state.at(trajectory_id).state !=
|
CHECK(data_.trajectories_state.at(trajectory_id).state !=
|
||||||
TrajectoryState::DELETED);
|
TrajectoryState::DELETED);
|
||||||
|
CHECK(data_.trajectories_state.at(trajectory_id).deletion_state ==
|
||||||
|
InternalTrajectoryState::DeletionState::NORMAL);
|
||||||
data_.trajectory_connectivity_state.Add(trajectory_id);
|
data_.trajectory_connectivity_state.Add(trajectory_id);
|
||||||
// Make sure we have a sampler for this trajectory.
|
// Make sure we have a sampler for this trajectory.
|
||||||
if (!global_localization_samplers_[trajectory_id]) {
|
if (!global_localization_samplers_[trajectory_id]) {
|
||||||
|
@ -164,6 +169,7 @@ void PoseGraph2D::AddTrajectoryIfNeeded(const int trajectory_id) {
|
||||||
void PoseGraph2D::AddImuData(const int trajectory_id,
|
void PoseGraph2D::AddImuData(const int trajectory_id,
|
||||||
const sensor::ImuData& imu_data) {
|
const sensor::ImuData& imu_data) {
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
|
if (!CanAddWorkItemModifying(trajectory_id)) return;
|
||||||
AddWorkItem([=]() REQUIRES(mutex_) {
|
AddWorkItem([=]() REQUIRES(mutex_) {
|
||||||
optimization_problem_->AddImuData(trajectory_id, imu_data);
|
optimization_problem_->AddImuData(trajectory_id, imu_data);
|
||||||
});
|
});
|
||||||
|
@ -172,6 +178,7 @@ void PoseGraph2D::AddImuData(const int trajectory_id,
|
||||||
void PoseGraph2D::AddOdometryData(const int trajectory_id,
|
void PoseGraph2D::AddOdometryData(const int trajectory_id,
|
||||||
const sensor::OdometryData& odometry_data) {
|
const sensor::OdometryData& odometry_data) {
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
|
if (!CanAddWorkItemModifying(trajectory_id)) return;
|
||||||
AddWorkItem([=]() REQUIRES(mutex_) {
|
AddWorkItem([=]() REQUIRES(mutex_) {
|
||||||
optimization_problem_->AddOdometryData(trajectory_id, odometry_data);
|
optimization_problem_->AddOdometryData(trajectory_id, odometry_data);
|
||||||
});
|
});
|
||||||
|
@ -184,9 +191,9 @@ void PoseGraph2D::AddFixedFramePoseData(
|
||||||
}
|
}
|
||||||
|
|
||||||
void PoseGraph2D::AddLandmarkData(int trajectory_id,
|
void PoseGraph2D::AddLandmarkData(int trajectory_id,
|
||||||
const sensor::LandmarkData& landmark_data)
|
const sensor::LandmarkData& landmark_data) {
|
||||||
EXCLUDES(mutex_) {
|
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
|
if (!CanAddWorkItemModifying(trajectory_id)) return;
|
||||||
AddWorkItem([=]() REQUIRES(mutex_) {
|
AddWorkItem([=]() REQUIRES(mutex_) {
|
||||||
for (const auto& observation : landmark_data.landmark_observations) {
|
for (const auto& observation : landmark_data.landmark_observations) {
|
||||||
data_.landmark_nodes[observation.id].landmark_observations.emplace_back(
|
data_.landmark_nodes[observation.id].landmark_observations.emplace_back(
|
||||||
|
@ -284,6 +291,8 @@ void PoseGraph2D::ComputeConstraintsForNode(
|
||||||
Constraint::INTRA_SUBMAP});
|
Constraint::INTRA_SUBMAP});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(gaschler): Consider not searching for constraints against trajectories
|
||||||
|
// scheduled for deletion.
|
||||||
for (const auto& submap_id_data : data_.submap_data) {
|
for (const auto& submap_id_data : data_.submap_data) {
|
||||||
if (submap_id_data.data.state == SubmapState::kFinished) {
|
if (submap_id_data.data.state == SubmapState::kFinished) {
|
||||||
CHECK_EQ(submap_id_data.data.node_ids.count(node_id), 0);
|
CHECK_EQ(submap_id_data.data.node_ids.count(node_id), 0);
|
||||||
|
@ -342,6 +351,23 @@ void PoseGraph2D::UpdateTrajectoryConnectivity(const Constraint& constraint) {
|
||||||
time);
|
time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PoseGraph2D::DeleteTrajectoriesIfNeeded() {
|
||||||
|
TrimmingHandle trimming_handle(this);
|
||||||
|
for (auto& it : data_.trajectories_state) {
|
||||||
|
if (it.second.deletion_state ==
|
||||||
|
InternalTrajectoryState::DeletionState::WAIT_FOR_DELETION) {
|
||||||
|
// TODO(gaschler): Consider directly deleting from data_, which may be
|
||||||
|
// more complete.
|
||||||
|
auto submap_ids = trimming_handle.GetSubmapIds(it.first);
|
||||||
|
for (auto& submap_id : submap_ids) {
|
||||||
|
trimming_handle.TrimSubmap(submap_id);
|
||||||
|
}
|
||||||
|
it.second.state = TrajectoryState::DELETED;
|
||||||
|
it.second.deletion_state = InternalTrajectoryState::DeletionState::NORMAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PoseGraph2D::HandleWorkQueue(
|
void PoseGraph2D::HandleWorkQueue(
|
||||||
const constraints::ConstraintBuilder2D::Result& result) {
|
const constraints::ConstraintBuilder2D::Result& result) {
|
||||||
{
|
{
|
||||||
|
@ -374,6 +400,7 @@ void PoseGraph2D::HandleWorkQueue(
|
||||||
for (const Constraint& constraint : result) {
|
for (const Constraint& constraint : result) {
|
||||||
UpdateTrajectoryConnectivity(constraint);
|
UpdateTrajectoryConnectivity(constraint);
|
||||||
}
|
}
|
||||||
|
DeleteTrajectoriesIfNeeded();
|
||||||
TrimmingHandle trimming_handle(this);
|
TrimmingHandle trimming_handle(this);
|
||||||
for (auto& trimmer : trimmers_) {
|
for (auto& trimmer : trimmers_) {
|
||||||
trimmer->Trim(&trimming_handle);
|
trimmer->Trim(&trimming_handle);
|
||||||
|
@ -438,6 +465,22 @@ void PoseGraph2D::WaitForAllComputations() {
|
||||||
locker.Await([¬ification]() { return notification; });
|
locker.Await([¬ification]() { return notification; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PoseGraph2D::DeleteTrajectory(const int trajectory_id) {
|
||||||
|
common::MutexLocker locker(&mutex_);
|
||||||
|
data_.trajectories_state.at(trajectory_id).deletion_state =
|
||||||
|
InternalTrajectoryState::DeletionState::SCHEDULED_FOR_DELETION;
|
||||||
|
AddWorkItem([this, trajectory_id]() REQUIRES(mutex_) {
|
||||||
|
CHECK(data_.trajectories_state.at(trajectory_id).state !=
|
||||||
|
TrajectoryState::ACTIVE);
|
||||||
|
CHECK(data_.trajectories_state.at(trajectory_id).state !=
|
||||||
|
TrajectoryState::DELETED);
|
||||||
|
CHECK(data_.trajectories_state.at(trajectory_id).deletion_state ==
|
||||||
|
InternalTrajectoryState::DeletionState::SCHEDULED_FOR_DELETION);
|
||||||
|
data_.trajectories_state.at(trajectory_id).deletion_state =
|
||||||
|
InternalTrajectoryState::DeletionState::WAIT_FOR_DELETION;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void PoseGraph2D::FinishTrajectory(const int trajectory_id) {
|
void PoseGraph2D::FinishTrajectory(const int trajectory_id) {
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
AddWorkItem([this, trajectory_id]() REQUIRES(mutex_) {
|
AddWorkItem([this, trajectory_id]() REQUIRES(mutex_) {
|
||||||
|
@ -488,6 +531,7 @@ void PoseGraph2D::AddSubmapFromProto(
|
||||||
|
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
AddTrajectoryIfNeeded(submap_id.trajectory_id);
|
AddTrajectoryIfNeeded(submap_id.trajectory_id);
|
||||||
|
if (!CanAddWorkItemModifying(submap_id.trajectory_id)) return;
|
||||||
data_.submap_data.Insert(submap_id, InternalSubmapData());
|
data_.submap_data.Insert(submap_id, InternalSubmapData());
|
||||||
data_.submap_data.at(submap_id).submap = submap_ptr;
|
data_.submap_data.at(submap_id).submap = submap_ptr;
|
||||||
// Immediately show the submap at the 'global_submap_pose'.
|
// Immediately show the submap at the 'global_submap_pose'.
|
||||||
|
@ -508,6 +552,7 @@ void PoseGraph2D::AddNodeFromProto(const transform::Rigid3d& global_pose,
|
||||||
|
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
AddTrajectoryIfNeeded(node_id.trajectory_id);
|
AddTrajectoryIfNeeded(node_id.trajectory_id);
|
||||||
|
if (!CanAddWorkItemModifying(node_id.trajectory_id)) return;
|
||||||
data_.trajectory_nodes.Insert(node_id,
|
data_.trajectory_nodes.Insert(node_id,
|
||||||
TrajectoryNode{constant_data, global_pose});
|
TrajectoryNode{constant_data, global_pose});
|
||||||
|
|
||||||
|
@ -535,6 +580,7 @@ void PoseGraph2D::SetTrajectoryDataFromProto(
|
||||||
void PoseGraph2D::AddNodeToSubmap(const NodeId& node_id,
|
void PoseGraph2D::AddNodeToSubmap(const NodeId& node_id,
|
||||||
const SubmapId& submap_id) {
|
const SubmapId& submap_id) {
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
|
if (!CanAddWorkItemModifying(submap_id.trajectory_id)) return;
|
||||||
AddWorkItem([this, node_id, submap_id]() REQUIRES(mutex_) {
|
AddWorkItem([this, node_id, submap_id]() REQUIRES(mutex_) {
|
||||||
data_.submap_data.at(submap_id).node_ids.insert(node_id);
|
data_.submap_data.at(submap_id).node_ids.insert(node_id);
|
||||||
});
|
});
|
||||||
|
@ -649,6 +695,37 @@ void PoseGraph2D::RunOptimization() {
|
||||||
data_.global_submap_poses_2d = submap_data;
|
data_.global_submap_poses_2d = submap_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PoseGraph2D::CanAddWorkItemModifying(int trajectory_id) {
|
||||||
|
auto it = data_.trajectories_state.find(trajectory_id);
|
||||||
|
if (it == data_.trajectories_state.end()) {
|
||||||
|
LOG(WARNING) << "trajectory_id:" << trajectory_id
|
||||||
|
<< " has not been added "
|
||||||
|
"but modification is requested.";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (it->second.state == TrajectoryState::FINISHED) {
|
||||||
|
// TODO(gaschler): Replace all FATAL to WARNING after some testing.
|
||||||
|
LOG(FATAL) << "trajectory_id " << trajectory_id
|
||||||
|
<< " has finished "
|
||||||
|
"but modification is requested, skipping.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (it->second.deletion_state !=
|
||||||
|
InternalTrajectoryState::DeletionState::NORMAL) {
|
||||||
|
LOG(FATAL) << "trajectory_id " << trajectory_id
|
||||||
|
<< " has been scheduled for deletion "
|
||||||
|
"but modification is requested, skipping.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (it->second.state == TrajectoryState::DELETED) {
|
||||||
|
LOG(FATAL) << "trajectory_id " << trajectory_id
|
||||||
|
<< " has been deleted "
|
||||||
|
"but modification is requested, skipping.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
MapById<NodeId, TrajectoryNode> PoseGraph2D::GetTrajectoryNodes() const {
|
MapById<NodeId, TrajectoryNode> PoseGraph2D::GetTrajectoryNodes() const {
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
return data_.trajectory_nodes;
|
return data_.trajectory_nodes;
|
||||||
|
|
|
@ -93,6 +93,7 @@ class PoseGraph2D : public PoseGraph {
|
||||||
const sensor::LandmarkData& landmark_data) override
|
const sensor::LandmarkData& landmark_data) override
|
||||||
EXCLUDES(mutex_);
|
EXCLUDES(mutex_);
|
||||||
|
|
||||||
|
void DeleteTrajectory(int trajectory_id) override;
|
||||||
void FinishTrajectory(int trajectory_id) override;
|
void FinishTrajectory(int trajectory_id) override;
|
||||||
bool IsTrajectoryFinished(int trajectory_id) const override REQUIRES(mutex_);
|
bool IsTrajectoryFinished(int trajectory_id) const override REQUIRES(mutex_);
|
||||||
void FreezeTrajectory(int trajectory_id) override;
|
void FreezeTrajectory(int trajectory_id) override;
|
||||||
|
@ -179,6 +180,10 @@ class PoseGraph2D : public PoseGraph {
|
||||||
void ComputeConstraintsForOldNodes(const SubmapId& submap_id)
|
void ComputeConstraintsForOldNodes(const SubmapId& submap_id)
|
||||||
REQUIRES(mutex_);
|
REQUIRES(mutex_);
|
||||||
|
|
||||||
|
// Deletes trajectories waiting for deletion. Must not be called during
|
||||||
|
// constraint search.
|
||||||
|
void DeleteTrajectoriesIfNeeded() REQUIRES(mutex_);
|
||||||
|
|
||||||
// Runs the optimization, executes the trimmers and processes the work queue.
|
// Runs the optimization, executes the trimmers and processes the work queue.
|
||||||
void HandleWorkQueue(const constraints::ConstraintBuilder2D::Result& result)
|
void HandleWorkQueue(const constraints::ConstraintBuilder2D::Result& result)
|
||||||
REQUIRES(mutex_);
|
REQUIRES(mutex_);
|
||||||
|
@ -191,6 +196,8 @@ class PoseGraph2D : public PoseGraph {
|
||||||
// optimization being run at a time.
|
// optimization being run at a time.
|
||||||
void RunOptimization() EXCLUDES(mutex_);
|
void RunOptimization() EXCLUDES(mutex_);
|
||||||
|
|
||||||
|
bool CanAddWorkItemModifying(int trajectory_id) REQUIRES(mutex_);
|
||||||
|
|
||||||
// Computes the local to global map frame transform based on the given
|
// Computes the local to global map frame transform based on the given
|
||||||
// 'global_submap_poses'.
|
// 'global_submap_poses'.
|
||||||
transform::Rigid3d ComputeLocalToGlobalTransform(
|
transform::Rigid3d ComputeLocalToGlobalTransform(
|
||||||
|
|
|
@ -111,6 +111,9 @@ NodeId PoseGraph3D::AddNode(
|
||||||
|
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
AddTrajectoryIfNeeded(trajectory_id);
|
AddTrajectoryIfNeeded(trajectory_id);
|
||||||
|
if (!CanAddWorkItemModifying(trajectory_id)) {
|
||||||
|
LOG(WARNING) << "AddNode was called for finished or deleted trajectory.";
|
||||||
|
}
|
||||||
const NodeId node_id = data_.trajectory_nodes.Append(
|
const NodeId node_id = data_.trajectory_nodes.Append(
|
||||||
trajectory_id, TrajectoryNode{constant_data, optimized_pose});
|
trajectory_id, TrajectoryNode{constant_data, optimized_pose});
|
||||||
++data_.num_trajectory_nodes;
|
++data_.num_trajectory_nodes;
|
||||||
|
@ -150,6 +153,8 @@ void PoseGraph3D::AddTrajectoryIfNeeded(const int trajectory_id) {
|
||||||
TrajectoryState::FINISHED);
|
TrajectoryState::FINISHED);
|
||||||
CHECK(data_.trajectories_state.at(trajectory_id).state !=
|
CHECK(data_.trajectories_state.at(trajectory_id).state !=
|
||||||
TrajectoryState::DELETED);
|
TrajectoryState::DELETED);
|
||||||
|
CHECK(data_.trajectories_state.at(trajectory_id).deletion_state ==
|
||||||
|
InternalTrajectoryState::DeletionState::NORMAL);
|
||||||
data_.trajectory_connectivity_state.Add(trajectory_id);
|
data_.trajectory_connectivity_state.Add(trajectory_id);
|
||||||
// Make sure we have a sampler for this trajectory.
|
// Make sure we have a sampler for this trajectory.
|
||||||
if (!global_localization_samplers_[trajectory_id]) {
|
if (!global_localization_samplers_[trajectory_id]) {
|
||||||
|
@ -162,6 +167,7 @@ void PoseGraph3D::AddTrajectoryIfNeeded(const int trajectory_id) {
|
||||||
void PoseGraph3D::AddImuData(const int trajectory_id,
|
void PoseGraph3D::AddImuData(const int trajectory_id,
|
||||||
const sensor::ImuData& imu_data) {
|
const sensor::ImuData& imu_data) {
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
|
if (!CanAddWorkItemModifying(trajectory_id)) return;
|
||||||
AddWorkItem([=]() REQUIRES(mutex_) {
|
AddWorkItem([=]() REQUIRES(mutex_) {
|
||||||
optimization_problem_->AddImuData(trajectory_id, imu_data);
|
optimization_problem_->AddImuData(trajectory_id, imu_data);
|
||||||
});
|
});
|
||||||
|
@ -170,6 +176,7 @@ void PoseGraph3D::AddImuData(const int trajectory_id,
|
||||||
void PoseGraph3D::AddOdometryData(const int trajectory_id,
|
void PoseGraph3D::AddOdometryData(const int trajectory_id,
|
||||||
const sensor::OdometryData& odometry_data) {
|
const sensor::OdometryData& odometry_data) {
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
|
if (!CanAddWorkItemModifying(trajectory_id)) return;
|
||||||
AddWorkItem([=]() REQUIRES(mutex_) {
|
AddWorkItem([=]() REQUIRES(mutex_) {
|
||||||
optimization_problem_->AddOdometryData(trajectory_id, odometry_data);
|
optimization_problem_->AddOdometryData(trajectory_id, odometry_data);
|
||||||
});
|
});
|
||||||
|
@ -179,6 +186,7 @@ void PoseGraph3D::AddFixedFramePoseData(
|
||||||
const int trajectory_id,
|
const int trajectory_id,
|
||||||
const sensor::FixedFramePoseData& fixed_frame_pose_data) {
|
const sensor::FixedFramePoseData& fixed_frame_pose_data) {
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
|
if (!CanAddWorkItemModifying(trajectory_id)) return;
|
||||||
AddWorkItem([=]() REQUIRES(mutex_) {
|
AddWorkItem([=]() REQUIRES(mutex_) {
|
||||||
optimization_problem_->AddFixedFramePoseData(trajectory_id,
|
optimization_problem_->AddFixedFramePoseData(trajectory_id,
|
||||||
fixed_frame_pose_data);
|
fixed_frame_pose_data);
|
||||||
|
@ -186,9 +194,9 @@ void PoseGraph3D::AddFixedFramePoseData(
|
||||||
}
|
}
|
||||||
|
|
||||||
void PoseGraph3D::AddLandmarkData(int trajectory_id,
|
void PoseGraph3D::AddLandmarkData(int trajectory_id,
|
||||||
const sensor::LandmarkData& landmark_data)
|
const sensor::LandmarkData& landmark_data) {
|
||||||
EXCLUDES(mutex_) {
|
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
|
if (!CanAddWorkItemModifying(trajectory_id)) return;
|
||||||
AddWorkItem([=]() REQUIRES(mutex_) {
|
AddWorkItem([=]() REQUIRES(mutex_) {
|
||||||
for (const auto& observation : landmark_data.landmark_observations) {
|
for (const auto& observation : landmark_data.landmark_observations) {
|
||||||
data_.landmark_nodes[observation.id].landmark_observations.emplace_back(
|
data_.landmark_nodes[observation.id].landmark_observations.emplace_back(
|
||||||
|
@ -299,6 +307,8 @@ void PoseGraph3D::ComputeConstraintsForNode(
|
||||||
Constraint::INTRA_SUBMAP});
|
Constraint::INTRA_SUBMAP});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(gaschler): Consider not searching for constraints against trajectories
|
||||||
|
// scheduled for deletion.
|
||||||
for (const auto& submap_id_data : data_.submap_data) {
|
for (const auto& submap_id_data : data_.submap_data) {
|
||||||
if (submap_id_data.data.state == SubmapState::kFinished) {
|
if (submap_id_data.data.state == SubmapState::kFinished) {
|
||||||
CHECK_EQ(submap_id_data.data.node_ids.count(node_id), 0);
|
CHECK_EQ(submap_id_data.data.node_ids.count(node_id), 0);
|
||||||
|
@ -358,6 +368,23 @@ void PoseGraph3D::UpdateTrajectoryConnectivity(const Constraint& constraint) {
|
||||||
time);
|
time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PoseGraph3D::DeleteTrajectoriesIfNeeded() {
|
||||||
|
TrimmingHandle trimming_handle(this);
|
||||||
|
for (auto& it : data_.trajectories_state) {
|
||||||
|
if (it.second.deletion_state ==
|
||||||
|
InternalTrajectoryState::DeletionState::WAIT_FOR_DELETION) {
|
||||||
|
// TODO(gaschler): Consider directly deleting from data_, which may be
|
||||||
|
// more complete.
|
||||||
|
auto submap_ids = trimming_handle.GetSubmapIds(it.first);
|
||||||
|
for (auto& submap_id : submap_ids) {
|
||||||
|
trimming_handle.TrimSubmap(submap_id);
|
||||||
|
}
|
||||||
|
it.second.state = TrajectoryState::DELETED;
|
||||||
|
it.second.deletion_state = InternalTrajectoryState::DeletionState::NORMAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PoseGraph3D::HandleWorkQueue(
|
void PoseGraph3D::HandleWorkQueue(
|
||||||
const constraints::ConstraintBuilder3D::Result& result) {
|
const constraints::ConstraintBuilder3D::Result& result) {
|
||||||
{
|
{
|
||||||
|
@ -390,6 +417,7 @@ void PoseGraph3D::HandleWorkQueue(
|
||||||
for (const Constraint& constraint : result) {
|
for (const Constraint& constraint : result) {
|
||||||
UpdateTrajectoryConnectivity(constraint);
|
UpdateTrajectoryConnectivity(constraint);
|
||||||
}
|
}
|
||||||
|
DeleteTrajectoriesIfNeeded();
|
||||||
TrimmingHandle trimming_handle(this);
|
TrimmingHandle trimming_handle(this);
|
||||||
for (auto& trimmer : trimmers_) {
|
for (auto& trimmer : trimmers_) {
|
||||||
trimmer->Trim(&trimming_handle);
|
trimmer->Trim(&trimming_handle);
|
||||||
|
@ -454,6 +482,22 @@ void PoseGraph3D::WaitForAllComputations() {
|
||||||
locker.Await([¬ification]() { return notification; });
|
locker.Await([¬ification]() { return notification; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PoseGraph3D::DeleteTrajectory(const int trajectory_id) {
|
||||||
|
common::MutexLocker locker(&mutex_);
|
||||||
|
data_.trajectories_state.at(trajectory_id).deletion_state =
|
||||||
|
InternalTrajectoryState::DeletionState::SCHEDULED_FOR_DELETION;
|
||||||
|
AddWorkItem([this, trajectory_id]() REQUIRES(mutex_) {
|
||||||
|
CHECK(data_.trajectories_state.at(trajectory_id).state !=
|
||||||
|
TrajectoryState::ACTIVE);
|
||||||
|
CHECK(data_.trajectories_state.at(trajectory_id).state !=
|
||||||
|
TrajectoryState::DELETED);
|
||||||
|
CHECK(data_.trajectories_state.at(trajectory_id).deletion_state ==
|
||||||
|
InternalTrajectoryState::DeletionState::SCHEDULED_FOR_DELETION);
|
||||||
|
data_.trajectories_state.at(trajectory_id).deletion_state =
|
||||||
|
InternalTrajectoryState::DeletionState::WAIT_FOR_DELETION;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void PoseGraph3D::FinishTrajectory(const int trajectory_id) {
|
void PoseGraph3D::FinishTrajectory(const int trajectory_id) {
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
AddWorkItem([this, trajectory_id]() REQUIRES(mutex_) {
|
AddWorkItem([this, trajectory_id]() REQUIRES(mutex_) {
|
||||||
|
@ -502,6 +546,7 @@ void PoseGraph3D::AddSubmapFromProto(
|
||||||
|
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
AddTrajectoryIfNeeded(submap_id.trajectory_id);
|
AddTrajectoryIfNeeded(submap_id.trajectory_id);
|
||||||
|
if (!CanAddWorkItemModifying(submap_id.trajectory_id)) return;
|
||||||
data_.submap_data.Insert(submap_id, InternalSubmapData());
|
data_.submap_data.Insert(submap_id, InternalSubmapData());
|
||||||
data_.submap_data.at(submap_id).submap = submap_ptr;
|
data_.submap_data.at(submap_id).submap = submap_ptr;
|
||||||
// Immediately show the submap at the 'global_submap_pose'.
|
// Immediately show the submap at the 'global_submap_pose'.
|
||||||
|
@ -522,6 +567,7 @@ void PoseGraph3D::AddNodeFromProto(const transform::Rigid3d& global_pose,
|
||||||
|
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
AddTrajectoryIfNeeded(node_id.trajectory_id);
|
AddTrajectoryIfNeeded(node_id.trajectory_id);
|
||||||
|
if (!CanAddWorkItemModifying(node_id.trajectory_id)) return;
|
||||||
data_.trajectory_nodes.Insert(node_id,
|
data_.trajectory_nodes.Insert(node_id,
|
||||||
TrajectoryNode{constant_data, global_pose});
|
TrajectoryNode{constant_data, global_pose});
|
||||||
|
|
||||||
|
@ -549,6 +595,7 @@ void PoseGraph3D::SetTrajectoryDataFromProto(
|
||||||
|
|
||||||
const int trajectory_id = data.trajectory_id();
|
const int trajectory_id = data.trajectory_id();
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
|
if (!CanAddWorkItemModifying(trajectory_id)) return;
|
||||||
AddWorkItem([this, trajectory_id, trajectory_data]() REQUIRES(mutex_) {
|
AddWorkItem([this, trajectory_id, trajectory_data]() REQUIRES(mutex_) {
|
||||||
optimization_problem_->SetTrajectoryData(trajectory_id, trajectory_data);
|
optimization_problem_->SetTrajectoryData(trajectory_id, trajectory_data);
|
||||||
});
|
});
|
||||||
|
@ -557,6 +604,7 @@ void PoseGraph3D::SetTrajectoryDataFromProto(
|
||||||
void PoseGraph3D::AddNodeToSubmap(const NodeId& node_id,
|
void PoseGraph3D::AddNodeToSubmap(const NodeId& node_id,
|
||||||
const SubmapId& submap_id) {
|
const SubmapId& submap_id) {
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
|
if (!CanAddWorkItemModifying(submap_id.trajectory_id)) return;
|
||||||
AddWorkItem([this, node_id, submap_id]() REQUIRES(mutex_) {
|
AddWorkItem([this, node_id, submap_id]() REQUIRES(mutex_) {
|
||||||
data_.submap_data.at(submap_id).node_ids.insert(node_id);
|
data_.submap_data.at(submap_id).node_ids.insert(node_id);
|
||||||
});
|
});
|
||||||
|
@ -691,6 +739,37 @@ void PoseGraph3D::RunOptimization() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PoseGraph3D::CanAddWorkItemModifying(int trajectory_id) {
|
||||||
|
auto it = data_.trajectories_state.find(trajectory_id);
|
||||||
|
if (it == data_.trajectories_state.end()) {
|
||||||
|
LOG(WARNING) << "trajectory_id:" << trajectory_id
|
||||||
|
<< " has not been added "
|
||||||
|
"but modification is requested.";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (it->second.state == TrajectoryState::FINISHED) {
|
||||||
|
// TODO(gaschler): Replace all FATAL to WARNING after some testing.
|
||||||
|
LOG(FATAL) << "trajectory_id " << trajectory_id
|
||||||
|
<< " has finished "
|
||||||
|
"but modification is requested, skipping.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (it->second.deletion_state !=
|
||||||
|
InternalTrajectoryState::DeletionState::NORMAL) {
|
||||||
|
LOG(FATAL) << "trajectory_id " << trajectory_id
|
||||||
|
<< " has been scheduled for deletion "
|
||||||
|
"but modification is requested, skipping.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (it->second.state == TrajectoryState::DELETED) {
|
||||||
|
LOG(FATAL) << "trajectory_id " << trajectory_id
|
||||||
|
<< " has been deleted "
|
||||||
|
"but modification is requested, skipping.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
MapById<NodeId, TrajectoryNode> PoseGraph3D::GetTrajectoryNodes() const {
|
MapById<NodeId, TrajectoryNode> PoseGraph3D::GetTrajectoryNodes() const {
|
||||||
common::MutexLocker locker(&mutex_);
|
common::MutexLocker locker(&mutex_);
|
||||||
return data_.trajectory_nodes;
|
return data_.trajectory_nodes;
|
||||||
|
|
|
@ -92,6 +92,7 @@ class PoseGraph3D : public PoseGraph {
|
||||||
const sensor::LandmarkData& landmark_data) override
|
const sensor::LandmarkData& landmark_data) override
|
||||||
EXCLUDES(mutex_);
|
EXCLUDES(mutex_);
|
||||||
|
|
||||||
|
void DeleteTrajectory(int trajectory_id) override;
|
||||||
void FinishTrajectory(int trajectory_id) override;
|
void FinishTrajectory(int trajectory_id) override;
|
||||||
bool IsTrajectoryFinished(int trajectory_id) const override REQUIRES(mutex_);
|
bool IsTrajectoryFinished(int trajectory_id) const override REQUIRES(mutex_);
|
||||||
void FreezeTrajectory(int trajectory_id) override;
|
void FreezeTrajectory(int trajectory_id) override;
|
||||||
|
@ -182,6 +183,10 @@ class PoseGraph3D : public PoseGraph {
|
||||||
void ComputeConstraintsForOldNodes(const SubmapId& submap_id)
|
void ComputeConstraintsForOldNodes(const SubmapId& submap_id)
|
||||||
REQUIRES(mutex_);
|
REQUIRES(mutex_);
|
||||||
|
|
||||||
|
// Deletes trajectories waiting for deletion. Must not be called during
|
||||||
|
// constraint search.
|
||||||
|
void DeleteTrajectoriesIfNeeded() REQUIRES(mutex_);
|
||||||
|
|
||||||
// Runs the optimization, executes the trimmers and processes the work queue.
|
// Runs the optimization, executes the trimmers and processes the work queue.
|
||||||
void HandleWorkQueue(const constraints::ConstraintBuilder3D::Result& result)
|
void HandleWorkQueue(const constraints::ConstraintBuilder3D::Result& result)
|
||||||
REQUIRES(mutex_);
|
REQUIRES(mutex_);
|
||||||
|
@ -190,6 +195,8 @@ class PoseGraph3D : public PoseGraph {
|
||||||
// optimization being run at a time.
|
// optimization being run at a time.
|
||||||
void RunOptimization() EXCLUDES(mutex_);
|
void RunOptimization() EXCLUDES(mutex_);
|
||||||
|
|
||||||
|
bool CanAddWorkItemModifying(int trajectory_id) REQUIRES(mutex_);
|
||||||
|
|
||||||
// Computes the local to global map frame transform based on the given
|
// Computes the local to global map frame transform based on the given
|
||||||
// 'global_submap_poses'.
|
// 'global_submap_poses'.
|
||||||
transform::Rigid3d ComputeLocalToGlobalTransform(
|
transform::Rigid3d ComputeLocalToGlobalTransform(
|
||||||
|
|
|
@ -50,6 +50,7 @@ class MockPoseGraph : public mapping::PoseGraphInterface {
|
||||||
std::map<std::string, transform::Rigid3d>());
|
std::map<std::string, transform::Rigid3d>());
|
||||||
MOCK_METHOD2(SetLandmarkPose,
|
MOCK_METHOD2(SetLandmarkPose,
|
||||||
void(const std::string&, const transform::Rigid3d&));
|
void(const std::string&, const transform::Rigid3d&));
|
||||||
|
MOCK_METHOD1(DeleteTrajectory, void(int));
|
||||||
MOCK_CONST_METHOD1(IsTrajectoryFinished, bool(int));
|
MOCK_CONST_METHOD1(IsTrajectoryFinished, bool(int));
|
||||||
MOCK_CONST_METHOD1(IsTrajectoryFrozen, bool(int));
|
MOCK_CONST_METHOD1(IsTrajectoryFrozen, bool(int));
|
||||||
MOCK_CONST_METHOD0(
|
MOCK_CONST_METHOD0(
|
||||||
|
|
|
@ -89,7 +89,7 @@ class MapBuilderTest : public ::testing::Test {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
int CreateTrajectoryWithFakeData() {
|
int CreateTrajectoryWithFakeData(double timestamps_add_duration = 0.) {
|
||||||
int trajectory_id = map_builder_->AddTrajectoryBuilder(
|
int trajectory_id = map_builder_->AddTrajectoryBuilder(
|
||||||
{kRangeSensorId}, trajectory_builder_options_,
|
{kRangeSensorId}, trajectory_builder_options_,
|
||||||
GetLocalSlamResultCallback());
|
GetLocalSlamResultCallback());
|
||||||
|
@ -98,6 +98,7 @@ class MapBuilderTest : public ::testing::Test {
|
||||||
auto measurements = testing::GenerateFakeRangeMeasurements(
|
auto measurements = testing::GenerateFakeRangeMeasurements(
|
||||||
kTravelDistance, kDuration, kTimeStep);
|
kTravelDistance, kDuration, kTimeStep);
|
||||||
for (auto& measurement : measurements) {
|
for (auto& measurement : measurements) {
|
||||||
|
measurement.time += common::FromSeconds(timestamps_add_duration);
|
||||||
trajectory_builder->AddSensorData(kRangeSensorId.id, measurement);
|
trajectory_builder->AddSensorData(kRangeSensorId.id, measurement);
|
||||||
}
|
}
|
||||||
map_builder_->FinishTrajectory(trajectory_id);
|
map_builder_->FinishTrajectory(trajectory_id);
|
||||||
|
@ -267,6 +268,47 @@ TEST_F(MapBuilderTest, GlobalSlam3D) {
|
||||||
0.1 * kTravelDistance);
|
0.1 * kTravelDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MapBuilderTest, DeleteFinishedTrajectory2D) {
|
||||||
|
SetOptionsEnableGlobalOptimization();
|
||||||
|
BuildMapBuilder();
|
||||||
|
int trajectory_id = CreateTrajectoryWithFakeData();
|
||||||
|
map_builder_->pose_graph()->RunFinalOptimization();
|
||||||
|
EXPECT_TRUE(map_builder_->pose_graph()->IsTrajectoryFinished(trajectory_id));
|
||||||
|
EXPECT_GE(map_builder_->pose_graph()->constraints().size(), 50);
|
||||||
|
EXPECT_GE(
|
||||||
|
map_builder_->pose_graph()->GetTrajectoryNodes().SizeOfTrajectoryOrZero(
|
||||||
|
trajectory_id),
|
||||||
|
20);
|
||||||
|
EXPECT_GE(
|
||||||
|
map_builder_->pose_graph()->GetAllSubmapData().SizeOfTrajectoryOrZero(
|
||||||
|
trajectory_id),
|
||||||
|
5);
|
||||||
|
map_builder_->pose_graph()->DeleteTrajectory(trajectory_id);
|
||||||
|
int another_trajectory_id = CreateTrajectoryWithFakeData(100.);
|
||||||
|
map_builder_->pose_graph()->RunFinalOptimization();
|
||||||
|
EXPECT_TRUE(
|
||||||
|
map_builder_->pose_graph()->IsTrajectoryFinished(another_trajectory_id));
|
||||||
|
EXPECT_EQ(
|
||||||
|
map_builder_->pose_graph()->GetTrajectoryNodes().SizeOfTrajectoryOrZero(
|
||||||
|
trajectory_id),
|
||||||
|
0);
|
||||||
|
EXPECT_EQ(
|
||||||
|
map_builder_->pose_graph()->GetAllSubmapData().SizeOfTrajectoryOrZero(
|
||||||
|
trajectory_id),
|
||||||
|
0);
|
||||||
|
map_builder_->pose_graph()->DeleteTrajectory(another_trajectory_id);
|
||||||
|
map_builder_->pose_graph()->RunFinalOptimization();
|
||||||
|
EXPECT_EQ(map_builder_->pose_graph()->constraints().size(), 0);
|
||||||
|
EXPECT_EQ(
|
||||||
|
map_builder_->pose_graph()->GetTrajectoryNodes().SizeOfTrajectoryOrZero(
|
||||||
|
another_trajectory_id),
|
||||||
|
0);
|
||||||
|
EXPECT_EQ(
|
||||||
|
map_builder_->pose_graph()->GetAllSubmapData().SizeOfTrajectoryOrZero(
|
||||||
|
another_trajectory_id),
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(MapBuilderTest, SaveLoadState) {
|
TEST_F(MapBuilderTest, SaveLoadState) {
|
||||||
for (int dimensions : {2, 3}) {
|
for (int dimensions : {2, 3}) {
|
||||||
if (dimensions == 3) {
|
if (dimensions == 3) {
|
||||||
|
|
|
@ -37,7 +37,6 @@ namespace mapping {
|
||||||
enum class SubmapState { kActive, kFinished };
|
enum class SubmapState { kActive, kFinished };
|
||||||
|
|
||||||
struct InternalTrajectoryState {
|
struct InternalTrajectoryState {
|
||||||
// TODO(gaschler): Implement PoseGraphInterface::DeleteTrajectory.
|
|
||||||
enum class DeletionState {
|
enum class DeletionState {
|
||||||
NORMAL,
|
NORMAL,
|
||||||
SCHEDULED_FOR_DELETION,
|
SCHEDULED_FOR_DELETION,
|
||||||
|
|
|
@ -124,6 +124,9 @@ class PoseGraphInterface {
|
||||||
virtual void SetLandmarkPose(const std::string& landmark_id,
|
virtual void SetLandmarkPose(const std::string& landmark_id,
|
||||||
const transform::Rigid3d& global_pose) = 0;
|
const transform::Rigid3d& global_pose) = 0;
|
||||||
|
|
||||||
|
// Deletes a trajectory asynchronously.
|
||||||
|
virtual void DeleteTrajectory(int trajectory_id) = 0;
|
||||||
|
|
||||||
// Checks if the given trajectory is finished.
|
// Checks if the given trajectory is finished.
|
||||||
virtual bool IsTrajectoryFinished(int trajectory_id) const = 0;
|
virtual bool IsTrajectoryFinished(int trajectory_id) const = 0;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue