Merge remote-tracking branch 'origin/user/aliberts/2025_02_25_refactor_robots' into user/aliberts/2025_04_03_add_hope_jr
This commit is contained in:
@@ -1,68 +0,0 @@
|
|||||||
{
|
|
||||||
"homing_offset": [
|
|
||||||
2048,
|
|
||||||
3072,
|
|
||||||
3072,
|
|
||||||
-1024,
|
|
||||||
-1024,
|
|
||||||
2048,
|
|
||||||
-2048,
|
|
||||||
2048,
|
|
||||||
-2048
|
|
||||||
],
|
|
||||||
"drive_mode": [
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"start_pos": [
|
|
||||||
2015,
|
|
||||||
3058,
|
|
||||||
3061,
|
|
||||||
1071,
|
|
||||||
1071,
|
|
||||||
2035,
|
|
||||||
2152,
|
|
||||||
2029,
|
|
||||||
2499
|
|
||||||
],
|
|
||||||
"end_pos": [
|
|
||||||
-1008,
|
|
||||||
-1963,
|
|
||||||
-1966,
|
|
||||||
2141,
|
|
||||||
2143,
|
|
||||||
-971,
|
|
||||||
3043,
|
|
||||||
-1077,
|
|
||||||
3144
|
|
||||||
],
|
|
||||||
"calib_mode": [
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"LINEAR"
|
|
||||||
],
|
|
||||||
"motor_names": [
|
|
||||||
"waist",
|
|
||||||
"shoulder",
|
|
||||||
"shoulder_shadow",
|
|
||||||
"elbow",
|
|
||||||
"elbow_shadow",
|
|
||||||
"forearm_roll",
|
|
||||||
"wrist_angle",
|
|
||||||
"wrist_rotate",
|
|
||||||
"gripper"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
{
|
|
||||||
"homing_offset": [
|
|
||||||
2048,
|
|
||||||
3072,
|
|
||||||
3072,
|
|
||||||
-1024,
|
|
||||||
-1024,
|
|
||||||
2048,
|
|
||||||
-2048,
|
|
||||||
2048,
|
|
||||||
-1024
|
|
||||||
],
|
|
||||||
"drive_mode": [
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"start_pos": [
|
|
||||||
2035,
|
|
||||||
3024,
|
|
||||||
3019,
|
|
||||||
979,
|
|
||||||
981,
|
|
||||||
1982,
|
|
||||||
2166,
|
|
||||||
2124,
|
|
||||||
1968
|
|
||||||
],
|
|
||||||
"end_pos": [
|
|
||||||
-990,
|
|
||||||
-2017,
|
|
||||||
-2015,
|
|
||||||
2078,
|
|
||||||
2076,
|
|
||||||
-1030,
|
|
||||||
3117,
|
|
||||||
-1016,
|
|
||||||
2556
|
|
||||||
],
|
|
||||||
"calib_mode": [
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"LINEAR"
|
|
||||||
],
|
|
||||||
"motor_names": [
|
|
||||||
"waist",
|
|
||||||
"shoulder",
|
|
||||||
"shoulder_shadow",
|
|
||||||
"elbow",
|
|
||||||
"elbow_shadow",
|
|
||||||
"forearm_roll",
|
|
||||||
"wrist_angle",
|
|
||||||
"wrist_rotate",
|
|
||||||
"gripper"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
{
|
|
||||||
"homing_offset": [
|
|
||||||
2048,
|
|
||||||
3072,
|
|
||||||
3072,
|
|
||||||
-1024,
|
|
||||||
-1024,
|
|
||||||
2048,
|
|
||||||
-2048,
|
|
||||||
2048,
|
|
||||||
-2048
|
|
||||||
],
|
|
||||||
"drive_mode": [
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"start_pos": [
|
|
||||||
2056,
|
|
||||||
2895,
|
|
||||||
2896,
|
|
||||||
1191,
|
|
||||||
1190,
|
|
||||||
2018,
|
|
||||||
2051,
|
|
||||||
2056,
|
|
||||||
2509
|
|
||||||
],
|
|
||||||
"end_pos": [
|
|
||||||
-1040,
|
|
||||||
-2004,
|
|
||||||
-2006,
|
|
||||||
2126,
|
|
||||||
2127,
|
|
||||||
-1010,
|
|
||||||
3050,
|
|
||||||
-1117,
|
|
||||||
3143
|
|
||||||
],
|
|
||||||
"calib_mode": [
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"LINEAR"
|
|
||||||
],
|
|
||||||
"motor_names": [
|
|
||||||
"waist",
|
|
||||||
"shoulder",
|
|
||||||
"shoulder_shadow",
|
|
||||||
"elbow",
|
|
||||||
"elbow_shadow",
|
|
||||||
"forearm_roll",
|
|
||||||
"wrist_angle",
|
|
||||||
"wrist_rotate",
|
|
||||||
"gripper"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
{
|
|
||||||
"homing_offset": [
|
|
||||||
2048,
|
|
||||||
3072,
|
|
||||||
3072,
|
|
||||||
-1024,
|
|
||||||
-1024,
|
|
||||||
2048,
|
|
||||||
-2048,
|
|
||||||
2048,
|
|
||||||
-2048
|
|
||||||
],
|
|
||||||
"drive_mode": [
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"start_pos": [
|
|
||||||
2068,
|
|
||||||
3034,
|
|
||||||
3030,
|
|
||||||
1038,
|
|
||||||
1041,
|
|
||||||
1991,
|
|
||||||
1948,
|
|
||||||
2090,
|
|
||||||
1985
|
|
||||||
],
|
|
||||||
"end_pos": [
|
|
||||||
-1025,
|
|
||||||
-2014,
|
|
||||||
-2015,
|
|
||||||
2058,
|
|
||||||
2060,
|
|
||||||
-955,
|
|
||||||
3091,
|
|
||||||
-940,
|
|
||||||
2576
|
|
||||||
],
|
|
||||||
"calib_mode": [
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"DEGREE",
|
|
||||||
"LINEAR"
|
|
||||||
],
|
|
||||||
"motor_names": [
|
|
||||||
"waist",
|
|
||||||
"shoulder",
|
|
||||||
"shoulder_shadow",
|
|
||||||
"elbow",
|
|
||||||
"elbow_shadow",
|
|
||||||
"forearm_roll",
|
|
||||||
"wrist_angle",
|
|
||||||
"wrist_rotate",
|
|
||||||
"gripper"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -11,7 +11,10 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
# Dev scripts
|
||||||
.dev
|
.dev
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
logs
|
logs
|
||||||
tmp
|
tmp
|
||||||
@@ -91,10 +94,8 @@ coverage.xml
|
|||||||
.hypothesis/
|
.hypothesis/
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
|
|
||||||
# Ignore .cache except calibration
|
# Ignore .cache
|
||||||
.cache/*
|
.cache/*
|
||||||
!.cache/calibration/
|
|
||||||
!.cache/calibration/**
|
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
*.mo
|
*.mo
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ conda install ffmpeg -c conda-forge
|
|||||||
|
|
||||||
Install 🤗 LeRobot:
|
Install 🤗 LeRobot:
|
||||||
```bash
|
```bash
|
||||||
cd lerobot && pip install ".[feetech]"
|
cd lerobot && pip install -e ".[feetech]"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|||||||
@@ -142,7 +142,6 @@ from lerobot.common.datasets.video_utils import (
|
|||||||
get_video_info,
|
get_video_info,
|
||||||
)
|
)
|
||||||
from lerobot.common.robots import RobotConfig
|
from lerobot.common.robots import RobotConfig
|
||||||
from lerobot.common.robots.utils import make_robot_config
|
|
||||||
|
|
||||||
V16 = "v1.6"
|
V16 = "v1.6"
|
||||||
V20 = "v2.0"
|
V20 = "v2.0"
|
||||||
@@ -598,6 +597,30 @@ def convert_dataset(
|
|||||||
create_branch(repo_id=repo_id, branch=V20, repo_type="dataset")
|
create_branch(repo_id=repo_id, branch=V20, repo_type="dataset")
|
||||||
|
|
||||||
|
|
||||||
|
def make_robot_config(robot_type: str, **kwargs) -> RobotConfig:
|
||||||
|
if robot_type == "aloha":
|
||||||
|
raise NotImplementedError # TODO
|
||||||
|
|
||||||
|
elif robot_type == "koch_follower":
|
||||||
|
from lerobot.common.robots.koch_follower import KochFollowerConfig
|
||||||
|
|
||||||
|
return KochFollowerConfig(**kwargs)
|
||||||
|
elif robot_type == "so100_follower":
|
||||||
|
from lerobot.common.robots.so100_follower import SO100FollowerConfig
|
||||||
|
|
||||||
|
return SO100FollowerConfig(**kwargs)
|
||||||
|
elif robot_type == "stretch":
|
||||||
|
from lerobot.common.robots.stretch3 import Stretch3RobotConfig
|
||||||
|
|
||||||
|
return Stretch3RobotConfig(**kwargs)
|
||||||
|
elif robot_type == "lekiwi":
|
||||||
|
from lerobot.common.robots.lekiwi import LeKiwiConfig
|
||||||
|
|
||||||
|
return LeKiwiConfig(**kwargs)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Robot type '{robot_type}' is not available.")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
task_args = parser.add_mutually_exclusive_group(required=True)
|
task_args = parser.add_mutually_exclusive_group(required=True)
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ class EnvConfig(draccus.ChoiceRegistry, abc.ABC):
|
|||||||
def type(self) -> str:
|
def type(self) -> str:
|
||||||
return self.get_choice_name(self.__class__)
|
return self.get_choice_name(self.__class__)
|
||||||
|
|
||||||
@abc.abstractproperty
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
def gym_kwargs(self) -> dict:
|
def gym_kwargs(self) -> dict:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ class DynamixelMotorsBus(MotorsBus):
|
|||||||
https://emanual.robotis.com/docs/en/software/dynamixel/dynamixel_sdk/sample_code/python_read_write_protocol_2_0/#python-read-write-protocol-20
|
https://emanual.robotis.com/docs/en/software/dynamixel/dynamixel_sdk/sample_code/python_read_write_protocol_2_0/#python-read-write-protocol-20
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
apply_drive_mode = False
|
||||||
available_baudrates = deepcopy(AVAILABLE_BAUDRATES)
|
available_baudrates = deepcopy(AVAILABLE_BAUDRATES)
|
||||||
default_baudrate = DEFAULT_BAUDRATE
|
default_baudrate = DEFAULT_BAUDRATE
|
||||||
default_timeout = DEFAULT_TIMEOUT_MS
|
default_timeout = DEFAULT_TIMEOUT_MS
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ class FeetechMotorsBus(MotorsBus):
|
|||||||
python feetech sdk to communicate with the motors, which is itself based on the dynamixel sdk.
|
python feetech sdk to communicate with the motors, which is itself based on the dynamixel sdk.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
apply_drive_mode = True
|
||||||
available_baudrates = deepcopy(SCAN_BAUDRATES)
|
available_baudrates = deepcopy(SCAN_BAUDRATES)
|
||||||
default_baudrate = DEFAULT_BAUDRATE
|
default_baudrate = DEFAULT_BAUDRATE
|
||||||
default_timeout = DEFAULT_TIMEOUT_MS
|
default_timeout = DEFAULT_TIMEOUT_MS
|
||||||
|
|||||||
@@ -252,6 +252,7 @@ class MotorsBus(abc.ABC):
|
|||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
apply_drive_mode: bool
|
||||||
available_baudrates: list[int]
|
available_baudrates: list[int]
|
||||||
default_baudrate: int
|
default_baudrate: int
|
||||||
default_timeout: int
|
default_timeout: int
|
||||||
@@ -756,7 +757,7 @@ class MotorsBus(abc.ABC):
|
|||||||
|
|
||||||
return mins, maxes
|
return mins, maxes
|
||||||
|
|
||||||
def _normalize(self, data_name: str, ids_values: dict[int, int]) -> dict[int, float]:
|
def _normalize(self, ids_values: dict[int, int]) -> dict[int, float]:
|
||||||
if not self.calibration:
|
if not self.calibration:
|
||||||
raise RuntimeError(f"{self} has no calibration registered.")
|
raise RuntimeError(f"{self} has no calibration registered.")
|
||||||
|
|
||||||
@@ -765,20 +766,24 @@ class MotorsBus(abc.ABC):
|
|||||||
motor = self._id_to_name(id_)
|
motor = self._id_to_name(id_)
|
||||||
min_ = self.calibration[motor].range_min
|
min_ = self.calibration[motor].range_min
|
||||||
max_ = self.calibration[motor].range_max
|
max_ = self.calibration[motor].range_max
|
||||||
|
drive_mode = self.apply_drive_mode and self.calibration[motor].drive_mode
|
||||||
|
if max_ == min_:
|
||||||
|
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:
|
||||||
normalized_values[id_] = (((bounded_val - min_) / (max_ - min_)) * 200) - 100
|
norm = (((bounded_val - min_) / (max_ - min_)) * 200) - 100
|
||||||
|
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:
|
||||||
normalized_values[id_] = ((bounded_val - min_) / (max_ - min_)) * 100
|
norm = ((bounded_val - min_) / (max_ - min_)) * 100
|
||||||
|
normalized_values[id_] = 100 - norm if drive_mode else norm
|
||||||
else:
|
else:
|
||||||
# TODO(alibers): velocity and degree modes
|
# TODO(alibers): degree mode
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
return normalized_values
|
return normalized_values
|
||||||
|
|
||||||
def _unnormalize(self, data_name: str, ids_values: dict[int, float]) -> dict[int, int]:
|
def _unnormalize(self, ids_values: dict[int, float]) -> dict[int, int]:
|
||||||
if not self.calibration:
|
if not self.calibration:
|
||||||
raise RuntimeError(f"{self} has no calibration registered.")
|
raise RuntimeError(f"{self} has no calibration registered.")
|
||||||
|
|
||||||
@@ -787,14 +792,20 @@ class MotorsBus(abc.ABC):
|
|||||||
motor = self._id_to_name(id_)
|
motor = self._id_to_name(id_)
|
||||||
min_ = self.calibration[motor].range_min
|
min_ = self.calibration[motor].range_min
|
||||||
max_ = self.calibration[motor].range_max
|
max_ = self.calibration[motor].range_max
|
||||||
|
drive_mode = self.apply_drive_mode and self.calibration[motor].drive_mode
|
||||||
|
if max_ == min_:
|
||||||
|
raise ValueError(f"Invalid calibration for motor '{motor}': min and max are equal.")
|
||||||
|
|
||||||
if self.motors[motor].norm_mode is MotorNormMode.RANGE_M100_100:
|
if self.motors[motor].norm_mode is MotorNormMode.RANGE_M100_100:
|
||||||
|
val = -val if drive_mode else val
|
||||||
bounded_val = min(100.0, max(-100.0, val))
|
bounded_val = min(100.0, max(-100.0, val))
|
||||||
unnormalized_values[id_] = int(((bounded_val + 100) / 200) * (max_ - min_) + min_)
|
unnormalized_values[id_] = int(((bounded_val + 100) / 200) * (max_ - min_) + min_)
|
||||||
elif self.motors[motor].norm_mode is MotorNormMode.RANGE_0_100:
|
elif self.motors[motor].norm_mode is MotorNormMode.RANGE_0_100:
|
||||||
|
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_)
|
||||||
else:
|
else:
|
||||||
# TODO(alibers): velocity and degree modes
|
# TODO(aliberts): degree mode
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
return unnormalized_values
|
return unnormalized_values
|
||||||
@@ -915,7 +926,7 @@ class MotorsBus(abc.ABC):
|
|||||||
id_value = self._decode_sign(data_name, {id_: value})
|
id_value = self._decode_sign(data_name, {id_: value})
|
||||||
|
|
||||||
if normalize and data_name in self.normalized_data:
|
if normalize and data_name in self.normalized_data:
|
||||||
id_value = self._normalize(data_name, id_value)
|
id_value = self._normalize(id_value)
|
||||||
|
|
||||||
return id_value[id_]
|
return id_value[id_]
|
||||||
|
|
||||||
@@ -982,7 +993,7 @@ class MotorsBus(abc.ABC):
|
|||||||
addr, length = get_address(self.model_ctrl_table, model, data_name)
|
addr, length = get_address(self.model_ctrl_table, model, data_name)
|
||||||
|
|
||||||
if normalize and data_name in self.normalized_data:
|
if normalize and data_name in self.normalized_data:
|
||||||
value = self._unnormalize(data_name, {id_: value})[id_]
|
value = self._unnormalize({id_: value})[id_]
|
||||||
|
|
||||||
value = self._encode_sign(data_name, {id_: value})[id_]
|
value = self._encode_sign(data_name, {id_: value})[id_]
|
||||||
|
|
||||||
@@ -1061,7 +1072,7 @@ class MotorsBus(abc.ABC):
|
|||||||
ids_values = self._decode_sign(data_name, ids_values)
|
ids_values = self._decode_sign(data_name, ids_values)
|
||||||
|
|
||||||
if normalize and data_name in self.normalized_data:
|
if normalize and data_name in self.normalized_data:
|
||||||
ids_values = self._normalize(data_name, ids_values)
|
ids_values = self._normalize(ids_values)
|
||||||
|
|
||||||
return {self._id_to_name(id_): value for id_, value in ids_values.items()}
|
return {self._id_to_name(id_): value for id_, value in ids_values.items()}
|
||||||
|
|
||||||
@@ -1147,7 +1158,7 @@ class MotorsBus(abc.ABC):
|
|||||||
addr, length = get_address(self.model_ctrl_table, model, data_name)
|
addr, length = get_address(self.model_ctrl_table, model, data_name)
|
||||||
|
|
||||||
if normalize and data_name in self.normalized_data:
|
if normalize and data_name in self.normalized_data:
|
||||||
ids_values = self._unnormalize(data_name, ids_values)
|
ids_values = self._unnormalize(ids_values)
|
||||||
|
|
||||||
ids_values = self._encode_sign(data_name, ids_values)
|
ids_values = self._encode_sign(data_name, ids_values)
|
||||||
|
|
||||||
|
|||||||
@@ -49,15 +49,18 @@ class Robot(abc.ABC):
|
|||||||
return f"{self.id} {self.__class__.__name__}"
|
return f"{self.id} {self.__class__.__name__}"
|
||||||
|
|
||||||
# TODO(aliberts): create a proper Feature class for this that links with datasets
|
# TODO(aliberts): create a proper Feature class for this that links with datasets
|
||||||
@abc.abstractproperty
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
def observation_features(self) -> dict:
|
def observation_features(self) -> dict:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractproperty
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
def action_features(self) -> dict:
|
def action_features(self) -> dict:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractproperty
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
def is_connected(self) -> bool:
|
def is_connected(self) -> bool:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -66,7 +69,8 @@ class Robot(abc.ABC):
|
|||||||
"""Connects to the robot."""
|
"""Connects to the robot."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractproperty
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
def is_calibrated(self) -> bool:
|
def is_calibrated(self) -> bool:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ conda install ffmpeg -c conda-forge
|
|||||||
|
|
||||||
Install 🤗 LeRobot:
|
Install 🤗 LeRobot:
|
||||||
```bash
|
```bash
|
||||||
cd lerobot && pip install ".[feetech]"
|
cd lerobot && pip install -e ".[feetech]"
|
||||||
```
|
```
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
|
|||||||
@@ -20,30 +20,6 @@ from lerobot.common.robots import RobotConfig
|
|||||||
from .robot import Robot
|
from .robot import Robot
|
||||||
|
|
||||||
|
|
||||||
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 == "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:
|
||||||
if config.type == "koch_follower":
|
if config.type == "koch_follower":
|
||||||
from .koch_follower import KochFollower
|
from .koch_follower import KochFollower
|
||||||
|
|||||||
@@ -47,15 +47,18 @@ class Teleoperator(abc.ABC):
|
|||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"{self.id} {self.__class__.__name__}"
|
return f"{self.id} {self.__class__.__name__}"
|
||||||
|
|
||||||
@abc.abstractproperty
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
def action_features(self) -> dict:
|
def action_features(self) -> dict:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractproperty
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
def feedback_features(self) -> dict:
|
def feedback_features(self) -> dict:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractproperty
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
def is_connected(self) -> bool:
|
def is_connected(self) -> bool:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -64,7 +67,8 @@ class Teleoperator(abc.ABC):
|
|||||||
"""Connects to the teleoperator."""
|
"""Connects to the teleoperator."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractproperty
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
def is_calibrated(self) -> bool:
|
def is_calibrated(self) -> bool:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -78,15 +78,18 @@ class PreTrainedConfig(draccus.ChoiceRegistry, HubMixin, abc.ABC):
|
|||||||
def type(self) -> str:
|
def type(self) -> str:
|
||||||
return self.get_choice_name(self.__class__)
|
return self.get_choice_name(self.__class__)
|
||||||
|
|
||||||
@abc.abstractproperty
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
def observation_delta_indices(self) -> list | None:
|
def observation_delta_indices(self) -> list | None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abc.abstractproperty
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
def action_delta_indices(self) -> list | None:
|
def action_delta_indices(self) -> list | None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abc.abstractproperty
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
def reward_delta_indices(self) -> list | None:
|
def reward_delta_indices(self) -> list | None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ def test_read(data_name, id_, value, dummy_motors):
|
|||||||
)
|
)
|
||||||
mock__decode_sign.assert_called_once_with(data_name, {id_: value})
|
mock__decode_sign.assert_called_once_with(data_name, {id_: value})
|
||||||
if data_name in bus.normalized_data:
|
if data_name in bus.normalized_data:
|
||||||
mock__normalize.assert_called_once_with(data_name, {id_: value})
|
mock__normalize.assert_called_once_with({id_: value})
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@@ -159,7 +159,7 @@ def test_write(data_name, id_, value, dummy_motors):
|
|||||||
)
|
)
|
||||||
mock__encode_sign.assert_called_once_with(data_name, {id_: value})
|
mock__encode_sign.assert_called_once_with(data_name, {id_: value})
|
||||||
if data_name in bus.normalized_data:
|
if data_name in bus.normalized_data:
|
||||||
mock__unnormalize.assert_called_once_with(data_name, {id_: value})
|
mock__unnormalize.assert_called_once_with({id_: value})
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@@ -196,7 +196,7 @@ def test_sync_read_by_str(data_name, id_, value, dummy_motors):
|
|||||||
)
|
)
|
||||||
mock__decode_sign.assert_called_once_with(data_name, {id_: value})
|
mock__decode_sign.assert_called_once_with(data_name, {id_: value})
|
||||||
if data_name in bus.normalized_data:
|
if data_name in bus.normalized_data:
|
||||||
mock__normalize.assert_called_once_with(data_name, {id_: value})
|
mock__normalize.assert_called_once_with({id_: value})
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@@ -233,7 +233,7 @@ def test_sync_read_by_list(data_name, ids_values, dummy_motors):
|
|||||||
)
|
)
|
||||||
mock__decode_sign.assert_called_once_with(data_name, ids_values)
|
mock__decode_sign.assert_called_once_with(data_name, ids_values)
|
||||||
if data_name in bus.normalized_data:
|
if data_name in bus.normalized_data:
|
||||||
mock__normalize.assert_called_once_with(data_name, ids_values)
|
mock__normalize.assert_called_once_with(ids_values)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@@ -270,7 +270,7 @@ def test_sync_read_by_none(data_name, ids_values, dummy_motors):
|
|||||||
)
|
)
|
||||||
mock__decode_sign.assert_called_once_with(data_name, ids_values)
|
mock__decode_sign.assert_called_once_with(data_name, ids_values)
|
||||||
if data_name in bus.normalized_data:
|
if data_name in bus.normalized_data:
|
||||||
mock__normalize.assert_called_once_with(data_name, ids_values)
|
mock__normalize.assert_called_once_with(ids_values)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@@ -304,7 +304,7 @@ def test_sync_write_by_single_value(data_name, value, dummy_motors):
|
|||||||
)
|
)
|
||||||
mock__encode_sign.assert_called_once_with(data_name, ids_values)
|
mock__encode_sign.assert_called_once_with(data_name, ids_values)
|
||||||
if data_name in bus.normalized_data:
|
if data_name in bus.normalized_data:
|
||||||
mock__unnormalize.assert_called_once_with(data_name, ids_values)
|
mock__unnormalize.assert_called_once_with(ids_values)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@@ -339,4 +339,4 @@ def test_sync_write_by_value_dict(data_name, ids_values, dummy_motors):
|
|||||||
)
|
)
|
||||||
mock__encode_sign.assert_called_once_with(data_name, ids_values)
|
mock__encode_sign.assert_called_once_with(data_name, ids_values)
|
||||||
if data_name in bus.normalized_data:
|
if data_name in bus.normalized_data:
|
||||||
mock__unnormalize.assert_called_once_with(data_name, ids_values)
|
mock__unnormalize.assert_called_once_with(ids_values)
|
||||||
|
|||||||
Reference in New Issue
Block a user