mirror of
https://github.com/huggingface/lerobot.git
synced 2026-07-25 02:36:11 +00:00
refactor(camera): apply feedback
This commit is contained in:
@@ -164,6 +164,9 @@ 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
|
Omitted controls leave the sensor's existing automatic or manual setting unchanged. These options
|
||||||
require `use_rgb=True`.
|
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.
|
||||||
|
|
||||||
</hfoption>
|
</hfoption>
|
||||||
</hfoptions>
|
</hfoptions>
|
||||||
|
|
||||||
|
|||||||
@@ -134,6 +134,9 @@ class RealSenseCamera(Camera):
|
|||||||
self.use_rgb = config.use_rgb
|
self.use_rgb = config.use_rgb
|
||||||
self.use_depth = config.use_depth
|
self.use_depth = config.use_depth
|
||||||
self.warmup_s = config.warmup_s
|
self.warmup_s = config.warmup_s
|
||||||
|
self.exposure: int | None = config.exposure
|
||||||
|
self.gain: int | None = config.gain
|
||||||
|
self.white_balance: int | None = config.white_balance
|
||||||
|
|
||||||
self.rs_pipeline: rs.pipeline | None = None
|
self.rs_pipeline: rs.pipeline | None = None
|
||||||
self.rs_profile: rs.pipeline_profile | None = None
|
self.rs_profile: rs.pipeline_profile | None = None
|
||||||
@@ -175,7 +178,8 @@ class RealSenseCamera(Camera):
|
|||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
DeviceAlreadyConnectedError: If the camera is already connected.
|
DeviceAlreadyConnectedError: If the camera is already connected.
|
||||||
ValueError: If the configuration is invalid (e.g., missing serial/name, name not unique).
|
ValueError: If the configuration is invalid, a requested sensor option is unsupported,
|
||||||
|
or a requested sensor value is invalid.
|
||||||
ConnectionError: If the camera is found but fails to start the pipeline or no RealSense devices are detected at all.
|
ConnectionError: If the camera is found but fails to start the pipeline or no RealSense devices are detected at all.
|
||||||
RuntimeError: If the pipeline starts but fails to apply requested settings.
|
RuntimeError: If the pipeline starts but fails to apply requested settings.
|
||||||
"""
|
"""
|
||||||
@@ -400,16 +404,15 @@ class RealSenseCamera(Camera):
|
|||||||
value is invalid. Invalid-value errors include the option name, requested
|
value is invalid. Invalid-value errors include the option name, requested
|
||||||
value, and supported range when available.
|
value, and supported range when available.
|
||||||
"""
|
"""
|
||||||
config = self.config
|
if self.exposure is None and self.gain is None and self.white_balance is None:
|
||||||
if config.exposure is None and config.gain is None and config.white_balance is None:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
color_sensor = self._get_color_sensor()
|
color_sensor = self._get_color_sensor()
|
||||||
|
|
||||||
requested_options = (
|
requested_options = (
|
||||||
(rs.option.exposure, config.exposure, "exposure"),
|
(rs.option.exposure, self.exposure, "exposure"),
|
||||||
(rs.option.gain, config.gain, "gain"),
|
(rs.option.gain, self.gain, "gain"),
|
||||||
(rs.option.white_balance, config.white_balance, "white balance"),
|
(rs.option.white_balance, self.white_balance, "white balance"),
|
||||||
)
|
)
|
||||||
unsupported_options = [
|
unsupported_options = [
|
||||||
label
|
label
|
||||||
@@ -421,30 +424,40 @@ class RealSenseCamera(Camera):
|
|||||||
f"{self}: color sensor does not support requested manual options: {unsupported_options}."
|
f"{self}: color sensor does not support requested manual options: {unsupported_options}."
|
||||||
)
|
)
|
||||||
|
|
||||||
if (config.exposure is not None or config.gain is not None) and color_sensor.supports(
|
manual_exposure_requested = self.exposure is not None or self.gain is not None
|
||||||
rs.option.enable_auto_exposure
|
if manual_exposure_requested:
|
||||||
):
|
if color_sensor.supports(rs.option.enable_auto_exposure):
|
||||||
self._set_sensor_option(color_sensor, rs.option.enable_auto_exposure, 0, "auto-exposure")
|
self._set_sensor_option(color_sensor, rs.option.enable_auto_exposure, 0, "auto-exposure")
|
||||||
logger.info(f"{self} auto-exposure disabled.")
|
logger.info(f"{self} auto-exposure disabled.")
|
||||||
|
else:
|
||||||
|
logger.warning(
|
||||||
|
f"{self} sensor does not support disabling auto-exposure; "
|
||||||
|
"applying manual exposure/gain directly."
|
||||||
|
)
|
||||||
|
|
||||||
if config.exposure is not None:
|
if self.exposure is not None:
|
||||||
self._set_sensor_option(color_sensor, rs.option.exposure, config.exposure, "exposure")
|
self._set_sensor_option(color_sensor, rs.option.exposure, self.exposure, "exposure")
|
||||||
logger.info(f"{self} exposure set to {config.exposure}.")
|
logger.info(f"{self} exposure set to {self.exposure}.")
|
||||||
|
|
||||||
if config.gain is not None:
|
if self.gain is not None:
|
||||||
self._set_sensor_option(color_sensor, rs.option.gain, config.gain, "gain")
|
self._set_sensor_option(color_sensor, rs.option.gain, self.gain, "gain")
|
||||||
logger.info(f"{self} gain set to {config.gain}.")
|
logger.info(f"{self} gain set to {self.gain}.")
|
||||||
|
|
||||||
if config.white_balance is not None:
|
if self.white_balance is not None:
|
||||||
if color_sensor.supports(rs.option.enable_auto_white_balance):
|
if color_sensor.supports(rs.option.enable_auto_white_balance):
|
||||||
self._set_sensor_option(
|
self._set_sensor_option(
|
||||||
color_sensor, rs.option.enable_auto_white_balance, 0, "auto white balance"
|
color_sensor, rs.option.enable_auto_white_balance, 0, "auto white balance"
|
||||||
)
|
)
|
||||||
logger.info(f"{self} auto white balance disabled.")
|
logger.info(f"{self} auto white balance disabled.")
|
||||||
self._set_sensor_option(
|
else:
|
||||||
color_sensor, rs.option.white_balance, config.white_balance, "white balance"
|
logger.warning(
|
||||||
|
f"{self} sensor does not support disabling auto white balance; "
|
||||||
|
"applying manual white balance directly."
|
||||||
)
|
)
|
||||||
logger.info(f"{self} white balance set to {config.white_balance}.")
|
self._set_sensor_option(
|
||||||
|
color_sensor, rs.option.white_balance, self.white_balance, "white balance"
|
||||||
|
)
|
||||||
|
logger.info(f"{self} white balance set to {self.white_balance}.")
|
||||||
|
|
||||||
@check_if_not_connected
|
@check_if_not_connected
|
||||||
def read_depth(self, timeout_ms: int = 200) -> NDArray[Any]:
|
def read_depth(self, timeout_ms: int = 200) -> NDArray[Any]:
|
||||||
|
|||||||
@@ -47,14 +47,15 @@ class RealSenseCameraConfig(CameraConfig):
|
|||||||
rotation: Image rotation setting (0°, 90°, 180°, or 270°). Defaults to no rotation.
|
rotation: Image rotation setting (0°, 90°, 180°, or 270°). Defaults to no rotation.
|
||||||
warmup_s: Time reading frames before returning from connect (in seconds)
|
warmup_s: Time reading frames before returning from connect (in seconds)
|
||||||
exposure: Manual exposure value for the color sensor. When set, auto-exposure is
|
exposure: Manual exposure value for the color sensor. When set, auto-exposure is
|
||||||
disabled and this fixed value is used. Valid range depends on the camera model
|
disabled and this fixed value is used. Valid ranges are camera-model specific
|
||||||
(e.g., 1-10000 for D400 series). Defaults to None (leave unchanged).
|
and reported if the value is rejected. Defaults to None (leave unchanged).
|
||||||
gain: Manual gain value for the color sensor. When set, auto-exposure is disabled
|
gain: Manual gain value for the color sensor. When set, auto-exposure is disabled
|
||||||
and this fixed gain is used. Valid range depends on the camera model
|
and this fixed gain is used, which also freezes exposure at its current value
|
||||||
(e.g., 16-248 for D400 series). Defaults to None (leave unchanged).
|
when no exposure is configured. Valid ranges are camera-model specific and
|
||||||
|
reported if the value is rejected. Defaults to None (leave unchanged).
|
||||||
white_balance: Manual white balance value for the color sensor. When set, auto
|
white_balance: Manual white balance value for the color sensor. When set, auto
|
||||||
white balance is disabled and this fixed value is used. Valid range depends on
|
white balance is disabled and this fixed value is used. Valid ranges are
|
||||||
the camera model (e.g., 2800-6500 for D400 series). Defaults to None
|
camera-model specific and reported if the value is rejected. Defaults to None
|
||||||
(leave unchanged).
|
(leave unchanged).
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
|
|||||||
@@ -347,14 +347,21 @@ def test_configure_sensor_options_raises_when_requested_option_is_unsupported(co
|
|||||||
sensor.set_option.assert_not_called()
|
sensor.set_option.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
def test_configure_sensor_options_only_exposure_disables_auto_exposure():
|
@pytest.mark.parametrize(
|
||||||
|
("config_field", "option", "value"),
|
||||||
|
[
|
||||||
|
("exposure", rs.option.exposure, 120),
|
||||||
|
("gain", rs.option.gain, 64),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_configure_sensor_options_exposure_or_gain_disables_auto_exposure(config_field, option, value):
|
||||||
"""white_balance=None should not touch auto white balance."""
|
"""white_balance=None should not touch auto white balance."""
|
||||||
config = RealSenseCameraConfig(serial_number_or_name="042", exposure=120)
|
config = RealSenseCameraConfig(serial_number_or_name="042", **{config_field: value})
|
||||||
camera = RealSenseCamera(config)
|
camera = RealSenseCamera(config)
|
||||||
|
|
||||||
sensor = _make_mock_sensor(
|
sensor = _make_mock_sensor(
|
||||||
"RGB Camera",
|
"RGB Camera",
|
||||||
supported_options={rs.option.enable_auto_exposure, rs.option.exposure},
|
supported_options={rs.option.enable_auto_exposure, option},
|
||||||
)
|
)
|
||||||
_attach_mock_color_sensor(camera, sensor)
|
_attach_mock_color_sensor(camera, sensor)
|
||||||
|
|
||||||
@@ -362,12 +369,40 @@ def test_configure_sensor_options_only_exposure_disables_auto_exposure():
|
|||||||
|
|
||||||
calls = [call.args for call in sensor.set_option.call_args_list]
|
calls = [call.args for call in sensor.set_option.call_args_list]
|
||||||
assert (rs.option.enable_auto_exposure, 0) in calls
|
assert (rs.option.enable_auto_exposure, 0) in calls
|
||||||
assert (rs.option.exposure, 120) in calls
|
assert (option, value) in calls
|
||||||
for opt, _ in calls:
|
for opt, _ in calls:
|
||||||
assert opt != rs.option.enable_auto_white_balance
|
assert opt != rs.option.enable_auto_white_balance
|
||||||
assert opt != rs.option.white_balance
|
assert opt != rs.option.white_balance
|
||||||
|
|
||||||
|
|
||||||
|
def test_configure_sensor_options_warns_when_auto_exposure_control_is_unsupported(caplog):
|
||||||
|
config = RealSenseCameraConfig(serial_number_or_name="042", exposure=120)
|
||||||
|
camera = RealSenseCamera(config)
|
||||||
|
|
||||||
|
sensor = _make_mock_sensor("RGB Camera", supported_options={rs.option.exposure})
|
||||||
|
_attach_mock_color_sensor(camera, sensor)
|
||||||
|
|
||||||
|
with caplog.at_level("WARNING"):
|
||||||
|
camera._configure_sensor_options()
|
||||||
|
|
||||||
|
sensor.set_option.assert_called_once_with(rs.option.exposure, 120)
|
||||||
|
assert "does not support disabling auto-exposure" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_configure_sensor_options_warns_when_auto_white_balance_control_is_unsupported(caplog):
|
||||||
|
config = RealSenseCameraConfig(serial_number_or_name="042", white_balance=4600)
|
||||||
|
camera = RealSenseCamera(config)
|
||||||
|
|
||||||
|
sensor = _make_mock_sensor("RGB Camera", supported_options={rs.option.white_balance})
|
||||||
|
_attach_mock_color_sensor(camera, sensor)
|
||||||
|
|
||||||
|
with caplog.at_level("WARNING"):
|
||||||
|
camera._configure_sensor_options()
|
||||||
|
|
||||||
|
sensor.set_option.assert_called_once_with(rs.option.white_balance, 4600)
|
||||||
|
assert "does not support disabling auto white balance" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
def test_configure_sensor_options_out_of_range_raises_value_error():
|
def test_configure_sensor_options_out_of_range_raises_value_error():
|
||||||
"""set_option errors should be re-raised as ValueError with range diagnostics."""
|
"""set_option errors should be re-raised as ValueError with range diagnostics."""
|
||||||
config = RealSenseCameraConfig(serial_number_or_name="042", exposure=999999)
|
config = RealSenseCameraConfig(serial_number_or_name="042", exposure=999999)
|
||||||
|
|||||||
Reference in New Issue
Block a user