mirror of
https://github.com/huggingface/lerobot.git
synced 2026-07-25 10:46:01 +00:00
feat(camera): add manual exposure, gain, and white balance options for RealSense cameras
The RealSense camera integration lacked sensor-level controls, causing issues like unstable lighting from auto-exposure hunting. This adds optional `exposure`, `gain`, and `white_balance` fields to RealSenseCameraConfig that disable the corresponding auto modes and apply fixed values when set.
This commit is contained in:
@@ -191,6 +191,7 @@ class RealSenseCamera(Camera):
|
||||
) from e
|
||||
|
||||
self._configure_capture_settings()
|
||||
self._configure_sensor_options()
|
||||
self._start_read_thread()
|
||||
|
||||
# NOTE(Steven/Caroline): Enforcing at least one second of warmup as RS cameras need a bit of time before the first read. If we don't wait, the first read from the warmup will raise.
|
||||
@@ -339,6 +340,59 @@ class RealSenseCamera(Camera):
|
||||
self.new_frame_event.clear()
|
||||
return self._async_read(timeout_ms=10000, read_depth=read_depth)
|
||||
|
||||
def _get_color_sensor(self) -> "rs.sensor":
|
||||
"""Returns the color sensor from the active pipeline profile."""
|
||||
if self.rs_profile is None:
|
||||
raise RuntimeError(f"{self}: rs_profile must be initialized before use.")
|
||||
|
||||
device = self.rs_profile.get_device()
|
||||
for sensor in device.query_sensors():
|
||||
if sensor.get_info(rs.camera_info.name) == "RGB Camera":
|
||||
return sensor
|
||||
raise RuntimeError(f"{self}: no RGB Camera sensor found on device.")
|
||||
|
||||
def _configure_sensor_options(self) -> None:
|
||||
"""Applies manual sensor options (exposure, gain, white balance) to the color sensor.
|
||||
|
||||
When exposure or gain is set, auto-exposure is disabled first. When white_balance
|
||||
is set, auto white balance is disabled first. Skipped entirely if no options are set.
|
||||
"""
|
||||
config = self.config
|
||||
if config.exposure is None and config.gain is None and config.white_balance is None:
|
||||
return
|
||||
|
||||
color_sensor = self._get_color_sensor()
|
||||
|
||||
if (config.exposure is not None or config.gain is not None) and color_sensor.supports(
|
||||
rs.option.enable_auto_exposure
|
||||
):
|
||||
color_sensor.set_option(rs.option.enable_auto_exposure, 0)
|
||||
logger.info(f"{self} auto-exposure disabled.")
|
||||
|
||||
if config.exposure is not None:
|
||||
if color_sensor.supports(rs.option.exposure):
|
||||
color_sensor.set_option(rs.option.exposure, config.exposure)
|
||||
logger.info(f"{self} exposure set to {config.exposure}.")
|
||||
else:
|
||||
logger.warning(f"{self} sensor does not support manual exposure.")
|
||||
|
||||
if config.gain is not None:
|
||||
if color_sensor.supports(rs.option.gain):
|
||||
color_sensor.set_option(rs.option.gain, config.gain)
|
||||
logger.info(f"{self} gain set to {config.gain}.")
|
||||
else:
|
||||
logger.warning(f"{self} sensor does not support manual gain.")
|
||||
|
||||
if config.white_balance is not None:
|
||||
if color_sensor.supports(rs.option.enable_auto_white_balance):
|
||||
color_sensor.set_option(rs.option.enable_auto_white_balance, 0)
|
||||
logger.info(f"{self} auto white balance disabled.")
|
||||
if color_sensor.supports(rs.option.white_balance):
|
||||
color_sensor.set_option(rs.option.white_balance, config.white_balance)
|
||||
logger.info(f"{self} white balance set to {config.white_balance}.")
|
||||
else:
|
||||
logger.warning(f"{self} sensor does not support manual white balance.")
|
||||
|
||||
@check_if_not_connected
|
||||
def read_depth(self, timeout_ms: int = 200) -> NDArray[Any]:
|
||||
"""
|
||||
|
||||
@@ -46,6 +46,15 @@ class RealSenseCameraConfig(CameraConfig):
|
||||
use_depth: Whether to enable depth stream. Defaults to False.
|
||||
rotation: Image rotation setting (0°, 90°, 180°, or 270°). Defaults to no rotation.
|
||||
warmup_s: Time reading frames before returning from connect (in seconds)
|
||||
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
|
||||
(e.g., 1-10000 for D400 series). Defaults to None (auto-exposure).
|
||||
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
|
||||
(e.g., 16-248 for D400 series). Defaults to None (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
|
||||
the camera model (e.g., 2800-6500 for D400 series). Defaults to None (auto).
|
||||
|
||||
Note:
|
||||
- Either name or serial_number must be specified.
|
||||
@@ -61,6 +70,9 @@ class RealSenseCameraConfig(CameraConfig):
|
||||
use_depth: bool = False
|
||||
rotation: Cv2Rotation = Cv2Rotation.NO_ROTATION
|
||||
warmup_s: int = 1
|
||||
exposure: int | None = None
|
||||
gain: int | None = None
|
||||
white_balance: int | None = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
self.color_mode = ColorMode(self.color_mode)
|
||||
|
||||
Reference in New Issue
Block a user