27 class SubmapCoverageGrid2D {
30 using CellId = std::pair<
int64 , int64 >;
31 using StoredType = std::vector<std::pair<SubmapId, common::Time>>;
33 SubmapCoverageGrid2D(
const MapLimits& map_limits)
36 void AddPoint(
const Eigen::Vector2d& point,
const SubmapId& submap_id,
40 cells_[cell_id].emplace_back(submap_id, time);
43 const std::map<CellId, StoredType>& cells()
const {
return cells_; }
54 std::set<SubmapId> AddSubmapsToSubmapCoverageGrid2D(
55 const std::map<SubmapId, common::Time>& submap_freshness,
56 const MapById<SubmapId, PoseGraphInterface::SubmapData>& submap_data,
57 SubmapCoverageGrid2D* coverage_grid) {
58 std::set<SubmapId> all_submap_ids;
60 for (
const auto& submap : submap_data) {
61 auto freshness = submap_freshness.find(submap.id);
62 if (freshness == submap_freshness.end())
continue;
63 if (!submap.data.submap->finished())
continue;
64 all_submap_ids.insert(submap.id);
66 *std::static_pointer_cast<
const Submap2D>(submap.data.submap)->grid();
68 Eigen::Array2i offset;
69 CellLimits cell_limits;
70 grid.ComputeCroppedLimits(&offset, &cell_limits);
71 if (cell_limits.num_x_cells == 0 || cell_limits.num_y_cells == 0) {
72 LOG(WARNING) <<
"Empty grid found in submap ID = " << submap.id;
78 submap.data.submap->local_pose().
inverse();
79 for (
const Eigen::Array2i& xy_index : XYIndexRangeIterator(cell_limits)) {
80 const Eigen::Array2i index = xy_index + offset;
81 if (!grid.IsKnown(index))
continue;
85 grid.limits().max().x() -
86 grid.limits().resolution() * (index.y() + 0.5),
87 grid.limits().max().y() -
88 grid.limits().resolution() * (index.x() + 0.5),
93 submap_frame_from_local_frame *
94 center_of_cell_in_local_frame);
95 coverage_grid->AddPoint(center_of_cell_in_global_frame.translation(),
96 submap.id, freshness->second);
99 return all_submap_ids;
104 std::map<SubmapId, common::Time> ComputeSubmapFreshness(
105 const MapById<SubmapId, PoseGraphInterface::SubmapData>& submap_data,
106 const MapById<NodeId, TrajectoryNode>& trajectory_nodes,
107 const std::vector<PoseGraphInterface::Constraint>& constraints) {
108 std::map<SubmapId, common::Time> submap_freshness;
111 std::map<SubmapId, NodeId> submap_to_latest_node;
112 for (
const PoseGraphInterface::Constraint& constraint : constraints) {
116 auto submap_to_node = submap_to_latest_node.find(constraint.submap_id);
117 if (submap_to_node == submap_to_latest_node.end()) {
118 submap_to_latest_node.insert(
119 std::make_pair(constraint.submap_id, constraint.node_id));
122 submap_to_node->second =
123 std::max(submap_to_node->second, constraint.node_id);
127 for (
const auto& submap_id_to_node_id : submap_to_latest_node) {
128 auto submap_data_item = submap_data.find(submap_id_to_node_id.first);
129 if (submap_data_item == submap_data.end()) {
130 LOG(WARNING) <<
"Intra-submap constraint between SubmapID = " 131 << submap_id_to_node_id.first <<
" and NodeID " 132 << submap_id_to_node_id.second <<
" is missing submap data";
135 auto latest_node_id = trajectory_nodes.find(submap_id_to_node_id.second);
136 if (latest_node_id == trajectory_nodes.end())
continue;
137 submap_freshness[submap_data_item->id] = latest_node_id->data.time();
139 return submap_freshness;
144 std::vector<SubmapId> FindSubmapIdsToTrim(
145 const SubmapCoverageGrid2D& coverage_grid,
146 const std::set<SubmapId>& all_submap_ids,
uint16 fresh_submaps_count,
147 uint16 min_covered_cells_count) {
148 std::map<SubmapId, uint16> submap_to_covered_cells_count;
149 for (
const auto& cell : coverage_grid.cells()) {
150 std::vector<std::pair<SubmapId, common::Time>> submaps_per_cell(
155 if (submaps_per_cell.size() > fresh_submaps_count) {
157 std::sort(submaps_per_cell.begin(), submaps_per_cell.end(),
158 [](
const std::pair<SubmapId, common::Time>& left,
159 const std::pair<SubmapId, common::Time>& right) {
160 return left.second > right.second;
162 submaps_per_cell.erase(submaps_per_cell.begin() + fresh_submaps_count,
163 submaps_per_cell.end());
165 for (
const std::pair<SubmapId, common::Time>& submap : submaps_per_cell) {
166 ++submap_to_covered_cells_count[submap.first];
169 std::vector<SubmapId> submap_ids_to_keep;
170 for (
const auto& id_to_cells_count : submap_to_covered_cells_count) {
171 if (id_to_cells_count.second < min_covered_cells_count)
continue;
172 submap_ids_to_keep.push_back(id_to_cells_count.first);
175 DCHECK(std::is_sorted(submap_ids_to_keep.begin(), submap_ids_to_keep.end()));
176 std::vector<SubmapId> result;
177 std::set_difference(all_submap_ids.begin(), all_submap_ids.end(),
178 submap_ids_to_keep.begin(), submap_ids_to_keep.end(),
179 std::back_inserter(result));
187 if (submap_data.size() - current_submap_count_ <= min_added_submaps_count_) {
191 const MapLimits first_submap_map_limits =
192 std::static_pointer_cast<
const Submap2D>(submap_data.begin()->data.submap)
195 SubmapCoverageGrid2D coverage_grid(first_submap_map_limits);
196 const std::map<SubmapId, common::Time> submap_freshness =
199 const std::set<SubmapId> all_submap_ids = AddSubmapsToSubmapCoverageGrid2D(
200 submap_freshness, submap_data, &coverage_grid);
201 const std::vector<SubmapId> submap_ids_to_remove =
202 FindSubmapIdsToTrim(coverage_grid, all_submap_ids, fresh_submaps_count_,
203 min_covered_cells_count_);
204 current_submap_count_ = submap_data.size() - submap_ids_to_remove.size();
205 for (
const SubmapId&
id : submap_ids_to_remove) {
virtual const MapById< NodeId, TrajectoryNode > & GetTrajectoryNodes() const =0
virtual void MarkSubmapAsTrimmed(const SubmapId &submap_id)=0
UniversalTimeScaleClock::time_point Time
std::map< CellId, StoredType > cells_
void Trim(Trimmable *pose_graph) override
virtual MapById< SubmapId, PoseGraphInterface::SubmapData > GetOptimizedSubmapData() const =0
int64 RoundToInt64(const float x)
virtual const std::vector< PoseGraphInterface::Constraint > & GetConstraints() const =0