Skip to content

Commit 4faa46d

Browse files
Merge branch 'borglab:develop' into develop
2 parents 6c7aec2 + ce5f93f commit 4faa46d

75 files changed

Lines changed: 4534 additions & 453 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env bash
2+
3+
# This script deletes all but the most recent release from the gtsam-develop project on PyPI
4+
# and can be used if the project size exceeds the PyPI limit of 10 GB. The user must have
5+
# owner or maintainer privileges on the project.
6+
7+
set -euo pipefail
8+
9+
usage() {
10+
cat <<EOF
11+
Usage: $(basename "$0") <pypi_username>
12+
13+
Deletes all but the most recent release from the gtsam-develop project on PyPI.
14+
You must supply the PyPI user name that has owner or maintainer privileges on
15+
the project. THIS OPERATION IS PERMANENT.
16+
17+
Examples
18+
$ $(basename "$0") yambati3
19+
$ $(basename "$0") # will prompt for user name
20+
EOF
21+
}
22+
23+
if [[ $# -ge 1 ]]; then
24+
PYPI_USER="$1"
25+
else
26+
read -rp "Enter your PyPI user name: " PYPI_USER
27+
[[ -z "$PYPI_USER" ]] && { echo "No user name supplied."; usage; exit 1; }
28+
fi
29+
30+
echo "-----------------------------------------------------------------------"
31+
echo "WARNING: This WILL permanently delete all but the most recent release"
32+
echo " of 'gtsam-develop' on PyPI for user '$PYPI_USER'."
33+
echo " This cannot be undone."
34+
echo "-----------------------------------------------------------------------"
35+
read -rp "Proceed? [y/N]: " REPLY
36+
REPLY=${REPLY,,} # to lowercase
37+
[[ "$REPLY" != "y" && "$REPLY" != "yes" ]] && { echo "Aborted."; exit 0; }
38+
39+
echo "Running pypi_cleanup for user '$PYPI_USER'..."
40+
python3 -m pypi_cleanup.__init__ \
41+
-p gtsam-develop \
42+
--leave-most-recent-only \
43+
--do-it \
44+
-u "$PYPI_USER"
45+
46+
echo "Done."

.github/workflows/build-cibw.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# This workflow builds the Python wheels using cibuildwheel and uploads them to TestPyPI.
22
# It can be triggered on push to the develop branch or manually via Github Actions.
33

4-
name: Build Wheels (cibuildwheel)
4+
name: Build Wheels for Develop
55

66
on:
77
push:

.github/workflows/build-special.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ jobs:
201201
if: runner.os == 'Linux'
202202
uses: pierotofy/set-swap-space@master
203203
with:
204-
swap-size-gb: 12
204+
swap-size-gb: 8
205205

206206
- name: Build & Test
207207
run: |

.github/workflows/prod-cibw.yml

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
# This workflow builds the Python wheels using cibuildwheel and uploads them to TestPyPI.
2+
# It can be triggered on push to the develop branch or manually via Github Actions.
3+
4+
name: Build Wheels for Release
5+
6+
on:
7+
release:
8+
types: [published]
9+
workflow_dispatch:
10+
11+
jobs:
12+
build_wheels:
13+
name: Build Wheels
14+
runs-on: ${{ matrix.os }}
15+
strategy:
16+
fail-fast: false
17+
matrix:
18+
include:
19+
# Linux x86_64
20+
- os: ubuntu-latest
21+
python_version: "3.10"
22+
cibw_python_version: 310
23+
platform_id: manylinux_x86_64
24+
manylinux_image: manylinux2014
25+
- os: ubuntu-latest
26+
python_version: "3.11"
27+
cibw_python_version: 311
28+
platform_id: manylinux_x86_64
29+
manylinux_image: manylinux2014
30+
- os: ubuntu-latest
31+
python_version: "3.12"
32+
cibw_python_version: 312
33+
platform_id: manylinux_x86_64
34+
manylinux_image: manylinux2014
35+
- os: ubuntu-latest
36+
python_version: "3.13"
37+
cibw_python_version: 313
38+
platform_id: manylinux_x86_64
39+
manylinux_image: manylinux2014
40+
41+
# Linux aarch64
42+
- os: ubuntu-24.04-arm
43+
python_version: "3.10"
44+
cibw_python_version: 310
45+
platform_id: manylinux_aarch64
46+
manylinux_image: manylinux2014
47+
- os: ubuntu-24.04-arm
48+
python_version: "3.11"
49+
cibw_python_version: 311
50+
platform_id: manylinux_aarch64
51+
manylinux_image: manylinux2014
52+
- os: ubuntu-24.04-arm
53+
python_version: "3.12"
54+
cibw_python_version: 312
55+
platform_id: manylinux_aarch64
56+
manylinux_image: manylinux2014
57+
- os: ubuntu-24.04-arm
58+
python_version: "3.13"
59+
cibw_python_version: 313
60+
platform_id: manylinux_aarch64
61+
manylinux_image: manylinux2014
62+
63+
# MacOS x86_64
64+
- os: macos-13
65+
python_version: "3.10"
66+
cibw_python_version: 310
67+
platform_id: macosx_x86_64
68+
- os: macos-13
69+
python_version: "3.11"
70+
cibw_python_version: 311
71+
platform_id: macosx_x86_64
72+
- os: macos-13
73+
python_version: "3.12"
74+
cibw_python_version: 312
75+
platform_id: macosx_x86_64
76+
- os: macos-13
77+
python_version: "3.13"
78+
cibw_python_version: 313
79+
platform_id: macosx_x86_64
80+
81+
# MacOS arm64
82+
- os: macos-14
83+
python_version: "3.10"
84+
cibw_python_version: 310
85+
platform_id: macosx_arm64
86+
- os: macos-14
87+
python_version: "3.11"
88+
cibw_python_version: 311
89+
platform_id: macosx_arm64
90+
- os: macos-14
91+
python_version: "3.12"
92+
cibw_python_version: 312
93+
platform_id: macosx_arm64
94+
- os: macos-14
95+
python_version: "3.13"
96+
cibw_python_version: 313
97+
platform_id: macosx_arm64
98+
99+
steps:
100+
- name: Checkout
101+
uses: actions/checkout@v4
102+
with:
103+
fetch-depth: 0
104+
ref: ${{ github.sha }}
105+
106+
- name: Set up Python ${{ matrix.python_version }}
107+
uses: actions/setup-python@v5
108+
with:
109+
python-version: ${{ matrix.python_version }}
110+
111+
- name: Install Dependencies
112+
run: |
113+
python3 -m pip install -r python/dev_requirements.txt
114+
if [ "$RUNNER_OS" == "Linux" ]; then
115+
sudo apt-get install -y wget libicu-dev python3-pip python3-setuptools libboost-all-dev ninja-build
116+
elif [ "$RUNNER_OS" == "macOS" ]; then
117+
brew install boost ninja python-setuptools
118+
else
119+
echo "$RUNNER_OS not supported"
120+
exit 1
121+
fi
122+
123+
# We first build the Python wrapper module on the host machine. This is done because cibuildwheel
124+
# expects a setup.py file to be present in the project directory.
125+
#
126+
# The Python wrapper module is then rebuilt within the cibuildwheel container before building
127+
# the wheels to ensure platform compatibility.
128+
- name: Run CMake
129+
run: |
130+
cmake . -B build -DGTSAM_BUILD_PYTHON=1 -DGTSAM_PYTHON_VERSION=${{ matrix.python_version }}
131+
132+
# If on macOS, we previously installed boost using homebrew for the first build.
133+
# We need to uninstall it before building the wheels with cibuildwheel, which will
134+
# install boost from source.
135+
- name: Uninstall Boost (MacOS)
136+
if: runner.os == 'macOS'
137+
run: |
138+
brew uninstall boost
139+
140+
- name: Build and test wheels
141+
env:
142+
# Generate the platform identifier. See https://cibuildwheel.pypa.io/en/stable/options/#build-skip.
143+
CIBW_BUILD: cp${{ matrix.cibw_python_version }}-${{ matrix.platform_id }}
144+
CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux_image }}
145+
CIBW_MANYLINUX_AARCH64_IMAGE: ${{ matrix.manylinux_image }}
146+
CIBW_ARCHS: all
147+
148+
# Set the minimum required MacOS version for the wheels.
149+
MACOSX_DEPLOYMENT_TARGET: 10.15
150+
151+
# Set DYLD_LIBRARY_PATH to REPAIR_LIBRARY_PATH, which is set in cibw_before_all.sh. REPAIR_LIBRARY_PATH
152+
# simply appends BOOST_LIBRARYDIR to the path, which is required during during link-time repair.
153+
CIBW_REPAIR_WHEEL_COMMAND_MACOS: DYLD_LIBRARY_PATH=$REPAIR_LIBRARY_PATH delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}
154+
155+
# Use build instead of pip wheel to build the wheels. This is recommended by PyPA.
156+
# See https://cibuildwheel.pypa.io/en/stable/options/#build-frontend.
157+
CIBW_BUILD_FRONTEND: "build"
158+
CIBW_BEFORE_ALL: bash .github/scripts/python_wheels/cibw_before_all.sh ${{ matrix.python_version }} {project}
159+
160+
CIBW_BUILD_VERBOSITY: 1
161+
162+
run: bash .github/scripts/python_wheels/build_wheels.sh
163+
164+
- name: Store artifacts
165+
uses: actions/upload-artifact@v4
166+
with:
167+
name: cibw-wheels-cp${{ matrix.cibw_python_version }}-${{ matrix.platform_id }}
168+
path: wheelhouse/*.whl
169+
170+
upload_all:
171+
name: Upload All
172+
needs: build_wheels
173+
runs-on: ubuntu-latest
174+
permissions:
175+
id-token: write
176+
steps:
177+
- name: Download Artifacts
178+
uses: actions/download-artifact@v4
179+
with:
180+
path: dist/
181+
merge-multiple: true
182+
183+
- name: Publish to PyPI
184+
uses: pypa/gh-action-pypi-publish@release/v1
185+
with:
186+
verbose: true
187+
packages-dir: dist/
188+
repository-url: https://test.pypi.org/legacy/

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ Optional prerequisites - used automatically if findable by CMake:
5555

5656
GTSAM 4 introduces several new features, most notably Expressions and a Python toolbox. It also introduces traits, a C++ technique that allows optimizing with non-GTSAM types. That opens the door to retiring geometric types such as Point2 and Point3 to pure Eigen types, which we also do. A significant change which will not trigger a compile error is that zero-initializing of Point2 and Point3 is deprecated, so please be aware that this might render functions using their default constructor incorrect.
5757

58+
There is a flag `GTSAM_ALLOW_DEPRECATED_SINCE_V43` for newly deprecated methods since the 4.3 release, which is on by default, allowing anyone to just pull version 4.3 and compile.
59+
60+
5861
## Wrappers
5962

6063
We provide support for [MATLAB](matlab/README.md) and [Python](python/README.md) wrappers for GTSAM. Please refer to the linked documents for more details.

examples/GEKF_Rot3Example.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/* ----------------------------------------------------------------------------
2+
3+
* GTSAM Copyright 2010, Georgia Tech Research Corporation,
4+
* Atlanta, Georgia 30332-0415
5+
* All Rights Reserved
6+
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
7+
8+
* See LICENSE for the license information
9+
10+
* -------------------------------------------------------------------------- */
11+
12+
/**
13+
* @file GEKF_Rot3Example.cpp
14+
* @brief Left‐Invariant EKF on SO(3) with state‐dependent pitch/roll control
15+
* and a single magnetometer update.
16+
* @date April 25, 2025
17+
* @authors Scott Baker, Matt Kielo, Frank Dellaert
18+
*/
19+
20+
#include <gtsam/base/Matrix.h>
21+
#include <gtsam/base/OptionalJacobian.h>
22+
#include <gtsam/geometry/Rot3.h>
23+
#include <gtsam/navigation/LieGroupEKF.h>
24+
25+
#include <iostream>
26+
27+
using namespace std;
28+
using namespace gtsam;
29+
30+
// --- 1) Closed‐loop dynamics f(X): xi = –k·[φx,φy,0], H = ∂xi/∂φ·Dφ ---
31+
static constexpr double k = 0.5;
32+
Vector3 dynamicsSO3(const Rot3& X, OptionalJacobian<3, 3> H = {}) {
33+
// φ = Logmap(R), Dφ = ∂φ/∂δR
34+
Matrix3 D_phi;
35+
Vector3 phi = Rot3::Logmap(X, D_phi);
36+
// zero out yaw
37+
phi[2] = 0.0;
38+
D_phi.row(2).setZero();
39+
40+
if (H) *H = -k * D_phi; // ∂(–kφ)/∂δR
41+
return -k * phi; // xi ∈ 𝔰𝔬(3)
42+
}
43+
44+
// --- 2) Magnetometer model: z = R⁻¹ m, H = –[z]_× ---
45+
static const Vector3 m_world(0, 0, -1);
46+
Vector3 h_mag(const Rot3& X, OptionalJacobian<3, 3> H = {}) {
47+
Vector3 z = X.inverse().rotate(m_world);
48+
if (H) *H = -skewSymmetric(z);
49+
return z;
50+
}
51+
52+
int main() {
53+
// Initial estimate (identity) and covariance
54+
const Rot3 R0 = Rot3::RzRyRx(0.1, -0.2, 0.3);
55+
const Matrix3 P0 = Matrix3::Identity() * 0.1;
56+
LieGroupEKF<Rot3> ekf(R0, P0);
57+
58+
// Timestep, process noise, measurement noise
59+
double dt = 0.1;
60+
Matrix3 Q = Matrix3::Identity() * 0.01;
61+
Matrix3 Rm = Matrix3::Identity() * 0.05;
62+
63+
cout << "=== Init ===\nR:\n"
64+
<< ekf.state().matrix() << "\nP:\n"
65+
<< ekf.covariance() << "\n\n";
66+
67+
// Predict using state‐dependent f
68+
ekf.predict(dynamicsSO3, dt, Q);
69+
cout << "--- After predict ---\nR:\n" << ekf.state().matrix() << "\n\n";
70+
71+
// Magnetometer measurement = body‐frame reading of m_world
72+
Vector3 z = h_mag(R0);
73+
ekf.update(h_mag, z, Rm);
74+
cout << "--- After update ---\nR:\n" << ekf.state().matrix() << "\n";
75+
76+
return 0;
77+
}

0 commit comments

Comments
 (0)