From 8b4af6e076469431efa2c88e8eba059af765b56a Mon Sep 17 00:00:00 2001 From: whats2000 Date: Thu, 9 Oct 2025 19:58:57 +0800 Subject: [PATCH 1/4] Fix: Use DirectShow for Windows (more compatible than MSMF) --- src/lerobot/cameras/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 042f864ce6638151b2f8b0af49f506eb13fae310 Mon Sep 17 00:00:00 2001 From: whats2000 Date: Thu, 9 Oct 2025 20:00:12 +0800 Subject: [PATCH 2/4] Fix: Add the retry for bus sync delay issue --- src/lerobot/robots/so101_follower/so101_follower.py | 2 +- src/lerobot/teleoperators/so101_leader/so101_leader.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/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") From c3b5c25a83a3a75e638413c57c8031582715447b Mon Sep 17 00:00:00 2001 From: whats2000 Date: Thu, 9 Oct 2025 20:02:42 +0800 Subject: [PATCH 3/4] Feat: Make camera width and height setting adjust from actual size automatic --- src/lerobot/cameras/opencv/camera_opencv.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) 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]]: From 10b836e155895e67c578ceb8d8ea3dddbbe4efa6 Mon Sep 17 00:00:00 2001 From: whats2000 Date: Thu, 16 Oct 2025 20:56:49 +0800 Subject: [PATCH 4/4] Fix: Fix the adjustment during record --- src/lerobot/scripts/lerobot_record.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) 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):