From cfa4702bef45334bcd9d4c059858d84f042e0118 Mon Sep 17 00:00:00 2001 From: Bryson Jones Date: Sat, 1 Mar 2025 07:51:30 -0800 Subject: [PATCH 1/3] update robot configs to add option for so100_bimanual --- .../common/robot_devices/robots/configs.py | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/lerobot/common/robot_devices/robots/configs.py b/lerobot/common/robot_devices/robots/configs.py index 88cb4e6f..b8ab94ea 100644 --- a/lerobot/common/robot_devices/robots/configs.py +++ b/lerobot/common/robot_devices/robots/configs.py @@ -480,6 +480,92 @@ class So100RobotConfig(ManipulatorRobotConfig): mock: bool = False +@RobotConfig.register_subclass("so100_bimanual") +@dataclass +class So100BimanualRobotConfig(ManipulatorRobotConfig): + calibration_dir: str = ".cache/calibration/so100_bimanual" + # `max_relative_target` limits the magnitude of the relative positional target vector for safety purposes. + # Set this to a positive scalar to have the same value for all motors, or a list that is the same length as + # the number of motors in your follower arms. + max_relative_target: int | None = None + + leader_arms: dict[str, MotorsBusConfig] = field( + default_factory=lambda: { + "left": FeetechMotorsBusConfig( + port="/dev/tty.usbmodem585A0085511", + motors={ + # name: (index, model) + "shoulder_pan": [1, "sts3215"], + "shoulder_lift": [2, "sts3215"], + "elbow_flex": [3, "sts3215"], + "wrist_flex": [4, "sts3215"], + "wrist_roll": [5, "sts3215"], + "gripper": [6, "sts3215"], + }, + ), + "right": FeetechMotorsBusConfig( + port="/dev/tty.usbmodem575E0031751", + motors={ + # name: (index, model) + "shoulder_pan": [1, "sts3215"], + "shoulder_lift": [2, "sts3215"], + "elbow_flex": [3, "sts3215"], + "wrist_flex": [4, "sts3215"], + "wrist_roll": [5, "sts3215"], + "gripper": [6, "sts3215"], + }, + ), + } + ) + + follower_arms: dict[str, MotorsBusConfig] = field( + default_factory=lambda: { + "left": FeetechMotorsBusConfig( + port="/dev/tty.usbmodem585A0076891", + motors={ + # name: (index, model) + "shoulder_pan": [1, "sts3215"], + "shoulder_lift": [2, "sts3215"], + "elbow_flex": [3, "sts3215"], + "wrist_flex": [4, "sts3215"], + "wrist_roll": [5, "sts3215"], + "gripper": [6, "sts3215"], + }, + ), + "right": FeetechMotorsBusConfig( + port="/dev/tty.usbmodem575E0032081", + motors={ + # name: (index, model) + "shoulder_pan": [1, "sts3215"], + "shoulder_lift": [2, "sts3215"], + "elbow_flex": [3, "sts3215"], + "wrist_flex": [4, "sts3215"], + "wrist_roll": [5, "sts3215"], + "gripper": [6, "sts3215"], + }, + ), + } + ) + + cameras: dict[str, CameraConfig] = field( + default_factory=lambda: { + "laptop": OpenCVCameraConfig( + camera_index=0, + fps=30, + width=640, + height=480, + ), + "phone": OpenCVCameraConfig( + camera_index=1, + fps=30, + width=640, + height=480, + ), + } + ) + + mock: bool = False + @RobotConfig.register_subclass("stretch") @dataclass class StretchRobotConfig(RobotConfig): From 172d21edd47ef801048f7ddccdb188625e15d6d8 Mon Sep 17 00:00:00 2001 From: Bryson Jones Date: Sat, 1 Mar 2025 08:13:15 -0800 Subject: [PATCH 2/3] clean up formatting with ruff --- lerobot/common/robot_devices/robots/configs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lerobot/common/robot_devices/robots/configs.py b/lerobot/common/robot_devices/robots/configs.py index b8ab94ea..c3a53498 100644 --- a/lerobot/common/robot_devices/robots/configs.py +++ b/lerobot/common/robot_devices/robots/configs.py @@ -566,6 +566,7 @@ class So100BimanualRobotConfig(ManipulatorRobotConfig): mock: bool = False + @RobotConfig.register_subclass("stretch") @dataclass class StretchRobotConfig(RobotConfig): From 5d8d7e4da154e132502cd1a0d61827fc41ed6206 Mon Sep 17 00:00:00 2001 From: Bryson Jones Date: Sat, 1 Mar 2025 08:34:37 -0800 Subject: [PATCH 3/3] update all areas in code that need references to new robot type for bimanual so100 --- lerobot/__init__.py | 1 + lerobot/common/robot_devices/robots/manipulator.py | 6 +++--- lerobot/common/robot_devices/robots/utils.py | 3 +++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lerobot/__init__.py b/lerobot/__init__.py index d61e4853..9551d5e8 100644 --- a/lerobot/__init__.py +++ b/lerobot/__init__.py @@ -181,6 +181,7 @@ available_robots = [ "koch_bimanual", "aloha", "so100", + "so100_bimanual", "moss", ] diff --git a/lerobot/common/robot_devices/robots/manipulator.py b/lerobot/common/robot_devices/robots/manipulator.py index 62e5416e..650cee5a 100644 --- a/lerobot/common/robot_devices/robots/manipulator.py +++ b/lerobot/common/robot_devices/robots/manipulator.py @@ -229,7 +229,7 @@ class ManipulatorRobot: if self.robot_type in ["koch", "koch_bimanual", "aloha"]: from lerobot.common.robot_devices.motors.dynamixel import TorqueMode - elif self.robot_type in ["so100", "moss", "lekiwi"]: + elif self.robot_type in ["so100", "so100_bimanual", "moss", "lekiwi"]: from lerobot.common.robot_devices.motors.feetech import TorqueMode # We assume that at connection time, arms are in a rest position, and torque can @@ -246,7 +246,7 @@ class ManipulatorRobot: self.set_koch_robot_preset() elif self.robot_type == "aloha": self.set_aloha_robot_preset() - elif self.robot_type in ["so100", "moss", "lekiwi"]: + elif self.robot_type in ["so100", "so100_bimanual", "moss", "lekiwi"]: self.set_so100_robot_preset() # Enable torque on all motors of the follower arms @@ -299,7 +299,7 @@ class ManipulatorRobot: calibration = run_arm_calibration(arm, self.robot_type, name, arm_type) - elif self.robot_type in ["so100", "moss", "lekiwi"]: + elif self.robot_type in ["so100", "so100_bimanual", "moss", "lekiwi"]: from lerobot.common.robot_devices.robots.feetech_calibration import ( run_arm_manual_calibration, ) diff --git a/lerobot/common/robot_devices/robots/utils.py b/lerobot/common/robot_devices/robots/utils.py index 47e2519b..c4d396ca 100644 --- a/lerobot/common/robot_devices/robots/utils.py +++ b/lerobot/common/robot_devices/robots/utils.py @@ -8,6 +8,7 @@ from lerobot.common.robot_devices.robots.configs import ( ManipulatorRobotConfig, MossRobotConfig, RobotConfig, + So100BimanualRobotConfig, So100RobotConfig, StretchRobotConfig, ) @@ -44,6 +45,8 @@ def make_robot_config(robot_type: str, **kwargs) -> RobotConfig: return MossRobotConfig(**kwargs) elif robot_type == "so100": return So100RobotConfig(**kwargs) + elif robot_type == "so100_bimanual": + return So100BimanualRobotConfig(**kwargs) elif robot_type == "stretch": return StretchRobotConfig(**kwargs) elif robot_type == "lekiwi":