GTSAM (Georgia Tech Smoothing and Mapping Library)
repository·develop·Indexed 25 days ago
https://github.com/borglab/gtsamA C++ library for robotics and vision that implements smoothing and mapping (SAM) using Factor Graphs and Bayes Networks. It includes specialized tools for SLAM constraints via SmartFactors, CMake utilities for build configuration and testing (GTSAMCMakeTools), and official Docker images for development and Python bindings.
What's inside GTSAM
- pybind11 is a lightweight, header-only C++ library designed to expose C++ types in Python and vice versa. It is primarily used to create Python bindings for existing C++ code by using compile-time introspection to minimize boilerplate. It is a compact alternative to Boost.Python, requiring only C++11 or newer and a Python implementation (CPython 3.8+, PyPy, or GraalPy).
Overview of EKF Variants in GTSAM
developGTSAM provides a hierarchy of Extended Kalman Filter (EKF) implementations in the
navigationmodule to handle state estimation on differentiable manifolds and Lie groups. While traditional EKFs operate in vector spaces, these specialized variants address linearization errors and geometric constraints.Available EKF Classes:
ManifoldEKF: For states on a differentiable manifold.LieGroupEKF: For states on a Lie group with state-dependent dynamics.InvariantEKF: For states on a Lie group with state-independent (group composition) dynamics (Left Invariant EKF).leftLinearEKF: A general "left linear observer" structure for dynamics involving group automorphisms.EquivariantFilter: Implements the Equivariant Filter (EqF) using symmetry principles.
Use SmartFactors for structure-less SLAM constraints
developGTSAM providesSmartFactors, which are "structure-less" factors. Unlike standard factors that introduce new variables for observed 3D points or landmarks, aSmartFactorcreates a single factor that provides multi-view constraints directly on several poses and/or cameras without needing to explicitly model the 3D landmarks.Available GTSAM Docker Images
developOfficial GTSAM Docker images are available on Docker Hub under the
borglabnamespace. These images provide pre-compiled environments for testing or building applications.borglab/gtsam: Contains the latestdevelopbranch of GTSAM. Best for quick testing or as a base image.borglab/gtsam-manylinux: Based onmanylinux2014, specifically designed for building Python wheels for GTSAM.borglab/ubuntu-boost-tbb: An Ubuntu 24.04 base image with Boost and TBB libraries pre-installed.- CI Images: Various images for Continuous Integration covering different Ubuntu versions (22.04, 24.04) and compilers (Clang, GCC).
Core GTSAM Components for Factor Graph Optimization
developGTSAM is built around three primary components used to construct factor graph representations and perform optimization:
- FactorGraph: A collection of variables to solve for (e.g., robot poses, landmark poses) and the constraints (factors) between them.
- Values: A single object containing labeled values for all variables in the graph. Variables are currently labeled with strings.
- Factors: Nonlinear factors that express constraints between variables, representing measurements such as odometry or visual readings on a landmark.
Use pybind11 conduit for type-safe Python/C++ interoperability
developThepybind11/conduitdirectory provides tools for type-safe interoperability between different, independent Python/C++ binding systems. The core functionality is provided bypybind11_conduit_v1.h.Quickstart: Build and install GTSAM from source
developTo build and install GTSAM from the root library folder using an out-of-source build, execute the following commands in your terminal:
- Create and enter a build directory.
- Run CMake to configure the project.
- (Optional) Build and run unit tests.
- Install the library to the default system path.
Note: It is highly recommended to use Release mode for finished code and timing, as GTSAM can run up to 10x faster compared to the default Debug mode.
$ mkdir build $ cd build $ cmake .. $ cmake --build . --target check # (optional, runs unit tests) $ cmake --build . --target installBuild and run the RangeFactor Plaza2 benchmark
developThe RangeFactor Plaza2 benchmark isolates the Plaza2 incremental SLAM workload for
RangeFactor<Pose2, Point2>. This is used to compare performance when changing theRangeFactorimplementation.Build
From the
builddirectory:make -j6 timeRangeFactorPlaza2Run the executable
Run from the
build/directory. Use--warmupto define untimed runs and--repeatsfor measured repetitions../timing/timeRangeFactorPlaza2 --warmup 1 --repeats 5 \ --output ../timing/results/range_factor_plaza2.csvRun the helper script
Run from the repository root using the
py312conda environment. This script runs the executable, preserves the CSV, and prints a summary for PR descriptions.conda run -n py312 python timing/benchmark_range_factor_plaza2.py \ --build-dir build \ --warmup 1 \ --repeats 5 \ --output timing/results/range_factor_plaza2.csvBest practices for sub-interpreter safety
developWhen working with multiple interpreters, follow these safety rules to prevent crashes and undefined behavior:
- Object Isolation: Never share Python objects across different interpreters.
- Exception Handling:
py::error_already_set::what()acquires the GIL. Therefore, you must catch Python exceptions inside the scope of thepy::subinterpreter_scoped_activateguard. Never let an exception propagate past the activation guard. - State Management: Avoid global or static C++ state. Use the interpreter's state dictionary via
py::subinterpreter::current().state_dict()to keep state isolated per interpreter. - Avoid Caching: Do not cache Python objects (like modules or functions) in C++ variables across function calls, as the object may belong to an interpreter that is no longer active.
- RAII Lifetime: Do not move or disarm RAII objects that manage GIL or sub-interpreter lifetimes (e.g., don't let a
subinterpreter_scoped_activateoutlive its parentsubinterpreter). - Thread Safety: While sub-interpreters have independent GILs, your underlying C++ code must still be thread-safe if multiple interpreters call into it concurrently.
Set up a Python Virtual Environment for GTSAM
developOn systems enforcing PEP 668 (like modern Ubuntu, macOS Homebrew, or Fedora), you should use a virtual environment to install requirements and build the wrapper.
- Create and activate the environment:
python3 -m venv .venv source .venv/bin/activate - Install development requirements:
pip install -r <gtsam_folder>/python/dev_requirements.txt - When running CMake, ensure
PYTHON_EXECUTABLEpoints to the venv interpreter:cmake .. -DGTSAM_BUILD_PYTHON=ON -DPYTHON_EXECUTABLE=$(which python3)
python3 -m venv .venv source .venv/bin/activate pip install -r <gtsam_folder>/python/dev_requirements.txt cmake .. -DGTSAM_BUILD_PYTHON=ON -DPYTHON_EXECUTABLE=$(which python3)- Create and activate the environment:
Expose C++ types to NumPy via the Buffer Protocol
developTo allow NumPy to access the raw internal data of a C++ class without copying (e.g., using
np.array(instance, copy=False)), implement the Python buffer protocol usingpybind11.- Add the
py::buffer_protocol()tag to thepy::class_constructor. - Use
.def_buffer()with a lambda that returns apy::buffer_infoobject describing the memory layout (pointer, item size, format, dimensions, and strides).
// Example: Binding a Matrix class to support the buffer protocol py::class_<Matrix>(m, "Matrix", py::buffer_protocol()) .def_buffer([](Matrix &m) -> py::buffer_info { return py::buffer_info( m.data(), /* Pointer to buffer */ sizeof(float), /* Size of one scalar */ py::format_descriptor<float>::format(), /* Python struct-style format descriptor */ 2, /* Number of dimensions */ { m.rows(), m.cols() }, /* Buffer dimensions */ { sizeof(float) * m.cols(), /* Strides (in bytes) for each index */ sizeof(float) } ); });- Add the
Build Python extension modules with meson-python
developYou can use
Mesonandmeson-pythonto build your package. Yourmeson.buildfile must define the extension module and its dependencies (includingpybind11), and yourpyproject.tomlmust specifymeson-pythonas the build backend. Note thatmeson-pythonrequires the project to be in a git or mercurial repository for SDist creation.project( 'example', 'cpp', version: '0.1.0', default_options: [ 'cpp_std=c++11', ], ) py = import('python').find_installation(pure: false) pybind11_dep = dependency('pybind11') py.extension_module('example', 'example.cpp', install: true, dependencies : [pybind11_dep], )[build-system] requires = ["meson-python", "pybind11"] build-backend = "mesonpy"