refactor(cameras): realsense config arg serial_number_or_name

This commit is contained in:
Steven Palma
2025-05-20 18:03:53 +02:00
parent 39a93a7b28
commit 18b56e1533
3 changed files with 21 additions and 31 deletions

View File

@@ -66,7 +66,7 @@ class RealSenseCamera(Camera):
from lerobot.common.cameras import ColorMode from lerobot.common.cameras import ColorMode
# Basic usage with serial number # Basic usage with serial number
config = RealSenseCameraConfig(serial_number="1234567890") # Replace with actual SN config = RealSenseCameraConfig(serial_number_or_name="1234567890") # Replace with actual SN
camera = RealSenseCamera(config) camera = RealSenseCamera(config)
camera.connect() camera.connect()
@@ -82,7 +82,7 @@ class RealSenseCamera(Camera):
# Example with depth capture and custom settings # Example with depth capture and custom settings
custom_config = RealSenseCameraConfig( custom_config = RealSenseCameraConfig(
serial_number="1234567890", # Replace with actual SN serial_number_or_name="1234567890", # Replace with actual SN
fps=30, fps=30,
width=1280, width=1280,
height=720, height=720,
@@ -99,7 +99,7 @@ class RealSenseCamera(Camera):
depth_camera.disconnect() depth_camera.disconnect()
# Example using a unique camera name # Example using a unique camera name
name_config = RealSenseCameraConfig(name="Intel RealSense D435") # If unique name_config = RealSenseCameraConfig(serial_number_or_name="Intel RealSense D435") # If unique
name_camera = RealSenseCamera(name_config) name_camera = RealSenseCamera(name_config)
# ... connect, read, disconnect ... # ... connect, read, disconnect ...
``` ```
@@ -117,12 +117,10 @@ class RealSenseCamera(Camera):
self.config = config self.config = config
if config.name is not None: # NOTE(Steven): Do we want to continue supporting this? if isinstance(config.serial_number_or_name, int):
self.serial_number = self._find_serial_number_from_name(config.name) self.serial_number = str(config.serial_number_or_name)
elif config.serial_number is not None:
self.serial_number = str(config.serial_number)
else: else:
raise ValueError("RealSenseCameraConfig must provide either 'serial_number' or 'name'.") self.serial_number = self._find_serial_number_from_name(config.serial_number_or_name)
self.fps = config.fps self.fps = config.fps
self.color_mode = config.color_mode self.color_mode = config.color_mode

View File

@@ -40,9 +40,7 @@ class RealSenseCameraConfig(CameraConfig):
fps: Requested frames per second for the color stream. fps: Requested frames per second for the color stream.
width: Requested frame width in pixels for the color stream. width: Requested frame width in pixels for the color stream.
height: Requested frame height in pixels for the color stream. height: Requested frame height in pixels for the color stream.
name: Optional human-readable name to identify the camera. serial_number_or_name: unique serial number or human-readable name to identify the camera.
serial_number: Optional unique serial number to identify the camera.
Either name or serial_number must be provided.
color_mode: Color mode for image output (RGB or BGR). Defaults to RGB. color_mode: Color mode for image output (RGB or BGR). Defaults to RGB.
use_depth: Whether to enable depth stream. Defaults to False. use_depth: Whether to enable depth stream. Defaults to False.
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.
@@ -54,8 +52,7 @@ class RealSenseCameraConfig(CameraConfig):
- Only 3-channel color output (RGB/BGR) is currently supported. - Only 3-channel color output (RGB/BGR) is currently supported.
""" """
name: str | None = None serial_number_or_name: int | str
serial_number: int | None = None
color_mode: ColorMode = ColorMode.RGB color_mode: ColorMode = ColorMode.RGB
use_depth: bool = False use_depth: bool = False
rotation: Cv2Rotation = Cv2Rotation.NO_ROTATION # NOTE(Steven): Check if draccus can parse to an enum rotation: Cv2Rotation = Cv2Rotation.NO_ROTATION # NOTE(Steven): Check if draccus can parse to an enum
@@ -75,8 +72,3 @@ class RealSenseCameraConfig(CameraConfig):
raise ValueError( raise ValueError(
f"`rotation` is expected to be in {(Cv2Rotation.NO_ROTATION, Cv2Rotation.ROTATE_90, Cv2Rotation.ROTATE_180, Cv2Rotation.ROTATE_270)}, but {self.rotation} is provided." f"`rotation` is expected to be in {(Cv2Rotation.NO_ROTATION, Cv2Rotation.ROTATE_90, Cv2Rotation.ROTATE_180, Cv2Rotation.ROTATE_270)}, but {self.rotation} is provided."
) )
if bool(self.name) and bool(self.serial_number):
raise ValueError(
f"One of them must be set: name or serial_number, but {self.name=} and {self.serial_number=} provided."
)

View File

@@ -51,13 +51,13 @@ def mock_rs_config_enable_device_bad_file(rs_config_instance, sn):
def test_abc_implementation(): def test_abc_implementation():
"""Instantiation should raise an error if the class doesn't implement abstract methods/properties.""" """Instantiation should raise an error if the class doesn't implement abstract methods/properties."""
config = RealSenseCameraConfig(serial_number=42) config = RealSenseCameraConfig(serial_number_or_name=42)
_ = RealSenseCamera(config) _ = RealSenseCamera(config)
@patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_from_file) @patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_from_file)
def test_connect(mock_enable_device): def test_connect(mock_enable_device):
config = RealSenseCameraConfig(serial_number=42) config = RealSenseCameraConfig(serial_number_or_name=42)
camera = RealSenseCamera(config) camera = RealSenseCamera(config)
camera.connect(warmup=False) camera.connect(warmup=False)
@@ -66,7 +66,7 @@ def test_connect(mock_enable_device):
@patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_from_file) @patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_from_file)
def test_connect_already_connected(mock_enable_device): def test_connect_already_connected(mock_enable_device):
config = RealSenseCameraConfig(serial_number=42) config = RealSenseCameraConfig(serial_number_or_name=42)
camera = RealSenseCamera(config) camera = RealSenseCamera(config)
camera.connect(warmup=False) camera.connect(warmup=False)
@@ -76,7 +76,7 @@ def test_connect_already_connected(mock_enable_device):
@patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_bad_file) @patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_bad_file)
def test_connect_invalid_camera_path(mock_enable_device): def test_connect_invalid_camera_path(mock_enable_device):
config = RealSenseCameraConfig(serial_number=42) config = RealSenseCameraConfig(serial_number_or_name=42)
camera = RealSenseCamera(config) camera = RealSenseCamera(config)
with pytest.raises(ConnectionError): with pytest.raises(ConnectionError):
@@ -85,7 +85,7 @@ def test_connect_invalid_camera_path(mock_enable_device):
@patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_from_file) @patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_from_file)
def test_invalid_width_connect(mock_enable_device): def test_invalid_width_connect(mock_enable_device):
config = RealSenseCameraConfig(serial_number=42, width=99999, height=480, fps=30) config = RealSenseCameraConfig(serial_number_or_name=42, width=99999, height=480, fps=30)
camera = RealSenseCamera(config) camera = RealSenseCamera(config)
with pytest.raises(ConnectionError): with pytest.raises(ConnectionError):
@@ -94,7 +94,7 @@ def test_invalid_width_connect(mock_enable_device):
@patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_from_file) @patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_from_file)
def test_read(mock_enable_device): def test_read(mock_enable_device):
config = RealSenseCameraConfig(serial_number=42, width=640, height=480, fps=30) config = RealSenseCameraConfig(serial_number_or_name=42, width=640, height=480, fps=30)
camera = RealSenseCamera(config) camera = RealSenseCamera(config)
camera.connect(warmup=False) camera.connect(warmup=False)
@@ -103,7 +103,7 @@ def test_read(mock_enable_device):
def test_read_before_connect(): def test_read_before_connect():
config = RealSenseCameraConfig(serial_number=42) config = RealSenseCameraConfig(serial_number_or_name=42)
camera = RealSenseCamera(config) camera = RealSenseCamera(config)
with pytest.raises(DeviceNotConnectedError): with pytest.raises(DeviceNotConnectedError):
@@ -112,7 +112,7 @@ def test_read_before_connect():
@patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_from_file) @patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_from_file)
def test_disconnect(mock_enable_device): def test_disconnect(mock_enable_device):
config = RealSenseCameraConfig(serial_number=42) config = RealSenseCameraConfig(serial_number_or_name=42)
camera = RealSenseCamera(config) camera = RealSenseCamera(config)
camera.connect(warmup=False) camera.connect(warmup=False)
@@ -122,7 +122,7 @@ def test_disconnect(mock_enable_device):
def test_disconnect_before_connect(): def test_disconnect_before_connect():
config = RealSenseCameraConfig(serial_number=42) config = RealSenseCameraConfig(serial_number_or_name=42)
camera = RealSenseCamera(config) camera = RealSenseCamera(config)
with pytest.raises(DeviceNotConnectedError): with pytest.raises(DeviceNotConnectedError):
@@ -131,7 +131,7 @@ def test_disconnect_before_connect():
@patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_from_file) @patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_from_file)
def test_async_read(mock_enable_device): def test_async_read(mock_enable_device):
config = RealSenseCameraConfig(serial_number=42, width=640, height=480, fps=30) config = RealSenseCameraConfig(serial_number_or_name=42, width=640, height=480, fps=30)
camera = RealSenseCamera(config) camera = RealSenseCamera(config)
camera.connect(warmup=False) camera.connect(warmup=False)
@@ -148,7 +148,7 @@ def test_async_read(mock_enable_device):
@patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_from_file) @patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_from_file)
def test_async_read_timeout(mock_enable_device): def test_async_read_timeout(mock_enable_device):
config = RealSenseCameraConfig(serial_number=42, width=640, height=480, fps=30) config = RealSenseCameraConfig(serial_number_or_name=42, width=640, height=480, fps=30)
camera = RealSenseCamera(config) camera = RealSenseCamera(config)
camera.connect(warmup=False) camera.connect(warmup=False)
@@ -163,7 +163,7 @@ def test_async_read_timeout(mock_enable_device):
def test_async_read_before_connect(): def test_async_read_before_connect():
config = RealSenseCameraConfig(serial_number=42) config = RealSenseCameraConfig(serial_number_or_name=42)
camera = RealSenseCamera(config) camera = RealSenseCamera(config)
with pytest.raises(DeviceNotConnectedError): with pytest.raises(DeviceNotConnectedError):
@@ -182,7 +182,7 @@ def test_async_read_before_connect():
) )
@patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_from_file) @patch("pyrealsense2.config.enable_device", side_effect=mock_rs_config_enable_device_from_file)
def test_rotation(mock_enable_device, rotation): def test_rotation(mock_enable_device, rotation):
config = RealSenseCameraConfig(serial_number=42, rotation=rotation) config = RealSenseCameraConfig(serial_number_or_name=42, rotation=rotation)
camera = RealSenseCamera(config) camera = RealSenseCamera(config)
camera.connect(warmup=False) camera.connect(warmup=False)