diff --git a/src/lerobot/cameras/opencv/camera_opencv.py b/src/lerobot/cameras/opencv/camera_opencv.py index 50e55f0c22..9f860b1827 100644 --- a/src/lerobot/cameras/opencv/camera_opencv.py +++ b/src/lerobot/cameras/opencv/camera_opencv.py @@ -233,15 +233,25 @@ def _validate_width_and_height(self) -> None: actual_width = int(round(self.videocapture.get(cv2.CAP_PROP_FRAME_WIDTH))) if not width_success or self.capture_width != actual_width: - raise RuntimeError( - f"{self} failed to set capture_width={self.capture_width} ({actual_width=}, {width_success=})." + logger.warning( + f"{self} failed to set capture_width={self.capture_width} ({actual_width=}, {width_success=}). " + f"Using camera's actual width={actual_width}." ) + self.capture_width = actual_width actual_height = int(round(self.videocapture.get(cv2.CAP_PROP_FRAME_HEIGHT))) if not height_success or self.capture_height != actual_height: - raise RuntimeError( - f"{self} failed to set capture_height={self.capture_height} ({actual_height=}, {height_success=})." + logger.warning( + f"{self} failed to set capture_height={self.capture_height} ({actual_height=}, {height_success=}). " + f"Using camera's actual height={actual_height}." ) + self.capture_height = actual_height + + # Update output dimensions based on actual capture dimensions and rotation + if self.rotation in [cv2.ROTATE_90_CLOCKWISE, cv2.ROTATE_90_COUNTERCLOCKWISE]: + self.width, self.height = self.capture_height, self.capture_width + else: + self.width, self.height = self.capture_width, self.capture_height @staticmethod def find_cameras() -> list[dict[str, Any]]: diff --git a/src/lerobot/cameras/utils.py b/src/lerobot/cameras/utils.py index aa6ff98b48..48cfec638d 100644 --- a/src/lerobot/cameras/utils.py +++ b/src/lerobot/cameras/utils.py @@ -69,7 +69,7 @@ def get_cv2_backend() -> int: import cv2 if platform.system() == "Windows": - return cv2.CAP_MSMF # Use MSMF for Windows instead of AVFOUNDATION + return cv2.CAP_DSHOW # Use DirectShow for Windows (more compatible than MSMF) # elif platform.system() == "Darwin": # macOS # return cv2.CAP_AVFOUNDATION else: # Linux and others diff --git a/src/lerobot/robots/so101_follower/so101_follower.py b/src/lerobot/robots/so101_follower/so101_follower.py index acfd4bd114..a1395db7d9 100644 --- a/src/lerobot/robots/so101_follower/so101_follower.py +++ b/src/lerobot/robots/so101_follower/so101_follower.py @@ -176,7 +176,7 @@ def get_observation(self) -> dict[str, Any]: # Read arm position start = time.perf_counter() - obs_dict = self.bus.sync_read("Present_Position") + obs_dict = self.bus.sync_read("Present_Position", num_retry=5) obs_dict = {f"{motor}.pos": val for motor, val in obs_dict.items()} dt_ms = (time.perf_counter() - start) * 1e3 logger.debug(f"{self} read state: {dt_ms:.1f}ms") diff --git a/src/lerobot/scripts/lerobot_record.py b/src/lerobot/scripts/lerobot_record.py index 55846ff63a..dd30655463 100644 --- a/src/lerobot/scripts/lerobot_record.py +++ b/src/lerobot/scripts/lerobot_record.py @@ -381,6 +381,19 @@ def record(cfg: RecordConfig) -> LeRobotDataset: robot = make_robot_from_config(cfg.robot) teleop = make_teleoperator_from_config(cfg.teleop) if cfg.teleop is not None else None + # Connect robot early to get actual camera dimensions + robot.connect() + if teleop is not None: + teleop.connect() + + # Update config with actual camera dimensions after connection + if hasattr(robot, "cameras") and robot.cameras: + for cam_name, cam in robot.cameras.items(): + if cam_name in cfg.robot.cameras: + # Update config with actual camera dimensions for dataset creation + cfg.robot.cameras[cam_name].width = cam.width + cfg.robot.cameras[cam_name].height = cam.height + teleop_action_processor, robot_action_processor, robot_observation_processor = make_default_processors() dataset_features = combine_feature_dicts( @@ -441,10 +454,6 @@ def record(cfg: RecordConfig) -> LeRobotDataset: }, ) - robot.connect() - if teleop is not None: - teleop.connect() - listener, events = init_keyboard_listener() with VideoEncodingManager(dataset): diff --git a/src/lerobot/teleoperators/so101_leader/so101_leader.py b/src/lerobot/teleoperators/so101_leader/so101_leader.py index be804bf702..2180aca667 100644 --- a/src/lerobot/teleoperators/so101_leader/so101_leader.py +++ b/src/lerobot/teleoperators/so101_leader/so101_leader.py @@ -138,7 +138,7 @@ def setup_motors(self) -> None: def get_action(self) -> dict[str, float]: start = time.perf_counter() - action = self.bus.sync_read("Present_Position") + action = self.bus.sync_read("Present_Position", num_retry=5) action = {f"{motor}.pos": val for motor, val in action.items()} dt_ms = (time.perf_counter() - start) * 1e3 logger.debug(f"{self} read action: {dt_ms:.1f}ms")