ADR 0023: A GoPro is steered by its gyro, levelled by its own gravity
Status: accepted (2026-10-20). Supersedes ADR 0021's attitude source and clock; its cube alignment and up source stand.
Context
ADR 0021 steered a GoPro by its own fused attitude, CORI, one quaternion per frame, 30 ms after each frame. Stabilized MAX2 footage then looked a little shaky beside the ONE R's: fine jitter, some wobble, even on still moments in the plane. ADR 0021's measures (stillness over a third of a second, horizon levelness) could not see frame-to-frame jitter.
Measurements (MAX2, developer harnesses)
Jitter (
stitch/residualMotion.ts,stitch/goproJitter.test.ts): sixteen consecutive frames at six moments (two in the plane, two in freefall, two under canopy) rendered underlock. The measure is the root mean square change of patch shifts from one pair of frames to the next, in degrees; a steady pan counts as none.Steering Mean jitter none 1.67 CORI, 30 ms (ADR 0021)1.25 CORI, 0 to 10 ms1.14 to 1.16 gyro levelled by GRAV, 10 ms1.13 CORIwithIORIcomposed, either way1.38 to 1.49 The rest is what turning the whole picture cannot remove: near objects moving on their own, texture-poor sky.
The gyro against
CORI(stitch/goproRates.test.ts): the gyro runs at 800 Hz. Fitting the rates ofCORI's frame-to-frame turns to the gyro's gives a scale of 1.000 and a turn under a twentieth of a degree, with the gyro about 5 ms behind. SoCORIis the gyro sampled once a frame, and ADR 0021's 30 ms put it a frame late for anything faster than a turn.Rolling shutter: steering each direction by the attitude when its sensor row was read (readouts from -30 to 30 ms, either way down the sensor) changed the jitter by no more than its noise, so the cube map is not corrected for it.
Judged by eye on MAX2 clips of the three candidates:
CORIat 5 ms and the gyro at 10 ms are both steadier than ADR 0021's, the gyro slightly so.
Decision
- The GoPro reader turns the gyro and accelerometer onto the orientation frame's axes by the measured half turn (x, y, z to -x, -z, -y), so
imuToCamerastays ADR 0021's, and setscaptureClockto 10 ms. CameraRecording.gravitycarries the camera's own up readings (GRAVnegated).attitudeOfalways integrates the gyro (ADR 0016) and levels it bygravitywhen a camera writes one, every reading counted alike, and by the accelerometer near one g otherwise.CameraRecordingcarries no reported attitude, and no renderer readsCORI.IORIstays unused.
Consequences
- MAX2 stabilization follows the gyro at 800 Hz, as the ONE R's does, with the horizon from the camera's gravity.
- The original MAX takes the MAX2's turn and clock until its footage is measured.
- The harnesses (
goproJitter,goproRates,goproPreview) are how a new body is measured.