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/configs.py b/lerobot/common/robot_devices/robots/configs.py index e940b442..34eaa529 100644 --- a/lerobot/common/robot_devices/robots/configs.py +++ b/lerobot/common/robot_devices/robots/configs.py @@ -494,6 +494,93 @@ 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): diff --git a/lerobot/common/robot_devices/robots/manipulator.py b/lerobot/common/robot_devices/robots/manipulator.py index 9173abc6..afd1443b 100644 --- a/lerobot/common/robot_devices/robots/manipulator.py +++ b/lerobot/common/robot_devices/robots/manipulator.py @@ -243,7 +243,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 @@ -260,7 +260,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 @@ -313,7 +313,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 dab514d5..fac5fde3 100644 --- a/lerobot/common/robot_devices/robots/utils.py +++ b/lerobot/common/robot_devices/robots/utils.py @@ -22,6 +22,7 @@ from lerobot.common.robot_devices.robots.configs import ( ManipulatorRobotConfig, MossRobotConfig, RobotConfig, + So100BimanualRobotConfig, So100RobotConfig, StretchRobotConfig, ) @@ -58,6 +59,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":