From dc0570586bfb75b2de7685dc193da95d0123b50e Mon Sep 17 00:00:00 2001 From: CarolinePascal Date: Thu, 30 Jul 2026 11:18:24 +0200 Subject: [PATCH] fix(RGB only): remove the stereo module fallback when setting RGB/color parameters to avoid unexpected impacts on depth sensing --- docs/source/cameras.mdx | 4 ++-- .../cameras/realsense/camera_realsense.py | 19 +++++++++++-------- tests/cameras/test_realsense.py | 7 ++++--- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/source/cameras.mdx b/docs/source/cameras.mdx index 82d6c8355..1d6b1b0f2 100644 --- a/docs/source/cameras.mdx +++ b/docs/source/cameras.mdx @@ -164,8 +164,8 @@ includes the range reported by the sensor. Requesting an unsupported control als Omitted controls leave the sensor's existing automatic or manual setting unchanged. These options require `use_rgb=True`. -On the RealSense D405, the color stream is provided by the Stereo Module, so changing manual -exposure or gain also affects the depth stream. +Manual color controls require a dedicated RGB module. Cameras without one, such as the RealSense +D405, do not support them and raise an error at connection time. diff --git a/src/lerobot/cameras/realsense/camera_realsense.py b/src/lerobot/cameras/realsense/camera_realsense.py index 34ee4348f..6f2606f5b 100644 --- a/src/lerobot/cameras/realsense/camera_realsense.py +++ b/src/lerobot/cameras/realsense/camera_realsense.py @@ -365,11 +365,12 @@ class RealSenseCamera(Camera): return self._async_read(timeout_ms=10000, read_depth=read_depth) def _get_color_sensor(self) -> "rs.sensor": - """Returns the sensor that controls the color stream. + """Returns the dedicated "RGB Camera" sensor that controls the color stream. - Most RealSense cameras expose "RGB Camera" for color. The D405 has no - separate RGB module — its color stream comes from "Stereo Module". - We try RGB Camera first, then fall back to Stereo Module. + Manual color controls are only applied to a dedicated RGB module. Cameras + without one (e.g. the D405, whose color stream comes from the shared + "Stereo Module") are unsupported, so we never fall back to another sensor + to avoid altering the depth stream. """ if self.rs_profile is None: raise RuntimeError(f"{self}: rs_profile must be initialized before use.") @@ -377,12 +378,14 @@ class RealSenseCamera(Camera): device = self.rs_profile.get_device() sensors = {s.get_info(rs.camera_info.name): s for s in device.query_sensors()} - for name in ("RGB Camera", "Stereo Module"): - if name in sensors: - return sensors[name] + if "RGB Camera" in sensors: + return sensors["RGB Camera"] available = list(sensors.keys()) - raise RuntimeError(f"{self}: no color sensor found. Available sensors: {available}") + raise RuntimeError( + f"{self}: manual color controls require a dedicated 'RGB Camera' module, which this camera does not have. ", + f"Available sensors: {available}." + ) def _set_sensor_option(self, sensor: "rs.sensor", option: "rs.option", value: float, label: str) -> None: """Sets a sensor option, re-raising range errors with actionable diagnostics.""" diff --git a/tests/cameras/test_realsense.py b/tests/cameras/test_realsense.py index e4ccef801..d4a22bf45 100644 --- a/tests/cameras/test_realsense.py +++ b/tests/cameras/test_realsense.py @@ -322,15 +322,16 @@ def test_get_color_sensor_prefers_rgb_camera(): assert camera._get_color_sensor() is rgb -def test_get_color_sensor_falls_back_to_stereo_module(): - """D405 has no separate RGB module; color comes from Stereo Module.""" +def test_get_color_sensor_raises_without_dedicated_rgb_module(): + """D405 has no separate RGB module; we refuse to touch the shared Stereo Module.""" config = RealSenseCameraConfig(serial_number_or_name="042") camera = RealSenseCamera(config) stereo = _make_mock_sensor("Stereo Module") _attach_mock_color_sensor(camera, stereo) - assert camera._get_color_sensor() is stereo + with pytest.raises(RuntimeError, match="dedicated 'RGB Camera' module"): + camera._get_color_sensor() def test_get_color_sensor_raises_with_available_sensors():