fix(rebase) deleting media related to tutorials
|
Before Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 127 KiB |
|
Before Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 82 KiB |
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:0389a716d51c1c615fb2a3bfa386d89f00b0deca08c4fa21b23e020a939d0213
|
oid sha256:6b1e600768a8771c5fe650e038a1193597e3810f032041b2a0d021e4496381c1
|
||||||
size 3686488
|
size 3686488
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ from lerobot.datasets.transforms import (
|
|||||||
from lerobot.utils.random_utils import seeded_context
|
from lerobot.utils.random_utils import seeded_context
|
||||||
|
|
||||||
ARTIFACT_DIR = Path("tests/artifacts/image_transforms")
|
ARTIFACT_DIR = Path("tests/artifacts/image_transforms")
|
||||||
DATASET_REPO_ID = "lerobot/aloha_mobile_shrimp"
|
DATASET_REPO_ID = "lerobot/aloha_static_cups_open"
|
||||||
|
|
||||||
|
|
||||||
def save_default_config_transform(original_frame: torch.Tensor, output_dir: Path):
|
def save_default_config_transform(original_frame: torch.Tensor, output_dir: Path):
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:0dc691503e7d90b2086bb408e89a65f772ce5ee6e3562ef8c127bcb09bd90851
|
oid sha256:9d4ebab73eabddc58879a4e770289d19e00a1a4cf2fa5fa33cd3a3246992bc90
|
||||||
size 40551392
|
size 40551392
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:cc67af1d60f95d84c98d6c9ebd648990e0f0705368bd6b72d2b39533950b0179
|
oid sha256:f3e4c8e85e146b043fd4e4984947c2a6f01627f174a19f18b5914cf690579d77
|
||||||
size 5104
|
size 5104
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:64518cf652105d15f5fd2cfc13d0681f66a4ec4797dc5d5dc2f7b0d91fe5dfd6
|
oid sha256:1a7a8b1a457149109f843c32bcbb047d09de2201847b9b79f7501b447f77ecf4
|
||||||
size 31672
|
size 31672
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:32b6d14fab4244b5140adb345e47f662b6739c04974e04b21c3127caa988abbb
|
oid sha256:5e6ce85296b2009e7c2060d336c0429b1c7197d9adb159e7df0ba18003067b36
|
||||||
size 68
|
size 68
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e1904ef0338f7b6efdec70ec235ee931b5751008bf4eb433edb0b3fa0838a4f1
|
oid sha256:9b5f557e30aead3731c38cbd85af8c706395d8689a918ad88805b5a886245603
|
||||||
size 33400
|
size 33400
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:fa544a97f00bf46393a09b006b44c2499bbf7d177782360a8c21cacbf200c07a
|
oid sha256:2e6625cabfeb4800abc80252cf9112a9271c154edd01eb291658f143c951610b
|
||||||
size 515400
|
size 515400
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:83c7a8ae912300b5cedba31904f7ba22542059fd60dd86548a95e415713f719e
|
oid sha256:224b5fa4828aa88171b68c036e8919c1eae563e2113f03b6461eadf5bf8525a6
|
||||||
size 31672
|
size 31672
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:5a010633237b3a1141603c65174c551daa9e7b4c474af5a1376d73e5425bfb5d
|
oid sha256:016d2fa8fe5f58017dfd46f4632fdc19dfd751e32a2c7cde2077c6f95546d6bd
|
||||||
size 68
|
size 68
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ec8b5c440e9fcec190c9be48b28ebb79f82ae63626afe7c811e4bb0c3dd08842
|
oid sha256:021562ee3e4814425e367ed0c144d6fbe2eb28838247085716cf0b58fd69a075
|
||||||
size 33400
|
size 33400
|
||||||
|
|||||||
@@ -1,443 +0,0 @@
|
|||||||
# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
"""
|
|
||||||
Tests for physical robots and their mocked versions.
|
|
||||||
If the physical robots are not connected to the computer, or not working,
|
|
||||||
the test will be skipped.
|
|
||||||
|
|
||||||
Example of running a specific test:
|
|
||||||
```bash
|
|
||||||
pytest -sx tests/test_control_robot.py::test_teleoperate
|
|
||||||
```
|
|
||||||
|
|
||||||
Example of running test on real robots connected to the computer:
|
|
||||||
```bash
|
|
||||||
pytest -sx 'tests/test_control_robot.py::test_teleoperate[koch-False]'
|
|
||||||
pytest -sx 'tests/test_control_robot.py::test_teleoperate[koch_bimanual-False]'
|
|
||||||
pytest -sx 'tests/test_control_robot.py::test_teleoperate[aloha-False]'
|
|
||||||
```
|
|
||||||
|
|
||||||
Example of running test on a mocked version of robots:
|
|
||||||
```bash
|
|
||||||
pytest -sx 'tests/test_control_robot.py::test_teleoperate[koch-True]'
|
|
||||||
pytest -sx 'tests/test_control_robot.py::test_teleoperate[koch_bimanual-True]'
|
|
||||||
pytest -sx 'tests/test_control_robot.py::test_teleoperate[aloha-True]'
|
|
||||||
```
|
|
||||||
"""
|
|
||||||
|
|
||||||
import multiprocessing
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from lerobot.common.policies.act.configuration_act import ACTConfig
|
|
||||||
from lerobot.common.policies.factory import make_policy
|
|
||||||
from lerobot.common.robot_devices.control_configs import (
|
|
||||||
CalibrateControlConfig,
|
|
||||||
RecordControlConfig,
|
|
||||||
ReplayControlConfig,
|
|
||||||
TeleoperateControlConfig,
|
|
||||||
)
|
|
||||||
from lerobot.configs.policies import PreTrainedConfig
|
|
||||||
from lerobot.scripts.control_robot import calibrate, record, replay, teleoperate
|
|
||||||
from tests.robots.test_robots import make_robot
|
|
||||||
from tests.utils import TEST_ROBOT_TYPES, mock_calibration_dir, require_robot
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("robot_type, mock", TEST_ROBOT_TYPES)
|
|
||||||
@require_robot
|
|
||||||
def test_teleoperate(tmp_path, request, robot_type, mock):
|
|
||||||
robot_kwargs = {"robot_type": robot_type, "mock": mock}
|
|
||||||
|
|
||||||
if mock and robot_type != "aloha":
|
|
||||||
request.getfixturevalue("patch_builtins_input")
|
|
||||||
|
|
||||||
# Create an empty calibration directory to trigger manual calibration
|
|
||||||
# and avoid writing calibration files in user .cache/calibration folder
|
|
||||||
calibration_dir = tmp_path / robot_type
|
|
||||||
mock_calibration_dir(calibration_dir)
|
|
||||||
robot_kwargs["calibration_dir"] = calibration_dir
|
|
||||||
else:
|
|
||||||
# Use the default .cache/calibration folder when mock=False
|
|
||||||
pass
|
|
||||||
|
|
||||||
robot = make_robot(**robot_kwargs)
|
|
||||||
teleoperate(robot, TeleoperateControlConfig(teleop_time_s=1))
|
|
||||||
teleoperate(robot, TeleoperateControlConfig(fps=30, teleop_time_s=1))
|
|
||||||
teleoperate(robot, TeleoperateControlConfig(fps=60, teleop_time_s=1))
|
|
||||||
del robot
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("robot_type, mock", TEST_ROBOT_TYPES)
|
|
||||||
@require_robot
|
|
||||||
def test_calibrate(tmp_path, request, robot_type, mock):
|
|
||||||
robot_kwargs = {"robot_type": robot_type, "mock": mock}
|
|
||||||
|
|
||||||
if mock:
|
|
||||||
request.getfixturevalue("patch_builtins_input")
|
|
||||||
|
|
||||||
# Create an empty calibration directory to trigger manual calibration
|
|
||||||
calibration_dir = tmp_path / robot_type
|
|
||||||
robot_kwargs["calibration_dir"] = calibration_dir
|
|
||||||
|
|
||||||
robot = make_robot(**robot_kwargs)
|
|
||||||
calib_cfg = CalibrateControlConfig(arms=robot.available_arms)
|
|
||||||
calibrate(robot, calib_cfg)
|
|
||||||
del robot
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("robot_type, mock", TEST_ROBOT_TYPES)
|
|
||||||
@require_robot
|
|
||||||
def test_record_without_cameras(tmp_path, request, robot_type, mock):
|
|
||||||
robot_kwargs = {"robot_type": robot_type, "mock": mock}
|
|
||||||
|
|
||||||
# Avoid using cameras
|
|
||||||
robot_kwargs["cameras"] = {}
|
|
||||||
|
|
||||||
if mock and robot_type != "aloha":
|
|
||||||
request.getfixturevalue("patch_builtins_input")
|
|
||||||
|
|
||||||
# Create an empty calibration directory to trigger manual calibration
|
|
||||||
# and avoid writing calibration files in user .cache/calibration folder
|
|
||||||
calibration_dir = tmp_path / robot_type
|
|
||||||
mock_calibration_dir(calibration_dir)
|
|
||||||
robot_kwargs["calibration_dir"] = calibration_dir
|
|
||||||
else:
|
|
||||||
# Use the default .cache/calibration folder when mock=False
|
|
||||||
pass
|
|
||||||
|
|
||||||
repo_id = "lerobot/debug"
|
|
||||||
root = tmp_path / "data" / repo_id
|
|
||||||
single_task = "Do something."
|
|
||||||
|
|
||||||
robot = make_robot(**robot_kwargs)
|
|
||||||
rec_cfg = RecordControlConfig(
|
|
||||||
repo_id=repo_id,
|
|
||||||
single_task=single_task,
|
|
||||||
root=root,
|
|
||||||
fps=30,
|
|
||||||
warmup_time_s=0.1,
|
|
||||||
episode_time_s=1,
|
|
||||||
reset_time_s=0.1,
|
|
||||||
num_episodes=2,
|
|
||||||
push_to_hub=False,
|
|
||||||
video=False,
|
|
||||||
play_sounds=False,
|
|
||||||
)
|
|
||||||
record(robot, rec_cfg)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("robot_type, mock", TEST_ROBOT_TYPES)
|
|
||||||
@require_robot
|
|
||||||
def test_record_and_replay_and_policy(tmp_path, request, robot_type, mock):
|
|
||||||
robot_kwargs = {"robot_type": robot_type, "mock": mock}
|
|
||||||
|
|
||||||
if mock and robot_type != "aloha":
|
|
||||||
request.getfixturevalue("patch_builtins_input")
|
|
||||||
|
|
||||||
# Create an empty calibration directory to trigger manual calibration
|
|
||||||
# and avoid writing calibration files in user .cache/calibration folder
|
|
||||||
calibration_dir = tmp_path / robot_type
|
|
||||||
mock_calibration_dir(calibration_dir)
|
|
||||||
robot_kwargs["calibration_dir"] = calibration_dir
|
|
||||||
else:
|
|
||||||
# Use the default .cache/calibration folder when mock=False
|
|
||||||
pass
|
|
||||||
|
|
||||||
repo_id = "lerobot_test/debug"
|
|
||||||
root = tmp_path / "data" / repo_id
|
|
||||||
single_task = "Do something."
|
|
||||||
|
|
||||||
robot = make_robot(**robot_kwargs)
|
|
||||||
rec_cfg = RecordControlConfig(
|
|
||||||
repo_id=repo_id,
|
|
||||||
single_task=single_task,
|
|
||||||
root=root,
|
|
||||||
fps=1,
|
|
||||||
warmup_time_s=0.1,
|
|
||||||
episode_time_s=1,
|
|
||||||
reset_time_s=0.1,
|
|
||||||
num_episodes=2,
|
|
||||||
push_to_hub=False,
|
|
||||||
# TODO(rcadene, aliberts): test video=True
|
|
||||||
video=False,
|
|
||||||
display_data=False,
|
|
||||||
play_sounds=False,
|
|
||||||
)
|
|
||||||
dataset = record(robot, rec_cfg)
|
|
||||||
assert dataset.meta.total_episodes == 2
|
|
||||||
assert len(dataset) == 2
|
|
||||||
|
|
||||||
replay_cfg = ReplayControlConfig(episode=0, fps=1, root=root, repo_id=repo_id, play_sounds=False)
|
|
||||||
replay(robot, replay_cfg)
|
|
||||||
|
|
||||||
policy_cfg = ACTConfig()
|
|
||||||
policy = make_policy(policy_cfg, ds_meta=dataset.meta)
|
|
||||||
|
|
||||||
out_dir = tmp_path / "logger"
|
|
||||||
|
|
||||||
pretrained_policy_path = out_dir / "checkpoints/last/pretrained_model"
|
|
||||||
policy.save_pretrained(pretrained_policy_path)
|
|
||||||
|
|
||||||
# In `examples/9_use_aloha.md`, we advise using `num_image_writer_processes=1`
|
|
||||||
# during inference, to reach constant fps, so we test this here.
|
|
||||||
if robot_type == "aloha":
|
|
||||||
num_image_writer_processes = 1
|
|
||||||
|
|
||||||
# `multiprocessing.set_start_method("spawn", force=True)` avoids a hanging issue
|
|
||||||
# before exiting pytest. However, it outputs the following error in the log:
|
|
||||||
# Traceback (most recent call last):
|
|
||||||
# File "<string>", line 1, in <module>
|
|
||||||
# File "/Users/rcadene/miniconda3/envs/lerobot/lib/python3.10/multiprocessing/spawn.py", line 116, in spawn_main
|
|
||||||
# exitcode = _main(fd, parent_sentinel)
|
|
||||||
# File "/Users/rcadene/miniconda3/envs/lerobot/lib/python3.10/multiprocessing/spawn.py", line 126, in _main
|
|
||||||
# self = reduction.pickle.load(from_parent)
|
|
||||||
# File "/Users/rcadene/miniconda3/envs/lerobot/lib/python3.10/multiprocessing/synchronize.py", line 110, in __setstate__
|
|
||||||
# self._semlock = _multiprocessing.SemLock._rebuild(*state)
|
|
||||||
# FileNotFoundError: [Errno 2] No such file or directory
|
|
||||||
# TODO(rcadene, aliberts): fix FileNotFoundError in multiprocessing
|
|
||||||
multiprocessing.set_start_method("spawn", force=True)
|
|
||||||
else:
|
|
||||||
num_image_writer_processes = 0
|
|
||||||
|
|
||||||
eval_repo_id = "lerobot/eval_debug"
|
|
||||||
eval_root = tmp_path / "data" / eval_repo_id
|
|
||||||
|
|
||||||
rec_eval_cfg = RecordControlConfig(
|
|
||||||
repo_id=eval_repo_id,
|
|
||||||
root=eval_root,
|
|
||||||
single_task=single_task,
|
|
||||||
fps=1,
|
|
||||||
warmup_time_s=0.1,
|
|
||||||
episode_time_s=1,
|
|
||||||
reset_time_s=0.1,
|
|
||||||
num_episodes=2,
|
|
||||||
push_to_hub=False,
|
|
||||||
video=False,
|
|
||||||
display_data=False,
|
|
||||||
play_sounds=False,
|
|
||||||
num_image_writer_processes=num_image_writer_processes,
|
|
||||||
)
|
|
||||||
|
|
||||||
rec_eval_cfg.policy = PreTrainedConfig.from_pretrained(pretrained_policy_path)
|
|
||||||
rec_eval_cfg.policy.pretrained_path = pretrained_policy_path
|
|
||||||
|
|
||||||
dataset = record(robot, rec_eval_cfg)
|
|
||||||
assert dataset.num_episodes == 2
|
|
||||||
assert len(dataset) == 2
|
|
||||||
|
|
||||||
del robot
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("robot_type, mock", [("koch", True)])
|
|
||||||
@require_robot
|
|
||||||
def test_resume_record(tmp_path, request, robot_type, mock):
|
|
||||||
robot_kwargs = {"robot_type": robot_type, "mock": mock}
|
|
||||||
|
|
||||||
if mock and robot_type != "aloha":
|
|
||||||
request.getfixturevalue("patch_builtins_input")
|
|
||||||
|
|
||||||
# Create an empty calibration directory to trigger manual calibration
|
|
||||||
# and avoid writing calibration files in user .cache/calibration folder
|
|
||||||
calibration_dir = tmp_path / robot_type
|
|
||||||
mock_calibration_dir(calibration_dir)
|
|
||||||
robot_kwargs["calibration_dir"] = calibration_dir
|
|
||||||
else:
|
|
||||||
# Use the default .cache/calibration folder when mock=False
|
|
||||||
pass
|
|
||||||
|
|
||||||
robot = make_robot(**robot_kwargs)
|
|
||||||
|
|
||||||
repo_id = "lerobot/debug"
|
|
||||||
root = tmp_path / "data" / repo_id
|
|
||||||
single_task = "Do something."
|
|
||||||
|
|
||||||
rec_cfg = RecordControlConfig(
|
|
||||||
repo_id=repo_id,
|
|
||||||
root=root,
|
|
||||||
single_task=single_task,
|
|
||||||
fps=1,
|
|
||||||
warmup_time_s=0,
|
|
||||||
episode_time_s=1,
|
|
||||||
push_to_hub=False,
|
|
||||||
video=False,
|
|
||||||
display_data=False,
|
|
||||||
play_sounds=False,
|
|
||||||
num_episodes=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
dataset = record(robot, rec_cfg)
|
|
||||||
assert len(dataset) == 1, f"`dataset` should contain 1 frame, not {len(dataset)}"
|
|
||||||
|
|
||||||
with pytest.raises(FileExistsError):
|
|
||||||
# Dataset already exists, but resume=False by default
|
|
||||||
record(robot, rec_cfg)
|
|
||||||
|
|
||||||
rec_cfg.resume = True
|
|
||||||
dataset = record(robot, rec_cfg)
|
|
||||||
assert len(dataset) == 2, f"`dataset` should contain 2 frames, not {len(dataset)}"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("robot_type, mock", [("koch", True)])
|
|
||||||
@require_robot
|
|
||||||
def test_record_with_event_rerecord_episode(tmp_path, request, robot_type, mock):
|
|
||||||
robot_kwargs = {"robot_type": robot_type, "mock": mock}
|
|
||||||
|
|
||||||
if mock and robot_type != "aloha":
|
|
||||||
request.getfixturevalue("patch_builtins_input")
|
|
||||||
|
|
||||||
# Create an empty calibration directory to trigger manual calibration
|
|
||||||
# and avoid writing calibration files in user .cache/calibration folder
|
|
||||||
calibration_dir = tmp_path / robot_type
|
|
||||||
mock_calibration_dir(calibration_dir)
|
|
||||||
robot_kwargs["calibration_dir"] = calibration_dir
|
|
||||||
else:
|
|
||||||
# Use the default .cache/calibration folder when mock=False
|
|
||||||
pass
|
|
||||||
|
|
||||||
robot = make_robot(**robot_kwargs)
|
|
||||||
|
|
||||||
with patch("lerobot.scripts.control_robot.init_keyboard_listener") as mock_listener:
|
|
||||||
mock_events = {}
|
|
||||||
mock_events["exit_early"] = True
|
|
||||||
mock_events["rerecord_episode"] = True
|
|
||||||
mock_events["stop_recording"] = False
|
|
||||||
mock_listener.return_value = (None, mock_events)
|
|
||||||
|
|
||||||
repo_id = "lerobot/debug"
|
|
||||||
root = tmp_path / "data" / repo_id
|
|
||||||
single_task = "Do something."
|
|
||||||
|
|
||||||
rec_cfg = RecordControlConfig(
|
|
||||||
repo_id=repo_id,
|
|
||||||
root=root,
|
|
||||||
single_task=single_task,
|
|
||||||
fps=1,
|
|
||||||
warmup_time_s=0,
|
|
||||||
episode_time_s=1,
|
|
||||||
num_episodes=1,
|
|
||||||
push_to_hub=False,
|
|
||||||
video=False,
|
|
||||||
display_data=False,
|
|
||||||
play_sounds=False,
|
|
||||||
)
|
|
||||||
dataset = record(robot, rec_cfg)
|
|
||||||
|
|
||||||
assert not mock_events["rerecord_episode"], "`rerecord_episode` wasn't properly reset to False"
|
|
||||||
assert not mock_events["exit_early"], "`exit_early` wasn't properly reset to False"
|
|
||||||
assert len(dataset) == 1, "`dataset` should contain only 1 frame"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("robot_type, mock", [("koch", True)])
|
|
||||||
@require_robot
|
|
||||||
def test_record_with_event_exit_early(tmp_path, request, robot_type, mock):
|
|
||||||
robot_kwargs = {"robot_type": robot_type, "mock": mock}
|
|
||||||
|
|
||||||
if mock:
|
|
||||||
request.getfixturevalue("patch_builtins_input")
|
|
||||||
|
|
||||||
# Create an empty calibration directory to trigger manual calibration
|
|
||||||
# and avoid writing calibration files in user .cache/calibration folder
|
|
||||||
calibration_dir = tmp_path / robot_type
|
|
||||||
mock_calibration_dir(calibration_dir)
|
|
||||||
robot_kwargs["calibration_dir"] = calibration_dir
|
|
||||||
else:
|
|
||||||
# Use the default .cache/calibration folder when mock=False
|
|
||||||
pass
|
|
||||||
|
|
||||||
robot = make_robot(**robot_kwargs)
|
|
||||||
|
|
||||||
with patch("lerobot.scripts.control_robot.init_keyboard_listener") as mock_listener:
|
|
||||||
mock_events = {}
|
|
||||||
mock_events["exit_early"] = True
|
|
||||||
mock_events["rerecord_episode"] = False
|
|
||||||
mock_events["stop_recording"] = False
|
|
||||||
mock_listener.return_value = (None, mock_events)
|
|
||||||
|
|
||||||
repo_id = "lerobot/debug"
|
|
||||||
root = tmp_path / "data" / repo_id
|
|
||||||
single_task = "Do something."
|
|
||||||
|
|
||||||
rec_cfg = RecordControlConfig(
|
|
||||||
repo_id=repo_id,
|
|
||||||
root=root,
|
|
||||||
single_task=single_task,
|
|
||||||
fps=2,
|
|
||||||
warmup_time_s=0,
|
|
||||||
episode_time_s=1,
|
|
||||||
num_episodes=1,
|
|
||||||
push_to_hub=False,
|
|
||||||
video=False,
|
|
||||||
display_data=False,
|
|
||||||
play_sounds=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
dataset = record(robot, rec_cfg)
|
|
||||||
|
|
||||||
assert not mock_events["exit_early"], "`exit_early` wasn't properly reset to False"
|
|
||||||
assert len(dataset) == 1, "`dataset` should contain only 1 frame"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"robot_type, mock, num_image_writer_processes", [("koch", True, 0), ("koch", True, 1)]
|
|
||||||
)
|
|
||||||
@require_robot
|
|
||||||
def test_record_with_event_stop_recording(tmp_path, request, robot_type, mock, num_image_writer_processes):
|
|
||||||
robot_kwargs = {"robot_type": robot_type, "mock": mock}
|
|
||||||
|
|
||||||
if mock:
|
|
||||||
request.getfixturevalue("patch_builtins_input")
|
|
||||||
|
|
||||||
# Create an empty calibration directory to trigger manual calibration
|
|
||||||
# and avoid writing calibration files in user .cache/calibration folder
|
|
||||||
calibration_dir = tmp_path / robot_type
|
|
||||||
mock_calibration_dir(calibration_dir)
|
|
||||||
robot_kwargs["calibration_dir"] = calibration_dir
|
|
||||||
else:
|
|
||||||
# Use the default .cache/calibration folder when mock=False
|
|
||||||
pass
|
|
||||||
|
|
||||||
robot = make_robot(**robot_kwargs)
|
|
||||||
|
|
||||||
with patch("lerobot.scripts.control_robot.init_keyboard_listener") as mock_listener:
|
|
||||||
mock_events = {}
|
|
||||||
mock_events["exit_early"] = True
|
|
||||||
mock_events["rerecord_episode"] = False
|
|
||||||
mock_events["stop_recording"] = True
|
|
||||||
mock_listener.return_value = (None, mock_events)
|
|
||||||
|
|
||||||
repo_id = "lerobot/debug"
|
|
||||||
root = tmp_path / "data" / repo_id
|
|
||||||
single_task = "Do something."
|
|
||||||
|
|
||||||
rec_cfg = RecordControlConfig(
|
|
||||||
repo_id=repo_id,
|
|
||||||
root=root,
|
|
||||||
single_task=single_task,
|
|
||||||
fps=1,
|
|
||||||
warmup_time_s=0,
|
|
||||||
episode_time_s=1,
|
|
||||||
reset_time_s=0.1,
|
|
||||||
num_episodes=2,
|
|
||||||
push_to_hub=False,
|
|
||||||
video=False,
|
|
||||||
display_data=False,
|
|
||||||
play_sounds=False,
|
|
||||||
num_image_writer_processes=num_image_writer_processes,
|
|
||||||
)
|
|
||||||
|
|
||||||
dataset = record(robot, rec_cfg)
|
|
||||||
|
|
||||||
assert not mock_events["exit_early"], "`exit_early` wasn't properly reset to False"
|
|
||||||
assert len(dataset) == 1, "`dataset` should contain only 1 frame"
|
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
from lerobot.calibrate import CalibrateConfig, calibrate
|
||||||
|
from lerobot.record import DatasetRecordConfig, RecordConfig, record
|
||||||
|
from lerobot.replay import DatasetReplayConfig, ReplayConfig, replay
|
||||||
|
from lerobot.teleoperate import TeleoperateConfig, teleoperate
|
||||||
|
from tests.fixtures.constants import DUMMY_REPO_ID
|
||||||
|
from tests.mocks.mock_robot import MockRobotConfig
|
||||||
|
from tests.mocks.mock_teleop import MockTeleopConfig
|
||||||
|
|
||||||
|
|
||||||
|
def test_calibrate():
|
||||||
|
robot_cfg = MockRobotConfig()
|
||||||
|
cfg = CalibrateConfig(robot=robot_cfg)
|
||||||
|
calibrate(cfg)
|
||||||
|
|
||||||
|
|
||||||
|
def test_teleoperate():
|
||||||
|
robot_cfg = MockRobotConfig()
|
||||||
|
teleop_cfg = MockTeleopConfig()
|
||||||
|
cfg = TeleoperateConfig(
|
||||||
|
robot=robot_cfg,
|
||||||
|
teleop=teleop_cfg,
|
||||||
|
teleop_time_s=0.1,
|
||||||
|
)
|
||||||
|
teleoperate(cfg)
|
||||||
|
|
||||||
|
|
||||||
|
def test_record_and_resume(tmp_path):
|
||||||
|
robot_cfg = MockRobotConfig()
|
||||||
|
teleop_cfg = MockTeleopConfig()
|
||||||
|
dataset_cfg = DatasetRecordConfig(
|
||||||
|
repo_id=DUMMY_REPO_ID,
|
||||||
|
single_task="Dummy task",
|
||||||
|
root=tmp_path / "record",
|
||||||
|
num_episodes=1,
|
||||||
|
episode_time_s=0.1,
|
||||||
|
reset_time_s=0,
|
||||||
|
push_to_hub=False,
|
||||||
|
)
|
||||||
|
cfg = RecordConfig(
|
||||||
|
robot=robot_cfg,
|
||||||
|
dataset=dataset_cfg,
|
||||||
|
teleop=teleop_cfg,
|
||||||
|
play_sounds=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
dataset = record(cfg)
|
||||||
|
|
||||||
|
assert dataset.fps == 30
|
||||||
|
assert dataset.meta.total_episodes == dataset.num_episodes == 1
|
||||||
|
assert dataset.meta.total_frames == dataset.num_frames == 3
|
||||||
|
assert dataset.meta.total_tasks == 1
|
||||||
|
|
||||||
|
cfg.resume = True
|
||||||
|
dataset = record(cfg)
|
||||||
|
|
||||||
|
assert dataset.meta.total_episodes == dataset.num_episodes == 2
|
||||||
|
assert dataset.meta.total_frames == dataset.num_frames == 6
|
||||||
|
assert dataset.meta.total_tasks == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_record_and_replay(tmp_path):
|
||||||
|
robot_cfg = MockRobotConfig()
|
||||||
|
teleop_cfg = MockTeleopConfig()
|
||||||
|
record_dataset_cfg = DatasetRecordConfig(
|
||||||
|
repo_id=DUMMY_REPO_ID,
|
||||||
|
single_task="Dummy task",
|
||||||
|
root=tmp_path / "record_and_replay",
|
||||||
|
num_episodes=1,
|
||||||
|
episode_time_s=0.1,
|
||||||
|
push_to_hub=False,
|
||||||
|
)
|
||||||
|
record_cfg = RecordConfig(
|
||||||
|
robot=robot_cfg,
|
||||||
|
dataset=record_dataset_cfg,
|
||||||
|
teleop=teleop_cfg,
|
||||||
|
play_sounds=False,
|
||||||
|
)
|
||||||
|
replay_dataset_cfg = DatasetReplayConfig(
|
||||||
|
repo_id=DUMMY_REPO_ID,
|
||||||
|
episode=0,
|
||||||
|
root=tmp_path / "record_and_replay",
|
||||||
|
)
|
||||||
|
replay_cfg = ReplayConfig(
|
||||||
|
robot=robot_cfg,
|
||||||
|
dataset=replay_dataset_cfg,
|
||||||
|
play_sounds=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
record(record_cfg)
|
||||||
|
replay(replay_cfg)
|
||||||