- name
- calibrate
- description
- BNO085 sensor calibration and install-time boat-frame alignment (mount_quat). TRIGGER when the user asks about calibration, mount orientation, "which way is forward", drift, roll/pitch showing wrong values, or installing the sensor on the boat. DO NOT trigger for general BNO085 datasheet questions unrelated to calibration.
# Calibration — sensor and boat-frame alignment
Two completely separate concerns live under "calibration":
1. **Sensor calibration** — BNO085's internal gyro/accel bias estimation
2. **Boat-frame alignment** — the `mount_quat` that rotates sensor axes into
bow-forward/port-left/up
Conflating them is the #1 source of bad data. Treat them independently.
## 1. Sensor calibration
The BNO085 self-calibrates gyro and accelerometer continuously. **Do not
enable magnetometer** — we use the Game Rotation Vector report, which is
mag-free. A steel hull, engine, or nearby stanchion destroys mag cal; any
yaw reference we derive will be wrong by tens of degrees and drift. Heading
relative to water/wind is read from the existing B&G instruments via helmlog;
the IMU's job is motion, not compass.
**Procedure (one-time, at the dock):**
1. Power the device on a flat, stable surface — *not* the boat. The workbench.
2. Leave it stationary for 30 seconds — gyro bias converges.
3. Tilt through all six faces slowly, ~2 s per face — accel bias converges.
4. **Do not rotate in a figure-8** (that's mag cal, which we explicitly avoid).
5. Check the status byte on the Game Rotation Vector report — the accuracy
flag should read `HIGH` (3) before proceeding.
6. Save calibration to the BNO085's FRS via `sh2_saveDcdNow()` — otherwise
it's re-learned from scratch on every power cycle.
Re-run sensor cal if: the device is dropped, stored below freezing, or
`lib/imu/` logs accuracy dropping to `LOW` persistently.
## 2. Boat-frame alignment (`mount_quat`)
The sensor is bolted into the boat at some arbitrary orientation. We want
all downstream data expressed in **boat frame**:
- **X** = forward (bow)
- **Y** = port
- **Z** = up
- Right-handed
`mount_quat` rotates **sensor → boat**. It is computed once at install and
stored in NVS. Every sample published passes through:
```
q_boat_world = mount_quat * q_sensor_world_from_bno * mount_quat^-1
```
(and similarly, linear accel vectors are rotated by `mount_quat`).
### Install procedure
1. **Bolt the device down** in its final location and orientation. It must
not move relative to the boat. Use threadlocker.
2. **Tie the boat to the dock in calm water.** No wake, no heel, no pitch.
3. Run `scripts/calibrate_mount.py --device imu4.local` from the Mac.
The script:
- Averages 10 s of Game Rotation Vector samples → "dock" quaternion
representing sensor orientation when boat is level
- Computes `mount_quat` = the rotation that takes "dock" to identity
in the boat frame (bow = X, port = Y, up = Z)
- **You still have to tell it which way is forward**: the script prompts
"push the bow button" — you physically tap the end of the sensor that's
pointing at the bow. The accelerometer impulse direction fixes the
X-axis; Z comes from gravity; Y is X × Z
- Writes `mount_quat` to NVS and prints it
4. **Verify**: pitch the bow up by hand — `pitch` should read positive. Heel
to port — `roll` should read positive (right-hand rule around X).
If signs are inverted, the "bow button" tap was registered wrong — re-run.
### Why not do this in software with an IMU-on-table cal?
Because the sensor might be mounted upside-down under a deck, behind
a bulkhead, with axes at 37° to bow. Any "assume X is forward" shortcut
lies. The physical tap is the only source of truth for "which way is
forward" that doesn't require GPS COG (which requires sailing) or a
theodolite.
## Sanity checks that live in code
`lib/packetizer/` should refuse to publish if:
- `mount_quat` is unset (zeros or all-NaN) — pre-install firmware
- Game Rotation Vector accuracy status is `UNRELIABLE` (0) for > 5 s
- Linear acceleration magnitude > 50 m/s² sustained (sensor fault or
catastrophic impact — alert, don't silently drop)
Log every rejection at `WARN` with counts.
## Known gotchas
- **BNO085 "tare" feature** — there's an on-chip tare that sets the current
orientation as identity. **Do not use it.** We store `mount_quat` in our
own NVS so it's versioned and auditable; relying on chip-internal state
loses that on a reflash
- **Z-axis gyro bias drifts with temperature** — Game Rotation Vector
compensates internally, but if you derive heading from raw gyro integration
(don't), it will walk several degrees per minute
- **The "dock quaternion" is meaningless if the dock is on a current-heeled
mooring** — redo calibration if roll/pitch show more than 1° at "rest"
Voir sur GitHub