forked from tangger/lerobot
- added back degrees mode back to motor bus for IK and FK to work properly
- created new so100followerendeffector robot that inherits from so100follower but takes eef actions and transforms them to joint positions - created teleop_gamepad device to use gamepad as a teleoperater
This commit is contained in:
committed by
AdilZouitine
parent
b166296ba5
commit
05fcfca374
@@ -786,13 +786,19 @@ class MotorsBus(abc.ABC):
|
|||||||
raise ValueError(f"Invalid calibration for motor '{motor}': min and max are equal.")
|
raise ValueError(f"Invalid calibration for motor '{motor}': min and max are equal.")
|
||||||
|
|
||||||
bounded_val = min(max_, max(min_, val))
|
bounded_val = min(max_, max(min_, val))
|
||||||
|
# TODO(Steven): normalization can go boom if max_ == min_, we should add a check probably in record_ranges_of_motions
|
||||||
|
# (which probably indicates the user forgot to move a motor, most likely a gripper-like one)
|
||||||
if self.motors[motor].norm_mode is MotorNormMode.RANGE_M100_100:
|
if self.motors[motor].norm_mode is MotorNormMode.RANGE_M100_100:
|
||||||
norm = (((bounded_val - min_) / (max_ - min_)) * 200) - 100
|
norm = (((bounded_val - min_) / (max_ - min_)) * 200) - 100
|
||||||
normalized_values[id_] = -norm if drive_mode else norm
|
normalized_values[id_] = -norm if drive_mode else norm
|
||||||
elif self.motors[motor].norm_mode is MotorNormMode.RANGE_0_100:
|
elif self.motors[motor].norm_mode is MotorNormMode.RANGE_0_100:
|
||||||
norm = ((bounded_val - min_) / (max_ - min_)) * 100
|
norm = ((bounded_val - min_) / (max_ - min_)) * 100
|
||||||
normalized_values[id_] = 100 - norm if drive_mode else norm
|
normalized_values[id_] = 100 - norm if drive_mode else norm
|
||||||
else:
|
elif self.motors[motor].norm_mode is MotorNormMode.DEGREE:
|
||||||
|
if drive_mode:
|
||||||
|
val *= -1
|
||||||
|
val += homing_offset
|
||||||
|
normalized_values[id_] = val / (self.model_resolution_table[self.motors[motor].model] // 2) * 180
|
||||||
# TODO(alibers): degree mode
|
# TODO(alibers): degree mode
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@@ -819,6 +825,12 @@ class MotorsBus(abc.ABC):
|
|||||||
val = 100 - val if drive_mode else val
|
val = 100 - val if drive_mode else val
|
||||||
bounded_val = min(100.0, max(0.0, val))
|
bounded_val = min(100.0, max(0.0, val))
|
||||||
unnormalized_values[id_] = int((bounded_val / 100) * (max_ - min_) + min_)
|
unnormalized_values[id_] = int((bounded_val / 100) * (max_ - min_) + min_)
|
||||||
|
elif self.motors[motor].norm_mode is MotorNormMode.DEGREE:
|
||||||
|
homing_offset = self.calibration[motor].homing_offset
|
||||||
|
unnormalized_values[id_] = int(val / 180 * (self.model_resolution_table[self.motors[motor].model] // 2))
|
||||||
|
unnormalized_values[id_] -= homing_offset
|
||||||
|
if drive_mode:
|
||||||
|
unnormalized_values[id_] *= -1
|
||||||
else:
|
else:
|
||||||
# TODO(aliberts): degree mode
|
# TODO(aliberts): degree mode
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
from .config import RobotConfig
|
from .config import RobotConfig
|
||||||
from .robot import Robot
|
from .robot import Robot
|
||||||
|
from .robot_wrapper import RobotWrapper
|
||||||
from .utils import make_robot_from_config
|
from .utils import make_robot_from_config
|
||||||
|
|||||||
@@ -14,10 +14,40 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
from lerobot.common.robots import RobotConfig
|
from lerobot.common.robots import RobotConfig
|
||||||
|
|
||||||
from .robot import Robot
|
from .robot import Robot
|
||||||
|
from .robot_wrapper import RobotWrapper
|
||||||
|
|
||||||
|
|
||||||
|
def make_robot_config(robot_type: str, **kwargs) -> RobotConfig:
|
||||||
|
if robot_type == "aloha":
|
||||||
|
raise NotImplementedError # TODO
|
||||||
|
|
||||||
|
elif robot_type == "koch_follower":
|
||||||
|
from .koch_follower.config_koch_follower import KochFollowerConfig
|
||||||
|
|
||||||
|
return KochFollowerConfig(**kwargs)
|
||||||
|
elif robot_type == "so100_follower":
|
||||||
|
from .so100_follower.config_so100_follower import SO100FollowerConfig
|
||||||
|
|
||||||
|
return SO100FollowerConfig(**kwargs)
|
||||||
|
elif robot_type == "so100_follower_end_effector":
|
||||||
|
from .so100_follower_end_effector.config_so100_follower_end_effector import SO100FollowerEndEffectorConfig
|
||||||
|
|
||||||
|
return SO100FollowerEndEffectorConfig(**kwargs)
|
||||||
|
elif robot_type == "stretch":
|
||||||
|
from .stretch3.configuration_stretch3 import Stretch3RobotConfig
|
||||||
|
|
||||||
|
return Stretch3RobotConfig(**kwargs)
|
||||||
|
elif robot_type == "lekiwi":
|
||||||
|
from .lekiwi.config_lekiwi import LeKiwiConfig
|
||||||
|
|
||||||
|
return LeKiwiConfig(**kwargs)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Robot type '{robot_type}' is not available.")
|
||||||
|
|
||||||
|
|
||||||
def make_robot_from_config(config: RobotConfig) -> Robot:
|
def make_robot_from_config(config: RobotConfig) -> Robot:
|
||||||
@@ -29,6 +59,10 @@ def make_robot_from_config(config: RobotConfig) -> Robot:
|
|||||||
from .so100_follower import SO100Follower
|
from .so100_follower import SO100Follower
|
||||||
|
|
||||||
return SO100Follower(config)
|
return SO100Follower(config)
|
||||||
|
elif config.type == "so100_follower_end_effector":
|
||||||
|
from .so100_follower_end_effector import SO100FollowerEndEffector
|
||||||
|
|
||||||
|
return SO100FollowerEndEffector(config)
|
||||||
elif config.type == "so101_follower":
|
elif config.type == "so101_follower":
|
||||||
from .so101_follower import SO101Follower
|
from .so101_follower import SO101Follower
|
||||||
|
|
||||||
@@ -97,3 +131,9 @@ def get_arm_id(name, arm_type):
|
|||||||
like Aloha, it could be left_follower, right_follower, left_leader, or right_leader.
|
like Aloha, it could be left_follower, right_follower, left_leader, or right_leader.
|
||||||
"""
|
"""
|
||||||
return f"{name}_{arm_type}"
|
return f"{name}_{arm_type}"
|
||||||
|
|
||||||
|
def convert_joint_m100_100_to_degrees(joint_positions: dict[str, float], mins: dict[str, float], maxs: dict[str, float]) -> dict[str, float]:
|
||||||
|
return {key: ((value + 100) / 200) * (maxs[key] - mins[key]) + mins[key] for key, value in joint_positions.items()}
|
||||||
|
|
||||||
|
def convert_joint_degrees_to_m100_100(joint_positions: dict[str, float], mins: dict[str, float], maxs: dict[str, float]) -> dict[str, float]:
|
||||||
|
return {key: (value - mins[key]) / (maxs[key] - mins[key]) * 200 - 100 for key, value in joint_positions.items()}
|
||||||
|
|||||||
@@ -45,5 +45,9 @@ def make_teleoperator_from_config(config: TeleoperatorConfig) -> Teleoperator:
|
|||||||
from tests.mocks.mock_teleop import MockTeleop
|
from tests.mocks.mock_teleop import MockTeleop
|
||||||
|
|
||||||
return MockTeleop(config)
|
return MockTeleop(config)
|
||||||
|
elif config.type == "gamepad":
|
||||||
|
from .gamepad.teleop_gamepad import GamepadTeleop
|
||||||
|
|
||||||
|
return GamepadTeleop(config)
|
||||||
else:
|
else:
|
||||||
raise ValueError(config.type)
|
raise ValueError(config.type)
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import os.path as osp
|
|||||||
import platform
|
import platform
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
import select
|
||||||
|
import sys
|
||||||
from copy import copy, deepcopy
|
from copy import copy, deepcopy
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -244,6 +246,14 @@ def is_valid_numpy_dtype_string(dtype_str: str) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def enter_pressed() -> bool:
|
||||||
|
return select.select([sys.stdin], [], [], 0)[0] and sys.stdin.readline().strip() == ""
|
||||||
|
|
||||||
|
|
||||||
|
def move_cursor_up(lines):
|
||||||
|
"""Move the cursor up by a specified number of lines."""
|
||||||
|
print(f"\033[{lines}A", end="")
|
||||||
|
|
||||||
class TimerManager:
|
class TimerManager:
|
||||||
"""
|
"""
|
||||||
Lightweight utility to measure elapsed time.
|
Lightweight utility to measure elapsed time.
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import time
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
from lerobot.common.robot_devices.utils import busy_wait
|
from lerobot.common.utils.robot_utils import busy_wait
|
||||||
from lerobot.common.utils.utils import init_logging
|
from lerobot.common.utils.utils import init_logging
|
||||||
from lerobot.scripts.server.kinematics import RobotKinematics
|
from lerobot.scripts.server.kinematics import RobotKinematics
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ class InputController:
|
|||||||
def gripper_command(self):
|
def gripper_command(self):
|
||||||
"""Return the current gripper command."""
|
"""Return the current gripper command."""
|
||||||
if self.open_gripper_command == self.close_gripper_command:
|
if self.open_gripper_command == self.close_gripper_command:
|
||||||
return "no-op"
|
return "stay"
|
||||||
elif self.open_gripper_command:
|
elif self.open_gripper_command:
|
||||||
return "open"
|
return "open"
|
||||||
elif self.close_gripper_command:
|
elif self.close_gripper_command:
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ from lerobot.common.robots import ( # noqa: F401
|
|||||||
koch_follower,
|
koch_follower,
|
||||||
make_robot_from_config,
|
make_robot_from_config,
|
||||||
so100_follower,
|
so100_follower,
|
||||||
|
so100_follower_end_effector,
|
||||||
so101_follower,
|
so101_follower,
|
||||||
)
|
)
|
||||||
from lerobot.common.teleoperators import (
|
from lerobot.common.teleoperators import (
|
||||||
@@ -57,7 +58,7 @@ from lerobot.common.teleoperators import (
|
|||||||
from lerobot.common.utils.utils import init_logging, move_cursor_up
|
from lerobot.common.utils.utils import init_logging, move_cursor_up
|
||||||
from lerobot.common.utils.visualization_utils import _init_rerun
|
from lerobot.common.utils.visualization_utils import _init_rerun
|
||||||
|
|
||||||
from .common.teleoperators import koch_leader, so100_leader, so101_leader # noqa: F401
|
from .common.teleoperators import koch_leader, so100_leader, gamepad # noqa: F401
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|||||||
Reference in New Issue
Block a user