The KarcherMeanFactor.h header provides functionality related to computing and constraining the Karcher mean (or Fréchet mean) of a set of rotations or other manifold values. The Karcher mean of a set of rotations is the rotation that minimizes the sum of squared geodesic distances on the manifold:
Functions/Classes:
FindKarcherMean
: Computes the Karcher mean of astd::vector
of rotations (or other suitable manifold typeT
). It solves the minimization problem above using a small internal optimization.KarcherMeanFactor<T>
: A factor that enforces a constraint related to the Karcher mean. It does not constrain the mean to a specific value. Instead, it acts as a gauge fixing constraint by ensuring that the sum of tangent space updates applied to the variables involved sums to zero. This effectively removes the rotational degree of freedom corresponding to simultaneously rotating all variables.
import gtsam
import numpy as np
from gtsam import Rot3, FindKarcherMeanRot3, KarcherMeanFactorRot3, Values
from gtsam.symbol_shorthand import R
1. FindKarcherMean
¶
Computes the Karcher mean of a list of rotations.
# Create a list of Rot3 objects
rotations = gtsam.Rot3Vector()
rotations.append(Rot3.Yaw(0.1))
rotations.append(Rot3.Yaw(0.15))
rotations.append(Rot3.Yaw(0.05))
rotations.append(Rot3.Yaw(0.12))
# Compute the Karcher mean
karcher_mean = FindKarcherMeanRot3(rotations)
print("Input Rotations (Yaw angles):")
for r in rotations: print(f" {r.yaw():.3f}")
print(f"\nComputed Karcher Mean (Yaw angle): {karcher_mean.yaw():.3f}")
# Note: For yaw rotations, the Karcher mean yaw is close to the arithmetic mean (0.105)
Input Rotations (Yaw angles):
0.100
0.150
0.050
0.120
Computed Karcher Mean (Yaw angle): 0.105
2. KarcherMeanFactor<Rot3>
¶
Creates a factor that constrains the rotational average of a set of Rot3
variables.
It acts as a soft gauge constraint. When linearized, it yields a Jacobian factor where each block corresponding to a variable is , and the error vector is zero. The beta
parameter (optional, defaults to 1) controls the strength (precision) of the constraint.
keys = [R(0), R(1), R(2)]
beta = 100.0 # Strength of the constraint
k_factor = KarcherMeanFactorRot3(keys, 3, beta)
k_factor.print("KarcherMeanFactorRot3: ")
# Linearization example
values = Values()
values.insert(R(0), Rot3.Yaw(0.1))
values.insert(R(1), Rot3.Yaw(0.2))
values.insert(R(2), Rot3.Yaw(0.3))
linearized_factor = k_factor.linearize(values)
# Check the Jacobian blocks (should be sqrt(beta)*Identity)
sqrt_beta = np.sqrt(beta)
A = linearized_factor.getA()
assert A.shape == (3, 9), f"Unexpected shape for A: {A.shape}"
A0 = A[:, :3]
A1 = A[:, 3:6]
A2 = A[:, 6:9]
b = linearized_factor.getb()
print(f"sqrt(beta): {sqrt_beta}")
print(f"\nJacobian for R(0):\n{A0}")
print(f"Jacobian for R(1):\n{A1}")
print(f"Jacobian for R(2):\n{A2}")
print(f"Error vector b:\n{b}")
KarcherMeanFactorRot3: keys = { r0 r1 r2 }
sqrt(beta): 10.0
Jacobian for R(0):
[[10. 0. 0.]
[ 0. 10. 0.]
[ 0. 0. 10.]]
Jacobian for R(1):
[[10. 0. 0.]
[ 0. 10. 0.]
[ 0. 0. 10.]]
Jacobian for R(2):
[[10. 0. 0.]
[ 0. 10. 0.]
[ 0. 0. 10.]]
Error vector b:
[0. 0. 0.]