Specific force, gravity, and acceleration
Core idea of strap-down IMU (where the IMU sensor moves and rotates with the body for which the estimate is sought) based position update is to double-integrate the acceleration sensed. Paul Groves explains the necessary equations in his GNSS 2nd ed. Chapter 5, but I had a tough time understanding how to take out the gravity sensed by the accelerometer. The technical term he uses for what the accelerometer is sensing is specific force, first explained in Section 2.4.7, as the difference between the true acceleration (what is sought) and the gravitational force the body is subject (which CANNOT be sensed by IMU):
f = a - 𝞬
where f is the specific acceleration vector, a is the acceleration vector, and 𝞬 (gamma) is the gravitational acceleration vector.I could not understand this explanation because of few observations I made with Unity's Input.acceleration while waving my phone about:
- When stationary, the sensed acceleration is downward. For example, [0; 0; -1] when the phone is lying screen-up on my desk. Does it mean that the 𝞬 is UP ([0; 0; 1])?
- He gave an example of the specific force felt by a passenger in an elevator accelerating up is greater than 1. But since > 0 in this example, f should be smaller than the stationary case!
Whether open-loop or more likely closed-loop (with force compensation electronics) design, MEMS accelerometers are inertial devices (hence the name IMU): the proof mass doesn't (at first) move while the case moves; hence the accelerometer senses the displacement (or force--in the case of the closed loop design) in the opposite direction of the case's movement. The stationary case can be understood by imagining the bottom spring being compressed due to the gravity pulling down the proof mass: 1 unit of the bottom spring being compressed is what is being reported as the "-1" acceleration by Input.acceleration! If I put the above case in an upward accelerating accelerometer, the bottom spring will compress even more, and I will get an even more negative value from the accelerometer.
Based on this observation, I concluded that the accelerometer is sensing reaction to the force on the body, rather than the force directly. I confirmed my hypothesis by printing the Input.acceleration values while accelerating my phone in the x/y/z directions, and finally the fundamental force equation made sense:
allb = G [ qlb × −accelbl𝞵 + 𝞬llb ]
where qlb is the average attitude of the local frame relative to the body rather than the other around and accelbl𝞵 is the average inertial acceleration (which is equal and opposite of the specific force the body is subjected to) during the most recent sample period: Once I rotate the specific force from the body frame to the local frame, I can then add the gravitational acceleration estimate to it, to get the estimated acceleration of the body in the local frame--scaled by the Earth's gravity constant.Above equation for the body's acceleration is incomplete: even though the local tangent frame is stationary relative to the ECEF (Earth centered Earth fixed) frame, since ECEF rotates relative to the ECI (Earth centered inertial) frame, the body experiences:
- Centrifugal acceleration (greatest at the Equator, in the +Z direction, but zero at the Poles). This is about 0.3% of the gravitational acceleration, of 3 mg. Anywhere else except at the Equator and the Poles, the centrifugal force has horizontal components in the local frame. But breaking down that horizontal vector into x and y requires an estimate of the local frame's True North heading as well as the current latitude.
- Coriolis acceleration proportional to the velocity of the body in the direction toward Earth's axis of rotation. This also requires the heading and latitude.
- 3 mg is on the order of accelerometer bias confidence interval of off-the-shelf MEMS accelerometers--which means that when you reboot your phone, the bias might change that much anyway, so that the factory calibration can leave this much residual.
- I expect the player to move at a speed in the order of 1 m/s. When I multiply this by the Earth's rotation speed 72.7 urad/s, the maximum Coriolis acceleration is on the order of 0.1E-3 m/s^2, or roughly 0.01 mg--which is BURIED under the noise floor.
Navigation update equations
Average attitude
In my last blog post, I showed discrete attitude update in Unity using the Input.gyro sensor input--using the quantity attitude increment αbib. If I assume that the angular rate is relatively constant during the sample period, I can do exactly the same calculation for the average attitude during the last sample period by forming a relative rotation quaternion using HALF of the attitude increment. I considered briefly whether to just use Unity's Quaternion.Slerp() function to spherically interpolate between qbl(-) and qbl(+), but decided I could better take advantage of the fact that for splitting the attitude increment into half, rather than iterating (as SLERP seemed to).
Velocity update
Once allb, the average acceleration during the last sample period is obtained, the velocity estimate is just a rectangular integral of allb with the sample period Ts: vbl(+) ~ vbl(-) + allb Ts
Average velocity
To integrate the velocity by sample period for position change, the average velocity is estimated as a simple average of the previous and the updated values: vbl(avg) = ( vbl(+) + vbl(-) ) / 2
Position update
Finally, the position is integrated from the initial position (I assumed 0 for now): rbl(+) ~ rbl(-) + vbl(avg) TsUnity implementation
My Unity implementation (my github repo) is mostly straight-forward--except for the fact I do NOT invert qbl (which is the current estimate of the body relative to the local frame) to obtain qlb. I did at first, and found that rotating the specific acceleration by qlb using Unity's Quaternion.operator*(Quaternion, Vector3) method rotated the vector to the opposite direction of rotation. In other words, Quaternion.operator*(qbl , -accelbl𝞵) yielded correct result!Even before I tried this implementation, I knew that double-integrating the cheap MEMS accelerometer output will end up with my player "going to the moon"--as I've heard the experts describe the problem--pretty quickly. But knowing it in your head and actually seeing it is quite another. See for yourself how bad it is.