업데이트 된 object
- place 간 연결 수정 (가장 가까운 place를 찾아 연결)2d
places를 도출void FrontendModule::updateMesh(const ReconstructionOutput& input) {
{ // start timing scope
ScopedTimer timer("frontend/mesh_archive", input.timestamp_ns, true, 1, false);
VLOG(5) << "[Hydra Frontend] Clearing " << input.archived_blocks.size()
<< " blocks from mesh";
if (!input.archived_blocks.empty()) {
mesh_compression_->clearArchivedBlocks(input.archived_blocks);
}
} // end timing scope
// TODO(nathan) prune archived blocks from input?
const auto& input_mesh = input.map().getMeshLayer();
{
ScopedTimer timer("frontend/mesh_compression", input.timestamp_ns, true, 1, false);
mesh_remapping_ = std::make_shared<kimera_pgmo::HashedIndexMapping>();
auto mesh = getActiveMesh(input_mesh, input.archived_blocks);
VLOG(5) << "[Hydra Frontend] Updating mesh with " << mesh->numBlocks() << " blocks";
auto interface = PgmoMeshLayerInterface(*mesh);
last_mesh_update_ =
mesh_compression_->update(interface, input.timestamp_ns, mesh_remapping_.get());
} // end timing scope
{ // start timing scope
// TODO(nathan) we should probably have a mutex before modifying the mesh, but
// nothing else uses it at the moment
ScopedTimer timer("frontend/mesh_update", input.timestamp_ns, true, 1, false);
last_mesh_update_->updateMesh(*dsg_->graph->mesh());
invalidateMeshEdges(*last_mesh_update_);
} // end timing scope
ScopedTimer timer(
"frontend/launch_postmesh_callbacks", input.timestamp_ns, true, 1, false);
launchCallbacks(post_mesh_callbacks_, input);
}
void FrontendModule::updateObjects(const ReconstructionOutput& input) {
if (!last_mesh_update_) {
LOG(ERROR) << "Cannot detect objects without valid mesh";
return;
}
const auto clusters =
segmenter_->detect(input.timestamp_ns, *last_mesh_update_, std::nullopt);
{ // start dsg critical section
std::unique_lock<std::mutex> lock(dsg_->mutex);
segmenter_->updateGraph(input.timestamp_ns,
clusters,
last_mesh_update_->getTotalArchivedVertices(),
*dsg_->graph);
addPlaceObjectEdges(input.timestamp_ns);
} // end dsg critical section
}
불필요한 메쉬 블록을 삭제
+ 압축된 메쉬 데이터를 생성
input.archived_blocks
는 이전 프레임에서 사용되지 않거나 불필요하게 된 메쉬 블록을 의미mesh_compression_
객체를 사용해 삭제clearArchivedBlocks()
함수로 처리됨const auto& input_mesh = input.map().getMeshLayer();
input_mesh
)로 설정mesh_remapping_
)std::make_shared<kimera_pgmo::HashedIndexMapping>()
로 리매핑 정보를 저장할 객체를 생성getActiveMesh(input_mesh, input.archived_blocks)
auto interface = PgmoMeshLayerInterface(*mesh);
last_mesh_update_ = mesh_compression_->update(interface, input.timestamp_ns, mesh_remapping_.get());
mesh_compression_->update
구현 부분을 찾아서 공부last_mesh_update_
에 저장된 압축된 메쉬 업데이트 정보를 실제로 그래프(dsg_
)에 반영last_mesh_update_->updateMesh(*dsg_->graph->mesh())
는 업데이트된 메쉬 데이터를 그래프에 적용하는 작업last_mesh_update_->updateMesh
에 대해 찾아서 공부해보기invalidateMeshEdges()
는 변경된 메쉬에 연결된 엣지(edge)들을 무효화하여 유효하지 않은 연결을 제거ScopedTimer timer("frontend/launch_postmesh_callbacks", input.timestamp_ns, true, 1, false);
launchCallbacks(post_mesh_callbacks_, input)
FrontendModule::updateObjects
, FrontendModule::updatePlaces2d
void FrontendModule::updateObjects(const ReconstructionOutput& input) {
if (!last_mesh_update_) {
LOG(ERROR) << "Cannot detect objects without valid mesh";
return;
}
const auto clusters =
segmenter_->detect(input.timestamp_ns, *last_mesh_update_, std::nullopt);
{ // start dsg critical section
std::unique_lock<std::mutex> lock(dsg_->mutex);
segmenter_->updateGraph(input.timestamp_ns,
clusters,
last_mesh_update_->getTotalArchivedVertices(),
*dsg_->graph);
addPlaceObjectEdges(input.timestamp_ns);
} // end dsg critical section
}
클러스터링을 통한 객체 탐지
const auto clusters =
segmenter_->detect(input.timestamp_ns, *last_mesh_update_, std::nullopt);
segmenter_
객체는 메쉬를 기반으로 객체를 클러스터링detect()
함수는 클러스터링된 객체 정보(clusters)를 반환DSG(Graph) 업데이트 (Critical Section)
{ // start dsg critical section
std::unique_lock<std::mutex> lock(dsg_->mutex);
segmenter_->updateGraph(input.timestamp_ns,
clusters,
last_mesh_update_->getTotalArchivedVertices(),
*dsg_->graph);
addPlaceObjectEdges(input.timestamp_ns);
} // end dsg critical section
segmenter_->updateGraph()
:last_mesh_update_->getTotalArchivedVertices()
는 메쉬의 아카이브된 전체 버텍스 수를 반환addPlaceObjectEdges(input.timestamp_ns)
함수를 호출하여 객체와 장소 노드 간의 연결을 추가segmenter_
에서 활성화된 노드들을 반환하는 getActiveNodes()
함수로부터 가져옵니다. 이 함수는 현재 메쉬에서 인식된 활성 객체들의 ID를 반환합니다.for (const auto& object_id : segmenter_->getActiveNodes()) {
node->attributes().position
)를 기준으로 가장 가까운 장소 노드를 탐색places_nn_finder_
는 K-최근접 이웃(KNN) 알고리즘을 사용해, 주어진 객체 위치에서 가장 가까운 장소 노드를 찾습니다. find
함수는 탐색된 장소 노드에 대해 콜백 함수를 실행합니다. places_nn_finder_->find(
node->attributes().position, 1, false, [&](NodeId place_id, size_t, double) {
dsg_->graph->insertParentEdge(place_id, object_id);
});
insertParentEdge(place_id, object_id)
함수는 place_id
를 부모로, object_id
를 자식으로 설정하여, 그래프에 부모-자식 관계를 정의합니다.dsg_->graph->insertParentEdge(place_id, object_id);
장소 탐지 (surface_places_
->detect)
NodeIdSet active_nodes;
{ // start timing scope
ScopedTimer timer("frontend/places_2d", input.timestamp_ns, true, 1, false);
surface_places_->detect(input, *last_mesh_update_, *dsg_->graph);
surface_places_->detect()
함수는 현재 메쉬 데이터(last_mesh_update_
)와 DSG를 기반으로 2D 장소를 탐지DSG 업데이트 (Critical Section)
{ // start graph critical section
std::unique_lock<std::mutex> graph_lock(dsg_->mutex);
surface_places_->updateGraph(input.timestamp_ns, input, *dsg_->graph);
archivePlaces2d(active_nodes);
previous_active_places_2d_ = active_nodes;
archivePlaces2d(active_nodes)
함수는 활성 상태에서 벗어난 장소를 아카이브 상태로 변경하는 작업을 수행active_places
)과 이전 활성 장소 집합(previous_active_places_2d_
)을 비교previous_active_places_2d_
에 현재 활성 노드 목록을 저장하여 다음 업데이트에서 비교할 수 있도록 합니다.