Skip to content

Commit de5b05e

Browse files
Feature/active dsg port (#120)
* initial merge tracker changes * Merge tracker and update cleanup functor interface * fixup * code review fixes * style changes and cleanup for pr * clean up registration and includes * fix compile warning * rework exhaustive merging call * change how exhaustive merging is configured * fix dsg updater verbosity --------- Co-authored-by: Nathan Hughes <nathan.h.hughes@gmail.com>
1 parent 39ec703 commit de5b05e

16 files changed

+421
-101
lines changed

include/hydra/backend/dsg_updater.h

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,14 @@
3737
#include <kimera_pgmo/kimera_pgmo_interface.h>
3838
#include <spark_dsg/scene_graph_logger.h>
3939

40-
#include <filesystem>
4140
#include <map>
4241
#include <memory>
43-
#include <mutex>
44-
#include <thread>
4542

46-
#include "hydra/backend/backend_input.h"
47-
#include "hydra/backend/external_loop_closure_receiver.h"
4843
#include "hydra/backend/merge_tracker.h"
49-
#include "hydra/backend/pgmo_configs.h"
5044
#include "hydra/common/output_sink.h"
5145
#include "hydra/common/shared_dsg_info.h"
52-
#include "hydra/common/shared_module_state.h"
5346
#include "hydra/utils/data_directory.h"
47+
#include "hydra/utils/logging.h"
5448

5549
namespace hydra {
5650

@@ -62,16 +56,20 @@ class DsgUpdater {
6256
const kimera_pgmo::DeformationGraph&>;
6357
using NodeToRobotMap = std::unordered_map<NodeId, size_t>;
6458

65-
struct Config {
59+
struct Config : VerbosityConfig {
60+
using FunctorConfig = config::VirtualConfig<UpdateFunctor, true>;
61+
62+
Config();
63+
//! Verbosity controls for functors
64+
VerbosityConfig functor_logging;
6665
//! Enable combining multiple nodes together
6766
bool enable_node_merging = true;
68-
//! Repeatedly run merge detection until no more merges are detected
69-
bool enable_exhaustive_merging = false;
7067
//! If true, reset the private DSG with the unmerged graph on every loop closure
7168
bool reset_dsg_on_loop_closure = false;
7269
//! Update functors that get applied in the specified order
73-
config::OrderedMap<std::string, config::VirtualConfig<UpdateFunctor, true>>
74-
update_functors;
70+
config::OrderedMap<std::string, FunctorConfig> update_functors;
71+
//! Names of functors to use exhaustive merging for
72+
std::vector<std::string> exhaustive_functors;
7573
} const config;
7674

7775
DsgUpdater(const Config& config,
@@ -91,7 +89,7 @@ class DsgUpdater {
9189
void callUpdateFunctions(size_t timestamp_ns, UpdateInfo::ConstPtr info);
9290

9391
private:
94-
MergeTracker merge_tracker;
92+
GroupedMergeTracker merge_tracker;
9593
std::map<std::string, UpdateFunctor::Ptr> update_functors_;
9694

9795
DynamicSceneGraph::Ptr source_graph_;

include/hydra/backend/merge_tracker.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,30 @@ struct MergeTracker {
4343
SharedDsgInfo& dsg,
4444
const MergeFunc& merge_attrs = MergeFunc());
4545

46+
void updateAllMergeAttributes(const DynamicSceneGraph& unmerged,
47+
DynamicSceneGraph& merged,
48+
const MergeFunc& merge_attrs);
49+
4650
void clear();
4751

52+
void erase_nodes(std::vector<NodeId> nodes_to_erase);
53+
std::string print() const;
54+
4855
private:
4956
void updateParents(std::map<NodeId, NodeId>& prior_merges, const Merge& merge);
5057

5158
std::map<NodeId, std::set<NodeId>> merge_sets_;
5259
};
5360

61+
struct GroupedMergeTracker {
62+
void initializeTracker(std::string name);
63+
void clear();
64+
void erase_nodes(std::vector<NodeId> nodes);
65+
std::string print() const;
66+
MergeTracker& getMergeGroup(std::string name);
67+
68+
private:
69+
std::map<std::string, MergeTracker> group_to_tracker_;
70+
};
71+
5472
} // namespace hydra

include/hydra/backend/update_buildings_functor.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@
3333
* purposes notwithstanding any copyright notation herein.
3434
* -------------------------------------------------------------------------- */
3535
#pragma once
36-
#include <config_utilities/factory.h>
37-
3836
#include "hydra/backend/update_functions.h"
3937

4038
namespace hydra {
@@ -50,11 +48,6 @@ struct UpdateBuildingsFunctor : public UpdateFunctor {
5048
void call(const DynamicSceneGraph& unmerged,
5149
SharedDsgInfo& dsg,
5250
const UpdateInfo::ConstPtr& info) const override;
53-
54-
private:
55-
inline static const auto registration_ =
56-
config::RegistrationWithConfig<UpdateFunctor, UpdateBuildingsFunctor, Config>(
57-
"UpdateBuildingsFunctor");
5851
};
5952

6053
void declare_config(UpdateBuildingsFunctor::Config& config);

include/hydra/backend/update_frontiers_functor.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,12 @@ namespace hydra {
4141

4242
struct UpdateFrontiersFunctor : public UpdateFunctor {
4343
struct Config {
44-
//! Frontiers with in this distance of the robot will be checked against places for
45-
//! potential removal
46-
double frontier_removal_check_threshold = 4;
44+
//! Frontiers within this distance will be checked against places for removal
45+
double frontier_removal_check_threshold = 4.0;
4746
//! Frontiers within this distance of the robot will be removed
48-
double frontier_removal_threshold = 1;
47+
double frontier_removal_threshold = 1.0;
48+
//! Distance around existing frontiers that blocks adding new frontiers
49+
double frontier_exclusion_radius = 0.0;
4950
} const config;
5051

5152
explicit UpdateFrontiersFunctor(const Config& config) : config(config) {}
@@ -54,12 +55,18 @@ struct UpdateFrontiersFunctor : public UpdateFunctor {
5455
SharedDsgInfo&,
5556
const UpdateInfo::ConstPtr&) const override;
5657

57-
void cleanup(uint64_t timestamp_ns, SharedDsgInfo& dsg) const;
58+
void cleanup(uint64_t timestamp_ns,
59+
DynamicSceneGraph& unmerged_dsg,
60+
SharedDsgInfo& dsg) const;
5861

5962
private:
6063
inline static const auto registration_ =
6164
config::RegistrationWithConfig<UpdateFunctor, UpdateFrontiersFunctor, Config>(
6265
"UpdateFrontiersFunctor");
66+
mutable NodeSymbol next_node_id_ = NodeSymbol('G', 0);
67+
mutable std::set<NodeId> deleted_frontiers_;
6368
};
6469

70+
void declare_config(UpdateFrontiersFunctor::Config& config);
71+
6572
} // namespace hydra

include/hydra/backend/update_functions.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,15 @@ struct UpdateInfo {
6565
kimera_pgmo::MeshOffsetInfo mesh_offsets = {};
6666
};
6767

68-
using LayerCleanupFunc =
69-
std::function<void(const UpdateInfo::ConstPtr&, SharedDsgInfo*)>;
68+
using LayerCleanupFunc = std::function<void(
69+
const UpdateInfo::ConstPtr&, DynamicSceneGraph&, SharedDsgInfo*)>;
7070
using FindMergeFunc =
7171
std::function<MergeList(const DynamicSceneGraph&, const UpdateInfo::ConstPtr&)>;
7272
using MergeFunc = std::function<NodeAttributes::Ptr(const DynamicSceneGraph&,
7373
const std::vector<NodeId>&)>;
7474

7575
struct UpdateFunctor {
7676
using Ptr = std::shared_ptr<UpdateFunctor>;
77-
7877
struct Hooks {
7978
LayerCleanupFunc cleanup;
8079
FindMergeFunc find_merges;

include/hydra/backend/update_rooms_functor.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@
3333
* purposes notwithstanding any copyright notation herein.
3434
* -------------------------------------------------------------------------- */
3535
#pragma once
36-
#include <config_utilities/factory.h>
37-
3836
#include "hydra/backend/update_functions.h"
3937
#include "hydra/common/output_sink.h"
4038
#include "hydra/rooms/room_finder.h"
@@ -60,9 +58,6 @@ struct UpdateRoomsFunctor : public UpdateFunctor {
6058
std::unique_ptr<RoomFinder> room_finder;
6159

6260
private:
63-
inline static const auto registration_ =
64-
config::RegistrationWithConfig<UpdateFunctor, UpdateRoomsFunctor, Config>(
65-
"UpdateRoomsFunctor");
6661
Sink::List sinks_;
6762
};
6863

include/hydra/backend/update_surface_places_functor.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,6 @@ struct Update2dPlacesFunctor : public UpdateFunctor {
8888

8989
private:
9090
mutable NodeSymbol next_node_id_ = NodeSymbol('S', 0);
91-
92-
inline static const auto registration_ =
93-
config::RegistrationWithConfig<UpdateFunctor, Update2dPlacesFunctor, Config>(
94-
"Update2dPlacesFunctor");
9591
};
9692

9793
void declare_config(Update2dPlacesFunctor::Config& conf);

include/hydra/utils/logging.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ struct VerbosityConfig {
4444
VerbosityConfig() = default;
4545
VerbosityConfig(int verbosity);
4646
VerbosityConfig(const std::string& prefix, int verbosity = 0);
47+
48+
//! Make a copy of the verbosity config with a prefix derived from the provided name
49+
VerbosityConfig with_name(const std::string& name) const;
4750
};
4851

4952
void declare_config(VerbosityConfig& config);

src/backend/dsg_updater.cpp

Lines changed: 83 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,53 @@
4141
#include <glog/stl_logging.h>
4242
#include <kimera_pgmo/utils/mesh_io.h>
4343

44-
#include "hydra/common/global_info.h"
45-
#include "hydra/common/launch_callbacks.h"
46-
#include "hydra/common/pipeline_queues.h"
47-
#include "hydra/utils/minimum_spanning_tree.h"
48-
#include "hydra/utils/pgmo_mesh_traits.h"
44+
#include "hydra/utils/pgmo_mesh_traits.h" // IWYU pragma: keep
4945
#include "hydra/utils/timing_utilities.h"
5046

5147
namespace hydra {
48+
namespace {
49+
50+
void findAndApplyMerges(const VerbosityConfig& config,
51+
const UpdateFunctor::Hooks& hooks,
52+
const UpdateInfo::ConstPtr& info,
53+
const DynamicSceneGraph& source,
54+
SharedDsgInfo& target,
55+
MergeTracker& tracker,
56+
bool exhaustive) {
57+
// TODO(nathan) handle given merges
58+
auto merges = hooks.find_merges(source, info);
59+
auto applied = tracker.applyMerges(source, merges, target, hooks.merge);
60+
MLOG(1) << "pass 0: " << merges.size() << " merges (applied " << applied << ")";
61+
if (!exhaustive) {
62+
return;
63+
}
64+
65+
applied = 0;
66+
size_t iter = 0;
67+
do {
68+
merges = hooks.find_merges(*target.graph, info);
69+
applied = tracker.applyMerges(source, merges, target, hooks.merge);
70+
MLOG(1) << "pass " << iter << ": " << merges.size() << " merges (applied "
71+
<< applied << ")";
72+
++iter;
73+
} while (applied > 0);
74+
}
75+
76+
} // namespace
5277

5378
using hydra::timing::ScopedTimer;
54-
using kimera_pgmo::KimeraPgmoInterface;
55-
using pose_graph_tools::PoseGraph;
79+
80+
DsgUpdater::Config::Config() : VerbosityConfig("[dsg_updated] ") {}
5681

5782
void declare_config(DsgUpdater::Config& config) {
5883
using namespace config;
5984
name("DsgUpdaterConfig");
85+
base<VerbosityConfig>(config);
86+
field(config.functor_logging, "functor_logging");
6087
field(config.enable_node_merging, "enable_node_merging");
61-
field(config.enable_exhaustive_merging, "enable_exhaustive_merging");
6288
field(config.reset_dsg_on_loop_closure, "reset_dsg_on_loop_closure");
6389
field(config.update_functors, "update_functors");
90+
field(config.exhaustive_functors, "exhaustive_functors");
6491
}
6592

6693
DsgUpdater::DsgUpdater(const Config& config,
@@ -69,6 +96,7 @@ DsgUpdater::DsgUpdater(const Config& config,
6996
: config(config::checkValid(config)), source_graph_(source), target_dsg_(target) {
7097
for (const auto& [name, functor] : config.update_functors) {
7198
update_functors_.emplace(name, functor.create());
99+
merge_tracker.initializeTracker(name);
72100
}
73101
}
74102

@@ -116,11 +144,28 @@ void DsgUpdater::callUpdateFunctions(size_t timestamp_ns, UpdateInfo::ConstPtr i
116144
if (config.reset_dsg_on_loop_closure && info->loop_closure_detected) {
117145
resetBackendDsg(timestamp_ns);
118146
}
147+
119148
GraphMergeConfig merge_config;
120149
merge_config.previous_merges = &target_dsg_->merges;
121150
merge_config.update_dynamic_attributes = false;
122151
target_dsg_->graph->mergeGraph(*source_graph_, merge_config);
123152

153+
// Nodes occasionally get added to the backend after they've left the active window,
154+
// which means they never get deformed or updated correctly. This forces them to be
155+
// active for at least one update
156+
std::vector<NodeId> active_nodes_to_restore;
157+
for (auto& [layer_id, layer] : source_graph_->layers()) {
158+
for (auto& [node_id, node] : layer->nodes()) {
159+
auto& attrs = node->attributes();
160+
if (source_graph_->checkNode(node_id) == NodeStatus::NEW && !attrs.is_active) {
161+
attrs.is_active = true;
162+
active_nodes_to_restore.push_back(node_id);
163+
}
164+
}
165+
}
166+
167+
const std::set<std::string> exhaustive_names(config.exhaustive_functors.begin(),
168+
config.exhaustive_functors.end());
124169
std::list<LayerCleanupFunc> cleanup_hooks;
125170
for (const auto& [name, functor] : update_functors_) {
126171
if (!functor) {
@@ -134,30 +179,39 @@ void DsgUpdater::callUpdateFunctions(size_t timestamp_ns, UpdateInfo::ConstPtr i
134179

135180
functor->call(*source_graph_, *target_dsg_, info);
136181
if (hooks.find_merges && enable_merging) {
137-
// TODO(nathan) handle given merges
138-
const auto merges = hooks.find_merges(*source_graph_, info);
139-
const auto applied =
140-
merge_tracker.applyMerges(*source_graph_, merges, *target_dsg_, hooks.merge);
141-
VLOG(1) << "[Backend: " << name << "] Found " << merges.size()
142-
<< " merges (applied " << applied << ")";
143-
144-
if (config.enable_exhaustive_merging) {
145-
size_t merge_iter = 0;
146-
size_t num_applied = 0;
147-
do {
148-
const auto new_merges = hooks.find_merges(*target_dsg_->graph, info);
149-
num_applied = merge_tracker.applyMerges(
150-
*source_graph_, new_merges, *target_dsg_, hooks.merge);
151-
VLOG(1) << "[Backend: " << name << "] Found " << new_merges.size()
152-
<< " merges at pass " << merge_iter << " (" << num_applied
153-
<< " applied)";
154-
++merge_iter;
155-
} while (num_applied > 0);
182+
auto& tracker = merge_tracker.getMergeGroup(name);
183+
findAndApplyMerges(config.functor_logging.with_name(name),
184+
hooks,
185+
info,
186+
*source_graph_,
187+
*target_dsg_,
188+
tracker,
189+
exhaustive_names.count(name));
190+
if (info->loop_closure_detected && hooks.merge) {
191+
MLOG(3) << "updating all merge attributes for " << name;
192+
MLOG(3) << "current tracker: " << tracker.print();
193+
tracker.updateAllMergeAttributes(
194+
*source_graph_, *target_dsg_->graph, hooks.merge);
156195
}
157196
}
158-
}
159197

160-
launchCallbacks(cleanup_hooks, info, target_dsg_.get());
198+
MLOG(2) << "all merges: " << merge_tracker.print();
199+
for (const auto& func : cleanup_hooks) {
200+
func(info, *source_graph_, target_dsg_.get());
201+
}
202+
203+
// We reset active flags for all new nodes that were inactive after one update
204+
for (auto node_id : active_nodes_to_restore) {
205+
auto node = source_graph_->findNode(node_id);
206+
if (node) {
207+
node->attributes().is_active = false;
208+
}
209+
}
210+
211+
// clear new node status
212+
// TODO(nathan) add API for marking new nodes
213+
source_graph_->getNewNodes(true);
214+
}
161215
}
162216

163217
} // namespace hydra

0 commit comments

Comments
 (0)