diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..70180795 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,142 @@ +# Misc +.git +tmp +wandb +data +outputs +.vscode +rl +media + + +# Logging +logs + +# HPC +nautilus/*.yaml +*.key + +# Slurm +sbatch*.sh + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +!tests/data +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5084567b..a9b733d4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,12 +1,29 @@ # What does this PR do? -Example: Fixes # (issue) +Examples: +- Fixes # (issue) +- Adds new dataset +- Optimizes something +## How was it tested? + +Examples: +- Added `test_something` in `tests/test_stuff.py`. +- Added `new_feature` and checked that training converges with policy X on dataset/environment Y. +- Optimized `some_function`, it now runs X times faster than previously. + +## How to checkout & try? (for the reviewer) + +Examples: +```bash +DATA_DIR=tests/data pytest -sx tests/test_stuff.py::test_something +``` +```bash +python lerobot/scripts/train.py --some.option=true +``` ## Before submitting -- Read the [contributor guideline](https://github.com/huggingface/lerobot/blob/main/CONTRIBUTING.md#submitting-a-pull-request-pr). -- Provide a minimal code example for the reviewer to checkout & try. -- Explain how you tested your changes. +Please read the [contributor guideline](https://github.com/huggingface/lerobot/blob/main/CONTRIBUTING.md#submitting-a-pull-request-pr). ## Who can review? diff --git a/.github/poetry/cpu/poetry.lock b/.github/poetry/cpu_DEPRECATED/poetry.lock similarity index 100% rename from .github/poetry/cpu/poetry.lock rename to .github/poetry/cpu_DEPRECATED/poetry.lock diff --git a/.github/poetry/cpu/pyproject.toml b/.github/poetry/cpu_DEPRECATED/pyproject.toml similarity index 100% rename from .github/poetry/cpu/pyproject.toml rename to .github/poetry/cpu_DEPRECATED/pyproject.toml diff --git a/.github/scripts/dep_build.py b/.github/scripts/dep_build.py new file mode 100644 index 00000000..5810bb0c --- /dev/null +++ b/.github/scripts/dep_build.py @@ -0,0 +1,30 @@ +PYPROJECT = "pyproject.toml" +DEPS = { + "gym-pusht": '{ git = "git@github.com:huggingface/gym-pusht.git", optional = true}', + "gym-xarm": '{ git = "git@github.com:huggingface/gym-xarm.git", optional = true}', + "gym-aloha": '{ git = "git@github.com:huggingface/gym-aloha.git", optional = true}', +} + + +def update_envs_as_path_dependencies(): + with open(PYPROJECT) as file: + lines = file.readlines() + + new_lines = [] + for line in lines: + if any(dep in line for dep in DEPS.values()): + for dep in DEPS: + if dep in line: + new_line = f'{dep} = {{ path = "envs/{dep}/", optional = true}}\n' + new_lines.append(new_line) + break + + else: + new_lines.append(line) + + with open(PYPROJECT, "w") as file: + file.writelines(new_lines) + + +if __name__ == "__main__": + update_envs_as_path_dependencies() diff --git a/.github/workflows/build-docker-images.yml b/.github/workflows/build-docker-images.yml new file mode 100644 index 00000000..90b28bc9 --- /dev/null +++ b/.github/workflows/build-docker-images.yml @@ -0,0 +1,207 @@ +# Inspired by +# https://github.com/huggingface/peft/blob/main/.github/workflows/build_docker_images.yml +name: Nightly Builds + +on: + workflow_dispatch: + workflow_call: + schedule: + - cron: "0 1 * * *" + +# concurrency: +# group: docker-image-builds +# cancel-in-progress: false + +env: + PYTHON_VERSION: "3.10" +# CI_SLACK_CHANNEL: ${{ secrets.CI_DOCKER_CHANNEL }} + +jobs: + latest-cpu: + name: "Build CPU" + runs-on: ubuntu-latest + steps: + - name: Cleanup disk + run: | + sudo df -h + # sudo ls -l /usr/local/lib/ + # sudo ls -l /usr/share/ + sudo du -sh /usr/local/lib/ + sudo du -sh /usr/share/ + sudo rm -rf /usr/local/lib/android + sudo rm -rf /usr/share/dotnet + sudo du -sh /usr/local/lib/ + sudo du -sh /usr/share/ + sudo df -h + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Check out code + uses: actions/checkout@v4 + + - name: Checkout gym-aloha + uses: actions/checkout@v4 + with: + repository: huggingface/gym-aloha + path: envs/gym-aloha + ssh-key: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Checkout gym-xarm + uses: actions/checkout@v4 + with: + repository: huggingface/gym-xarm + path: envs/gym-xarm + ssh-key: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Checkout gym-pusht + uses: actions/checkout@v4 + with: + repository: huggingface/gym-pusht + path: envs/gym-pusht + ssh-key: ${{ secrets.SSH_PRIVATE_KEY }} + + # HACK(aliberts): to be removed for release + # ----------------------------------------- + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Change envs dependencies as local path + run: python .github/scripts/dep_build.py + # ----------------------------------------- + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Build and Push CPU + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/lerobot-cpu/Dockerfile + push: true + tags: huggingface/lerobot-cpu + build-args: PYTHON_VERSION=${{ env.PYTHON_VERSION }} + + # - name: Post to a Slack channel + # id: slack + # #uses: slackapi/slack-github-action@v1.25.0 + # uses: slackapi/slack-github-action@6c661ce58804a1a20f6dc5fbee7f0381b469e001 + # with: + # # Slack channel id, channel name, or user id to post message. + # # See also: https://api.slack.com/methods/chat.postMessage#channels + # channel-id: ${{ env.CI_SLACK_CHANNEL }} + # # For posting a rich message using Block Kit + # payload: | + # { + # "text": "lerobot-cpu Docker Image build result: ${{ job.status }}\n${{ github.event.pull_request.html_url || github.event.head_commit.url }}", + # "blocks": [ + # { + # "type": "section", + # "text": { + # "type": "mrkdwn", + # "text": "lerobot-cpu Docker Image build result: ${{ job.status }}\n${{ github.event.pull_request.html_url || github.event.head_commit.url }}" + # } + # } + # ] + # } + # env: + # SLACK_BOT_TOKEN: ${{ secrets.SLACK_CIFEEDBACK_BOT_TOKEN }} + + latest-cuda: + name: "Build GPU" + runs-on: ubuntu-latest + steps: + - name: Cleanup disk + run: | + sudo df -h + # sudo ls -l /usr/local/lib/ + # sudo ls -l /usr/share/ + sudo du -sh /usr/local/lib/ + sudo du -sh /usr/share/ + sudo rm -rf /usr/local/lib/android + sudo rm -rf /usr/share/dotnet + sudo du -sh /usr/local/lib/ + sudo du -sh /usr/share/ + sudo df -h + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Check out code + uses: actions/checkout@v4 + + - name: Checkout gym-aloha + uses: actions/checkout@v4 + with: + repository: huggingface/gym-aloha + path: envs/gym-aloha + ssh-key: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Checkout gym-xarm + uses: actions/checkout@v4 + with: + repository: huggingface/gym-xarm + path: envs/gym-xarm + ssh-key: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Checkout gym-pusht + uses: actions/checkout@v4 + with: + repository: huggingface/gym-pusht + path: envs/gym-pusht + ssh-key: ${{ secrets.SSH_PRIVATE_KEY }} + + # HACK(aliberts): to be removed for release + # ----------------------------------------- + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Change envs dependencies as local path + run: python .github/scripts/dep_build.py + # ----------------------------------------- + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Build and Push GPU + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/lerobot-gpu/Dockerfile + push: true + tags: huggingface/lerobot-gpu + build-args: PYTHON_VERSION=${{ env.PYTHON_VERSION }} + + # - name: Post to a Slack channel + # id: slack + # #uses: slackapi/slack-github-action@v1.25.0 + # uses: slackapi/slack-github-action@6c661ce58804a1a20f6dc5fbee7f0381b469e001 + # with: + # # Slack channel id, channel name, or user id to post message. + # # See also: https://api.slack.com/methods/chat.postMessage#channels + # channel-id: ${{ env.CI_SLACK_CHANNEL }} + # # For posting a rich message using Block Kit + # payload: | + # { + # "text": "lerobot-gpu Docker Image build result: ${{ job.status }}\n${{ github.event.pull_request.html_url || github.event.head_commit.url }}", + # "blocks": [ + # { + # "type": "section", + # "text": { + # "type": "mrkdwn", + # "text": "lerobot-gpu Docker Image build result: ${{ job.status }}\n${{ github.event.pull_request.html_url || github.event.head_commit.url }}" + # } + # } + # ] + # } + # env: + # SLACK_BOT_TOKEN: ${{ secrets.SLACK_CIFEEDBACK_BOT_TOKEN }} diff --git a/.github/workflows/nightly-tests.yml b/.github/workflows/nightly-tests.yml new file mode 100644 index 00000000..63511d16 --- /dev/null +++ b/.github/workflows/nightly-tests.yml @@ -0,0 +1,85 @@ +# Inspired by +# https://github.com/huggingface/peft/blob/main/.github/workflows/nightly.yml +name: Nightly Tests + +on: + workflow_dispatch: + schedule: + - cron: "0 2 * * *" + +env: + DATA_DIR: tests/data + # SLACK_API_TOKEN: ${{ secrets.SLACK_API_TOKEN }} + + +jobs: + run_all_tests_cpu: + name: "Test CPU" + strategy: + fail-fast: false + runs-on: ubuntu-latest + container: + image: huggingface/lerobot-cpu:latest + options: --shm-size "16gb" + credentials: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + defaults: + run: + shell: bash + working-directory: /lerobot + steps: + - name: Tests + env: + DATA_DIR: tests/data + run: pytest -v --cov=./lerobot --disable-warnings tests + - name: Tests end-to-end + env: + DATA_DIR: tests/data + run: make test-end-to-end + + run_all_tests_single_gpu: + name: "Test GPU" + strategy: + fail-fast: false + runs-on: [single-gpu, nvidia-gpu, t4, ci] + env: + CUDA_VISIBLE_DEVICES: "0" + TEST_TYPE: "single_gpu" + container: + image: huggingface/lerobot-gpu:latest + options: --gpus all --shm-size "16gb" + credentials: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + defaults: + run: + shell: bash + working-directory: /lerobot + steps: + - name: Nvidia-smi + run: nvidia-smi + - name: Test + run: pytest -v --cov=./lerobot --cov-report=xml --disable-warnings tests + # TODO(aliberts): Link with HF Codecov account + # - name: Upload coverage reports to Codecov with GitHub Action + # uses: codecov/codecov-action@v4 + # with: + # files: ./coverage.xml + # verbose: true + - name: Tests end-to-end + run: make test-end-to-end + - name: Tailscale Wait + if: ${{ failure() || runner.debug == '1' }} + uses: huggingface/tailscale-action@v1 + with: + waitForSSH: true + authkey: ${{ secrets.TAILSCALE_SSH_AUTHKEY }} + slackChannel: ${{ secrets.SLACK_CIFEEDBACK_CHANNEL }} + slackToken: ${{ secrets.SLACK_CIFEEDBACK_BOT_TOKEN }} + + # - name: Generate Report + # if: always() + # run: | + # pip install slack_sdk tabulate + # python scripts/log_reports.py >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/test-poetry_DEPRECATED.yml b/.github/workflows/test-poetry_DEPRECATED.yml new file mode 100644 index 00000000..e8153b0b --- /dev/null +++ b/.github/workflows/test-poetry_DEPRECATED.yml @@ -0,0 +1,120 @@ +# name: Tests poetry + +# on: +# pull_request: +# branches: +# - main +# push: +# branches: +# - main + +# jobs: +# tests: +# runs-on: ubuntu-latest +# env: +# POETRY_VERSION: 1.8.2 +# DATA_DIR: tests/data +# MUJOCO_GL: egl +# steps: +# #---------------------------------------------- +# # check-out repo and set-up python +# #---------------------------------------------- +# - name: Check out repository +# uses: actions/checkout@v4 +# with: +# lfs: true + +# - name: Set up python +# id: setup-python +# uses: actions/setup-python@v5 +# with: +# python-version: '3.10' + +# - name: Add SSH key for installing envs +# uses: webfactory/ssh-agent@v0.9.0 +# with: +# ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + +# #---------------------------------------------- +# # install & configure poetry +# #---------------------------------------------- +# - name: Load cached Poetry installation +# id: restore-poetry-cache +# uses: actions/cache/restore@v3 +# with: +# path: ~/.local +# key: poetry-${{ env.POETRY_VERSION }} + +# - name: Install Poetry +# if: steps.restore-poetry-cache.outputs.cache-hit != 'true' +# uses: snok/install-poetry@v1 +# with: +# version: ${{ env.POETRY_VERSION }} +# virtualenvs-create: true +# installer-parallel: true + +# - name: Save cached Poetry installation +# if: | +# steps.restore-poetry-cache.outputs.cache-hit != 'true' && +# github.ref_name == 'main' +# id: save-poetry-cache +# uses: actions/cache/save@v3 +# with: +# path: ~/.local +# key: poetry-${{ env.POETRY_VERSION }} + +# - name: Configure Poetry +# run: poetry config virtualenvs.in-project true + +# #---------------------------------------------- +# # install dependencies +# #---------------------------------------------- +# # TODO(aliberts): move to gpu runners +# - name: Select cpu dependencies # HACK +# run: cp -t . .github/poetry/cpu/pyproject.toml .github/poetry/cpu/poetry.lock + +# - name: Load cached venv +# id: restore-dependencies-cache +# uses: actions/cache/restore@v3 +# with: +# path: .venv +# key: venv-${{ steps.setup-python.outputs.python-version }}-${{ env.POETRY_VERSION }}-${{ hashFiles('**/poetry.lock') }} + +# - name: Install dependencies +# if: steps.restore-dependencies-cache.outputs.cache-hit != 'true' +# run: poetry install --no-interaction --no-root --all-extras + +# - name: Save cached venv +# if: | +# steps.restore-dependencies-cache.outputs.cache-hit != 'true' && +# github.ref_name == 'main' +# id: save-dependencies-cache +# uses: actions/cache/save@v3 +# with: +# path: .venv +# key: venv-${{ steps.setup-python.outputs.python-version }}-${{ env.POETRY_VERSION }}-${{ hashFiles('**/poetry.lock') }} + +# - name: Install EGL +# run: sudo apt-get update && sudo apt-get install -y libegl1-mesa-dev + +# #---------------------------------------------- +# # install project +# #---------------------------------------------- +# - name: Install project +# run: poetry install --no-interaction --all-extras + +# #---------------------------------------------- +# # run tests & coverage +# #---------------------------------------------- +# - name: Run tests +# run: | +# source .venv/bin/activate +# pytest -v --cov=./lerobot tests + +# #---------------------------------------------- +# # run end-to-end tests +# #---------------------------------------------- +# - name: Tests end-to-end +# run: | +# source .venv/bin/activate +# make test-end-to-end diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e56ceeaf..06b92f1e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,210 +4,43 @@ on: pull_request: branches: - main - types: [opened, synchronize, reopened, labeled] push: branches: - main jobs: tests: - if: | - ${{ github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'CI') }} || - ${{ github.event_name == 'push' }} runs-on: ubuntu-latest env: - POETRY_VERSION: 1.8.2 DATA_DIR: tests/data MUJOCO_GL: egl steps: - #---------------------------------------------- - # check-out repo and set-up python - #---------------------------------------------- - - name: Check out repository - uses: actions/checkout@v4 - with: - lfs: true - - - name: Set up python - id: setup-python - uses: actions/setup-python@v5 - with: - python-version: '3.10' - - name: Add SSH key for installing envs uses: webfactory/ssh-agent@v0.9.0 with: ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} - #---------------------------------------------- - # install & configure poetry - #---------------------------------------------- - - name: Load cached Poetry installation - id: restore-poetry-cache - uses: actions/cache/restore@v3 + - uses: actions/checkout@v4 with: - path: ~/.local - key: poetry-${{ env.POETRY_VERSION }} + lfs: true - - name: Install Poetry - if: steps.restore-poetry-cache.outputs.cache-hit != 'true' - uses: snok/install-poetry@v1 + - name: Set up Python 3.10 + uses: actions/setup-python@v5 with: - version: ${{ env.POETRY_VERSION }} - virtualenvs-create: true - installer-parallel: true + python-version: "3.10" + cache: ${{ (github.ref == 'refs/heads/main') && 'pip' || '' }} + cache-dependency-path: pyproject.toml - - name: Save cached Poetry installation - if: | - steps.restore-poetry-cache.outputs.cache-hit != 'true' && - github.ref_name == 'main' - id: save-poetry-cache - uses: actions/cache/save@v3 - with: - path: ~/.local - key: poetry-${{ env.POETRY_VERSION }} - - - name: Configure Poetry - run: poetry config virtualenvs.in-project true - - #---------------------------------------------- - # install dependencies - #---------------------------------------------- - # TODO(aliberts): move to gpu runners - - name: Select cpu dependencies # HACK - run: cp -t . .github/poetry/cpu/pyproject.toml .github/poetry/cpu/poetry.lock - - - name: Load cached venv - id: restore-dependencies-cache - uses: actions/cache/restore@v3 - with: - path: .venv - key: venv-${{ steps.setup-python.outputs.python-version }}-${{ env.POETRY_VERSION }}-${{ hashFiles('**/poetry.lock') }} - - - name: Install dependencies - if: steps.restore-dependencies-cache.outputs.cache-hit != 'true' - env: - TMPDIR: ~/tmp - TEMP: ~/tmp - TMP: ~/tmp - run: | - mkdir ~/tmp - poetry install --no-interaction --no-root --all-extras - - - name: Save cached venv - if: | - steps.restore-dependencies-cache.outputs.cache-hit != 'true' && - github.ref_name == 'main' - id: save-dependencies-cache - uses: actions/cache/save@v3 - with: - path: .venv - key: venv-${{ steps.setup-python.outputs.python-version }}-${{ env.POETRY_VERSION }}-${{ hashFiles('**/poetry.lock') }} - - - name: Install libegl1-mesa-dev (to use MUJOCO_GL=egl) + - name: Install EGL run: sudo apt-get update && sudo apt-get install -y libegl1-mesa-dev - #---------------------------------------------- - # install project - #---------------------------------------------- - - name: Install project - run: poetry install --no-interaction --all-extras - - #---------------------------------------------- - # run tests & coverage - #---------------------------------------------- - - name: Run tests + - name: Install pip dependencies run: | - source .venv/bin/activate - pytest -v --cov=./lerobot --cov-report=xml tests + python -m pip install --upgrade pip + pip install -e ".[test, aloha, xarm, pusht]" - # TODO(aliberts): Link with HF Codecov account - # - name: Upload coverage reports to Codecov with GitHub Action - # uses: codecov/codecov-action@v4 - # with: - # files: ./coverage.xml - # verbose: true + - name: Test with pytest + run: pytest -v --cov=./lerobot tests - #---------------------------------------------- - # run end-to-end tests - #---------------------------------------------- - - name: Test train ACT on ALOHA end-to-end - run: | - source .venv/bin/activate - python lerobot/scripts/train.py \ - policy=act \ - env=aloha \ - wandb.enable=False \ - offline_steps=2 \ - online_steps=0 \ - eval_episodes=1 \ - device=cpu \ - save_model=true \ - save_freq=2 \ - policy.n_action_steps=20 \ - policy.chunk_size=20 \ - policy.batch_size=2 \ - hydra.run.dir=tests/outputs/act/ - - - name: Test eval ACT on ALOHA end-to-end - run: | - source .venv/bin/activate - python lerobot/scripts/eval.py \ - --config tests/outputs/act/.hydra/config.yaml \ - eval_episodes=1 \ - env.episode_length=8 \ - device=cpu \ - policy.pretrained_model_path=tests/outputs/act/models/2.pt - - - name: Test train Diffusion on PushT end-to-end - run: | - source .venv/bin/activate - python lerobot/scripts/train.py \ - policy=diffusion \ - env=pusht \ - wandb.enable=False \ - offline_steps=2 \ - online_steps=0 \ - eval_episodes=1 \ - device=cpu \ - save_model=true \ - save_freq=2 \ - policy.batch_size=2 \ - hydra.run.dir=tests/outputs/diffusion/ - - - name: Test eval Diffusion on PushT end-to-end - run: | - source .venv/bin/activate - python lerobot/scripts/eval.py \ - --config tests/outputs/diffusion/.hydra/config.yaml \ - eval_episodes=1 \ - env.episode_length=8 \ - device=cpu \ - policy.pretrained_model_path=tests/outputs/diffusion/models/2.pt - - - name: Test train TDMPC on Simxarm end-to-end - run: | - source .venv/bin/activate - python lerobot/scripts/train.py \ - policy=tdmpc \ - env=xarm \ - wandb.enable=False \ - offline_steps=1 \ - online_steps=2 \ - eval_episodes=1 \ - env.episode_length=2 \ - device=cpu \ - save_model=true \ - save_freq=2 \ - policy.batch_size=2 \ - hydra.run.dir=tests/outputs/tdmpc/ - - - name: Test eval TDMPC on Simxarm end-to-end - run: | - source .venv/bin/activate - python lerobot/scripts/eval.py \ - --config tests/outputs/tdmpc/.hydra/config.yaml \ - eval_episodes=1 \ - env.episode_length=8 \ - device=cpu \ - policy.pretrained_model_path=tests/outputs/tdmpc/models/2.pt + - name: Test end-to-end + run: make test-end-to-end diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..cb53172c --- /dev/null +++ b/Makefile @@ -0,0 +1,84 @@ +.PHONY: tests + +build-cpu: + docker build -t lerobot:latest -f docker/lerobot-cpu/Dockerfile . + +build-gpu: + docker build -t lerobot:latest -f docker/lerobot-gpu/Dockerfile . + +test-end-to-end: + ${MAKE} test-act-ete-train + ${MAKE} test-act-ete-eval + ${MAKE} test-diffusion-ete-train + ${MAKE} test-diffusion-ete-eval + ${MAKE} test-tdmpc-ete-train + ${MAKE} test-tdmpc-ete-eval + +test-act-ete-train: + python lerobot/scripts/train.py \ + policy=act \ + env=aloha \ + wandb.enable=False \ + offline_steps=2 \ + online_steps=0 \ + eval_episodes=1 \ + device=cpu \ + save_model=true \ + save_freq=2 \ + policy.n_action_steps=20 \ + policy.chunk_size=20 \ + policy.batch_size=2 \ + hydra.run.dir=tests/outputs/act/ + +test-act-ete-eval: + python lerobot/scripts/eval.py \ + --config tests/outputs/act/.hydra/config.yaml \ + eval_episodes=1 \ + env.episode_length=8 \ + device=cpu \ + policy.pretrained_model_path=tests/outputs/act/models/2.pt + +test-diffusion-ete-train: + python lerobot/scripts/train.py \ + policy=diffusion \ + env=pusht \ + wandb.enable=False \ + offline_steps=2 \ + online_steps=0 \ + eval_episodes=1 \ + device=cpu \ + save_model=true \ + save_freq=2 \ + policy.batch_size=2 \ + hydra.run.dir=tests/outputs/diffusion/ + +test-diffusion-ete-eval: + python lerobot/scripts/eval.py \ + --config tests/outputs/diffusion/.hydra/config.yaml \ + eval_episodes=1 \ + env.episode_length=8 \ + device=cpu \ + policy.pretrained_model_path=tests/outputs/diffusion/models/2.pt + +test-tdmpc-ete-train: + python lerobot/scripts/train.py \ + policy=tdmpc \ + env=xarm \ + wandb.enable=False \ + offline_steps=1 \ + online_steps=2 \ + eval_episodes=1 \ + env.episode_length=2 \ + device=cpu \ + save_model=true \ + save_freq=2 \ + policy.batch_size=2 \ + hydra.run.dir=tests/outputs/tdmpc/ + +test-tdmpc-ete-eval: + python lerobot/scripts/eval.py \ + --config tests/outputs/tdmpc/.hydra/config.yaml \ + eval_episodes=1 \ + env.episode_length=8 \ + device=cpu \ + policy.pretrained_model_path=tests/outputs/tdmpc/models/2.pt diff --git a/docker/lerobot-cpu/Dockerfile b/docker/lerobot-cpu/Dockerfile new file mode 100644 index 00000000..de3abaf5 --- /dev/null +++ b/docker/lerobot-cpu/Dockerfile @@ -0,0 +1,31 @@ +# Configure image +ARG PYTHON_VERSION=3.10 + +FROM python:${PYTHON_VERSION}-slim +ARG PYTHON_VERSION +ARG DEBIAN_FRONTEND=noninteractive + +# Install apt dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential cmake \ + libglib2.0-0 libgl1-mesa-glx libegl1-mesa \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Create virtual environment +RUN ln -s /usr/bin/python${PYTHON_VERSION} /usr/bin/python +RUN python -m venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" +RUN echo "source /opt/venv/bin/activate" >> /root/.bashrc + +# Install LeRobot +COPY . /lerobot +WORKDIR /lerobot +RUN pip install --upgrade --no-cache-dir pip +RUN pip install --no-cache-dir ".[test, aloha, xarm, pusht]" \ + --extra-index-url https://download.pytorch.org/whl/cpu + +# Set EGL as the rendering backend for MuJoCo +ENV MUJOCO_GL="egl" + +# Execute in bash shell rather than python +CMD ["/bin/bash"] diff --git a/docker/lerobot-gpu/Dockerfile b/docker/lerobot-gpu/Dockerfile new file mode 100644 index 00000000..2c36b484 --- /dev/null +++ b/docker/lerobot-gpu/Dockerfile @@ -0,0 +1,29 @@ +FROM nvidia/cuda:12.4.1-base-ubuntu22.04 + +# Configure image +ARG PYTHON_VERSION=3.10 +ARG DEBIAN_FRONTEND=noninteractive + + +# Install apt dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential cmake \ + libglib2.0-0 libgl1-mesa-glx libegl1-mesa \ + python${PYTHON_VERSION} python${PYTHON_VERSION}-venv \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + + +# Create virtual environment +RUN ln -s /usr/bin/python${PYTHON_VERSION} /usr/bin/python +RUN python -m venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" +RUN echo "source /opt/venv/bin/activate" >> /root/.bashrc + +# Install LeRobot +COPY . /lerobot +WORKDIR /lerobot +RUN pip install --upgrade --no-cache-dir pip +RUN pip install --no-cache-dir ".[test, aloha, xarm, pusht]" + +# Set EGL as the rendering backend for MuJoCo +ENV MUJOCO_GL="egl" diff --git a/poetry.lock b/poetry.lock index 7b66604a..c41e49b7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1164,7 +1164,7 @@ mujoco = "^2.3.7" type = "git" url = "git@github.com:huggingface/gym-xarm.git" reference = "HEAD" -resolved_reference = "6a88f7d63833705dfbec4b997bf36cac6b4a448c" +resolved_reference = "27e65c981f9a8d252eca8f157f83508ba6149db7" [[package]] name = "gymnasium" @@ -2909,6 +2909,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..856ca455 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,5 @@ +from .utils import DEVICE + + +def pytest_collection_finish(): + print(f"\nTesting with {DEVICE=}")