Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

ImuFactor vs. CombinedImuFactor

This notebook shows that the two ways of incorporating pre-integrated IMU data in GTSAM are mathematically equivalent as long as the same continuous-time noise is used:

  1. gtsam.ImuFactor + gtsam.BetweenFactorConstantBias – bias random walk injected afterwards with a BetweenFactor.

  2. gtsam.CombinedImuFactor – folds that BetweenFactor into the IMU factor itself.

We build the two graphs side-by-side and show they produce identical posteriors.

Open In Colab

Continuous-time Qc versus discrete-time Σ

IMU bias drift is modelled as Brownian motion with power-spectral density (PSD)

b˙(t)=wb,E[wbwbT]=Qc  (variance⋅s−1).\dot b(t)=w_b,\qquad\mathbb E[w_b w_b^T]=Q_c\;(\text{variance} \cdot \text{s}^{-1}).

Over a finite interval Δt\Delta t the discrete covariance is

Σ=Qc Δt.\Sigma = Q_c\,\Delta t.
  • CombinedImuFactor – pass continuous QcQ_c via setBiasAccCovariance() / setBiasOmegaCovariance(). The implementation multiplies it by dt inside every integrateMeasurement(dt), so the factor automatically accumulates Σ=Qc T\Sigma = Q_c\,T after T=∑dtT=\sum dt.

  • ImuFactor + Between-factor – do not add bias noise during pre-integration; instead attach a single BetweenFactorConstantBias spanning the same interval T and give it the discrete covariance Σ\Sigma (implemented by scaling the standard deviation by T\sqrt{T}).

Because both graphs end up with the same Σ, they contain the same information.

Practical tip – when is CombinedImuFactor worth it?

IMU biases drift slowly (seconds to minutes). If your key-frames are already that far apart, inserting one BetweenFactorConstantBias per key-frame is cheaper and accurate. Reserve CombinedImuFactor for cases where you need bias estimates at every IMU integration interval.

Step 1 – Common setup and imports

We simulate a stationary IMU: only gravity accelerates the sensor and the true angular velocity is zero.

Pre-integration parameters. PreintegrationCombinedParams additionally needs the bias PSDs (continuous-time QcQ_c); for the classic ImuFactor those are supplied later via the Between-factor.

Step 2 – Pre-integration and covariance comparison

Now we create the preintegrated measurement objects (PIMs) and integrate our simulated measurements. We can then directly compare their preintegrated measurement covariances.

The classic PIM’s 9 × 9 covariance contains only sensor noise. The combined PIM’s 15 × 15 covariance also contains the accumulated bias-random-walk noise: therefore its top-left 9 × 9 block is larger – exactly by Qc TQ_c\,T – and we explain later how the ImuFactor graph adds that same amount via the Between-factor.

IMU-only 9×9  vs Combined 15×15 (×1e9):
[[ 10.   0.   0.   0. -14.   0.   0. -44.   0.]    | [[ 13.   0.   0.   0. -16.   0.   0. -53.   0.   0.   0.   0.   5.   0.   0.]
 [  0.  10.   0.  14.   0.   0.  44.   0.   0.]    |  [  0.  13.   0.  16.   0.   0.  53.   0.   0.   0.   0.   0.   0.   5.   0.]
 [  0.   0.  10.   0.   0.   0.   0.   0.   0.]    |  [  0.   0.  13.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   5.]
 [  0.  14.   0.  50.   0.   0. 102.   0.   0.]    |  [  0.  16.   0.  52.   0.   0. 110.   0.   0.   1.   0.   0.   0.   3.   0.]
 [-14.   0.   0.   0.  50.   0.   0. 102.   0.]    |  [-16.   0.   0.   0.  52.   0.   0. 110.   0.   0.   1.   0.  -3.   0.   0.]
 [  0.   0.   0.   0.   0.  13.   0.   0.   5.]    |  [  0.   0.   0.   0.   0.  14.   0.   0.   6.   0.   0.   1.   0.   0.   0.]
 [  0.  44.   0. 102.   0.   0. 284.   0.   0.]    |  [  0.  53.   0. 110.   0.   0. 315.   0.   0.   5.   0.   0.   0.  12.   0.]
 [-44.   0.   0.   0. 102.   0.   0. 284.   0.]    |  [-53.   0.   0.   0. 110.   0.   0. 315.   0.   0.   5.   0. -12.   0.   0.]
 [  0.   0.   0.   0.   0.   5.   0.   0.  10.]]   |  [  0.   0.   0.   0.   0.   6.   0.   0.  13.   0.   0.   5.   0.   0.   0.]
                                                   |  [  0.   0.   0.   1.   0.   0.   5.   0.   0.  10.   0.   0.   0.   0.   0.]
                                                   |  [  0.   0.   0.   0.   1.   0.   0.   5.   0.   0.  10.   0.   0.   0.   0.]
                                                   |  [  0.   0.   0.   0.   0.   1.   0.   0.   5.   0.   0.  10.   0.   0.   0.]
                                                   |  [  5.   0.   0.   0.  -3.   0.   0. -12.   0.   0.   0.   0.  10.   0.   0.]
                                                   |  [  0.   5.   0.   3.   0.   0.  12.   0.   0.   0.   0.   0.   0.  10.   0.]
                                                   |  [  0.   0.   5.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.  10.]]

Step 3: Building Equivalent Factor Graphs

Next, we construct the two factor graphs for a trajectory with two IMU integration steps (three states at times k=0, 1, 2). We will add identical priors to both graphs.

Graph 1 (ImuFactor + BetweenFactor):
size: 7

Factor 0: PriorFactor on x0
  prior mean:  R: [
	1, 0, 0;
	0, 1, 0;
	0, 0, 1
]
t: 0 0 0
isotropic dim=6 sigma=0.01

Factor 1: PriorFactor on v0
  prior mean: [
	0;
	0;
	0
]
isotropic dim=3 sigma=0.01

Factor 2: PriorFactor on b0
  prior mean: acc = 0 0 0 gyro = 0 0 0
isotropic dim=6 sigma=0.01

Factor 3: 
ImuFactor(x0,v0,x1,v1,b0)
preintegrated measurements:

    deltaTij = 1
    deltaRij.ypr = ( 0 -0  0)
    deltaPij =     0     0 4.905
    deltaVij =    0    0 9.81
    gyrobias = 0 0 0
    acc_bias = 0 0 0

    preintMeasCov 
[       1e-08            0            0            0 -1.39793e-08            0            0  -4.4145e-08            0
           0        1e-08            0  1.39793e-08            0            0   4.4145e-08            0            0
           0            0        1e-08            0            0            0            0            0            0
           0  1.39793e-08            0  5.02147e-08            0            0  1.02439e-07            0            0
-1.39793e-08            0            0            0  5.02147e-08            0            0  1.02439e-07            0
           0            0            0            0            0   1.3325e-08            0            0        5e-09
           0   4.4145e-08            0  1.02439e-07            0            0  2.84273e-07            0            0
 -4.4145e-08            0            0            0  1.02439e-07            0            0  2.84273e-07            0
           0            0            0            0            0        5e-09            0            0        1e-08]
  noise model sigmas:      0.0001      0.0001      0.0001 0.000224086 0.000224086 0.000115434 0.000533172 0.000533172      0.0001

Factor 4: BetweenFactor(b0,b1)
  measured: acc = 0 0 0 gyro = 0 0 0
isotropic dim=6 sigma=0.0001

Factor 5: 
ImuFactor(x1,v1,x2,v2,b1)
preintegrated measurements:

    deltaTij = 1
    deltaRij.ypr = ( 0 -0  0)
    deltaPij =     0     0 4.905
    deltaVij =    0    0 9.81
    gyrobias = 0 0 0
    acc_bias = 0 0 0

    preintMeasCov 
[       1e-08            0            0            0 -1.39793e-08            0            0  -4.4145e-08            0
           0        1e-08            0  1.39793e-08            0            0   4.4145e-08            0            0
           0            0        1e-08            0            0            0            0            0            0
           0  1.39793e-08            0  5.02147e-08            0            0  1.02439e-07            0            0
-1.39793e-08            0            0            0  5.02147e-08            0            0  1.02439e-07            0
           0            0            0            0            0   1.3325e-08            0            0        5e-09
           0   4.4145e-08            0  1.02439e-07            0            0  2.84273e-07            0            0
 -4.4145e-08            0            0            0  1.02439e-07            0            0  2.84273e-07            0
           0            0            0            0            0        5e-09            0            0        1e-08]
  noise model sigmas:      0.0001      0.0001      0.0001 0.000224086 0.000224086 0.000115434 0.000533172 0.000533172      0.0001

Factor 6: BetweenFactor(b1,b2)
  measured: acc = 0 0 0 gyro = 0 0 0
isotropic dim=6 sigma=0.0001


Graph 2 (CombinedImuFactor):
size: 5

Factor 0: PriorFactor on x0
  prior mean:  R: [
	1, 0, 0;
	0, 1, 0;
	0, 0, 1
]
t: 0 0 0
isotropic dim=6 sigma=0.01

Factor 1: PriorFactor on v0
  prior mean: [
	0;
	0;
	0
]
isotropic dim=3 sigma=0.01

Factor 2: PriorFactor on b0
  prior mean: acc = 0 0 0 gyro = 0 0 0
isotropic dim=6 sigma=0.01

Factor 3: 
CombinedImuFactor(x0,v0,x1,v1,b0,b1)
  preintegrated measurements:
    deltaTij = 1
    deltaRij.ypr = ( 0 -0  0)
    deltaPij =     0     0 4.905
    deltaVij =    0    0 9.81
    gyrobias = 0 0 0
    acc_bias = 0 0 0

  preintMeasCov [    1.285e-08            0            0            0 -1.60129e-08            0            0 -5.26797e-08            0            0            0            0      4.5e-09            0            0
           0    1.285e-08            0  1.60129e-08            0            0  5.26797e-08            0            0            0            0            0            0      4.5e-09            0
           0            0    1.285e-08            0            0            0            0            0            0            0            0            0            0            0      4.5e-09
           0  1.60129e-08            0  5.23698e-08            0            0  1.10381e-07            0            0    1.425e-09            0            0            0   2.6487e-09            0
-1.60129e-08            0            0            0  5.23698e-08            0            0  1.10381e-07            0            0    1.425e-09            0  -2.6487e-09            0            0
           0            0            0            0            0  1.37083e-08            0            0   6.0125e-09            0            0    1.425e-09            0            0            0
           0  5.26797e-08            0  1.10381e-07            0            0  3.14954e-07            0            0      4.5e-09            0            0            0   1.1772e-08            0
-5.26797e-08            0            0            0  1.10381e-07            0            0  3.14954e-07            0            0      4.5e-09            0  -1.1772e-08            0            0
           0            0            0            0            0   6.0125e-09            0            0    1.285e-08            0            0      4.5e-09            0            0            0
           0            0            0    1.425e-09            0            0      4.5e-09            0            0        1e-08            0            0            0            0            0
           0            0            0            0    1.425e-09            0            0      4.5e-09            0            0        1e-08            0            0            0            0
           0            0            0            0            0    1.425e-09            0            0      4.5e-09            0            0        1e-08            0            0            0
     4.5e-09            0            0            0  -2.6487e-09            0            0  -1.1772e-08            0            0            0            0        1e-08            0            0
           0      4.5e-09            0   2.6487e-09            0            0   1.1772e-08            0            0            0            0            0            0        1e-08            0
           0            0      4.5e-09            0            0            0            0            0            0            0            0            0            0            0        1e-08 ]
  noise model: Gaussian [
	18259.6, 0, 0, 0, -2811.03, 0, -0, 3895.04, 0, 0, 1352.2, 0, 4376.12, 0, 0;
	0, 18259.6, 0, 2811.03, 0, 0, -3895.04, 0, 0, -1352.2, 0, 0, 0, 4376.12, 0;
	0, 0, 9611.39, 0, 0, 0, -0, 0, 0, 0, 0, 0, 0, 0, 4325.12;
	0, 0, 0, 8621.36, 0, 0, -3073.58, 0, 0, -154.568, 0, 0, 0, -1334.68, 0;
	0, 0, 0, 0, 8621.36, 0, -0, -3073.58, 0, 0, -154.568, 0, 1334.68, 0, 0;
	0, 0, 0, 0, 0, 9604.69, -0, 0, -4765.74, 0, 0, -775.917, 0, 0, 0;
	0, 0, 0, 0, 0, 0, 1828.57, 0, 0, 822.858, 0, 0, 0, 2152.6, 0;
	0, 0, 0, 0, 0, 0, 0, 1828.57, 0, 0, 822.858, 0, -2152.6, 0, 0;
	0, 0, 0, 0, 0, 0, 0, 0, 9611.39, 0, 0, 4325.12, 0, 0, 0;
	0, 0, 0, 0, 0, 0, 0, 0, 0, 10000, 0, 0, 0, 1.39698e-13, 0;
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10000, 0, -1.39698e-13, 0, 0;
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10000, 0, 0, 0;
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10000, 0, 0;
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10000, 0;
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10000
]

Factor 4: 
CombinedImuFactor(x1,v1,x2,v2,b1,b2)
  preintegrated measurements:
    deltaTij = 1
    deltaRij.ypr = ( 0 -0  0)
    deltaPij =     0     0 4.905
    deltaVij =    0    0 9.81
    gyrobias = 0 0 0
    acc_bias = 0 0 0

  preintMeasCov [    1.285e-08            0            0            0 -1.60129e-08            0            0 -5.26797e-08            0            0            0            0      4.5e-09            0            0
           0    1.285e-08            0  1.60129e-08            0            0  5.26797e-08            0            0            0            0            0            0      4.5e-09            0
           0            0    1.285e-08            0            0            0            0            0            0            0            0            0            0            0      4.5e-09
           0  1.60129e-08            0  5.23698e-08            0            0  1.10381e-07            0            0    1.425e-09            0            0            0   2.6487e-09            0
-1.60129e-08            0            0            0  5.23698e-08            0            0  1.10381e-07            0            0    1.425e-09            0  -2.6487e-09            0            0
           0            0            0            0            0  1.37083e-08            0            0   6.0125e-09            0            0    1.425e-09            0            0            0
           0  5.26797e-08            0  1.10381e-07            0            0  3.14954e-07            0            0      4.5e-09            0            0            0   1.1772e-08            0
-5.26797e-08            0            0            0  1.10381e-07            0            0  3.14954e-07            0            0      4.5e-09            0  -1.1772e-08            0            0
           0            0            0            0            0   6.0125e-09            0            0    1.285e-08            0            0      4.5e-09            0            0            0
           0            0            0    1.425e-09            0            0      4.5e-09            0            0        1e-08            0            0            0            0            0
           0            0            0            0    1.425e-09            0            0      4.5e-09            0            0        1e-08            0            0            0            0
           0            0            0            0            0    1.425e-09            0            0      4.5e-09            0            0        1e-08            0            0            0
     4.5e-09            0            0            0  -2.6487e-09            0            0  -1.1772e-08            0            0            0            0        1e-08            0            0
           0      4.5e-09            0   2.6487e-09            0            0   1.1772e-08            0            0            0            0            0            0        1e-08            0
           0            0      4.5e-09            0            0            0            0            0            0            0            0            0            0            0        1e-08 ]
  noise model: Gaussian [
	18259.6, 0, 0, 0, -2811.03, 0, -0, 3895.04, 0, 0, 1352.2, 0, 4376.12, 0, 0;
	0, 18259.6, 0, 2811.03, 0, 0, -3895.04, 0, 0, -1352.2, 0, 0, 0, 4376.12, 0;
	0, 0, 9611.39, 0, 0, 0, -0, 0, 0, 0, 0, 0, 0, 0, 4325.12;
	0, 0, 0, 8621.36, 0, 0, -3073.58, 0, 0, -154.568, 0, 0, 0, -1334.68, 0;
	0, 0, 0, 0, 8621.36, 0, -0, -3073.58, 0, 0, -154.568, 0, 1334.68, 0, 0;
	0, 0, 0, 0, 0, 9604.69, -0, 0, -4765.74, 0, 0, -775.917, 0, 0, 0;
	0, 0, 0, 0, 0, 0, 1828.57, 0, 0, 822.858, 0, 0, 0, 2152.6, 0;
	0, 0, 0, 0, 0, 0, 0, 1828.57, 0, 0, 822.858, 0, -2152.6, 0, 0;
	0, 0, 0, 0, 0, 0, 0, 0, 9611.39, 0, 0, 4325.12, 0, 0, 0;
	0, 0, 0, 0, 0, 0, 0, 0, 0, 10000, 0, 0, 0, 1.39698e-13, 0;
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10000, 0, -1.39698e-13, 0, 0;
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10000, 0, 0, 0;
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10000, 0, 0;
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10000, 0;
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10000
]

Step 4: Creating Initial Estimates

We need to provide an initial guess for the optimizer. Since the problem is simple (no motion), we can initialize all states and biases to zero. The initial estimates for both graphs will be identical.

Initial Estimate:
 Values with 9 values:
Value b0: (gtsam::imuBias::ConstantBias)
acc = 0 0 0 gyro = 0 0 0

Value b1: (gtsam::imuBias::ConstantBias)
acc = 0 0 0 gyro = 0 0 0

Value b2: (gtsam::imuBias::ConstantBias)
acc = 0 0 0 gyro = 0 0 0

Value v0: (Eigen::Matrix<double, -1, 1, 0, -1, 1>)
[
	0;
	0;
	0
]

Value v1: (Eigen::Matrix<double, -1, 1, 0, -1, 1>)
[
	0;
	0;
	0
]

Value v2: (Eigen::Matrix<double, -1, 1, 0, -1, 1>)
[
	0;
	0;
	0
]

Value x0: (gtsam::Pose3)
R: [
	1, 0, 0;
	0, 1, 0;
	0, 0, 1
]
t: 0 0 0

Value x1: (gtsam::Pose3)
R: [
	1, 0, 0;
	0, 1, 0;
	0, 0, 1
]
t: 0 0 0

Value x2: (gtsam::Pose3)
R: [
	1, 0, 0;
	0, 1, 0;
	0, 0, 1
]
t: 0 0 0


Step 5: Comparing Factor Jacobians

Before optimizing, we can inspect the factors themselves. A nonlinear factor’s linearize method produces a JacobianFactor, which is a linear approximation of the factor’s error function around a given state. However, it does mix in the PIM covariances, which we do know are different from Step 2. Hence, here we compare the linearization using numerical derivatives of evaluateError, which gives us the unwhitened error.

Below we show that the combination of Jacobians from ImuFactor and BetweenFactor for the interval k=0 to k=1 is identical to the single Jacobian from CombinedImuFactor, at least for the first 9 rows, which correspond to the NavState error.

For the first four arguments (really NavState 0 and NavState 1) the Jacobians are indeed identical:

Comparing Jacobians w.r.t. pose:
[[ 1.     0.     0.     0.     0.     0.   ]       | [[ 1.     0.     0.     0.     0.     0.   ]
 [ 0.     1.     0.     0.     0.     0.   ]       |  [ 0.     1.     0.     0.     0.     0.   ]
 [ 0.     0.     1.     0.     0.     0.   ]       |  [ 0.     0.     1.     0.     0.     0.   ]
 [ 0.     4.905  0.     1.     0.     0.   ]       |  [ 0.     4.905  0.     1.     0.     0.   ]
 [-4.905  0.     0.     0.     1.     0.   ]       |  [-4.905  0.     0.     0.     1.     0.   ]
 [ 0.     0.     0.     0.     0.     1.   ]       |  [ 0.     0.     0.     0.     0.     1.   ]
 [ 0.     9.81   0.     0.     0.     0.   ]       |  [ 0.     9.81   0.     0.     0.     0.   ]
 [-9.81   0.     0.     0.     0.     0.   ]       |  [-9.81   0.     0.     0.     0.     0.   ]
 [ 0.     0.     0.     0.     0.     0.   ]]      |  [ 0.     0.     0.     0.     0.     0.   ]
                                                   |  [ 0.     0.     0.     0.     0.     0.   ]
                                                   |  [ 0.     0.     0.     0.     0.     0.   ]
                                                   |  [ 0.     0.     0.     0.     0.     0.   ]
                                                   |  [ 0.     0.     0.     0.     0.     0.   ]
                                                   |  [ 0.     0.     0.     0.     0.     0.   ]
                                                   |  [ 0.     0.     0.     0.     0.     0.   ]]
Comparing Jacobians w.r.t. vel:
[[0. 0. 0.]                                        | [[0. 0. 0.]
 [0. 0. 0.]                                        |  [0. 0. 0.]
 [0. 0. 0.]                                        |  [0. 0. 0.]
 [1. 0. 0.]                                        |  [1. 0. 0.]
 [0. 1. 0.]                                        |  [0. 1. 0.]
 [0. 0. 1.]                                        |  [0. 0. 1.]
 [1. 0. 0.]                                        |  [1. 0. 0.]
 [0. 1. 0.]                                        |  [0. 1. 0.]
 [0. 0. 1.]]                                       |  [0. 0. 1.]
                                                   |  [0. 0. 0.]
                                                   |  [0. 0. 0.]
                                                   |  [0. 0. 0.]
                                                   |  [0. 0. 0.]
                                                   |  [0. 0. 0.]
                                                   |  [0. 0. 0.]]
Comparing Jacobians w.r.t. second pose:
[[-1.  0.  0.  0.  0.  0.]                         | [[-1.  0.  0.  0.  0.  0.]
 [ 0. -1.  0.  0.  0.  0.]                         |  [ 0. -1.  0.  0.  0.  0.]
 [ 0.  0. -1.  0.  0.  0.]                         |  [ 0.  0. -1.  0.  0.  0.]
 [ 0. -0.  0. -1.  0.  0.]                         |  [ 0. -0.  0. -1.  0.  0.]
 [ 0.  0.  0.  0. -1.  0.]                         |  [ 0.  0.  0.  0. -1.  0.]
 [ 0.  0.  0.  0.  0. -1.]                         |  [ 0.  0.  0.  0.  0. -1.]
 [ 0. -0.  0.  0.  0.  0.]                         |  [ 0. -0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]                         |  [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]]                        |  [ 0.  0.  0.  0.  0.  0.]
                                                   |  [ 0.  0.  0.  0.  0.  0.]
                                                   |  [ 0.  0.  0.  0.  0.  0.]
                                                   |  [ 0.  0.  0.  0.  0.  0.]
                                                   |  [ 0.  0.  0.  0.  0.  0.]
                                                   |  [ 0.  0.  0.  0.  0.  0.]
                                                   |  [ 0.  0.  0.  0.  0.  0.]]
Comparing Jacobians w.r.t. second velocity:
[[ 0.  0.  0.]                                     | [[ 0.  0.  0.]
 [ 0.  0.  0.]                                     |  [ 0.  0.  0.]
 [ 0.  0.  0.]                                     |  [ 0.  0.  0.]
 [ 0.  0.  0.]                                     |  [ 0.  0.  0.]
 [ 0.  0.  0.]                                     |  [ 0.  0.  0.]
 [ 0.  0.  0.]                                     |  [ 0.  0.  0.]
 [-1.  0.  0.]                                     |  [-1.  0.  0.]
 [ 0. -1.  0.]                                     |  [ 0. -1.  0.]
 [ 0.  0. -1.]]                                    |  [ 0.  0. -1.]
                                                   |  [ 0.  0.  0.]
                                                   |  [ 0.  0.  0.]
                                                   |  [ 0.  0.  0.]
                                                   |  [ 0.  0.  0.]
                                                   |  [ 0.  0.  0.]
                                                   |  [ 0.  0.  0.]]

For the 5th argument, the initial bias, we see that again the first 9 rows are identical, but obviously the 6 last rows correspond to the bias evolution, and its Jacobian is I6I_6 in the first bias.

Comparing Jacobians w.r.t. bias B(0):
[[ 0.     0.     0.    -1.     0.     0.   ]       | [[ 0.     0.     0.    -1.     0.     0.   ]
 [ 0.     0.     0.     0.    -1.     0.   ]       |  [ 0.     0.     0.     0.    -1.     0.   ]
 [ 0.     0.     0.     0.     0.    -1.   ]       |  [ 0.     0.     0.     0.     0.    -1.   ]
 [-0.5    0.     0.     0.    -1.398  0.   ]       |  [-0.5    0.     0.     0.    -1.398  0.   ]
 [ 0.    -0.5    0.     1.398  0.     0.   ]       |  [ 0.    -0.5    0.     1.398  0.     0.   ]
 [ 0.     0.    -0.5    0.     0.     0.   ]       |  [ 0.     0.    -0.5    0.     0.     0.   ]
 [-1.     0.     0.     0.    -4.414  0.   ]       |  [-1.     0.     0.     0.    -4.414  0.   ]
 [ 0.    -1.     0.     4.414  0.     0.   ]       |  [ 0.    -1.     0.     4.414  0.     0.   ]
 [ 0.     0.    -1.     0.     0.     0.   ]]      |  [ 0.     0.    -1.     0.     0.     0.   ]
                                                   |  [ 1.     0.     0.     0.     0.     0.   ]
                                                   |  [ 0.     1.     0.     0.     0.     0.   ]
                                                   |  [ 0.     0.     1.     0.     0.     0.   ]
                                                   |  [ 0.     0.     0.     1.     0.     0.   ]
                                                   |  [ 0.     0.     0.     0.     1.     0.   ]
                                                   |  [ 0.     0.     0.     0.     0.     1.   ]]

The derivative in B(1) is negative I6I_6:

Note: The IMU factor does not have a B(1) term, so we only show the combined factor:
[[ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [-1.  0.  0.  0.  0.  0.]
 [ 0. -1.  0.  0.  0.  0.]
 [ 0.  0. -1.  0.  0.  0.]
 [ 0.  0.  0. -1.  0.  0.]
 [ 0.  0.  0.  0. -1.  0.]
 [ 0.  0.  0.  0.  0. -1.]]

Step 6: Optimizing and Comparing Final Results

Now that we’ve established the underlying mathematical equivalence of the factors, let’s optimize both graphs and compare the final results. They should be identical.

--- Results from ImuFactor + BetweenFactor Graph ---
Values with 9 values:
Value b0: (gtsam::imuBias::ConstantBias)
acc =          0          0 1.6433e-23 gyro = 0 0 0

Value b1: (gtsam::imuBias::ConstantBias)
acc =           0           0 1.64335e-23 gyro = 0 0 0

Value b2: (gtsam::imuBias::ConstantBias)
acc =           0           0 1.64335e-23 gyro = 0 0 0

Value v0: (Eigen::Matrix<double, -1, 1, 0, -1, 1>)
[
	0;
	0;
	-1.33133e-23
]

Value v1: (Eigen::Matrix<double, -1, 1, 0, -1, 1>)
[
	0;
	0;
	1.77636e-15
]

Value v2: (Eigen::Matrix<double, -1, 1, 0, -1, 1>)
[
	0;
	0;
	3.55271e-15
]

Value x0: (gtsam::Pose3)
R: [
	1, 0, 0;
	0, 1, 0;
	0, 0, 1
]
t:            0            0 -4.43817e-24

Value x1: (gtsam::Pose3)
R: [
	1, 0, 0;
	0, 1, 0;
	0, 0, 1
]
t:           0           0 8.88178e-16

Value x2: (gtsam::Pose3)
R: [
	1, 0, 0;
	0, 1, 0;
	0, 0, 1
]
t:           0           0 3.55271e-15


--- Results from CombinedImuFactor Graph ---
Values with 9 values:
Value b0: (gtsam::imuBias::ConstantBias)
acc =           0           0 1.64055e-23 gyro = 0 0 0

Value b1: (gtsam::imuBias::ConstantBias)
acc =           0           0 1.64065e-23 gyro = 0 0 0

Value b2: (gtsam::imuBias::ConstantBias)
acc =           0           0 1.64067e-23 gyro = 0 0 0

Value v0: (Eigen::Matrix<double, -1, 1, 0, -1, 1>)
[
	0;
	0;
	-1.32995e-23
]

Value v1: (Eigen::Matrix<double, -1, 1, 0, -1, 1>)
[
	0;
	0;
	1.77636e-15
]

Value v2: (Eigen::Matrix<double, -1, 1, 0, -1, 1>)
[
	0;
	0;
	3.55271e-15
]

Value x0: (gtsam::Pose3)
R: [
	1, 0, 0;
	0, 1, 0;
	0, 0, 1
]
t:            0            0 -4.43611e-24

Value x1: (gtsam::Pose3)
R: [
	1, 0, 0;
	0, 1, 0;
	0, 0, 1
]
t:           0           0 8.88178e-16

Value x2: (gtsam::Pose3)
R: [
	1, 0, 0;
	0, 1, 0;
	0, 0, 1
]
t:           0           0 3.55271e-15


Success! The final results from both optimizers are equal.

Finally, we can check the posterior marginal covariances on the bias estimates. As expected, these are also identical.


Covariance for Bias B(0):
[[100.   0.   0.   0.   0.   0.]                   | [[100.   0.   0.   0.   0.   0.]
 [  0. 100.   0.  -0.   0.   0.]                   |  [  0. 100.   0.  -0.   0.   0.]
 [  0.   0. 100.   0.   0.   0.]                   |  [  0.   0. 100.   0.   0.   0.]
 [  0.  -0.   0. 100.   0.   0.]                   |  [  0.  -0.   0. 100.   0.   0.]
 [  0.   0.   0.   0. 100.   0.]                   |  [  0.   0.   0.   0. 100.   0.]
 [  0.   0.   0.   0.   0. 100.]]                  |  [  0.   0.   0.   0.   0. 100.]]

Covariance for Bias B(1):
[[100.01   0.     0.     0.    -0.     0.  ]       | [[100.01   0.     0.     0.     0.     0.  ]
 [  0.   100.01   0.     0.     0.     0.  ]       |  [  0.   100.01   0.    -0.     0.     0.  ]
 [  0.     0.   100.01   0.     0.     0.  ]       |  [  0.     0.   100.01   0.     0.     0.  ]
 [  0.     0.     0.   100.01   0.     0.  ]       |  [  0.    -0.     0.   100.01   0.     0.  ]
 [ -0.     0.     0.     0.   100.01   0.  ]       |  [  0.     0.     0.     0.   100.01   0.  ]
 [  0.     0.     0.     0.     0.   100.01]]      |  [  0.     0.     0.     0.     0.   100.01]]

Covariance for Bias B(2):
[[100.02   0.     0.     0.    -0.     0.  ]       | [[100.02   0.     0.     0.     0.     0.  ]
 [  0.   100.02   0.     0.     0.     0.  ]       |  [  0.   100.02   0.    -0.     0.     0.  ]
 [  0.     0.   100.02   0.     0.     0.  ]       |  [  0.     0.   100.02   0.     0.     0.  ]
 [  0.     0.     0.   100.02   0.     0.  ]       |  [  0.    -0.     0.   100.02   0.     0.  ]
 [ -0.     0.     0.     0.   100.02   0.  ]       |  [  0.     0.     0.     0.   100.02   0.  ]
 [  0.     0.     0.     0.     0.   100.02]]      |  [  0.     0.     0.     0.     0.   100.02]]

Covariance for Pose X(0):
[[100.   0.   0.   0.  -0.   0.]                   | [[100.   0.   0.   0.   0.   0.]
 [  0. 100.   0.   0.   0.   0.]                   |  [  0. 100.   0.   0.   0.   0.]
 [  0.   0. 100.   0.   0.   0.]                   |  [  0.   0. 100.   0.   0.   0.]
 [  0.   0.   0. 100.   0.   0.]                   |  [  0.   0.   0. 100.   0.   0.]
 [ -0.   0.   0.   0. 100.   0.]                   |  [  0.   0.   0.   0. 100.   0.]
 [  0.   0.   0.   0.   0. 100.]]                  |  [  0.   0.   0.   0.   0. 100.]]

Covariance for Velocity V(0):
[[100.   0.   0.]                                  | [[100.   0.   0.]
 [  0. 100.   0.]                                  |  [  0. 100.   0.]
 [  0.   0. 100.]]                                 |  [  0.   0. 100.]]

Covariance for Pose X(1):
[[ 200.01     0.       0.       0.    -630.306    0.   ] | [[ 200.013    0.       0.       0.    -630.309    0.   ]
 [   0.     200.01     0.     630.306    0.       0.   ] |  [   0.     200.013    0.     630.309    0.       0.   ]
 [   0.       0.     200.01     0.       0.       0.   ] |  [   0.       0.     200.013    0.       0.       0.   ]
 [   0.     630.306    0.    2826.372    0.       0.   ] |  [   0.     630.309    0.    2826.374    0.       0.   ]
 [-630.306    0.       0.       0.    2826.372    0.   ] |  [-630.309    0.       0.       0.    2826.374    0.   ]
 [   0.       0.       0.       0.       0.     225.013]] |  [   0.       0.       0.       0.       0.     225.014]]

Covariance for Velocity V(1):
[[11772.675     0.        0.   ]                   | [[11772.706     0.        0.   ]
 [    0.    11772.675     0.   ]                   |  [    0.    11772.706     0.   ]
 [    0.        0.      200.01 ]]                  |  [    0.        0.      200.013]]

Covariance for Pose X(2):
[[  500.03      0.        0.        0.    -4385.205     0.   ] | [[  500.045     0.        0.        0.    -4385.275     0.   ]
 [    0.      500.03      0.     4385.205     0.        0.   ] |  [    0.      500.045     0.     4385.274     0.        0.   ]
 [    0.        0.      500.03      0.        0.        0.   ] |  [    0.        0.      500.045     0.        0.        0.   ]
 [    0.     4385.205     0.    54074.033     0.        0.   ] |  [    0.     4385.274     0.    54074.364     0.        0.   ]
 [-4385.205     0.        0.        0.    54074.033     0.   ] |  [-4385.275     0.        0.        0.    54074.364     0.   ]
 [    0.        0.        0.        0.        0.      900.049]] |  [    0.        0.        0.        0.        0.      900.061]]

Covariance for Velocity V(2):
[[73738.274     0.        0.   ]                   | [[73739.28      0.        0.   ]
 [    0.    73738.274     0.   ]                   |  [    0.    73739.28      0.   ]
 [    0.        0.      500.03 ]]                  |  [    0.        0.      500.045]]

Conclusion

We have successfully demonstrated that gtsam.CombinedImuFactor is a mathematically equivalent but more convenient way to model IMU measurements compared to using a separate gtsam.ImuFactor and gtsam.BetweenFactorConstantBias. We showed this by verifying that:

  1. Their preintegrated measurement covariances are consistent.

  2. The linearization (Jacobians) of a CombinedImuFactor is identical to the combined Jacobians of an ImuFactor and a BetweenFactor.

  3. The final optimized results and posterior covariances from factor graphs built with both methods are identical.

The explanation is that,

  • with the same continuous-time PSD QcQ_c both formulations propagate the same bias noise: CombinedImuFactor does it inside the PIM, whereas the classic ImuFactor relies on a BetweenFactorConstantBias whose covariance is Σ=Qc T\Sigma = Q_c\,T;

  • consequently their Jacobians, optimized estimates and posterior covariances are identical (up to round-off), as the notebook demonstrates;

Final note: because biases evolve much more slowly than pose/velocity, using the simpler ImuFactor + Between-factor is usually sufficient and faster. Reach for CombinedImuFactor only when you truly need highly accurate bias estimates at every interval.