Skip to content

Conversation

@xuweiwu
Copy link

@xuweiwu xuweiwu commented Dec 21, 2025

Title

feat(camera): allow immediate read in async_read without checking freshness

Type / Scope

  • Type: Feature
  • Scope: lerobot.cameras.opencv and lerobot.cameras.realsense (OpenCV/RealSense camera async API)

Summary / Motivation

This PR improves the camera async API for low-latency sampling. It adds an optional immediate path to async_read, so callers can fetch the most recent frame without blocking when freshness is not required, while preserving the original wait->read->clear behavior when waiting is acceptable. This reduces unnecessary waits and latency during teleop, data collection, and policy inference, especially in multi-camera setups. It also decouples the achievable control frequency from individual camera streaming rates. Trade-off: clients that truly require a strict “new since last read” guarantee should continue to use the event-driven path, which is the case by default through require_new=True.

Related issues

None.

What changed

OpenCVCamera.async_read and RealsenseCamera.async_read:

  • Added require_new: bool parameter (defaults to True, i.e., defaults to original behavior).
  • Fast path: when require_new=False and a frame is already available, return immediately (no wait).
  • Kept original event-driven path invariant if require_new=True

No breaking changes to existing call sites that pass only timeout_ms.

How was this tested

  • Tests performed: ./tests/cameras/test_realsense.py and ./tests/cameras/test_opencv.py all passed.
  • Tests added (not included in PR):
@pytest.mark.parametrize("index_or_path", TEST_IMAGE_PATHS, ids=TEST_IMAGE_SIZES)
def test_async_read_no_freshness_check(index_or_path):
    config = OpenCVCameraConfig(index_or_path=index_or_path)
    camera = OpenCVCamera(config)
    camera.connect(warmup=False)

    try:
        img = camera.async_read(require_new=False)

        assert camera.thread is not None
        assert camera.thread.is_alive()
        assert isinstance(img, np.ndarray)
    finally:
        if camera.is_connected:
            camera.disconnect()  # To stop/join the thread. Otherwise get warnings when the test ends
  • Dataset collected using the new feature : xuweiwu/bimanual-toy-box-cleanup (available on hf)

How to run locally (reviewer)

  • Run the relevant tests:
python -m pytest -sv ./tests/cameras/test_realsense.py
python -m pytest -sv ./tests/cameras/test_opencv.py

Checklist (required before merge)

  • Linting/formatting run (pre-commit run -a)
  • All tests pass locally (pytest)
  • Documentation updated
  • CI is green

Reviewer notes

The impact is most relevant in multi-camera setups where a slow source can stall the loop.
In my tests with three cameras, using require_new=True (freshness, original behavior) introduces waits when a camera lags:

read right_wrist: 5.7ms
read head: 27.5ms
read rear: 0.0ms
control delay: 35.3ms

Switching to require_new=False (no freshness check) returns whatever is already available and avoids blocking on the slow camera:

read right_wrist: 0.0ms
read head: 0.0ms
read rear: 0.0ms
control delay: 2.1ms

@github-actions github-actions bot added the sensors Everything related to sensors label Dec 21, 2025
xuweiwu added a commit to xuweiwu/XLeRobot that referenced this pull request Dec 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sensors Everything related to sensors

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant