{"id":50296753,"url":"https://github.com/alvgaona/dual-pose-graph","last_synced_at":"2026-05-28T09:03:11.890Z","repository":{"id":352125989,"uuid":"1213954567","full_name":"alvgaona/dual-pose-graph","owner":"alvgaona","description":"Generic dual pose-graph SLAM library in C++. Fuses odometry with semantic detections via a main graph plus a temp graph that validates observations before merging them to the main graph.","archived":false,"fork":false,"pushed_at":"2026-04-18T00:48:56.000Z","size":143,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-18T02:40:16.544Z","etag":null,"topics":["cmake","cpp","loop-closure","pose-graph-optimization","slam","state-estimation"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alvgaona.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-18T00:15:54.000Z","updated_at":"2026-04-18T00:49:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/alvgaona/dual-pose-graph","commit_stats":null,"previous_names":["alvgaona/dual-pose-graph"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/alvgaona/dual-pose-graph","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvgaona%2Fdual-pose-graph","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvgaona%2Fdual-pose-graph/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvgaona%2Fdual-pose-graph/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvgaona%2Fdual-pose-graph/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alvgaona","download_url":"https://codeload.github.com/alvgaona/dual-pose-graph/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvgaona%2Fdual-pose-graph/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33601382,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cmake","cpp","loop-closure","pose-graph-optimization","slam","state-estimation"],"created_at":"2026-05-28T09:03:10.887Z","updated_at":"2026-05-28T09:03:11.884Z","avatar_url":"https://github.com/alvgaona.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dual Pose Graph\n\n[![CI](https://github.com/alvgaona/dual-pose-graph/actions/workflows/ci.yaml/badge.svg)](https://github.com/alvgaona/dual-pose-graph/actions/workflows/ci.yaml)\n[![License: BSD-3-Clause](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](LICENSE)\n[![C++17](https://img.shields.io/badge/C%2B%2B-17-blue.svg)](https://isocpp.org/std/the-standard)\n[![pixi](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/prefix-dev/pixi/main/assets/badge/v0.json)](https://pixi.sh)\n\nGeneric dual pose-graph SLAM library. Extracted from the AeroStack2\n`as2_semantic_slam` package and stripped of every ROS, drone-racing, and\nvisualization dependency.\n\nThe library fuses odometry with semantic object detections by maintaining two\npose graphs: a long-term **main** graph for state estimation and a short-lived\n**temp** graph that validates new detections before merging. Any detection kind\nworks: subclass `ObjectDetection` + `GraphNode`, implement one virtual, and the\noptimizer integrates it without further changes.\n\n## Build\n\n```sh\npixi install -e test\npixi run -e test build\npixi run -e test test\npixi run -e test example\n```\n\n## Coverage\n\n```sh\npixi run -e coverage coverage\n```\n\nBuilds with `--coverage`, runs the full test suite, and emits an HTML report at\n`build-coverage/coverage.html` plus a Cobertura `coverage.xml` for CI.\n\n## Packaging as a conda package\n\n```sh\npixi build\n```\n\nUses pixi's preview `pixi-build-cmake` backend to wrap the library as a conda\npackage. The resulting `.conda` artifact installs headers, the shared library,\nand CMake config files, so a downstream project can depend on it and use\n`find_package(dual_pose_graph CONFIG REQUIRED)`.\n\nThe `libg2o` binary is pulled from the `robostack-humble` conda channel. The\npackage name is ROS-prefixed for packaging reasons only — the library itself is\nplain C++ with no ROS code.\n\n## Using it\n\nMinimum viable usage — feed odometry + generic SE3 landmark detections:\n\n```cpp\n#include \u003cdual_pose_graph/optimizer_g2o.hpp\u003e\n#include \u003cdual_pose_graph/object_detection_types.hpp\u003e\n\nOptimizerG2O optimizer;\nOptimizerG2OParameters params;\nparams.main_graph_odometry_distance_threshold = 0.5;\nparams.use_dual_graph = true;\noptimizer.set_parameters(params);\n\nOdometryWithCovariance odom;\nodom.odometry = /* Eigen::Isometry3d */;\nodom.covariance = /* Eigen::MatrixXd 6x6 */;\noptimizer.handle_new_odom(odom);\n\nSE3ObjectDetection det(\"my_kind\", \"instance_7\", pose, cov_6x6, /*is_absolute=*/true);\nOdometryInfo info;\nif (optimizer.check_adding_new_detection(odom, info)) {\n  optimizer.handle_new_object_detection(\u0026det, info);\n}\n\nEigen::Isometry3d corrected = optimizer.get_optimized_pose();\n```\n\nSee `examples/minimal_example.cpp` for a runnable version.\n\n## Extending with a custom detection kind\n\nThe optimizer's temp→main merge is polymorphic: each concrete `GraphNode`\nrebuilds its own absolute-frame `ObjectDetection` via `buildAbsoluteDetection`.\nShip your own kind without touching the library:\n\n```cpp\nclass MyDetection : public ObjectDetectionSE3 {\n  // override create_node() to return new MyNode{...}\n  // override create_edge() to wire into the graph\n};\n\nclass MyNode : public GraphNodeSE3 {\n  // override build_absolute_detection() to return std::make_unique\u003cMyDetection\u003e(...)\n};\n```\n\nThen feed `MyDetection` through `handle_new_object_detection` exactly like the\ngeneric types. The optimizer will merge it the same way.\n\nSee `tests/test_custom_detection_extension.cpp` for a worked example.\n\n## API surface\n\n- **`OptimizerG2O`** — dual-graph orchestrator. Entry points: `set_parameters`,\n  `handle_new_odom`, `check_adding_new_detection`, `handle_new_object_detection`,\n  `get_optimized_pose`, `get_optimized_map_pose`, `get_map_odom_transform`.\n- **`GraphG2O`** — thin wrapper over `g2o::SparseOptimizer`.\n- **`ObjectDetection`** — base for detections. Concrete helpers:\n  `SE3ObjectDetection`, `Point3DObjectDetection`. Key virtuals: `create_node`,\n  `create_edge`, `prepare_measurements`.\n- **`GraphNode`** — base for graph vertices. Concrete helpers: `OdomNode`,\n  `SE3ObjectNode`, `Point3DObjectNode`. Key virtual: `build_absolute_detection`.\n- **`GraphEdge`** — base for graph edges. Concrete helpers: `OdomEdge`,\n  `GraphEdgeSE3`, `GraphEdgeSE3Point3D`.\n\n## What this library intentionally does NOT ship\n\n- ROS integration (subscribers, TF, message conversions) — build it on top.\n- Visualization (`visualization_msgs::Marker` or equivalent) — use public\n  getters (`get_pose()`, `get_position()`, `type_id()`) to drive any visualizer.\n- Drone-specific landmark types (ArUco markers, racing gates) — subclass the\n  generic types in your downstream package.\n\n## Citation\n\nIf you use this library in academic work, please cite the accompanying paper:\n\n```bibtex\n@misc{perezsaura2026dualposegraphsemanticlocalization,\n      title={Dual Pose-Graph Semantic Localization for Vision-Based Autonomous Drone Racing},\n      author={David Perez-Saura and Miguel Fernandez-Cortizas and Alvaro J. Gaona and Pascual Campoy},\n      year={2026},\n      eprint={2604.15168},\n      archivePrefix={arXiv},\n      primaryClass={cs.RO},\n      url={https://arxiv.org/abs/2604.15168},\n}\n```\n\n## License\n\nBSD 3-Clause. See `LICENSE`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falvgaona%2Fdual-pose-graph","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falvgaona%2Fdual-pose-graph","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falvgaona%2Fdual-pose-graph/lists"}