GTSAM (Georgia Tech Smoothing and Mapping Library)

repository·develop·Indexed 25 days ago

https://github.com/borglab/gtsam

A 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.

Tokens
113.4K
Snippets
256
Records
538
Agent score
80%

What's inside GTSAM

  1. Overview of pybind11

    develop
    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).
  2. Overview of EKF Variants in GTSAM

    develop

    GTSAM provides a hierarchy of Extended Kalman Filter (EKF) implementations in the navigation module 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.
  3. Use SmartFactors for structure-less SLAM constraints

    develop
    GTSAM provides SmartFactors, which are "structure-less" factors. Unlike standard factors that introduce new variables for observed 3D points or landmarks, a SmartFactor creates a single factor that provides multi-view constraints directly on several poses and/or cameras without needing to explicitly model the 3D landmarks.
  4. Available GTSAM Docker Images

    develop

    Official GTSAM Docker images are available on Docker Hub under the borglab namespace. These images provide pre-compiled environments for testing or building applications.

    • borglab/gtsam: Contains the latest develop branch of GTSAM. Best for quick testing or as a base image.
    • borglab/gtsam-manylinux: Based on manylinux2014, 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).
  5. Core GTSAM Components for Factor Graph Optimization

    develop

    GTSAM 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.
  6. Quickstart: Build and install GTSAM from source

    develop

    To build and install GTSAM from the root library folder using an out-of-source build, execute the following commands in your terminal:

    1. Create and enter a build directory.
    2. Run CMake to configure the project.
    3. (Optional) Build and run unit tests.
    4. 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 install
  7. Build and run the RangeFactor Plaza2 benchmark

    develop

    The RangeFactor Plaza2 benchmark isolates the Plaza2 incremental SLAM workload for RangeFactor<Pose2, Point2>. This is used to compare performance when changing the RangeFactor implementation.

    Build

    From the build directory:

    make -j6 timeRangeFactorPlaza2

    Run the executable

    Run from the build/ directory. Use --warmup to define untimed runs and --repeats for measured repetitions.

    ./timing/timeRangeFactorPlaza2 --warmup 1 --repeats 5 \
      --output ../timing/results/range_factor_plaza2.csv

    Run the helper script

    Run from the repository root using the py312 conda 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.csv
  8. Best practices for sub-interpreter safety

    develop

    When 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 the py::subinterpreter_scoped_activate guard. 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_activate outlive its parent subinterpreter).
    • Thread Safety: While sub-interpreters have independent GILs, your underlying C++ code must still be thread-safe if multiple interpreters call into it concurrently.
  9. Set up a Python Virtual Environment for GTSAM

    develop

    On systems enforcing PEP 668 (like modern Ubuntu, macOS Homebrew, or Fedora), you should use a virtual environment to install requirements and build the wrapper.

    1. Create and activate the environment:
      python3 -m venv .venv
      source .venv/bin/activate
    2. Install development requirements:
      pip install -r <gtsam_folder>/python/dev_requirements.txt
    3. When running CMake, ensure PYTHON_EXECUTABLE points 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)
  10. Expose C++ types to NumPy via the Buffer Protocol

    develop

    To 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 using pybind11.

    1. Add the py::buffer_protocol() tag to the py::class_ constructor.
    2. Use .def_buffer() with a lambda that returns a py::buffer_info object 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) }
            );
        });
  11. Build Python extension modules with meson-python

    develop

    You can use Meson and meson-python to build your package. Your meson.build file must define the extension module and its dependencies (including pybind11), and your pyproject.toml must specify meson-python as the build backend. Note that meson-python requires 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"