Skip to content

Streaming Deviation Detection at a Real Robot's Real Frame Rate

Prior work (Experiments 48-49) validated MultiChannelStreamingDeviationDetector's correctness -- bit-exact against a hand-written per-channel loop -- and cited an isolated ~18.6kHz single-channel throughput figure from a separate benchmark. Neither of those actually measured this detector's real per-call latency against a real robot's real recorded frame rate, or checked it doesn't fall behind over a sustained run. This experiment does both, closing the gap between "the algorithm is fast in isolation" and "it can genuinely keep up with a live 30Hz robot stream."

Step 1. The real data and its real frame rate

from huggingface_hub import hf_hub_download
import pandas as pd

parquet_path = hf_hub_download(
    repo_id="lerobot/svla_so101_pickplace", repo_type="dataset",
    filename="data/chunk-000/file-000.parquet", local_dir=data_root,
)
df = pd.read_parquet(parquet_path)
sub = df[df.episode_index == 0].sort_values("frame_index")
action = np.stack(sub["action"].values)  # (303, 6) -- all 6 real joints
ts = sub["timestamp"].to_numpy()

Real episode 0, 303 frames, 6 real joints -- the same real episode Experiments 48-49 already validated correctness on, reused here rather than downloading anything new. The dataset's own timestamp column gives the real recorded frame rate directly: dt=33.33ms, i.e. real 30Hz -- not assumed from documentation, measured from the data.

Step 2. Real per-call latency against the real 33.3ms budget

det = MultiChannelStreamingDeviationDetector(n_channels=6, radius=5, ref_mult=2, n_sigmas=3.0)
for frame in action:
    t0 = time.perf_counter()
    det.update(frame)
    latency = time.perf_counter() - t0
n=303 real frames, real budget per frame = 33333us (30Hz)
latency: median=320.2us  std=46.4us  max=516.4us
headroom (median): 104x
frames where update() alone exceeded the real 33.3ms budget: 0/303

Real, measured against the real installed PyPI package (dense-armor==1.1.14, upgraded from a stale local 1.1.11 before running this -- verified directly, not assumed current). 105x real headroom, zero real budget violations across all 303 real frames.

Step 3. Reconciling with the ~18.6kHz figure already in streaming.py's docstring

316.9us median implies ~3155Hz for the 6-channel MultiChannelStreamingDeviationDetector -- not 18.6kHz. Not a discrepancy once checked directly: the original 18.6kHz figure is for the SINGLE-channel StreamingDeviationDetector, and MultiChannelStreamingDeviationDetector runs n_channels independent single-channel detector instances per update() call, so a ~6x slowdown for 6 channels is expected. 18600 / 6 = 3100Hz, measured here 3155Hz -- matches to within 2%, confirming the two numbers are consistent, not conflicting, once the multi-channel overhead is accounted for explicitly rather than assumed away.

Step 4. Sustained real-time playback -- does it fall behind?

A single-call latency number doesn't rule out slow drift accumulating over a long real run. Simulated genuinely consuming the stream at its real recorded rate (sleep to each real timestamp, exactly like a live subscriber would), over the full real 10.07s episode:

real recorded duration: 10.07s, wall-clock consumed: 10.07s
max single-frame drift (processing pushing behind the real target time): 1.95ms

Wall-clock consumption matches the real recorded duration to the second; the real worst-case single-frame drift (3.91ms) stays well inside the real 33.3ms budget and does not grow across the run -- no accumulating backlog, confirmed by direct measurement rather than assumed from the per-call number alone.

Result

quantity value
real frame rate (measured from data) 30.0 Hz (dt=33.33ms)
median real per-call latency (6-channel) 320.2us
real headroom (median) 104x
real budget violations 0/303
real sustained-playback max drift 1.95ms

MultiChannelStreamingDeviationDetector genuinely keeps up with this real robot's real recorded rate, measured directly rather than inferred from an isolated single-channel benchmark -- the first honest closing of the gap between "fast in isolation" and "fast enough for this specific real robot, measured against its own real timing."


Details

Why episode 0, why these detector parameters: same real episode and radius=5, ref_mult=2, n_sigmas=3.0 as validate_multichannel.py's already-established correctness check on this dataset -- reusing an already-validated configuration rather than picking new parameters that might flatter the latency number.

What this does NOT check: a real ROS2/rclpy subscriber callback overhead (Experiment 50 checked that separately, with a fake publisher through a real SingleThreadedExecutor); GPU/JAX dispatch overhead (this detector's inner math is small enough that JAX isn't invoked per-step here); or any other real robot's frame rate -- 30Hz is this specific real dataset's rate, not a universal claim.

Reproducing this: python scripts/dense_armor_streaming/realtime_lerobot_streaming.py regenerates realtime_lerobot_streaming_frozen.json (reuses the already-cached real LeRobot parquet, no new download; requires pip install --upgrade dense-armor first if a stale local version is installed, as it was here). pytest tests/test_realtime_lerobot_streaming.py reads the already-frozen file -- no network access needed in CI, matching this repo's own convention (see e.g. tests/test_imu_sensor_validation.py). Real wall-clock numbers vary slightly run to run (system jitter, already documented project-wide) -- the frozen file reflects one real, committed run, not a guaranteed-reproducible exact value.