Lesson 1 ended on a question. glass-core's integrate() subtracts the
biases but never touches gravity, so where does \(\mathbf{g}\) come back? This lesson answers it
by building Forster's eq. (31): the update that turns one IMU sample into a change of
orientation, velocity and position. If an interviewer says "integrate this IMU sample", this is
what you write first.
0 · Warm-up, from memory
Before anything new, one retrieval from last time. Don't open lesson 1.
From memory: write eq. (28), the accelerometer's measurement model.
- \(\mathbf{R}_{WB}^{\top}\) in front of the bracket
- \(\mathbf{a} - \mathbf{g}\) inside it, in \(W\)
- Bias and noise outside it, in \(B\)
Keep it in view: in section 3 you'll solve this equation for \(\mathbf{a}\).
1 · How the state moves
Forster writes the kinematics of orientation, velocity and position like this:Forster et al., On-Manifold Preintegration…, §V, eq. (29). The hat \((\cdot)^{\wedge}\) turns a 3-vector into its skew-symmetric matrix (§III-A).
Velocity and position live in the world frame and are plain calculus. The rotation equation is the interesting one: the angular rate is measured in the body frame, so it multiplies \(\mathbf{R}_{WB}\) on the right.
2 · Hold everything constant for one step
An IMU samples every \(\Delta t\). Assume \(\boldsymbol{\omega}\) and \(\mathbf{a}\) stay constant across one sample interval, and eq. (29) integrates in closed form:Forster §V, eq. (30).
The last two lines are the constant-acceleration formulas from school physics. The first uses \(\mathrm{Exp}(\boldsymbol{\phi}) = \exp(\boldsymbol{\phi}^{\wedge})\), which turns a rotation vector (axis times angle) into a rotation matrix.Forster eq. (6). For the Lie-theory side, see Solà, Deray & Atchuthan, A micro Lie theory for state estimation in robotics. Here \(\boldsymbol{\omega}\,\Delta t\) is the small rotation the body turned through in one step. Multiplying on the right applies it in the body frame, where the gyroscope measured it.
3 · Put the IMU in: gravity comes back
Eq. (30) needs the true \(\boldsymbol{\omega}\) and \(\mathbf{a}\), but the IMU only gives measurements. So invert eqs. (27) and (28). The gyroscope is one subtraction. The accelerometer takes three short steps:
The middle step multiplies both sides by \(\mathbf{R}\), using \(\mathbf{R}\mathbf{R}^{\top} = \mathbf{I}\). That answers lesson 1's question: gravity comes back here, outside \(\mathbf{R}\). \(\mathbf{R}\) carries the bias-corrected specific force out of the body frame into the world, and gravity was never in the body frame, so there's nothing to rotate.
Substitute both into eq. (30) and you get Forster's discrete-time IMU update:Forster
§V, eq. (31). The superscript \(d\) marks discrete-time noise: over one step,
\(\mathrm{Cov}(\boldsymbol{\eta}^{gd}) = \mathrm{Cov}(\boldsymbol{\eta}^{g})/\Delta t\). glass-core
builds exactly that, \(\sigma^2/\Delta t\), in preintegration.cpp,
lines 59–66.
The measurements, biases and noise are all taken at time \(t\): Forster writes the \((t)\) out, and it's dropped here to fit the page. Read it term by term: gravity appears as \(\mathbf{g}\,\Delta t\) and \(\tfrac{1}{2}\mathbf{g}\,\Delta t^{2}\), unrotated, because it's a constant world-frame acceleration. The IMU's contribution is rotated by \(\mathbf{R}(t)\).Holding \(\mathbf{R}(t)\) fixed across the step is not exact while the IMU rotates. Forster notes that a high-rate IMU keeps the error small, and that slower IMUs may call for higher-order integration (§V).
4 · The update-order trap
Look at what sits on the right-hand side of eq. (31): everything is at time \(t\), the start of the step. Velocity and position both use \(\mathbf{R}(t)\), not the rotation you've just computed, and position uses \(\mathbf{v}(t)\), not the new velocity. In code, that forces the order: position first, then velocity, rotation last.
glass-core does exactly this.preintegration.cpp,
lines 85–90. Its comment: "Update dR first and you have integrated position with the wrong
orientation." And on the last line: "compose ON the manifold, on the RIGHT (body-frame increment)". Here \(\mathbf{R}(t)\) becomes dR_, the orientation relative to
the start of the preintegration window, and \(\mathbf{g}\) is left out:
dp_ += dv_ * dt + 0.5 * dR_mat * a * dt * dt;
dv_ += dR_mat * a * dt;
dR_ = dR_ * dRk;
5 · Where glass-core puts gravity back
The deltas are gravity-free, so gravity has to return somewhere. It comes back once, over the
whole interval, in predictState():nav_residual.hpp,
lines 152–162.
xj.R = xi.R * pre.dR();
xj.v = xi.v + gravity * dt + xi.R * pre.dv();
xj.p = xi.p + xi.v * dt + 0.5 * gravity * dt * dt + xi.R * pre.dp();
It's the same shape as eq. (31): gravity unrotated in the world frame, and the IMU part
rotated by the orientation at the start, xi.R. The code's comment says why gravity
was kept out of the deltas: "so the deltas would not depend on xi". Why that matters is
lesson 3.
Say it at a whiteboard
"For one IMU step, orientation is right-multiplied by the Exp of the bias-corrected gyro times \(\Delta t\), because the rate is in the body frame. For velocity and position, I rotate the bias-corrected specific force into the world with the orientation at the start of the step, then add gravity back unrotated, because gravity lives in the world frame. Everything on the right-hand side is at the start of the step, so in code I update position, then velocity, and rotation last."
6 · Practice
From memory, no scrolling. The last question comes from lesson 1: mixing old and new material is what makes it stick.
Solve eq. (28) for the true world acceleration \(\mathbf{a}\), ignoring noise. Which is right?
Subtract the bias, multiply by \(\mathbf{R}\) to leave the body frame (since \(\mathbf{R}\mathbf{R}^{\top} = \mathbf{I}\)), then add \(\mathbf{g}\) to both sides. \(\mathbf{R}^{\top}\) would take you into the body frame, the wrong direction.
In the velocity line of eq. (31), why isn't \(\mathbf{g}\) multiplied by \(\mathbf{R}\)?
\(\mathbf{R}\)'s job is to carry the body-frame specific force into the world. Gravity is a world-frame vector and never lived in the body frame, so there's nothing to rotate.
In eq. (31), and in glass-core's integrate(), which orientation
multiplies the specific force in the velocity and position updates?
Everything on the right-hand side of (31) is at time \(t\). That's why the code updates
position, then velocity, then rotation last. Averaging start and end (a midpoint scheme) is a
legitimate higher-order method, but it isn't what (31) or integrate() do.
Why does the rotation update right-multiply, \(\mathbf{R}(t)\,\mathrm{Exp}(\boldsymbol{\omega}\Delta t)\), rather than \(\mathrm{Exp}(\boldsymbol{\omega}\Delta t)\,\mathbf{R}(t)\)?
In eq. (29) the rate multiplies \(\mathbf{R}\) on the right because \(\boldsymbol{\omega}\) is measured in \(B\). Left-multiplying would apply the step in the world frame instead: in general a different rotation, since \(\mathrm{Exp}(\boldsymbol{\phi})\mathbf{R} = \mathbf{R}\,\mathrm{Exp}(\mathbf{R}^{\top}\boldsymbol{\phi})\) (Forster eq. 11).
From lesson 1. A level IMU accelerates straight up at 1 g. What does its \(z\) axis read?
\(\mathbf{a} = [0,0,+9.81]\) and \(\mathbf{g} = [0,0,-9.81]\), so \(\mathbf{a} - \mathbf{g} = [0,0,+19.62]\). The sensor feels 2 g: the floor pushing twice as hard as at rest.
From memory: write the three lines of eq. (31), bias-corrected, with the noise dropped. Then say the whiteboard sentence out loud.
- \(\mathrm{Exp}\) of the bias-corrected gyro times \(\Delta t\), on the right
- \(\mathbf{g}\,\Delta t\) and \(\tfrac{1}{2}\mathbf{g}\,\Delta t^{2}\), never rotated
- \(\mathbf{R}(t)\), the step's start, in front of \((\tilde{\mathbf{a}} - \mathbf{b}^{a})\) in both
- The old velocity \(\mathbf{v}(t)\,\Delta t\) in the position line
- \(\tfrac{1}{2}\) on both \(\Delta t^{2}\) terms
Missed something? Try this box again in a couple of days, before rereading anything.
Apply it: complete the practical worksheet exercise before moving on. Derive an answer and test it on the supplied inputs.