This guide develops the left-invariant Galilean IMU preintegration used by GTSAM: a direct-product symmetry, compatible left actions, exact zero-order-held Galilean increments, adjoint covariance transport, right-applied bias correction, and exact constant-rate rotating-frame prediction.
The construction is motivated by Delama, Fornasier, Mahony, and Weiss, Equivariant IMU Preintegration with Biases: a Galilean Group Approach, but deliberately uses a different symmetry and perturbation convention. It closes on the physical six-dimensional IMU input and bias in accelerometer-then-gyroscope order, and it induces the same left-invariant, component-wise right-retracted endpoint error used by GTSAM.
For background, see the EquivariantFilter guide, the Gal3 guide, the Gal3ImuEKF guide, the NavState guide, and the standard ImuFactor guide. The companion NEES comparison evaluates all four GTSAM preintegration backends under identical high-dynamic IMU samples.
import numpy as np
import gtsam1. Conventions: left-invariant error means an update on the right¶
We fix the convention before introducing the dynamics. For a matrix Lie group with Lie algebra , GTSAM uses
| Operation | Definition | Consequence |
|---|---|---|
| Retraction | A tangent increment is applied on the right. | |
| Local coordinates | The displacement is expressed in the local frame of . | |
| Error about | . |
The group error is left-invariant: replacing both arguments by and leaves it unchanged. Thus, throughout this guide, left-invariant error and right perturbation describe the same convention.
Delama et al.'s motivating construction instead uses the navigation error , which is right-invariant, together with a left-applied first-order correction. Reversing only the error while retaining their right action would not produce an invariant EqF error; the direct-product left action introduced in Section 5 is what induces .
Do not conflate multiplication sides.
ActionType::Leftbelow describes how the symmetry moves the physical state and input. GTSAM’sEquivariantFilterprediction still composes the lifted increment on the right, while its measurement update composes the innovation on the left. The side of a symmetry action, the side of a group increment, and the invariance of an error are related choices, but they are not synonyms.
2. Physical state¶
The preintegration state consists of the accumulated Galilean motion and the physical six-axis IMU bias:
Here is the motion being preintegrated. The second component is exactly gtsam::imuBias::ConstantBias; its vector representation stores accelerometer bias before gyroscope bias. There are no additional bias coordinates. Bias perturbations and bias-Jacobian columns retain this ordering throughout.
3. The Galilean motion component¶
A Galilean element stores rotation, velocity, position, and elapsed time:
Matrix multiplication gives
The term is the defining Galilean coupling: during the second interval, the velocity accumulated by the first interval advances position. The inverse is
Using GTSAM’s tangent ordering, an algebra element is
For an integrated IMU increment, is an angle, a velocity increment, a position increment, and an elapsed time. Before multiplication by , the corresponding rate vector has units .
Exponential, Jacobians, and adjoint¶
Let be the left Jacobian and its second integral. Then
The term produces the familiar when rotation is zero. For the complete 10D algebra, define the right Jacobian by
This is the Jacobian required when both the state perturbation and the increment are applied on the right. It is related to the complete Galilean left Jacobian by
For , the adjoint in the ordering is
We write for the algebra adjoint and use the identities and .
4. Mean preintegration¶
The measured input is the physical six-axis IMU sample supplied to GTSAM’s integrateMeasurement(measuredAcc, measuredOmega, deltaT) interface:
It follows the same accelerometer-then-gyroscope ordering as the bias. Holding this sample constant over an interval of duration , its bias-corrected Galilean body rate is
The exact discrete dynamics for this held input are
The bias is constant inside preintegration because the three-way IMU factor uses a separate factor for bias evolution. No bias-process input is part of . For a measured sample and linearization bias , let
Starting with , the preintegrated mean is updated on the right:
The group law automatically performs all four component updates. In particular, old velocity advances position by , while the exponential contributes the rotation-aware acceleration integral. No separate Euler position update is needed.
Gal(3) is internal to preintegration; the optimized keyframe states remain . NavState stores and retracts it as , and we define
and project the corrected to the body-frame increment
The inertial endpoint prediction is exactly
This separation is important: gravity acts in the navigation frame on the left, autonomous coasting advances the existing position, and bias-corrected IMU motion acts in the body/local frame on the right.
5. The equivariant-filter construction¶
GTSAM’s EquivariantFilter<M, Symmetry> separates the physical state manifold from a symmetry group that moves points of . A complete model also supplies an action on the input space and a lift that reconstructs the dynamics on . For this factor the template roles are
| EqF role | Galilean preintegration object |
|---|---|
| Physical state | |
| Symmetry group | |
| State action | |
| Input action | |
| Lift |
Direct-product symmetry group¶
Choose the direct product
where the second factor is the additive physical bias space in GTSAM order. For and ,
This group has the same dimension as , but its role is different: contains physical states, whereas supplies transformations of those states.
Left action on the state¶
Define
It is a left action because
In GTSAM terminology the corresponding Symmetry derives from GroupAction and declares ActionType::Left; its call signature is operator()(group, state). Fix the reference state
The orbit map used by EquivariantFilter is especially simple:
Consequently the lifted group estimate and the physical estimate have identical coordinates,
Left action on the input¶
The compatible input action is
It is also a left action. The Galilean component acts trivially on the physical IMU input, while translates it in the same GTSAM order as the bias. Consequently
and direct substitution verifies system equivariance:
The EquivariantFilter maps an input to the reference orbit by applying its InputOrbit to the inverse group estimate. Here
Thus the input at the EqF origin is exactly the bias-corrected body/local input; no adjoint transport is required.
Lift and reconstruction¶
Use the right-trivialized lift expected by GTSAM’s prediction path,
The direct-product exponential gives
The lift is also where the two coordinate orderings meet. For the linearization below, record its constant derivative with respect to a physical GTSAM-ordered six-vector:
This single matrix places angular rate and acceleration into Gal3’s internal order. It is a derivative of the lift, not a second representation of the bias; the fixed zero position-rate and unit clock-rate coordinates have zero derivative.
The lifted state is propagated exactly as in EquivariantFilter::predictWithJacobian, by composing the increment on the right and then returning through the reference orbit:
For a left state action, this reconstruction equation is not , which would put the Galilean increment on the wrong side. The lift is invariant under the simultaneous state and input actions:
Relation to Delama et al. Their construction closes its model over the full Galilean algebra: its input and bias coordinates lie in , including virtual coordinates, and its symmetry is the semidirect product . It uses the right actions
That coherent pairing induces the invariant error . The adjoint in their input action does not preserve the six-dimensional physical IMU subspace by itself, which motivates completing the input and bias to the full algebra. The direct-product construction used here needs no adjoint transport: it is closed on the physical input and bias spaces, while the lift supplies the fixed Gal3 coordinates. The group and both actions change together; this is not merely a reversal of the error.
6. Equivariant error and left-invariant discrete dynamics¶
For a left action, the EqF error is obtained by acting on the true state with the inverse group estimate:
This error is invariant under the common left action: replacing by and by leaves unchanged. Because the reference orbit is the identity, its normal coordinates are simply
These are exactly the error coordinates in which EquivariantFilter::errorCovariance() is interpreted: a tangent vector at the fixed reference . The navigation component is also exactly GTSAM’s right-retracted local coordinate.
Exact discrete linearization¶
Adopt the six-dimensional measurement convention
The true bias is constant during this preintegration step. For one nominal increment define
The true corrected physical input is . By the definition of the complete Galilean right Jacobian, its increment satisfies
Write . After one mean update,
Transporting the old right perturbation through and retaining first-order terms gives
Therefore, with ,
where
The upper-left block is not generally the identity: transports a body/local error through the next increment. The upper-right block maps the physical GTSAM-ordered bias error directly into Gal3 coordinates through ; no virtual bias columns are introduced.
Using the generic filter template literally. For an input held constant during the sample, the continuous-time error matrix is
The current
EquivariantFilterautomatic path rejects left actions because alone omits the transport associated with a left action and a right-composed prediction. A literal filter implementation must callpredictWithJacobianwith the continuous-time ; it must not pass the already-discrete , which that method would discretize again. TheGalileanImuFactoritself does not instantiateEquivariantFilter; the template terminology specifies the geometry used by its preintegration.
Covariance propagation¶
Let be the covariance of . In GTSAM order, the continuous-time IMU noise density is
where is the covariance of the sampled rate noise. The first-order augmented propagation is
Only physical accelerometer and gyroscope noise enters this model. If a caller already supplies per-sample rather than continuous-time covariances, the conversion must not be applied a second time.
The three-way IMU factor does not itself constrain temporal bias evolution. It uses the navigation uncertainty conditioned on the bias linearization value, while a separate bias between-factor supplies the chosen random-walk model. Therefore initialize and propagate the conditional navigation covariance directly:
The deterministic bias sensitivity derived next accounts for changing the optimizer’s bias away from its linearization value; it is not part of this conditional covariance. A combined-bias factor could extend the input with a physical six-dimensional bias random walk and retain the full augmented covariance, without introducing virtual coordinates.
The augmented covariance above belongs to the direct-product, left-action EqF error used in GTSAM. It must not be identified directly with Delama et al.'s semidirect-product covariance, whose navigation and bias components use different frames and coordinates. For the conditioned navigation covariance consumed by the ordinary factor, the descriptions agree after the corresponding frame conversion.
7. Bias correction must also act on the right¶
Preintegration is performed once at a linearization bias in GTSAM order. When optimization proposes , recomputing every IMU sample would be wasteful. Define directly by
This correction is on the right, matching the retraction convention. For and , transport of the old correction through the new increment gives
The minus sign follows from differentiating . The same recursion is obtained by accumulating the upper-right block of : the direct-product EqF transition exposes the physical bias-to-navigation sensitivity directly. Apply it as
The six columns of are ordered , exactly like ConstantBias::vector(). There is no intermediate virtual-bias Jacobian and no final column permutation. For the standard Galilean PIM, bias evolution remains the responsibility of a separate factor.
8. From the Galilean uncertainty to a GTSAM factor¶
The factor consumes a nine-dimensional NavState residual rather than the ten-dimensional Gal3 local error. In their respective tangent orderings,
Define the required selection and permutation when constructing the factor:
The final column removes elapsed time, which is known exactly from the measurement timestamps. This projection applies to local errors, covariances, and Jacobians. The mean Galilean element is converted separately to NavState as .
Let be the conditional Galilean covariance propagated above. The factor’s NavState residual covariance is
Likewise, the first-order bias sensitivity of the local navigation error is
For the mean, first apply the Galilean bias correction and then construct
The known gravity and elapsed-time terms combine this delta with to form predictedState_j. The residual used by the existing GTSAM preintegration machinery is
In the C++ interface this is NavState::localCoordinates; the argument order above is intentional because GTSAM’s IMU residual asks how the measured state retracts to the prediction.
Conceptually this is a three-variable relation . The GalileanImuFactor alias uses ImuFactorT, so each NavState is exposed as separate pose and velocity keys and the concrete graph factor has five keys. In both views there is one bias variable for the entire interval. Bias evolution remains a separate factor, exactly as for the standard ImuFactor.
When bias evolution and state--bias correlation should be modeled inside the IMU factor, use PreintegratedCombinedMeasurementsG with GalileanCombinedImuFactor. The Combined PIM retains the same Galilean mean while propagating a public covariance over . The Gal(3) clock coordinate is deterministic and is projected out exactly. The resulting six-way factor adds the six bias-random-walk residuals to the nine navigation residuals.
9. Exact prediction in a rotating navigation frame¶
Let be the constant angular velocity of the navigation frame, expressed in navigation-frame coordinates, and let . In the display order, the continuous dynamics are
GTSAM stores the same state as NavState(R,p,v). Define the mutually inverse transported-velocity maps in that order by
For , form the exact kernels
and the rotating-frame world increment, now written directly in NavState order,
The complete endpoint prediction preserves the same left-linear backbone:
Writing and in GTSAM order makes the implementation explicit:
This is an exact constant-rate rotating-frame transition: Coriolis and centrifugal terms arise from the lift, group composition, and projection rather than an appended acceleration correction. The body-frame increment , its covariance, and its bias correction are unchanged; only endpoint prediction and its Jacobians depend on . In Python, enable this path with params.setOmegaCoriolis(omega_n). Omitting it (or setting exactly zero) selects the inertial prediction.
10. Intended use¶
A user-facing workflow mirrors the existing IMU factors:
Construct preintegration parameters and a
PreintegratedImuMeasurementsGat the current bias estimate. When the navigation frame rotates, callsetOmegaCorioliswith its constant angular velocity in navigation-frame coordinates.Integrate each accelerometer/gyroscope sample. The public API and all six-dimensional quantities use GTSAM order ; the lift places them in Gal3 order internally. Each call advances the Galilean mean on the right and propagates its conditional covariance.
Construct a
GalileanImuFactorbetween the two pose/velocity pairs and the interval’s bias key. During optimization, use the right-applied first-order bias correction rather than reintegrating immediately.Add a separate factor between consecutive bias keys when bias random-walk evolution is part of the model. Alternatively, construct
PreintegratedCombinedMeasurementsGand aGalileanCombinedImuFactorto model the second bias key, its random walk, and state--bias correlation inside one factor.
In schematic C++ form:
PreintegratedImuMeasurementsG pim(params, biasHat);
for (const ImuSample& sample : samples) {
pim.integrateMeasurement(sample.acceleration, sample.angularRate,
sample.deltaT);
}
graph.emplace_shared<GalileanImuFactor>(
X(i), V(i), X(j), V(j), B(i), pim);The Combined alternative has the corresponding form:
PreintegratedCombinedMeasurementsG combinedPim(combinedParams, biasHat);
// Integrate the same samples as above.
graph.emplace_shared<GalileanCombinedImuFactor>(
X(i), V(i), X(j), V(j), B(i), B(j), combinedPim);The API is deliberately familiar; the essential differences are internal geometric choices. The executable Python example below follows the same sequence. It predicts a consistent endpoint only to demonstrate factor construction; in a real graph, is an optimizer variable rather than the prediction itself.
params = gtsam.PreintegrationParams.MakeSharedD(9.81)
params.setAccelerometerCovariance(1e-4 * np.eye(3))
params.setGyroscopeCovariance(1e-6 * np.eye(3))
params.setIntegrationCovariance(1e-8 * np.eye(3))
bias_hat = gtsam.imuBias.ConstantBias(
np.array([0.01, -0.02, 0.03]),
np.array([-0.01, 0.02, 0.01]),
)
pim = gtsam.PreintegratedImuMeasurementsG(params, bias_hat)
samples = [
(np.array([0.2, -0.1, 9.7]), np.array([0.03, -0.02, 0.01]), 0.01),
(np.array([0.1, 0.2, 9.8]), np.array([-0.01, 0.04, 0.02]), 0.02),
]
for measured_acc, measured_omega, delta_t in samples:
pim.integrateMeasurement(measured_acc, measured_omega, delta_t)
print(f"Integrated {pim.deltaTij():.3f} s")
print("NavState residual covariance shape:", pim.preintMeasCov().shape)Integrated 0.030 s
NavState residual covariance shape: (9, 9)
state_i = gtsam.NavState(
gtsam.Rot3.RzRyRx(0.1, -0.2, 0.3),
np.array([1.0, -2.0, 0.5]),
np.array([0.4, -0.1, 0.2]),
)
state_j = pim.predict(state_i, bias_hat)
Xi, Vi = gtsam.symbol("x", 0), gtsam.symbol("v", 0)
Xj, Vj = gtsam.symbol("x", 1), gtsam.symbol("v", 1)
Bi = gtsam.symbol("b", 0)
factor = gtsam.GalileanImuFactor(Xi, Vi, Xj, Vj, Bi, pim)
graph = gtsam.NonlinearFactorGraph()
graph.add(factor)
error = factor.evaluateError(
state_i.pose(), state_i.velocity(),
state_j.pose(), state_j.velocity(), bias_hat,
)
print("Factor error at the predicted endpoint:", error)Factor error at the predicted endpoint: [-5.69983774e-21 -4.35407115e-19 0.00000000e+00 0.00000000e+00
0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
0.00000000e+00]
References¶
G. Delama, A. Fornasier, R. Mahony, and S. Weiss, Equivariant IMU Preintegration with Biases: a Galilean Group Approach, IEEE Robotics and Automation Letters, 2025.
EquivariantFilter: GTSAM’s state, symmetry, orbit, input action, lift, and equivariant-error template vocabulary.gtsam::Gal3: group operations, exponential and logarithmic maps, and adjoints.Gal3ImuEKF: Galilean state propagation in an invariant EKF.NavState: GTSAM’s navigation manifold and local-coordinate convention.ImuFactor: standard preintegration and factor-graph usage.