diff --git a/media/tutorial/img1.jpg b/media/tutorial/img1.jpg deleted file mode 100644 index c16fbf5e9..000000000 Binary files a/media/tutorial/img1.jpg and /dev/null differ diff --git a/media/tutorial/img10.jpg b/media/tutorial/img10.jpg deleted file mode 100644 index 925ed9182..000000000 Binary files a/media/tutorial/img10.jpg and /dev/null differ diff --git a/media/tutorial/img11.jpg b/media/tutorial/img11.jpg deleted file mode 100644 index ac0403123..000000000 Binary files a/media/tutorial/img11.jpg and /dev/null differ diff --git a/media/tutorial/img12.jpg b/media/tutorial/img12.jpg deleted file mode 100644 index dd500afba..000000000 Binary files a/media/tutorial/img12.jpg and /dev/null differ diff --git a/media/tutorial/img13.jpg b/media/tutorial/img13.jpg deleted file mode 100644 index 99c6836f7..000000000 Binary files a/media/tutorial/img13.jpg and /dev/null differ diff --git a/media/tutorial/img14.jpg b/media/tutorial/img14.jpg deleted file mode 100644 index 8b544e247..000000000 Binary files a/media/tutorial/img14.jpg and /dev/null differ diff --git a/media/tutorial/img15.jpg b/media/tutorial/img15.jpg deleted file mode 100644 index 76cee5521..000000000 Binary files a/media/tutorial/img15.jpg and /dev/null differ diff --git a/media/tutorial/img16.jpg b/media/tutorial/img16.jpg deleted file mode 100644 index 363b729da..000000000 Binary files a/media/tutorial/img16.jpg and /dev/null differ diff --git a/media/tutorial/img17.jpg b/media/tutorial/img17.jpg deleted file mode 100644 index dc211bc63..000000000 Binary files a/media/tutorial/img17.jpg and /dev/null differ diff --git a/media/tutorial/img18.jpg b/media/tutorial/img18.jpg deleted file mode 100644 index c9732b65c..000000000 Binary files a/media/tutorial/img18.jpg and /dev/null differ diff --git a/media/tutorial/img19.jpg b/media/tutorial/img19.jpg deleted file mode 100644 index 25c5f0e38..000000000 Binary files a/media/tutorial/img19.jpg and /dev/null differ diff --git a/media/tutorial/img2.jpg b/media/tutorial/img2.jpg deleted file mode 100644 index 47d3671c9..000000000 Binary files a/media/tutorial/img2.jpg and /dev/null differ diff --git a/media/tutorial/img20.jpg b/media/tutorial/img20.jpg deleted file mode 100644 index effe9c96d..000000000 Binary files a/media/tutorial/img20.jpg and /dev/null differ diff --git a/media/tutorial/img21.jpg b/media/tutorial/img21.jpg deleted file mode 100644 index 0acc51945..000000000 Binary files a/media/tutorial/img21.jpg and /dev/null differ diff --git a/media/tutorial/img22.jpg b/media/tutorial/img22.jpg deleted file mode 100644 index 3f223a8b3..000000000 Binary files a/media/tutorial/img22.jpg and /dev/null differ diff --git a/media/tutorial/img23.jpg b/media/tutorial/img23.jpg deleted file mode 100644 index b9c411f4a..000000000 Binary files a/media/tutorial/img23.jpg and /dev/null differ diff --git a/media/tutorial/img24.jpg b/media/tutorial/img24.jpg deleted file mode 100644 index 4011d190d..000000000 Binary files a/media/tutorial/img24.jpg and /dev/null differ diff --git a/media/tutorial/img25.jpg b/media/tutorial/img25.jpg deleted file mode 100644 index 727dbadf2..000000000 Binary files a/media/tutorial/img25.jpg and /dev/null differ diff --git a/media/tutorial/img26.jpg b/media/tutorial/img26.jpg deleted file mode 100644 index ae38e9802..000000000 Binary files a/media/tutorial/img26.jpg and /dev/null differ diff --git a/media/tutorial/img27.jpg b/media/tutorial/img27.jpg deleted file mode 100644 index 628b8a84b..000000000 Binary files a/media/tutorial/img27.jpg and /dev/null differ diff --git a/media/tutorial/img28.jpg b/media/tutorial/img28.jpg deleted file mode 100644 index e9a7fb5de..000000000 Binary files a/media/tutorial/img28.jpg and /dev/null differ diff --git a/media/tutorial/img29.jpg b/media/tutorial/img29.jpg deleted file mode 100644 index 78210b295..000000000 Binary files a/media/tutorial/img29.jpg and /dev/null differ diff --git a/media/tutorial/img3.jpg b/media/tutorial/img3.jpg deleted file mode 100644 index bf9b7bca8..000000000 Binary files a/media/tutorial/img3.jpg and /dev/null differ diff --git a/media/tutorial/img30.jpg b/media/tutorial/img30.jpg deleted file mode 100644 index 0fa59bba1..000000000 Binary files a/media/tutorial/img30.jpg and /dev/null differ diff --git a/media/tutorial/img31.jpg b/media/tutorial/img31.jpg deleted file mode 100644 index 1409d2f01..000000000 Binary files a/media/tutorial/img31.jpg and /dev/null differ diff --git a/media/tutorial/img32.jpg b/media/tutorial/img32.jpg deleted file mode 100644 index 74dbee5fd..000000000 Binary files a/media/tutorial/img32.jpg and /dev/null differ diff --git a/media/tutorial/img4.jpg b/media/tutorial/img4.jpg deleted file mode 100644 index 9d155c165..000000000 Binary files a/media/tutorial/img4.jpg and /dev/null differ diff --git a/media/tutorial/img5.jpg b/media/tutorial/img5.jpg deleted file mode 100644 index afd3c4284..000000000 Binary files a/media/tutorial/img5.jpg and /dev/null differ diff --git a/media/tutorial/img6.jpg b/media/tutorial/img6.jpg deleted file mode 100644 index c669d37d9..000000000 Binary files a/media/tutorial/img6.jpg and /dev/null differ diff --git a/media/tutorial/img7.jpg b/media/tutorial/img7.jpg deleted file mode 100644 index 3f6c3501b..000000000 Binary files a/media/tutorial/img7.jpg and /dev/null differ diff --git a/media/tutorial/img8.jpg b/media/tutorial/img8.jpg deleted file mode 100644 index 009102cb4..000000000 Binary files a/media/tutorial/img8.jpg and /dev/null differ diff --git a/media/tutorial/img9.jpg b/media/tutorial/img9.jpg deleted file mode 100644 index 06a4f58eb..000000000 Binary files a/media/tutorial/img9.jpg and /dev/null differ diff --git a/tests/artifacts/image_transforms/default_transforms.safetensors b/tests/artifacts/image_transforms/default_transforms.safetensors index 43e6a71a2..2c08499f6 100644 --- a/tests/artifacts/image_transforms/default_transforms.safetensors +++ b/tests/artifacts/image_transforms/default_transforms.safetensors @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0389a716d51c1c615fb2a3bfa386d89f00b0deca08c4fa21b23e020a939d0213 +oid sha256:6b1e600768a8771c5fe650e038a1193597e3810f032041b2a0d021e4496381c1 size 3686488 diff --git a/tests/artifacts/image_transforms/save_image_transforms_to_safetensors.py b/tests/artifacts/image_transforms/save_image_transforms_to_safetensors.py index 7b037af47..be47d9a47 100644 --- a/tests/artifacts/image_transforms/save_image_transforms_to_safetensors.py +++ b/tests/artifacts/image_transforms/save_image_transforms_to_safetensors.py @@ -28,7 +28,7 @@ from lerobot.common.datasets.transforms import ( from lerobot.common.utils.random_utils import seeded_context 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): diff --git a/tests/artifacts/image_transforms/single_transforms.safetensors b/tests/artifacts/image_transforms/single_transforms.safetensors index 3c451db57..7a0599d9e 100644 --- a/tests/artifacts/image_transforms/single_transforms.safetensors +++ b/tests/artifacts/image_transforms/single_transforms.safetensors @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0dc691503e7d90b2086bb408e89a65f772ce5ee6e3562ef8c127bcb09bd90851 +oid sha256:9d4ebab73eabddc58879a4e770289d19e00a1a4cf2fa5fa33cd3a3246992bc90 size 40551392 diff --git a/tests/artifacts/policies/aloha_sim_insertion_human_act_/actions.safetensors b/tests/artifacts/policies/aloha_sim_insertion_human_act_/actions.safetensors index 6fec6b228..8bd63e894 100644 --- a/tests/artifacts/policies/aloha_sim_insertion_human_act_/actions.safetensors +++ b/tests/artifacts/policies/aloha_sim_insertion_human_act_/actions.safetensors @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc67af1d60f95d84c98d6c9ebd648990e0f0705368bd6b72d2b39533950b0179 +oid sha256:f3e4c8e85e146b043fd4e4984947c2a6f01627f174a19f18b5914cf690579d77 size 5104 diff --git a/tests/artifacts/policies/aloha_sim_insertion_human_act_/grad_stats.safetensors b/tests/artifacts/policies/aloha_sim_insertion_human_act_/grad_stats.safetensors index 7136a69f0..5209ae6ab 100644 --- a/tests/artifacts/policies/aloha_sim_insertion_human_act_/grad_stats.safetensors +++ b/tests/artifacts/policies/aloha_sim_insertion_human_act_/grad_stats.safetensors @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64518cf652105d15f5fd2cfc13d0681f66a4ec4797dc5d5dc2f7b0d91fe5dfd6 +oid sha256:1a7a8b1a457149109f843c32bcbb047d09de2201847b9b79f7501b447f77ecf4 size 31672 diff --git a/tests/artifacts/policies/aloha_sim_insertion_human_act_/output_dict.safetensors b/tests/artifacts/policies/aloha_sim_insertion_human_act_/output_dict.safetensors index 864feebe8..736aff94f 100644 --- a/tests/artifacts/policies/aloha_sim_insertion_human_act_/output_dict.safetensors +++ b/tests/artifacts/policies/aloha_sim_insertion_human_act_/output_dict.safetensors @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:32b6d14fab4244b5140adb345e47f662b6739c04974e04b21c3127caa988abbb +oid sha256:5e6ce85296b2009e7c2060d336c0429b1c7197d9adb159e7df0ba18003067b36 size 68 diff --git a/tests/artifacts/policies/aloha_sim_insertion_human_act_/param_stats.safetensors b/tests/artifacts/policies/aloha_sim_insertion_human_act_/param_stats.safetensors index bbabade64..724d22b58 100644 --- a/tests/artifacts/policies/aloha_sim_insertion_human_act_/param_stats.safetensors +++ b/tests/artifacts/policies/aloha_sim_insertion_human_act_/param_stats.safetensors @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1904ef0338f7b6efdec70ec235ee931b5751008bf4eb433edb0b3fa0838a4f1 +oid sha256:9b5f557e30aead3731c38cbd85af8c706395d8689a918ad88805b5a886245603 size 33400 diff --git a/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/actions.safetensors b/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/actions.safetensors index 1093b45d5..6d912d81a 100644 --- a/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/actions.safetensors +++ b/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/actions.safetensors @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa544a97f00bf46393a09b006b44c2499bbf7d177782360a8c21cacbf200c07a +oid sha256:2e6625cabfeb4800abc80252cf9112a9271c154edd01eb291658f143c951610b size 515400 diff --git a/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/grad_stats.safetensors b/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/grad_stats.safetensors index 092e0040c..c58bb44bc 100644 --- a/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/grad_stats.safetensors +++ b/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/grad_stats.safetensors @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:83c7a8ae912300b5cedba31904f7ba22542059fd60dd86548a95e415713f719e +oid sha256:224b5fa4828aa88171b68c036e8919c1eae563e2113f03b6461eadf5bf8525a6 size 31672 diff --git a/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/output_dict.safetensors b/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/output_dict.safetensors index 6561116c5..9b6ef7f5d 100644 --- a/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/output_dict.safetensors +++ b/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/output_dict.safetensors @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5a010633237b3a1141603c65174c551daa9e7b4c474af5a1376d73e5425bfb5d +oid sha256:016d2fa8fe5f58017dfd46f4632fdc19dfd751e32a2c7cde2077c6f95546d6bd size 68 diff --git a/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/param_stats.safetensors b/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/param_stats.safetensors index 09772ea32..cc6b4a24b 100644 --- a/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/param_stats.safetensors +++ b/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/param_stats.safetensors @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec8b5c440e9fcec190c9be48b28ebb79f82ae63626afe7c811e4bb0c3dd08842 +oid sha256:021562ee3e4814425e367ed0c144d6fbe2eb28838247085716cf0b58fd69a075 size 33400 diff --git a/tests/robots/test_control_robot.py b/tests/robots/test_control_robot.py deleted file mode 100644 index 3f618fc27..000000000 --- a/tests/robots/test_control_robot.py +++ /dev/null @@ -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 "", line 1, in - # 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" diff --git a/tests/test_control_robot.py b/tests/test_control_robot.py new file mode 100644 index 000000000..2ec6a2905 --- /dev/null +++ b/tests/test_control_robot.py @@ -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)