Skip to content

imu.hpp

ProsImu — IImu over pros::Imu (chunk R1a): the load-bearing < 1° heading source behind the HAL.

This header declares 2 types (10 members).

Extracted from include/shulib/hal/pros/imu.hpp — this page is that header's documentation, reformatted, so it cannot disagree with the code. Prose about how to think about the API lives in the user guide; worked recipes live in the cookbook; this page is the complete, mechanical list of what exists.

Contents

enum class YawRateSource

enum class YawRateSource

Which hardware quantity yawRate() differentiates or reads (header: the T4 ruling and both costs).

enum class, declared at include/shulib/hal/pros/imu.hpp:78.

YawRateSource::DifferentiateRotation

DifferentiateRotation

d/dt of get_rotation() — documented sign (default)

enumerator, declared at include/shulib/hal/pros/imu.hpp:79.

YawRateSource::GyroRateZ

GyroRateZ

get_gyro_rate().z — real rate, UNDOCUMENTED sign (HA-04)

enumerator, declared at include/shulib/hal/pros/imu.hpp:80.

class ProsImu

class ProsImu final : public IImu

IImu over pros::Imu — the load-bearing heading source. Binds get_rotation() (cumulative CW degrees) and never get_heading(); applies bootHeading exactly ONCE, here at the edge; never tares, because a tare re-zeros the sensor underneath a live bootHeading offset. STATEFUL despite the const readers: each reader screens a non-finite value and returns the last good one, and the default yaw-rate path caches a sample to differentiate — which is why construction takes an IClock. One owner, the loop; not safe to read concurrently.

class, declared at include/shulib/hal/pros/imu.hpp:89.

ProsImu::ProsImu

ProsImu(std::uint8_t port, math::Angle bootHeading, const IClock& clock, YawRateSource yawSource = YawRateSource::DifferentiateRotation)

bootHeading: the robot's canonical field heading AT calibration — ONE owner, the robot's start pose (HA-05). clock: required by the differentiation path; must outlive this adapter.

function, declared at include/shulib/hal/pros/imu.hpp:94.

ProsImu::calibrate

void calibrate()

Start calibration (reset(false), HA-108). Call ONCE from initialize(); never blocks — the loop waits on isReady() (C1's wait-for-live). A second call is a precondition violation: re-calibrating mid-run re-zeros the sensor under a live bootHeading offset (HA-05's hazard).

function, declared at include/shulib/hal/pros/imu.hpp:106.

ProsImu::heading

[[nodiscard]] math::Angle heading() const override

Canonical heading via imuHeadingToCanonical (bootHeading applied ONCE, here). Screened reads hold last-good; before any good read this is bootHeading itself (a robot that has not moved).

function, declared at include/shulib/hal/pros/imu.hpp:117.

ProsImu::yawRate

[[nodiscard]] units::AngularVelocity yawRate() const override

Canonical yaw rate (CCW-positive rad/s) from the YawRateSource fixed at construction. DifferentiateRotation (the default) differentiates get_rotation() BETWEEN SUCCESSIVE CALLS, so the interval is however long since you last asked rather than a fixed dt: the first call after construction returns 0 (one sample is not a derivative), and a second call at the same clock instant repeats the previous answer instead of dividing by zero. Call it once per tick. GyroRateZ instead reads the hardware rate, converted on an UNMEASURED sign assumption (HA-04). Either way a screened read holds last-good.

function, declared at include/shulib/hal/pros/imu.hpp:134.

ProsImu::isReady

[[nodiscard]] bool isReady() const override

True once calibrate() was called and calibration has finished (header: never-calibrated must read not-ready). POSITIVE polarity (imu.hpp:31-33).

function, declared at include/shulib/hal/pros/imu.hpp:141.

ProsImu::pitch

[[nodiscard]] math::Angle pitch() const override

Chassis pitch from get_pitch() (degrees, (-180, 180)) as a canonical Angle, UNNEGATED: the as-mounted sign convention is unmeasured (HA-110), so consume the MAGNITUDE until the bench settles it. Nothing reads this yet — the tip detection it exists for is a master-plan M2 item — so the first consumer is also the first chance to bake in a wrong sign. A non-finite read holds the last good value and counts in faultedReads(); before any good read, 0.

function, declared at include/shulib/hal/pros/imu.hpp:151.

ProsImu::roll

[[nodiscard]] math::Angle roll() const override

Chassis roll from get_roll() (degrees, (-180, 180)) as a canonical Angle, on the same terms as pitch(): UNNEGATED because the mounting sign is unmeasured (HA-110), a non-finite read holds last-good and counts in faultedReads(), and 0 before any good read. Note that roll and pitch each hold their own last-good value, independently.

function, declared at include/shulib/hal/pros/imu.hpp:165.

ProsImu::faultedReads

[[nodiscard]] int faultedReads() const noexcept

How many reads were screened to last-good (T7 observability).

function, declared at include/shulib/hal/pros/imu.hpp:176.

Design commentary, from the header

The header opens with the reasoning behind these shapes. It is reproduced here in full because a reference that only lists signatures teaches nobody why.

The header’s own reasoning — 55 lines, click to expand
 ProsImu — IImu over pros::Imu (chunk R1a): the load-bearing < 1° heading
 source behind the HAL.

 BINDS: pros::Imu::get_rotation() — CUMULATIVE CW degrees, "theoretically
 unbounded" — and NEVER get_heading(), which is bounded [0,360) and loses the
 revolution continuity the cumulative contract assumes (imu_conversion.hpp
 binding contract, clause 1; HA-03). Conversion: imuHeadingToCanonical(),
 which applies bootHeading exactly ONCE, here at the edge (HA-05) — no
 downstream consumer re-applies it.

 NEVER CALLS tare/tare_rotation/set_rotation/tare_heading/set_heading —
 each re-zeros the sensor independently of bootHeading and silently
 invalidates the additive offset (HA-05; the < 1° budget has no room for
 it). The ONE sanctioned reset() call is calibrate(), below, which IS the
 calibration — not a post-calibration tare. The fence guard test greps this
 file to keep all of that true structurally.

 YAW RATE — BOTH SOURCES SHIP, behind YawRateSource (team-lead ruling, brief
 T4; default = DifferentiateRotation):
  * DifferentiateRotation: d/dt of get_rotation(), whose CW-positive sign is
    DOCUMENTED — so the CW→CCW negate in imuYawRateToCanonical() is provably
    right. Costs: the adapter becomes STATEFUL (previous sample + timestamp,
    hence the injected IClock and the `mutable` members below — yawRate() is
    const, and the differentiation cache is part of the READ, not commanded
    state), and differentiating a quantized angle AMPLIFIES quantization
    noise — a real cost against the heading-owned < 1° budget, measured at
    the bench before trusting.
  * GyroRateZ: get_gyro_rate().z — a real hardware rate, but its SIGN is
    UNDOCUMENTED (HA-04) and its deg/s unit is itself a belief (HA-109).
    Assumed CW-positive until the bench measures it (runbook step: spin CW
    at a steady rate; canonical yaw rate must be negative).
  Rejected: shipping only one path (either loses a real hardware rate or
  defaults the load-bearing quantity to a sign nobody has measured).

 CALIBRATION: calibrate() starts it (reset(false), HA-108) — call it once
 from initialize(); it never blocks. isReady() is true only after
 calibrate() was called AND is_calibrating() has gone false: an
 never-calibrated IMU emits garbage-that-moves (HA-23), so "I was never
 calibrated" must read as not-ready, not as ready-by-default. POSITIVE
 polarity per imu.hpp:31-33.

 SENTINELS (T7): PROS_ERR_F reads (calibrating / unplugged) hold the last
 good value; heading() before any good read returns bootHeading (the best
 available estimate of a robot that has not moved). faultedReads() exposes
 the screen count; the loop's HealthMonitor owns raising IMU_LOST (its
 boot-window rule already handles the calibration phase).

 PITCH/ROLL: get_pitch()/get_roll() degrees (-180,180) → math::Angle,
 UNNEGATED — the as-mounted sign convention is unmeasured (HA-110). No
 consumer exists yet (the tip detection these exist for is a master-plan M2
 item); whoever writes the first one consumes MAGNITUDES until the bench
 settles the signs.

 HA register: HA-02..HA-05, HA-23, HA-108..HA-110.

Last updated 2026-08-15 — published from the main release branch, commit fa545ff. This site tracks releases, so between them it can lag the repository — that lag is deliberate, and it is what keeps the development log off the public site. For the current state of the code, see the repository.