Skip to content

Rate limiter (causal command damping)

A robot's real motor cannot execute an unbounded instantaneous jump safely -- rate_limited_follower bounds how fast an applied command can physically change (velocity + acceleration), instead of trying to classify whether a deviation is a spurious spike or a genuine intended change (the classification approach a neighbor-consensus filter would need, and which a causal version of it failed at -- see the module's own docstring). Grounded in Berscheid & Kroger (2021)'s Ruckig (RSS 2021, arXiv:2105.04830); this is a simpler velocity+acceleration-limited special case, not a reimplementation of Ruckig's full time-optimal jerk synthesis. Promoted from Dense-Evolution-Discovery after validation on two independent real physical domains (SO-101, ALOHA) -- see the module's own docstring for the honest safety-vs-fidelity tradeoff found there.

rate_limiter

utility/rate_limiter.py

Causal velocity+acceleration-limited command follower -- bounds how fast an applied command can physically change, instead of trying to classify whether a deviation is a spurious spike or a genuine intended change.

WHY THIS EXISTS, NOT A SIMPLER DETECTOR: an earlier attempt at real-time command damping used a neighbor-consensus classifier (the same family as this package's own arbiter.py/healing-style filters) made causal (past- only window, same convention as streaming.py's own port of arbiter.py). That attempt hit a real, structural dead end, tested on real robot joint commands: causally, a real spike and a real regime change are indistinguishable at the instant they happen -- the classifier needs a future window to tell them apart, and a causal version of it lost to a trivial same-radius moving median by 5.5x RMSE (1.7% win rate, 120 real trials). This module sidesteps that classification problem entirely: it never asks whether a deviation is real, it just bounds the physically possible rate of change -- the same principle real robot controllers already use (torque/velocity/acceleration limits).

THEORY: grounded in Berscheid, L. & Kroger, T. (2021), "Jerk-limited Real-time Trajectory Generation with Arbitrary Target States", Robotics: Science and Systems XVII, arXiv:2105.04830 (fetched and verified directly before use, indexed in Dense-Evolution-Discovery's quantumrag under robotica_generazione_traiettoria) -- Ruckig, a time-optimal online trajectory generator respecting velocity/acceleration/jerk limits, causal by construction (uses only the current kinematic state, no lookahead), validated on 1e9 real trajectories, ~20us real compute per DoF. rate_limited_follower below is a deliberately SIMPLER special case, NOT a reimplementation of Ruckig's full time-optimal jerk synthesis -- a causal double-integrator velocity+acceleration limiter (bounds the first two derivatives; jerk itself is still a step function at each clamp transition).

VALIDATED, honestly, on TWO independent real physical domains (real LeRobot robot-arm command streams, not synthetic), promoted from Dense-Evolution-Discovery only after both checked out (see docs/rate_limiter_real_joint_commands.md there): - SO-101 (single 6-DoF arm, real 30Hz): 120 real trials (6 joints x 20 seeds, 5% spike density, 5x real local std magnitude). Real safety metric (max instantaneous jump in the applied output) wins 120/120 against a trivial same-radius moving median. Average tracking fidelity (RMSE vs the real clean signal) LOSES to the moving median (mean 0.79 vs 0.44, wins only 19/120 individual trials) -- a hard rate limiter cannot distinguish an injected spike from genuine fast real motion, so it lags on joints/seeds with more real fast movement. - ALOHA (bimanual, 14-DoF, real 50Hz -- a genuinely different real robot, not just a different episode of the same one): 280 real trials. Safety metric again wins 280/280. Fidelity is an honest, real divergence from SO-101, reported as found: per-trial win rate stays a minority (20.0%) but the MEAN RMSE here actually favors the rate limiter (0.0045 vs 0.0107) -- likely a heavy-tailed moving-median failure mode on some real trials, not investigated further since the safety property (this mechanism's actual purpose) is what needed cross-domain confirmation, and it replicated exactly.

HONEST TAKEAWAY: this is a safety bound, not a general-purpose signal cleaner. If the real goal is "never let a raw command reach the motor unbounded", this mechanism delivers that with a 100% real track record across two independent real robots. If the real goal is "recover the closest approximation to the true intended signal", it does not reliably beat a trivial moving median -- use a detector (arbiter.py, streaming.py, cusum.py) for that instead.

rate_limited_follower

rate_limited_follower(x_raw: ndarray, max_vel: float, max_accel: float, dt: float = 1.0) -> np.ndarray

Causally tracks a raw incoming command stream with an applied output whose velocity and acceleration are both bounded -- uses only the previous applied position/velocity and the CURRENT raw target, never future values (verified directly: truncating the array to end at the current index gives an identical result at that index).

Parameters

x_raw : np.ndarray Raw incoming command stream (e.g. an LLM's per-step target position for a robot joint). max_vel, max_accel : float Real physical limits, in the same units as x_raw per real dt. Not universal constants -- derive from the real target system's own real limits (e.g. a joint's real 99th-percentile observed velocity/acceleration on known-good data), the same convention used in both real-domain validations above. dt : float Real time step between samples.

Returns

np.ndarray The applied (rate-limited) command stream, same length as x_raw.

Source code in dense_armor/utility/rate_limiter.py
def rate_limited_follower(x_raw: np.ndarray, max_vel: float, max_accel: float, dt: float = 1.0) -> np.ndarray:
    """Causally tracks a raw incoming command stream with an applied
    output whose velocity and acceleration are both bounded -- uses only
    the previous applied position/velocity and the CURRENT raw target,
    never future values (verified directly: truncating the array to end
    at the current index gives an identical result at that index).

    Parameters
    ----------
    x_raw : np.ndarray
        Raw incoming command stream (e.g. an LLM's per-step target
        position for a robot joint).
    max_vel, max_accel : float
        Real physical limits, in the same units as x_raw per real dt.
        Not universal constants -- derive from the real target system's
        own real limits (e.g. a joint's real 99th-percentile observed
        velocity/acceleration on known-good data), the same convention
        used in both real-domain validations above.
    dt : float
        Real time step between samples.

    Returns
    -------
    np.ndarray
        The applied (rate-limited) command stream, same length as x_raw.
    """
    n = len(x_raw)
    out = np.empty(n)
    pos = float(x_raw[0])
    vel = 0.0
    out[0] = pos
    for i in range(1, n):
        target = float(x_raw[i])
        desired_vel = (target - pos) / dt
        max_dvel = max_accel * dt
        vel = float(np.clip(desired_vel, vel - max_dvel, vel + max_dvel))
        vel = float(np.clip(vel, -max_vel, max_vel))
        pos = pos + vel * dt
        out[i] = pos
    return out

See also: CUSUM + ARL theory -- if the real goal is recovering/classifying a signal rather than bounding a command's rate of change, a detector is the right tool, not this module.