desaturate.hpp¶
desaturateUniform — the downstream wheel-speed safety scale.
This header declares 1 free function.
Extracted from include/shulib/kinematics/desaturate.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¶
desaturateUniform— free function
desaturateUniform¶
[[nodiscard]] inline WheelSpeeds desaturateUniform(const WheelSpeeds& wheels, units::Velocity maxWheelSpeed)
Scale EVERY wheel by ONE common factor so the largest just reaches maxWheelSpeed. Uniform scaling preserves the ratios between wheels — and therefore the DIRECTION of the commanded motion — trading only speed for feasibility. A set already inside the budget (the all-zero set included) is returned unchanged: this never scales UP, so it cannot be used to reach a speed floor. The result has the same size() as the input, and both the input and the limit are canonical velocity. Precondition: maxWheelSpeed > 0 — a zero or negative limit is a caller bug, not a request to hold still.
free function, declared at include/shulib/kinematics/desaturate.hpp:38.
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 — 22 lines
desaturateUniform — the downstream wheel-speed safety scale (master plan §5
data-flow, §13 #5).
If any wheel exceeds `maxWheelSpeed` in magnitude, scale EVERY wheel by the
same factor so the largest just reaches the limit. Scaling uniformly preserves
the ratios between wheels — and therefore the *direction* of the commanded
motion — trading only speed for feasibility. If the command already fits within
budget it is returned unchanged (never scaled UP).
This is deliberately separate from strafeAuthority() clamping (§13 #5): that
shapes the command upstream; this bounds the MAGNITUDE of a finite wheel set.
It is NOT the last line, and used to claim it was. Non-finite input passes
straight through: WheelSpeeds::maxMagnitude() uses std::max, which ignores NaN,
so a NaN never wins the peak and the early return treats the set as within
budget — and in the scaling branch NaN * scale is NaN either way. The actual
last line is diag::recoverWheelVoltage at the motor edge (plausibility_guard.hpp
invariant 3), which zeroes a non-finite volt and raises Implausible. Enforcing
finiteness HERE was considered and rejected twice over: it would narrow the
contract of LOCKED register row F5, and it would turn an A3 hostile-sensor
pathology into a thrown precondition — an aborted motion where the design says
log and recover. toWheels() itself stays clamp-free.
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.