├── docs
└── source
│ ├── contributing.md
│ ├── policy_tdmpc_README.md
│ ├── policy_act_README.md
│ ├── policy_vqbet_README.md
│ ├── policy_diffusion_README.md
│ ├── policy_smolvla_README.md
│ ├── index.mdx
│ ├── policy_groot_README.md
│ ├── torch_accelerators.mdx
│ ├── feetech.mdx
│ └── notebooks.mdx
├── src
└── lerobot
│ ├── robots
│ ├── hope_jr
│ │ ├── hope_jr.mdx
│ │ ├── __init__.py
│ │ └── config_hope_jr.py
│ ├── koch_follower
│ │ ├── koch.mdx
│ │ ├── __init__.py
│ │ └── config_koch_follower.py
│ ├── lekiwi
│ │ ├── lekiwi.mdx
│ │ └── __init__.py
│ ├── so100_follower
│ │ ├── so100.mdx
│ │ ├── __init__.py
│ │ └── config_so100_follower.py
│ ├── so101_follower
│ │ ├── so101.mdx
│ │ ├── __init__.py
│ │ └── config_so101_follower.py
│ ├── earthrover_mini_plus
│ │ ├── earthrover_mini_plus.mdx
│ │ ├── __init__.py
│ │ └── config_earthrover_mini_plus.py
│ ├── unitree_g1
│ │ ├── __init__.py
│ │ ├── config_unitree_g1.py
│ │ └── g1_utils.py
│ ├── __init__.py
│ ├── bi_so100_follower
│ │ ├── __init__.py
│ │ └── config_bi_so100_follower.py
│ ├── alohamini
│ │ └── __init__.py
│ ├── reachy2
│ │ └── __init__.py
│ └── config.py
│ ├── policies
│ ├── act
│ │ └── README.md
│ ├── groot
│ │ ├── README.md
│ │ ├── action_head
│ │ │ ├── __init__.py
│ │ │ └── action_encoder.py
│ │ ├── __init__.py
│ │ └── utils.py
│ ├── tdmpc
│ │ └── README.md
│ ├── vqbet
│ │ └── README.md
│ ├── smolvla
│ │ └── README.md
│ ├── diffusion
│ │ └── README.md
│ ├── xvla
│ │ └── __init__.py
│ ├── pi0
│ │ ├── __init__.py
│ │ └── README.md
│ ├── pi05
│ │ ├── __init__.py
│ │ └── README.md
│ ├── rtc
│ │ ├── README.md
│ │ ├── configuration_rtc.py
│ │ └── latency_tracker.py
│ ├── __init__.py
│ └── sac
│ │ └── reward_model
│ │ └── configuration_classifier.py
│ ├── optim
│ ├── __init__.py
│ └── factory.py
│ ├── envs
│ └── __init__.py
│ ├── motors
│ ├── __init__.py
│ ├── feetech
│ │ └── __init__.py
│ ├── dynamixel
│ │ └── __init__.py
│ └── encoding_utils.py
│ ├── cameras
│ ├── reachy2_camera
│ │ └── __init__.py
│ ├── realsense
│ │ └── __init__.py
│ ├── __init__.py
│ ├── opencv
│ │ └── __init__.py
│ ├── configs.py
│ └── utils.py
│ ├── teleoperators
│ ├── phone
│ │ ├── __init__.py
│ │ └── config_phone.py
│ ├── koch_leader
│ │ ├── __init__.py
│ │ └── config_koch_leader.py
│ ├── so100_leader
│ │ ├── __init__.py
│ │ └── config_so100_leader.py
│ ├── so101_leader
│ │ ├── __init__.py
│ │ └── config_so101_leader.py
│ ├── gamepad
│ │ ├── __init__.py
│ │ └── configuration_gamepad.py
│ ├── bi_so100_leader
│ │ ├── __init__.py
│ │ └── config_bi_so100_leader.py
│ ├── __init__.py
│ ├── homunculus
│ │ ├── __init__.py
│ │ └── config_homunculus.py
│ ├── reachy2_teleoperator
│ │ ├── __init__.py
│ │ └── config_reachy2_teleoperator.py
│ ├── config.py
│ └── keyboard
│ │ ├── __init__.py
│ │ └── configuration_keyboard.py
│ ├── __version__.py
│ ├── datasets
│ ├── card_template.md
│ ├── backward_compatibility.py
│ └── sampler.py
│ ├── async_inference
│ └── constants.py
│ ├── utils
│ ├── errors.py
│ └── robot_utils.py
│ ├── configs
│ └── types.py
│ ├── rl
│ ├── queue.py
│ └── eval_policy.py
│ ├── processor
│ ├── core.py
│ ├── factory.py
│ └── policy_robot_bridge.py
│ └── scripts
│ ├── lerobot_find_port.py
│ ├── lerobot_setup_motors.py
│ └── lerobot_calibrate.py
├── media
├── wandb.png
├── gym
│ ├── aloha_act.gif
│ ├── simxarm_tdmpc.gif
│ └── pusht_diffusion.gif
├── lekiwi
│ └── kiwi.webp
├── so101
│ ├── so101.webp
│ └── so101-leader.webp
├── hope_jr
│ └── hopejr.png
├── lerobot-logo-light.png
├── lerobot-logo-thumbnail.png
└── so100
│ └── leader_follower.webp
├── MANIFEST.in
├── examples
├── alohamini
│ ├── media
│ │ ├── alohamini3a.png
│ │ └── mid_position_so100.png
│ └── replay_bi.py
├── debug
│ ├── test_network.py
│ ├── test_cuda.py
│ ├── action_scripts
│ │ ├── go_to_midpoint.txt
│ │ └── go_to_restposition.txt
│ ├── test_cv.py
│ ├── test_dataset.py
│ ├── README.md
│ └── test_input.py
├── tutorial
│ ├── async-inf
│ │ ├── policy_server.py
│ │ └── robot_client.py
│ ├── rl
│ │ └── reward_classifier_example.py
│ ├── act
│ │ └── act_using_example.py
│ └── diffusion
│ │ └── diffusion_using_example.py
├── lekiwi
│ └── replay.py
└── port_datasets
│ └── display_error_files.py
├── docs-requirements.txt
├── tests
├── artifacts
│ ├── cameras
│ │ ├── image_128x128.png
│ │ ├── image_160x120.png
│ │ ├── test_rs.bag
│ │ ├── image_320x180.png
│ │ └── image_480x270.png
│ ├── datasets
│ │ └── lerobot
│ │ │ ├── pusht
│ │ │ ├── frame_0.safetensors
│ │ │ ├── frame_1.safetensors
│ │ │ ├── frame_159.safetensors
│ │ │ ├── frame_160.safetensors
│ │ │ ├── frame_80.safetensors
│ │ │ └── frame_81.safetensors
│ │ │ ├── xarm_lift_medium
│ │ │ ├── frame_0.safetensors
│ │ │ ├── frame_1.safetensors
│ │ │ ├── frame_12.safetensors
│ │ │ ├── frame_13.safetensors
│ │ │ ├── frame_23.safetensors
│ │ │ └── frame_24.safetensors
│ │ │ └── aloha_sim_insertion_human
│ │ │ ├── frame_0.safetensors
│ │ │ ├── frame_1.safetensors
│ │ │ ├── frame_250.safetensors
│ │ │ ├── frame_251.safetensors
│ │ │ ├── frame_498.safetensors
│ │ │ └── frame_499.safetensors
│ ├── policies
│ │ ├── pusht_diffusion_
│ │ │ ├── actions.safetensors
│ │ │ ├── grad_stats.safetensors
│ │ │ ├── output_dict.safetensors
│ │ │ └── param_stats.safetensors
│ │ ├── aloha_sim_insertion_human_act_
│ │ │ ├── actions.safetensors
│ │ │ ├── output_dict.safetensors
│ │ │ ├── grad_stats.safetensors
│ │ │ └── param_stats.safetensors
│ │ ├── xarm_lift_medium_tdmpc_use_mpc
│ │ │ ├── actions.safetensors
│ │ │ ├── grad_stats.safetensors
│ │ │ ├── output_dict.safetensors
│ │ │ └── param_stats.safetensors
│ │ ├── xarm_lift_medium_tdmpc_use_policy
│ │ │ ├── actions.safetensors
│ │ │ ├── grad_stats.safetensors
│ │ │ ├── output_dict.safetensors
│ │ │ └── param_stats.safetensors
│ │ └── aloha_sim_insertion_human_act_1000_steps
│ │ │ ├── actions.safetensors
│ │ │ ├── output_dict.safetensors
│ │ │ ├── grad_stats.safetensors
│ │ │ └── param_stats.safetensors
│ └── image_transforms
│ │ ├── default_transforms.safetensors
│ │ ├── single_transforms.safetensors
│ │ └── save_image_transforms_to_safetensors.py
├── __init__.py
├── datasets
│ └── test_visualize_dataset.py
├── fixtures
│ ├── optimizers.py
│ └── constants.py
├── plugins
│ └── reachy2_sdk.py
├── mocks
│ └── mock_serial_patch.py
├── test_available.py
├── policies
│ └── rtc
│ │ └── test_configuration_rtc.py
└── processor
│ └── test_libero_processor.py
├── requirements.in
├── .gitattributes
└── .github
├── PULL_REQUEST_TEMPLATE.md
├── workflows
├── documentation-upload-pr.yml
├── security.yml
├── quality.yml
└── documentation.yml
└── ISSUE_TEMPLATE
└── bug-report.yml
/docs/source/contributing.md:
--------------------------------------------------------------------------------
1 | ../../CONTRIBUTING.md
--------------------------------------------------------------------------------
/src/lerobot/robots/hope_jr/hope_jr.mdx:
--------------------------------------------------------------------------------
1 | ../../../../docs/source/hope_jr.mdx
--------------------------------------------------------------------------------
/src/lerobot/robots/koch_follower/koch.mdx:
--------------------------------------------------------------------------------
1 | ../../../../docs/source/koch.mdx
--------------------------------------------------------------------------------
/src/lerobot/robots/lekiwi/lekiwi.mdx:
--------------------------------------------------------------------------------
1 | ../../../../docs/source/lekiwi.mdx
--------------------------------------------------------------------------------
/src/lerobot/policies/act/README.md:
--------------------------------------------------------------------------------
1 | ../../../../docs/source/policy_act_README.md
--------------------------------------------------------------------------------
/src/lerobot/robots/so100_follower/so100.mdx:
--------------------------------------------------------------------------------
1 | ../../../../docs/source/so100.mdx
--------------------------------------------------------------------------------
/src/lerobot/robots/so101_follower/so101.mdx:
--------------------------------------------------------------------------------
1 | ../../../../docs/source/so101.mdx
--------------------------------------------------------------------------------
/src/lerobot/policies/groot/README.md:
--------------------------------------------------------------------------------
1 | ../../../../docs/source/policy_groot_README.md
--------------------------------------------------------------------------------
/src/lerobot/policies/tdmpc/README.md:
--------------------------------------------------------------------------------
1 | ../../../../docs/source/policy_tdmpc_README.md
--------------------------------------------------------------------------------
/src/lerobot/policies/vqbet/README.md:
--------------------------------------------------------------------------------
1 | ../../../../docs/source/policy_vqbet_README.md
--------------------------------------------------------------------------------
/src/lerobot/policies/smolvla/README.md:
--------------------------------------------------------------------------------
1 | ../../../../docs/source/policy_smolvla_README.md
--------------------------------------------------------------------------------
/media/wandb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyiteng/lerobot_alohamini/HEAD/media/wandb.png
--------------------------------------------------------------------------------
/src/lerobot/policies/diffusion/README.md:
--------------------------------------------------------------------------------
1 | ../../../../docs/source/policy_diffusion_README.md
--------------------------------------------------------------------------------
/media/gym/aloha_act.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyiteng/lerobot_alohamini/HEAD/media/gym/aloha_act.gif
--------------------------------------------------------------------------------
/media/lekiwi/kiwi.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyiteng/lerobot_alohamini/HEAD/media/lekiwi/kiwi.webp
--------------------------------------------------------------------------------
/media/so101/so101.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyiteng/lerobot_alohamini/HEAD/media/so101/so101.webp
--------------------------------------------------------------------------------
/media/hope_jr/hopejr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyiteng/lerobot_alohamini/HEAD/media/hope_jr/hopejr.png
--------------------------------------------------------------------------------
/src/lerobot/robots/earthrover_mini_plus/earthrover_mini_plus.mdx:
--------------------------------------------------------------------------------
1 | ../../../../docs/source/earthrover_mini_plus.mdx
--------------------------------------------------------------------------------
/media/gym/simxarm_tdmpc.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyiteng/lerobot_alohamini/HEAD/media/gym/simxarm_tdmpc.gif
--------------------------------------------------------------------------------
/media/lerobot-logo-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyiteng/lerobot_alohamini/HEAD/media/lerobot-logo-light.png
--------------------------------------------------------------------------------
/media/gym/pusht_diffusion.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyiteng/lerobot_alohamini/HEAD/media/gym/pusht_diffusion.gif
--------------------------------------------------------------------------------
/media/so101/so101-leader.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyiteng/lerobot_alohamini/HEAD/media/so101/so101-leader.webp
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include src/lerobot/templates/lerobot_modelcard_template.md
2 | include src/lerobot/datasets/card_template.md
3 |
--------------------------------------------------------------------------------
/media/lerobot-logo-thumbnail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyiteng/lerobot_alohamini/HEAD/media/lerobot-logo-thumbnail.png
--------------------------------------------------------------------------------
/media/so100/leader_follower.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyiteng/lerobot_alohamini/HEAD/media/so100/leader_follower.webp
--------------------------------------------------------------------------------
/examples/alohamini/media/alohamini3a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyiteng/lerobot_alohamini/HEAD/examples/alohamini/media/alohamini3a.png
--------------------------------------------------------------------------------
/docs-requirements.txt:
--------------------------------------------------------------------------------
1 | # docs-requirements.txt
2 | hf-doc-builder @ git+https://github.com/huggingface/doc-builder.git@main
3 | watchdog>=6.0.0
4 |
--------------------------------------------------------------------------------
/examples/alohamini/media/mid_position_so100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyiteng/lerobot_alohamini/HEAD/examples/alohamini/media/mid_position_so100.png
--------------------------------------------------------------------------------
/tests/artifacts/cameras/image_128x128.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:9dc9df05797dc0e7b92edc845caab2e4c37c3cfcabb4ee6339c67212b5baba3b
3 | size 38023
4 |
--------------------------------------------------------------------------------
/tests/artifacts/cameras/image_160x120.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:7e11af87616b83c1cdb30330e951b91e86b51c64a1326e1ba5b4a3fbcdec1a11
3 | size 55698
4 |
--------------------------------------------------------------------------------
/tests/artifacts/cameras/test_rs.bag:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:a8d6e64d6cb0e02c94ae125630ee758055bd2e695772c0463a30d63ddc6c5e17
3 | size 3520862
4 |
--------------------------------------------------------------------------------
/tests/artifacts/cameras/image_320x180.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:b8840fb643afe903191248703b1f95a57faf5812ecd9978ac502ee939646fdb2
3 | size 121115
4 |
--------------------------------------------------------------------------------
/tests/artifacts/cameras/image_480x270.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:f79d14daafb1c0cf2fec5d46ee8029a73fe357402fdd31a7cd4a4794d7319a7c
3 | size 260367
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/pusht/frame_0.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:c3dcff0a705ebfdaf11b7f49ad85b464eff03477ace3d63ce45d6a3a10b429d5
3 | size 111338
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/pusht/frame_1.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:d8ab0274761cdd758bafdf274ce3e6398cd6f0df23393971f3e1b6b465d66ef3
3 | size 111338
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/pusht/frame_159.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:aee60956925da9687546aafa770d5e6a04f99576f903b08d0bd5f8003a7f4f3e
3 | size 111338
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/pusht/frame_160.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:c8d9f9cc9e232820760fe4a46b47000c921fa5d868420e55d8dbc05dae56e8bd
3 | size 111338
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/pusht/frame_80.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:01cfe50c537e3aef0cd5947ec0b15b321b54ecb461baf7b4f2506897158eebc8
3 | size 111338
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/pusht/frame_81.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:96431ca3479eef2379406ef901cad7ba5eac4f7edcc48ecc9e8d1fa0e99d8017
3 | size 111338
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/pusht_diffusion_/actions.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:19eaaa85f66ba4aa6388dbb83819ffad6ea4363247208f871a8dc385689f6fc8
3 | size 992
4 |
--------------------------------------------------------------------------------
/tests/artifacts/image_transforms/default_transforms.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:6b1e600768a8771c5fe650e038a1193597e3810f032041b2a0d021e4496381c1
3 | size 3686488
4 |
--------------------------------------------------------------------------------
/tests/artifacts/image_transforms/single_transforms.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:9d4ebab73eabddc58879a4e770289d19e00a1a4cf2fa5fa33cd3a3246992bc90
3 | size 40551392
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/pusht_diffusion_/grad_stats.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:227296eaeeb54acdc3dae2eb8af3d4d08fb87e245337624447140b1e91cfd002
3 | size 47424
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/pusht_diffusion_/output_dict.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:271b00cb2f0cd5fd26b1d53463638e3d1a6e92692ec625fcffb420ca190869e5
3 | size 68
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/pusht_diffusion_/param_stats.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:778fddbbaa64248cee35cb377c02cc2b6076f7ce5855146de677128900617ddf
3 | size 47424
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/xarm_lift_medium/frame_0.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:3763d7bff7873cb40ea9d6f2f98d45fcf163addcd2809b6c59f273b6c3627ad5
3 | size 85353
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/xarm_lift_medium/frame_1.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:24150994c6959631dc081b43e4001a8664e13b194ac194a32100f7d3fd2c0d0f
3 | size 85353
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/xarm_lift_medium/frame_12.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:c9c3fdf34debe47d4b80570a19e676185449df749f37daa2111184c1f439ae5f
3 | size 85353
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/xarm_lift_medium/frame_13.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:f8cfbe444c14d643da2faea9f6a402ddb37114ab15395c381f1a7982e541f868
3 | size 85353
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/xarm_lift_medium/frame_23.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:07c5c1a63998884ee747a6d0aa8f49217da3c32af2760dad2a9da794d3517003
3 | size 85353
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/xarm_lift_medium/frame_24.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:9927ec508e3335f8b10cf3682e41dedb7e647f92a2063a4196f1e48749c47bc5
3 | size 85353
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/aloha_sim_insertion_human_act_/actions.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:ee0c29d3782aa1cadcf4dc6ed767d9460ff00fff9fc70b460502340b832eefcc
3 | size 5104
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/aloha_sim_insertion_human_act_/output_dict.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:5e6ce85296b2009e7c2060d336c0429b1c7197d9adb159e7df0ba18003067b36
3 | size 68
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/xarm_lift_medium_tdmpc_use_mpc/actions.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:d640988f2269cf6aa03c8ee17f9d096edace83d837f90025011fafec5bf53c61
3 | size 200
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/xarm_lift_medium_tdmpc_use_policy/actions.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:2212ae7b910d14d723214f5af50985e419f7bd0f4261565ef48b1ef495443d6d
3 | size 200
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/aloha_sim_insertion_human/frame_0.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:6bdf22208d49cd36d24bc844d4d8bda5e321eafe39d2b470e4fc95c7812fdb24
3 | size 3687117
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/aloha_sim_insertion_human/frame_1.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:8920d5ebab36ffcba9aa74dcd91677c121f504b4d945b472352d379f9272fabf
3 | size 3687117
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/aloha_sim_insertion_human_act_/grad_stats.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:1a7a8b1a457149109f843c32bcbb047d09de2201847b9b79f7501b447f77ecf4
3 | size 31672
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/aloha_sim_insertion_human_act_/param_stats.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:ea76e6711959fd3f905ec2bdc306f488920f00ec99421e4870d05f6205eb323e
3 | size 31672
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/xarm_lift_medium_tdmpc_use_mpc/grad_stats.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:32ddf36af25791935b395c7641531cda14d5c4a2cf654a2e76ac45271665d07a
3 | size 16904
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/xarm_lift_medium_tdmpc_use_mpc/output_dict.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:22a1031a2acfc36a455bff73ffbe097cfeb7742b6485e7422507e78d7a682703
3 | size 164
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/xarm_lift_medium_tdmpc_use_mpc/param_stats.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:b5dca7940998421ae58e9e26b2b2641b058d23b0270b7a147ebf85fbbdce7184
3 | size 35496
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/xarm_lift_medium_tdmpc_use_policy/grad_stats.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:32ddf36af25791935b395c7641531cda14d5c4a2cf654a2e76ac45271665d07a
3 | size 16904
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/xarm_lift_medium_tdmpc_use_policy/output_dict.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:22a1031a2acfc36a455bff73ffbe097cfeb7742b6485e7422507e78d7a682703
3 | size 164
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/aloha_sim_insertion_human/frame_250.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:35723f2db499da3d9d121aa79d2ff4c748effd7c2ea92f277ec543a82fb843ca
3 | size 3687117
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/aloha_sim_insertion_human/frame_251.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:53172b773d4a78bb3140f10280105c2c4ebcb467f3097579988d42cb87790ab9
3 | size 3687117
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/aloha_sim_insertion_human/frame_498.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:58a5d91573e7dd2352a1454a5c9118c9ad3798428a0104e5e0b57fc01f780ae7
3 | size 3687117
4 |
--------------------------------------------------------------------------------
/tests/artifacts/datasets/lerobot/aloha_sim_insertion_human/frame_499.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:bb65a25e989a32a8b6258d368bd077e4548379c74ab5ada01cc532d658670df0
3 | size 3687117
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/actions.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:c2b8f8532c7a0b776de5e536b8b54e30b1a0c2e3d5cc25a2d86fe43e40ae5e8c
3 | size 515400
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/output_dict.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:016d2fa8fe5f58017dfd46f4632fdc19dfd751e32a2c7cde2077c6f95546d6bd
3 | size 68
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/xarm_lift_medium_tdmpc_use_policy/param_stats.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:b5dca7940998421ae58e9e26b2b2641b058d23b0270b7a147ebf85fbbdce7184
3 | size 35496
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/grad_stats.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:224b5fa4828aa88171b68c036e8919c1eae563e2113f03b6461eadf5bf8525a6
3 | size 31672
4 |
--------------------------------------------------------------------------------
/tests/artifacts/policies/aloha_sim_insertion_human_act_1000_steps/param_stats.safetensors:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:eca0d87a699620e4fec7e68539b0be91e4cc933f6bf12032da52c182ab6f38cf
3 | size 31672
4 |
--------------------------------------------------------------------------------
/src/lerobot/policies/xvla/__init__.py:
--------------------------------------------------------------------------------
1 | # register the processor steps
2 | from lerobot.policies.xvla.processor_xvla import (
3 | XVLAAddDomainIdProcessorStep,
4 | XVLAImageNetNormalizeProcessorStep,
5 | XVLAImageToFloatProcessorStep,
6 | )
7 |
--------------------------------------------------------------------------------
/docs/source/policy_tdmpc_README.md:
--------------------------------------------------------------------------------
1 | ## Paper
2 |
3 | https://www.nicklashansen.com/td-mpc/
4 |
5 | ## Citation
6 |
7 | ```bibtex
8 | @inproceedings{Hansen2022tdmpc,
9 | title={Temporal Difference Learning for Model Predictive Control},
10 | author={Nicklas Hansen and Xiaolong Wang and Hao Su},
11 | booktitle={ICML},
12 | year={2022}
13 | }
14 | ```
15 |
--------------------------------------------------------------------------------
/examples/debug/test_network.py:
--------------------------------------------------------------------------------
1 | import requests
2 |
3 | try:
4 | response = requests.get("https://www.google.com")
5 | print("Google Connection successful:", response.status_code)
6 | response = requests.get("https://huggingface.co")
7 | print("Huggingface Connection successful:", response.status_code)
8 | except requests.ConnectionError as e:
9 | print("Connection failed:", e)
--------------------------------------------------------------------------------
/examples/debug/test_cuda.py:
--------------------------------------------------------------------------------
1 | import torch
2 | print("PyTorch version:", torch.__version__)
3 | print("CUDA version:", torch.version.cuda)
4 | print("torch.cuda.is_available",torch.cuda.is_available()) # Should return True
5 | print("torch.cuda.device_coun",torch.cuda.device_count()) # Should return the number of GPUs
6 | print("torch.cuda.get_device_name(0)",torch.cuda.get_device_name(0)) # Display the GPU name
--------------------------------------------------------------------------------
/docs/source/policy_act_README.md:
--------------------------------------------------------------------------------
1 | ## Paper
2 |
3 | https://tonyzhaozh.github.io/aloha
4 |
5 | ## Citation
6 |
7 | ```bibtex
8 | @article{zhao2023learning,
9 | title={Learning fine-grained bimanual manipulation with low-cost hardware},
10 | author={Zhao, Tony Z and Kumar, Vikash and Levine, Sergey and Finn, Chelsea},
11 | journal={arXiv preprint arXiv:2304.13705},
12 | year={2023}
13 | }
14 | ```
15 |
--------------------------------------------------------------------------------
/docs/source/policy_vqbet_README.md:
--------------------------------------------------------------------------------
1 | ## Paper
2 |
3 | https://sjlee.cc/vq-bet/
4 |
5 | ## Citation
6 |
7 | ```bibtex
8 | @article{lee2024behavior,
9 | title={Behavior generation with latent actions},
10 | author={Lee, Seungjae and Wang, Yibin and Etukuru, Haritheja and Kim, H Jin and Shafiullah, Nur Muhammad Mahi and Pinto, Lerrel},
11 | journal={arXiv preprint arXiv:2403.03181},
12 | year={2024}
13 | }
14 | ```
15 |
--------------------------------------------------------------------------------
/examples/tutorial/async-inf/policy_server.py:
--------------------------------------------------------------------------------
1 | from lerobot.async_inference.configs import PolicyServerConfig
2 | from lerobot.async_inference.policy_server import serve
3 |
4 |
5 | def main():
6 | host = ... # something like "127.0.0.1" if you're exposing to localhost
7 | port = ... # something like 8080
8 |
9 | config = PolicyServerConfig(
10 | host=host,
11 | port=port,
12 | )
13 | serve(config)
14 |
15 |
16 | if __name__ == "__main__":
17 | main()
18 |
--------------------------------------------------------------------------------
/docs/source/policy_diffusion_README.md:
--------------------------------------------------------------------------------
1 | ## Paper
2 |
3 | https://diffusion-policy.cs.columbia.edu
4 |
5 | ## Citation
6 |
7 | ```bibtex
8 | @article{chi2024diffusionpolicy,
9 | author = {Cheng Chi and Zhenjia Xu and Siyuan Feng and Eric Cousineau and Yilun Du and Benjamin Burchfiel and Russ Tedrake and Shuran Song},
10 | title ={Diffusion Policy: Visuomotor Policy Learning via Action Diffusion},
11 | journal = {The International Journal of Robotics Research},
12 | year = {2024},
13 | }
14 | ```
15 |
--------------------------------------------------------------------------------
/requirements.in:
--------------------------------------------------------------------------------
1 | # requirements.in
2 |
3 | # requirements-macos.txt was generated on macOS and is platform-specific (macOS 26.0.1 25A362 arm64).
4 | # Darwin MacBook-Pro.local 25.0.0 Darwin Kernel Version 25.0.0: Wed Sep 17 21:42:08 PDT 2025; root:xnu-12377.1.9~141/RELEASE_ARM64_T8132 arm64
5 |
6 | # requirements-ubuntu.txt was generated on Linux and is platform-specific (Ubuntu 24.04.3 LTS x86_64).
7 | # Linux mlerobot-linux 6.14.0-33-generic #33~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Sep 19 17:02:30 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
8 |
9 | -e .[all]
10 |
--------------------------------------------------------------------------------
/docs/source/policy_smolvla_README.md:
--------------------------------------------------------------------------------
1 | ## Paper
2 |
3 | https://arxiv.org/abs/2506.01844
4 |
5 | ## Citation
6 |
7 | ```bibtex
8 | @article{shukor2025smolvla,
9 | title={SmolVLA: A Vision-Language-Action Model for Affordable and Efficient Robotics},
10 | author={Shukor, Mustafa and Aubakirova, Dana and Capuano, Francesco and Kooijmans, Pepijn and Palma, Steven and Zouitine, Adil and Aractingi, Michel and Pascal, Caroline and Russi, Martino and Marafioti, Andres and Alibert, Simon and Cord, Matthieu and Wolf, Thomas and Cadene, Remi},
11 | journal={arXiv preprint arXiv:2506.01844},
12 | year={2025}
13 | }
14 | ```
15 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
--------------------------------------------------------------------------------
/examples/debug/action_scripts/go_to_midpoint.txt:
--------------------------------------------------------------------------------
1 |
2 | shoulder_pan, read, ID
3 | shoulder_pan, read, Present_Position
4 | shoulder_pan, write, Goal_Position, 2048
5 |
6 | gripper, read, ID
7 | gripper, read, Present_Position
8 | gripper, write, Goal_Position, 2048
9 |
10 | wrist_roll, read, ID
11 | wrist_roll, read, Present_Position
12 | wrist_roll, write, Goal_Position, 2048
13 |
14 |
15 | wrist_flex, read, ID
16 | wrist_flex, read, Present_Position
17 | wrist_flex, write, Goal_Position, 2048
18 |
19 | elbow_flex, read, ID
20 | elbow_flex, read, Present_Position
21 | elbow_flex, write, Goal_Position, 2048
22 |
23 | shoulder_lift, read, ID
24 | shoulder_lift, read, Present_Position
25 | shoulder_lift, write, Goal_Position, 2048
26 |
--------------------------------------------------------------------------------
/examples/debug/action_scripts/go_to_restposition.txt:
--------------------------------------------------------------------------------
1 |
2 | shoulder_pan, read, ID
3 | shoulder_pan, read, Present_Position
4 | shoulder_pan, write, Goal_Position, 2048
5 |
6 | gripper, read, ID
7 | gripper, read, Present_Position
8 | gripper, write, Goal_Position, 2048
9 |
10 | wrist_roll, read, ID
11 | wrist_roll, read, Present_Position
12 | wrist_roll, write, Goal_Position, 2048
13 |
14 |
15 | wrist_flex, read, ID
16 | wrist_flex, read, Present_Position
17 | wrist_flex, write, Goal_Position, 1600
18 |
19 | elbow_flex, read, ID
20 | elbow_flex, read, Present_Position
21 | elbow_flex, write, Goal_Position, 3000
22 |
23 | shoulder_lift, read, ID
24 | shoulder_lift, read, Present_Position
25 | shoulder_lift, write, Goal_Position, 900
26 |
--------------------------------------------------------------------------------
/src/lerobot/optim/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | from .optimizers import OptimizerConfig as OptimizerConfig
16 |
--------------------------------------------------------------------------------
/src/lerobot/envs/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | from .configs import AlohaEnv, EnvConfig, PushtEnv # noqa: F401
16 |
--------------------------------------------------------------------------------
/src/lerobot/policies/groot/action_head/__init__.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2 | # SPDX-License-Identifier: Apache-2.0
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
--------------------------------------------------------------------------------
/examples/debug/test_cv.py:
--------------------------------------------------------------------------------
1 | import sys, platform, time
2 | import cv2
3 |
4 | print("Python:", sys.version.split()[0])
5 | print("OS:", platform.system(), platform.release())
6 | print("cv2 path:", cv2.__file__)
7 | print("cv2 version:", cv2.__version__)
8 |
9 | cap = cv2.VideoCapture(0)
10 | if not cap.isOpened():
11 | raise RuntimeError("Failed to open camera index 0")
12 |
13 | cv2.namedWindow("test", cv2.WINDOW_NORMAL)
14 |
15 | while True:
16 | ret, frame = cap.read()
17 | if not ret or frame is None:
18 | print("⚠️ read failed, continue...")
19 | time.sleep(0.05)
20 | continue
21 | cv2.imshow("test", frame)
22 | if (cv2.waitKey(1) & 0xFF) in (27, ord('q')):
23 | break
24 |
25 | cap.release()
26 | cv2.destroyAllWindows()
27 |
--------------------------------------------------------------------------------
/src/lerobot/motors/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .motors_bus import Motor, MotorCalibration, MotorNormMode, MotorsBus
18 |
--------------------------------------------------------------------------------
/src/lerobot/cameras/reachy2_camera/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | from .configuration_reachy2_camera import Reachy2CameraConfig
16 | from .reachy2_camera import Reachy2Camera
17 |
--------------------------------------------------------------------------------
/src/lerobot/cameras/realsense/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | from .camera_realsense import RealSenseCamera
16 | from .configuration_realsense import RealSenseCameraConfig
17 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/phone/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .config_phone import PhoneConfig
18 | from .teleop_phone import Phone
19 |
--------------------------------------------------------------------------------
/src/lerobot/robots/unitree_g1/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .config_unitree_g1 import UnitreeG1Config
18 | from .unitree_g1 import UnitreeG1
19 |
--------------------------------------------------------------------------------
/src/lerobot/cameras/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | from .camera import Camera
16 | from .configs import CameraConfig, ColorMode, Cv2Rotation
17 | from .utils import make_cameras_from_configs
18 |
--------------------------------------------------------------------------------
/src/lerobot/motors/feetech/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .feetech import DriveMode, FeetechMotorsBus, OperatingMode, TorqueMode
18 | from .tables import *
19 |
--------------------------------------------------------------------------------
/src/lerobot/robots/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .config import RobotConfig
18 | from .robot import Robot
19 | from .utils import make_robot_from_config
20 |
--------------------------------------------------------------------------------
/src/lerobot/robots/koch_follower/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .config_koch_follower import KochFollowerConfig
18 | from .koch_follower import KochFollower
19 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/koch_leader/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .config_koch_leader import KochLeaderConfig
18 | from .koch_leader import KochLeader
19 |
--------------------------------------------------------------------------------
/src/lerobot/motors/dynamixel/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .dynamixel import DriveMode, DynamixelMotorsBus, OperatingMode, TorqueMode
18 | from .tables import *
19 |
--------------------------------------------------------------------------------
/src/lerobot/robots/so100_follower/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .config_so100_follower import SO100FollowerConfig
18 | from .so100_follower import SO100Follower
19 |
--------------------------------------------------------------------------------
/src/lerobot/robots/so101_follower/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .config_so101_follower import SO101FollowerConfig
18 | from .so101_follower import SO101Follower
19 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/so100_leader/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .config_so100_leader import SO100LeaderConfig
18 | from .so100_leader import SO100Leader
19 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/so101_leader/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .config_so101_leader import SO101LeaderConfig
18 | from .so101_leader import SO101Leader
19 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/gamepad/__init__.py:
--------------------------------------------------------------------------------
1 | # !/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .configuration_gamepad import GamepadTeleopConfig
18 | from .teleop_gamepad import GamepadTeleop
19 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/bi_so100_leader/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .bi_so100_leader import BiSO100Leader
18 | from .config_bi_so100_leader import BiSO100LeaderConfig
19 |
--------------------------------------------------------------------------------
/src/lerobot/robots/bi_so100_follower/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .bi_so100_follower import BiSO100Follower
18 | from .config_bi_so100_follower import BiSO100FollowerConfig
19 |
--------------------------------------------------------------------------------
/src/lerobot/cameras/opencv/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | from .camera_opencv import OpenCVCamera
16 | from .configuration_opencv import OpenCVCameraConfig
17 |
18 | __all__ = ["OpenCVCamera", "OpenCVCameraConfig"]
19 |
--------------------------------------------------------------------------------
/src/lerobot/robots/alohamini/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .config_lekiwi import LeKiwiClientConfig, LeKiwiConfig
18 | from .lekiwi import LeKiwi
19 | from .lekiwi_client import LeKiwiClient
20 |
--------------------------------------------------------------------------------
/src/lerobot/robots/lekiwi/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .config_lekiwi import LeKiwiClientConfig, LeKiwiConfig
18 | from .lekiwi import LeKiwi
19 | from .lekiwi_client import LeKiwiClient
20 |
--------------------------------------------------------------------------------
/src/lerobot/robots/hope_jr/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .config_hope_jr import HopeJrArmConfig, HopeJrHandConfig
18 | from .hope_jr_arm import HopeJrArm
19 | from .hope_jr_hand import HopeJrHand
20 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .config import TeleoperatorConfig
18 | from .teleoperator import Teleoperator
19 | from .utils import TeleopEvents, make_teleoperator_from_config
20 |
--------------------------------------------------------------------------------
/src/lerobot/__version__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | """To enable `lerobot.__version__`"""
17 |
18 | from importlib.metadata import PackageNotFoundError, version
19 |
20 | try:
21 | __version__ = version("lerobot")
22 | except PackageNotFoundError:
23 | __version__ = "unknown"
24 |
--------------------------------------------------------------------------------
/src/lerobot/datasets/card_template.md:
--------------------------------------------------------------------------------
1 | ---
2 | # For reference on dataset card metadata, see the spec: https://github.com/huggingface/hub-docs/blob/main/datasetcard.md?plain=1
3 | # Doc / guide: https://huggingface.co/docs/hub/datasets-cards
4 | # prettier-ignore
5 | {{card_data}}
6 | ---
7 |
8 | This dataset was created using [LeRobot](https://github.com/huggingface/lerobot).
9 |
10 | ## Dataset Description
11 |
12 | {{ dataset_description | default("", true) }}
13 |
14 | - **Homepage:** {{ url | default("[More Information Needed]", true)}}
15 | - **Paper:** {{ paper | default("[More Information Needed]", true)}}
16 | - **License:** {{ license | default("[More Information Needed]", true)}}
17 |
18 | ## Dataset Structure
19 |
20 | {{ dataset_structure | default("[More Information Needed]", true)}}
21 |
22 | ## Citation
23 |
24 | **BibTeX:**
25 |
26 | ```bibtex
27 | {{ citation_bibtex | default("[More Information Needed]", true)}}
28 | ```
29 |
--------------------------------------------------------------------------------
/src/lerobot/robots/earthrover_mini_plus/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .config_earthrover_mini_plus import EarthRoverMiniPlusConfig
18 | from .robot_earthrover_mini_plus import EarthRoverMiniPlus
19 |
20 | __all__ = ["EarthRoverMiniPlus", "EarthRoverMiniPlusConfig"]
21 |
--------------------------------------------------------------------------------
/examples/debug/test_dataset.py:
--------------------------------------------------------------------------------
1 | from datasets import load_dataset
2 |
3 | # 1. 直接把 parquet 读成一个 Dataset(train split)
4 | ds = load_dataset(
5 | "parquet",
6 | data_files="./episode_000003.parquet",
7 | split="train" # 直接指定 split,返回 Dataset 而非 DatasetDict
8 | )
9 |
10 | # 2. 看一下有哪些列
11 | print("columns:", ds.column_names)
12 |
13 | # 3. 把前几行打印出来确认一下
14 | df = ds.to_pandas()
15 | print(df.head())
16 |
17 | # 4. 用宽松的阈值筛选 timestamp 接近 8.63 秒的行
18 | targ = 8.63
19 | tol = 1 # 1 毫秒容差
20 | ds_8_63 = ds.filter(lambda ex: abs(ex["timestamp"] - targ) < tol)
21 |
22 | print(f"Matched rows: {ds_8_63.num_rows}")
23 | for ex in ds_8_63:
24 | print(ex)
25 |
26 | # # —— 或者 ——
27 | # # 如果你更习惯按 frame_index 来选(需要知道 fps)
28 | # fps = 30
29 | # target_frame = round(targ * fps) # e.g. round(8.63*30)=259
30 | # ds_frame = ds.filter(lambda ex: ex["frame_index"] == target_frame)
31 | # print(f"By frame_index ({target_frame}):", ds_frame.num_rows)
32 |
--------------------------------------------------------------------------------
/src/lerobot/policies/pi0/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 Physical Intelligence and The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .configuration_pi0 import PI0Config
18 | from .modeling_pi0 import PI0Policy
19 | from .processor_pi0 import make_pi0_pre_post_processors
20 |
21 | __all__ = ["PI0Config", "PI0Policy", "make_pi0_pre_post_processors"]
22 |
--------------------------------------------------------------------------------
/src/lerobot/policies/groot/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 Nvidia and The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .configuration_groot import GrootConfig
18 | from .modeling_groot import GrootPolicy
19 | from .processor_groot import make_groot_pre_post_processors
20 |
21 | __all__ = ["GrootConfig", "GrootPolicy", "make_groot_pre_post_processors"]
22 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/homunculus/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .config_homunculus import HomunculusArmConfig, HomunculusGloveConfig
18 | from .homunculus_arm import HomunculusArm
19 | from .homunculus_glove import HomunculusGlove
20 | from .joints_translation import homunculus_glove_to_hope_jr_hand
21 |
--------------------------------------------------------------------------------
/src/lerobot/policies/pi05/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 Physical Intelligence and The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .configuration_pi05 import PI05Config
18 | from .modeling_pi05 import PI05Policy
19 | from .processor_pi05 import make_pi05_pre_post_processors
20 |
21 | __all__ = ["PI05Config", "PI05Policy", "make_pi05_pre_post_processors"]
22 |
--------------------------------------------------------------------------------
/src/lerobot/robots/reachy2/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .configuration_reachy2 import Reachy2RobotConfig
18 | from .robot_reachy2 import (
19 | REACHY2_ANTENNAS_JOINTS,
20 | REACHY2_L_ARM_JOINTS,
21 | REACHY2_NECK_JOINTS,
22 | REACHY2_R_ARM_JOINTS,
23 | REACHY2_VEL,
24 | Reachy2Robot,
25 | )
26 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/gamepad/configuration_gamepad.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from dataclasses import dataclass
18 |
19 | from ..config import TeleoperatorConfig
20 |
21 |
22 | @TeleoperatorConfig.register_subclass("gamepad")
23 | @dataclass
24 | class GamepadTeleopConfig(TeleoperatorConfig):
25 | use_gripper: bool = True
26 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/so100_leader/config_so100_leader.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from dataclasses import dataclass
18 |
19 | from ..config import TeleoperatorConfig
20 |
21 |
22 | @TeleoperatorConfig.register_subclass("so100_leader")
23 | @dataclass
24 | class SO100LeaderConfig(TeleoperatorConfig):
25 | # Port to connect to the arm
26 | port: str
27 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/bi_so100_leader/config_bi_so100_leader.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from dataclasses import dataclass
18 |
19 | from ..config import TeleoperatorConfig
20 |
21 |
22 | @TeleoperatorConfig.register_subclass("bi_so100_leader")
23 | @dataclass
24 | class BiSO100LeaderConfig(TeleoperatorConfig):
25 | left_arm_port: str
26 | right_arm_port: str
27 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/reachy2_teleoperator/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .config_reachy2_teleoperator import Reachy2TeleoperatorConfig
18 | from .reachy2_teleoperator import (
19 | REACHY2_ANTENNAS_JOINTS,
20 | REACHY2_L_ARM_JOINTS,
21 | REACHY2_NECK_JOINTS,
22 | REACHY2_R_ARM_JOINTS,
23 | REACHY2_VEL,
24 | Reachy2Teleoperator,
25 | )
26 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | *.memmap filter=lfs diff=lfs merge=lfs -text
15 | *.stl filter=lfs diff=lfs merge=lfs -text
16 | *.safetensors filter=lfs diff=lfs merge=lfs -text
17 | *.mp4 filter=lfs diff=lfs merge=lfs -text
18 | *.arrow filter=lfs diff=lfs merge=lfs -text
19 | *.json !text !filter !merge !diff
20 | tests/artifacts/cameras/*.png filter=lfs diff=lfs merge=lfs -text
21 | *.bag filter=lfs diff=lfs merge=lfs -text
22 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/so101_leader/config_so101_leader.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from dataclasses import dataclass
18 |
19 | from ..config import TeleoperatorConfig
20 |
21 |
22 | @TeleoperatorConfig.register_subclass("so101_leader")
23 | @dataclass
24 | class SO101LeaderConfig(TeleoperatorConfig):
25 | # Port to connect to the arm
26 | port: str
27 |
28 | use_degrees: bool = False
29 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/config.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | import abc
16 | from dataclasses import dataclass
17 | from pathlib import Path
18 |
19 | import draccus
20 |
21 |
22 | @dataclass(kw_only=True)
23 | class TeleoperatorConfig(draccus.ChoiceRegistry, abc.ABC):
24 | # Allows to distinguish between different teleoperators of the same type
25 | id: str | None = None
26 | # Directory to store calibration file
27 | calibration_dir: Path | None = None
28 |
29 | @property
30 | def type(self) -> str:
31 | return self.get_choice_name(self.__class__)
32 |
--------------------------------------------------------------------------------
/docs/source/index.mdx:
--------------------------------------------------------------------------------
1 |
10 |
11 | # LeRobot
12 |
13 | **State-of-the-art machine learning for real-world robotics**
14 |
15 | 🤗 LeRobot aims to provide models, datasets, and tools for real-world robotics in PyTorch. The goal is to lower the barrier for entry to robotics so that everyone can contribute and benefit from sharing datasets and pretrained models.
16 |
17 | 🤗 LeRobot contains state-of-the-art approaches that have been shown to transfer to the real-world with a focus on imitation learning and reinforcement learning.
18 |
19 | 🤗 LeRobot already provides a set of pretrained models, datasets with human collected demonstrations, and simulated environments so that everyone can get started.
20 |
21 | 🤗 LeRobot hosts pretrained models and datasets on the LeRobot HuggingFace page.
22 |
23 | Join the LeRobot community on [Discord](https://discord.gg/s3KuuzsPFb)
24 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/keyboard/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .configuration_keyboard import (
18 | KeyboardEndEffectorTeleopConfig,
19 | KeyboardRoverTeleopConfig,
20 | KeyboardTeleopConfig,
21 | )
22 | from .teleop_keyboard import KeyboardEndEffectorTeleop, KeyboardRoverTeleop, KeyboardTeleop
23 |
24 | __all__ = [
25 | "KeyboardTeleopConfig",
26 | "KeyboardTeleop",
27 | "KeyboardEndEffectorTeleopConfig",
28 | "KeyboardEndEffectorTeleop",
29 | "KeyboardRoverTeleopConfig",
30 | "KeyboardRoverTeleop",
31 | ]
32 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/koch_leader/config_koch_leader.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from dataclasses import dataclass
18 |
19 | from ..config import TeleoperatorConfig
20 |
21 |
22 | @TeleoperatorConfig.register_subclass("koch_leader")
23 | @dataclass
24 | class KochLeaderConfig(TeleoperatorConfig):
25 | # Port to connect to the arm
26 | port: str
27 |
28 | # Sets the arm in torque mode with the gripper motor set to this value. This makes it possible to squeeze
29 | # the gripper and have it spring back to an open position on its own.
30 | gripper_open_pos: float = 50.0
31 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/phone/config_phone.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from dataclasses import dataclass
18 | from enum import Enum
19 |
20 | import numpy as np
21 |
22 | from ..config import TeleoperatorConfig
23 |
24 |
25 | class PhoneOS(Enum):
26 | ANDROID = "android"
27 | IOS = "ios"
28 |
29 |
30 | @TeleoperatorConfig.register_subclass("phone")
31 | @dataclass
32 | class PhoneConfig(TeleoperatorConfig):
33 | phone_os: PhoneOS = PhoneOS.IOS
34 | camera_offset = np.array(
35 | [0.0, -0.02, 0.04]
36 | ) # iPhone 14 Pro camera is 2cm off center and 4cm above center
37 |
--------------------------------------------------------------------------------
/src/lerobot/async_inference/constants.py:
--------------------------------------------------------------------------------
1 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | """Client side: The environment evolves with a time resolution equal to 1/fps"""
16 |
17 | DEFAULT_FPS = 30
18 |
19 | """Server side: Running inference on (at most) 1/fps"""
20 | DEFAULT_INFERENCE_LATENCY = 1 / DEFAULT_FPS
21 |
22 | """Server side: Timeout for observation queue in seconds"""
23 | DEFAULT_OBS_QUEUE_TIMEOUT = 2
24 |
25 | # All action chunking policies
26 | SUPPORTED_POLICIES = ["act", "smolvla", "diffusion", "tdmpc", "vqbet", "pi0", "pi05"]
27 |
28 | # TODO: Add all other robots
29 | SUPPORTED_ROBOTS = ["so100_follower", "so101_follower", "bi_so100_follower"]
30 |
--------------------------------------------------------------------------------
/tests/datasets/test_visualize_dataset.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | import pytest
17 |
18 | from lerobot.scripts.lerobot_dataset_viz import visualize_dataset
19 |
20 |
21 | @pytest.mark.skip("TODO: add dummy videos")
22 | def test_visualize_local_dataset(tmp_path, lerobot_dataset_factory):
23 | root = tmp_path / "dataset"
24 | output_dir = tmp_path / "outputs"
25 | dataset = lerobot_dataset_factory(root=root)
26 | rrd_path = visualize_dataset(
27 | dataset,
28 | episode_index=0,
29 | batch_size=32,
30 | save=True,
31 | output_dir=output_dir,
32 | )
33 | assert rrd_path.exists()
34 |
--------------------------------------------------------------------------------
/examples/debug/README.md:
--------------------------------------------------------------------------------
1 | #### View all motor states
2 | ```
3 | python examples/debug/motors.py get_motors_states \
4 | --port /dev/ttyACM0
5 | ```
6 | #### Control the mobile base only
7 | ```
8 | python examples/debug/wheels.py \
9 | --port /dev/ttyACM0
10 | ```
11 |
12 | #### Control the lift axis only
13 | ```
14 | python examples/debug/axis.py \
15 | --port /dev/ttyACM0
16 | ```
17 |
18 | #### Disable torque for all arm motors
19 | ```
20 | python examples/debug/motors.py reset_motors_torque \
21 | --port /dev/ttyACM0
22 | ```
23 |
24 | #### Rotate a specific motor by ID
25 | ```
26 | python examples/debug/motors.py move_motor_to_position \
27 | --id 1 \
28 | --position 2 \
29 | --port /dev/ttyACM1
30 | ```
31 |
32 |
33 | #### Set a new motor ID
34 | ```
35 | python examples/debug/motors.py configure_motor_id \
36 | --id 10 \
37 | --set_id 8 \
38 | --port /dev/ttyACM0
39 | ```
40 |
41 |
42 | #### Reset current position as the motor midpoint
43 | ```
44 | python examples/debug/motors.py reset_motors_to_midpoint \
45 | --port /dev/ttyACM1
46 | ```
47 |
48 |
49 | #### Execute an action script on the robot arm
50 | ```
51 | python examples/debug/motors.py move_motors_by_script \
52 | --script_path action_scripts/test_dance.txt \
53 | --port /dev/ttyACM0
54 | ```
55 |
56 |
--------------------------------------------------------------------------------
/src/lerobot/utils/errors.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 |
16 | class DeviceNotConnectedError(ConnectionError):
17 | """Exception raised when the device is not connected."""
18 |
19 | def __init__(self, message="This device is not connected. Try calling `connect()` first."):
20 | self.message = message
21 | super().__init__(self.message)
22 |
23 |
24 | class DeviceAlreadyConnectedError(ConnectionError):
25 | """Exception raised when the device is already connected."""
26 |
27 | def __init__(
28 | self,
29 | message="This device is already connected. Try not calling `connect()` twice.",
30 | ):
31 | self.message = message
32 | super().__init__(self.message)
33 |
--------------------------------------------------------------------------------
/docs/source/policy_groot_README.md:
--------------------------------------------------------------------------------
1 | ## Research Paper
2 |
3 | Paper: https://research.nvidia.com/labs/gear/gr00t-n1_5/
4 |
5 | ## Repository
6 |
7 | Code: https://github.com/NVIDIA/Isaac-GR00T
8 |
9 | ## Citation
10 |
11 | ```bibtex
12 | @inproceedings{gr00tn1_2025,
13 | archivePrefix = {arxiv},
14 | eprint = {2503.14734},
15 | title = {{GR00T} {N1}: An Open Foundation Model for Generalist Humanoid Robots},
16 | author = {NVIDIA and Johan Bjorck andFernando Castañeda, Nikita Cherniadev and Xingye Da and Runyu Ding and Linxi "Jim" Fan and Yu Fang and Dieter Fox and Fengyuan Hu and Spencer Huang and Joel Jang and Zhenyu Jiang and Jan Kautz and Kaushil Kundalia and Lawrence Lao and Zhiqi Li and Zongyu Lin and Kevin Lin and Guilin Liu and Edith Llontop and Loic Magne and Ajay Mandlekar and Avnish Narayan and Soroush Nasiriany and Scott Reed and You Liang Tan and Guanzhi Wang and Zu Wang and Jing Wang and Qi Wang and Jiannan Xiang and Yuqi Xie and Yinzhen Xu and Zhenjia Xu and Seonghyeon Ye and Zhiding Yu and Ao Zhang and Hao Zhang and Yizhou Zhao and Ruijie Zheng and Yuke Zhu},
17 | month = {March},
18 | year = {2025},
19 | booktitle = {ArXiv Preprint},
20 | }
21 | ```
22 |
23 | ## Additional Resources
24 |
25 | Blog: https://developer.nvidia.com/isaac/gr00t
26 |
27 | Hugging Face Model: https://huggingface.co/nvidia/GR00T-N1.5-3B
28 |
--------------------------------------------------------------------------------
/src/lerobot/policies/rtc/README.md:
--------------------------------------------------------------------------------
1 | # Real-Time Chunking (RTC)
2 |
3 | This module contains the LeRobot implementation of **Real-Time Chunking (RTC)**, an inference-time technique for flow-matching based policies.
4 |
5 | **Note**: RTC is not a policy itself, but rather an inference enhancement that works with flow-matching based policies including [π₀](../pi0/), [π₀.₅](../pi05/), and [SmolVLA](../smolvla/).
6 |
7 | ---
8 |
9 | ## Citation
10 |
11 | If you use Real-Time Chunking in your work, please cite:
12 |
13 | ```bibtex
14 | @misc{openpi2024,
15 | author = {Physical Intelligence Lab},
16 | title = {OpenPI: PyTorch Implementation of π0 and π0.5 Policies},
17 | year = {2024},
18 | publisher = {GitHub},
19 | howpublished = {\url{https://github.com/Physical-Intelligence/openpi}},
20 | license = {Apache-2.0}
21 | }
22 |
23 | @misc{black2025realtimeexecutionactionchunking,
24 | title={Real-Time Execution of Action Chunking Flow Policies},
25 | author={Kevin Black and Manuel Y. Galliker and Sergey Levine},
26 | year={2025},
27 | eprint={2506.07339},
28 | archivePrefix={arXiv},
29 | primaryClass={cs.RO},
30 | url={https://arxiv.org/abs/2506.07339},
31 | }
32 | ```
33 |
34 | ---
35 |
36 | ## License
37 |
38 | This implementation follows the **Apache 2.0 License**, consistent with the LeRobot project.
39 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## What this does
2 |
3 | Explain what this PR does. Feel free to tag your PR with the appropriate label(s).
4 |
5 | Examples:
6 | | Title | Label |
7 | |----------------------|-----------------|
8 | | Fixes #[issue] | (🐛 Bug) |
9 | | Adds new dataset | (🗃️ Dataset) |
10 | | Optimizes something | (⚡️ Performance) |
11 |
12 | ## How it was tested
13 |
14 | Explain/show how you tested your changes.
15 |
16 | Examples:
17 |
18 | - Added `test_something` in `tests/test_stuff.py`.
19 | - Added `new_feature` and checked that training converges with policy X on dataset/environment Y.
20 | - Optimized `some_function`, it now runs X times faster than previously.
21 |
22 | ## How to checkout & try? (for the reviewer)
23 |
24 | Provide a simple way for the reviewer to try out your changes.
25 |
26 | Examples:
27 |
28 | ```bash
29 | pytest -sx tests/test_stuff.py::test_something
30 | ```
31 |
32 | ```bash
33 | lerobot-train --some.option=true
34 | ```
35 |
36 | ## SECTION TO REMOVE BEFORE SUBMITTING YOUR PR
37 |
38 | **Note**: Anyone in the community is free to review the PR once the tests have passed. Feel free to tag
39 | members/contributors who may be interested in your PR. Try to avoid tagging more than 3 people.
40 |
41 | **Note**: Before submitting this PR, please read the [contributor guideline](https://github.com/huggingface/lerobot/blob/main/CONTRIBUTING.md#submitting-a-pull-request-pr).
42 |
--------------------------------------------------------------------------------
/src/lerobot/robots/earthrover_mini_plus/config_earthrover_mini_plus.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | """Configuration for EarthRover Mini Plus robot."""
17 |
18 | from dataclasses import dataclass
19 |
20 | from ..config import RobotConfig
21 |
22 |
23 | @RobotConfig.register_subclass("earthrover_mini_plus")
24 | @dataclass
25 | class EarthRoverMiniPlusConfig(RobotConfig):
26 | """Configuration for EarthRover Mini Plus robot using Frodobots SDK.
27 |
28 | This robot uses cloud-based control via the Frodobots SDK HTTP API.
29 | Camera frames are accessed directly through SDK HTTP endpoints.
30 |
31 | Attributes:
32 | sdk_url: URL of the Frodobots SDK server (default: http://localhost:8000)
33 | """
34 |
35 | sdk_url: str = "http://localhost:8000"
36 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/homunculus/config_homunculus.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from dataclasses import dataclass
18 |
19 | from ..config import TeleoperatorConfig
20 |
21 |
22 | @TeleoperatorConfig.register_subclass("homunculus_glove")
23 | @dataclass
24 | class HomunculusGloveConfig(TeleoperatorConfig):
25 | port: str # Port to connect to the glove
26 | side: str # "left" / "right"
27 | baud_rate: int = 115_200
28 |
29 | def __post_init__(self):
30 | if self.side not in ["right", "left"]:
31 | raise ValueError(self.side)
32 |
33 |
34 | @TeleoperatorConfig.register_subclass("homunculus_arm")
35 | @dataclass
36 | class HomunculusArmConfig(TeleoperatorConfig):
37 | port: str # Port to connect to the arm
38 | baud_rate: int = 115_200
39 |
--------------------------------------------------------------------------------
/src/lerobot/cameras/configs.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | import abc
18 | from dataclasses import dataclass
19 | from enum import Enum
20 |
21 | import draccus # type: ignore # TODO: add type stubs for draccus
22 |
23 |
24 | class ColorMode(str, Enum):
25 | RGB = "rgb"
26 | BGR = "bgr"
27 |
28 |
29 | class Cv2Rotation(int, Enum):
30 | NO_ROTATION = 0
31 | ROTATE_90 = 90
32 | ROTATE_180 = 180
33 | ROTATE_270 = -90
34 |
35 |
36 | @dataclass(kw_only=True)
37 | class CameraConfig(draccus.ChoiceRegistry, abc.ABC): # type: ignore # TODO: add type stubs for draccus
38 | fps: int | None = None
39 | width: int | None = None
40 | height: int | None = None
41 |
42 | @property
43 | def type(self) -> str:
44 | return str(self.get_choice_name(self.__class__))
45 |
--------------------------------------------------------------------------------
/tests/fixtures/optimizers.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | import pytest
15 | import torch
16 |
17 | from lerobot.optim.optimizers import AdamConfig
18 | from lerobot.optim.schedulers import VQBeTSchedulerConfig
19 |
20 |
21 | @pytest.fixture
22 | def model_params():
23 | return [torch.nn.Parameter(torch.randn(10, 10))]
24 |
25 |
26 | @pytest.fixture
27 | def optimizer(model_params):
28 | optimizer = AdamConfig().build(model_params)
29 | # Dummy step to populate state
30 | loss = sum(param.sum() for param in model_params)
31 | loss.backward()
32 | optimizer.step()
33 | return optimizer
34 |
35 |
36 | @pytest.fixture
37 | def scheduler(optimizer):
38 | config = VQBeTSchedulerConfig(num_warmup_steps=10, num_vqvae_training_steps=20, num_cycles=0.5)
39 | return config.build(optimizer, num_training_steps=100)
40 |
--------------------------------------------------------------------------------
/src/lerobot/robots/bi_so100_follower/config_bi_so100_follower.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from dataclasses import dataclass, field
18 |
19 | from lerobot.cameras import CameraConfig
20 |
21 | from ..config import RobotConfig
22 |
23 |
24 | @RobotConfig.register_subclass("bi_so100_follower")
25 | @dataclass
26 | class BiSO100FollowerConfig(RobotConfig):
27 | left_arm_port: str
28 | right_arm_port: str
29 |
30 | # Optional
31 | left_arm_disable_torque_on_disconnect: bool = True
32 | left_arm_max_relative_target: float | dict[str, float] | None = None
33 | left_arm_use_degrees: bool = False
34 | right_arm_disable_torque_on_disconnect: bool = True
35 | right_arm_max_relative_target: float | dict[str, float] | None = None
36 | right_arm_use_degrees: bool = False
37 |
38 | # cameras (shared between both arms)
39 | cameras: dict[str, CameraConfig] = field(default_factory=dict)
40 |
--------------------------------------------------------------------------------
/docs/source/torch_accelerators.mdx:
--------------------------------------------------------------------------------
1 | # PyTorch accelerators
2 |
3 | LeRobot supports multiple hardware acceleration options for both training and inference.
4 |
5 | These options include:
6 |
7 | - **CPU**: CPU executes all computations, no dedicated accelerator is used
8 | - **CUDA**: acceleration with NVIDIA & AMD GPUs
9 | - **MPS**: acceleration with Apple Silicon GPUs
10 | - **XPU**: acceleration with Intel integrated and discrete GPUs
11 |
12 | ## Getting Started
13 |
14 | To use particular accelerator, a suitable version of PyTorch should be installed.
15 |
16 | For CPU, CUDA, and MPS backends follow instructions provided on [PyTorch installation page](https://pytorch.org/get-started/locally).
17 | For XPU backend, follow instructions from [PyTorch documentation](https://docs.pytorch.org/docs/stable/notes/get_start_xpu.html).
18 |
19 | ### Verifying the installation
20 |
21 | After installation, accelerator availability can be verified by running
22 |
23 | ```python
24 | import torch
25 | print(torch..is_available()) # is cuda, mps, or xpu
26 | ```
27 |
28 | ## How to run training or evaluation
29 |
30 | To select the desired accelerator, use the `--policy.device` flag when running `lerobot-train` or `lerobot-eval`. For example, to use MPS on Apple Silicon, run:
31 |
32 | ```bash
33 | lerobot-train
34 | --policy.device=mps ...
35 | ```
36 |
37 | ```bash
38 | lerobot-eval \
39 | --policy.device=mps ...
40 | ```
41 |
42 | However, in most cases, presence of an accelerator is detected automatically and `policy.device` parameter can be omitted from CLI commands.
43 |
--------------------------------------------------------------------------------
/src/lerobot/policies/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | from .act.configuration_act import ACTConfig as ACTConfig
16 | from .diffusion.configuration_diffusion import DiffusionConfig as DiffusionConfig
17 | from .groot.configuration_groot import GrootConfig as GrootConfig
18 | from .pi0.configuration_pi0 import PI0Config as PI0Config
19 | from .pi05.configuration_pi05 import PI05Config as PI05Config
20 | from .smolvla.configuration_smolvla import SmolVLAConfig as SmolVLAConfig
21 | from .smolvla.processor_smolvla import SmolVLANewLineProcessor
22 | from .tdmpc.configuration_tdmpc import TDMPCConfig as TDMPCConfig
23 | from .vqbet.configuration_vqbet import VQBeTConfig as VQBeTConfig
24 | from .xvla.configuration_xvla import XVLAConfig as XVLAConfig
25 |
26 | __all__ = [
27 | "ACTConfig",
28 | "DiffusionConfig",
29 | "PI0Config",
30 | "PI05Config",
31 | "SmolVLAConfig",
32 | "TDMPCConfig",
33 | "VQBeTConfig",
34 | "GrootConfig",
35 | "XVLAConfig",
36 | ]
37 |
--------------------------------------------------------------------------------
/src/lerobot/robots/config.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | import abc
16 | from dataclasses import dataclass
17 | from pathlib import Path
18 |
19 | import draccus
20 |
21 |
22 | @dataclass(kw_only=True)
23 | class RobotConfig(draccus.ChoiceRegistry, abc.ABC):
24 | # Allows to distinguish between different robots of the same type
25 | id: str | None = None
26 | # Directory to store calibration file
27 | calibration_dir: Path | None = None
28 |
29 | def __post_init__(self):
30 | if hasattr(self, "cameras") and self.cameras:
31 | for _, config in self.cameras.items():
32 | for attr in ["width", "height", "fps"]:
33 | if getattr(config, attr) is None:
34 | raise ValueError(
35 | f"Specifying '{attr}' is required for the camera to be used in a robot"
36 | )
37 |
38 | @property
39 | def type(self) -> str:
40 | return self.get_choice_name(self.__class__)
41 |
--------------------------------------------------------------------------------
/src/lerobot/robots/koch_follower/config_koch_follower.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | from dataclasses import dataclass, field
16 |
17 | from lerobot.cameras import CameraConfig
18 |
19 | from ..config import RobotConfig
20 |
21 |
22 | @RobotConfig.register_subclass("koch_follower")
23 | @dataclass
24 | class KochFollowerConfig(RobotConfig):
25 | # Port to connect to the arm
26 | port: str
27 |
28 | disable_torque_on_disconnect: bool = True
29 |
30 | # `max_relative_target` limits the magnitude of the relative positional target vector for safety purposes.
31 | # Set this to a positive scalar to have the same value for all motors, or a dictionary that maps motor
32 | # names to the max_relative_target value for that motor.
33 | max_relative_target: float | dict[str, float] | None = None
34 |
35 | # cameras
36 | cameras: dict[str, CameraConfig] = field(default_factory=dict)
37 |
38 | # Set to `True` for backward compatibility with previous policies/dataset
39 | use_degrees: bool = False
40 |
--------------------------------------------------------------------------------
/src/lerobot/configs/types.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | # Note: We subclass str so that serialization is straightforward
15 | # https://stackoverflow.com/questions/24481852/serialising-an-enum-member-to-json
16 | from dataclasses import dataclass
17 | from enum import Enum
18 |
19 |
20 | class FeatureType(str, Enum):
21 | STATE = "STATE"
22 | VISUAL = "VISUAL"
23 | ENV = "ENV"
24 | ACTION = "ACTION"
25 | REWARD = "REWARD"
26 | LANGUAGE = "LANGUAGE"
27 |
28 |
29 | class PipelineFeatureType(str, Enum):
30 | ACTION = "ACTION"
31 | OBSERVATION = "OBSERVATION"
32 |
33 |
34 | class NormalizationMode(str, Enum):
35 | MIN_MAX = "MIN_MAX"
36 | MEAN_STD = "MEAN_STD"
37 | IDENTITY = "IDENTITY"
38 | QUANTILES = "QUANTILES"
39 | QUANTILE10 = "QUANTILE10"
40 |
41 |
42 | @dataclass
43 | class PolicyFeature:
44 | type: FeatureType
45 | shape: tuple[int, ...]
46 |
47 |
48 | class RTCAttentionSchedule(str, Enum):
49 | ZEROS = "ZEROS"
50 | ONES = "ONES"
51 | LINEAR = "LINEAR"
52 | EXP = "EXP"
53 |
--------------------------------------------------------------------------------
/src/lerobot/robots/so100_follower/config_so100_follower.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from dataclasses import dataclass, field
18 |
19 | from lerobot.cameras import CameraConfig
20 |
21 | from ..config import RobotConfig
22 |
23 |
24 | @RobotConfig.register_subclass("so100_follower")
25 | @dataclass
26 | class SO100FollowerConfig(RobotConfig):
27 | # Port to connect to the arm
28 | port: str
29 |
30 | disable_torque_on_disconnect: bool = True
31 |
32 | # `max_relative_target` limits the magnitude of the relative positional target vector for safety purposes.
33 | # Set this to a positive scalar to have the same value for all motors, or a dictionary that maps motor
34 | # names to the max_relative_target value for that motor.
35 | max_relative_target: float | dict[str, float] | None = None
36 |
37 | # cameras
38 | cameras: dict[str, CameraConfig] = field(default_factory=dict)
39 |
40 | # Set to `True` for backward compatibility with previous policies/dataset
41 | use_degrees: bool = False
42 |
--------------------------------------------------------------------------------
/src/lerobot/robots/so101_follower/config_so101_follower.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from dataclasses import dataclass, field
18 |
19 | from lerobot.cameras import CameraConfig
20 |
21 | from ..config import RobotConfig
22 |
23 |
24 | @RobotConfig.register_subclass("so101_follower")
25 | @dataclass
26 | class SO101FollowerConfig(RobotConfig):
27 | # Port to connect to the arm
28 | port: str
29 |
30 | disable_torque_on_disconnect: bool = True
31 |
32 | # `max_relative_target` limits the magnitude of the relative positional target vector for safety purposes.
33 | # Set this to a positive scalar to have the same value for all motors, or a dictionary that maps motor
34 | # names to the max_relative_target value for that motor.
35 | max_relative_target: float | dict[str, float] | None = None
36 |
37 | # cameras
38 | cameras: dict[str, CameraConfig] = field(default_factory=dict)
39 |
40 | # Set to `True` for backward compatibility with previous policies/dataset
41 | use_degrees: bool = False
42 |
--------------------------------------------------------------------------------
/.github/workflows/documentation-upload-pr.yml:
--------------------------------------------------------------------------------
1 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | # This workflow uploads the documentation preview built for a PR and comments the link on the PR.
16 | name: Documentation PR Upload
17 | permissions:
18 | contents: read
19 | pull-requests: write
20 |
21 | on:
22 | # Triggered by the completion of the main 'Documentation' workflow.
23 | workflow_run: # zizmor: ignore[dangerous-triggers] We follow the same pattern as in Transformers
24 | workflows: ["Documentation"]
25 | types:
26 | - completed
27 |
28 | jobs:
29 | # This job uploads a preview of the documentation for a pull request.
30 | upload_and_comment:
31 | name: Upload Preview and Comment
32 | if: >
33 | github.event.workflow_run.event == 'pull_request' &&
34 | github.event.workflow_run.conclusion == 'success'
35 | uses: huggingface/doc-builder/.github/workflows/upload_pr_documentation.yml@main
36 | with:
37 | package_name: lerobot
38 | secrets:
39 | hf_token: ${{ secrets.HF_DOC_BUILD_PUSH }}
40 | comment_bot_token: ${{ secrets.COMMENT_BOT_TOKEN }}
41 |
--------------------------------------------------------------------------------
/tests/fixtures/constants.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | from lerobot.utils.constants import ACTION, HF_LEROBOT_HOME
15 |
16 | LEROBOT_TEST_DIR = HF_LEROBOT_HOME / "_testing"
17 | DUMMY_REPO_ID = "dummy/repo"
18 | DUMMY_ROBOT_TYPE = "dummy_robot"
19 | DUMMY_MOTOR_FEATURES = {
20 | ACTION: {
21 | "dtype": "float32",
22 | "shape": (6,),
23 | "names": ["shoulder_pan", "shoulder_lift", "elbow_flex", "wrist_flex", "wrist_roll", "gripper"],
24 | },
25 | "state": {
26 | "dtype": "float32",
27 | "shape": (6,),
28 | "names": ["shoulder_pan", "shoulder_lift", "elbow_flex", "wrist_flex", "wrist_roll", "gripper"],
29 | },
30 | }
31 | DUMMY_CAMERA_FEATURES = {
32 | "laptop": {"shape": (64, 96, 3), "names": ["height", "width", "channels"], "info": None},
33 | "phone": {"shape": (64, 96, 3), "names": ["height", "width", "channels"], "info": None},
34 | }
35 | DEFAULT_FPS = 30
36 | DUMMY_VIDEO_INFO = {
37 | "video.fps": DEFAULT_FPS,
38 | "video.codec": "av1",
39 | "video.pix_fmt": "yuv420p",
40 | "video.is_depth_map": False,
41 | "has_audio": False,
42 | }
43 | DUMMY_CHW = (3, 96, 128)
44 | DUMMY_HWC = (96, 128, 3)
45 |
--------------------------------------------------------------------------------
/tests/plugins/reachy2_sdk.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | import sys
18 | import types
19 | from unittest.mock import MagicMock
20 |
21 |
22 | def _install_reachy2_sdk_stub():
23 | sdk = types.ModuleType("reachy2_sdk")
24 | sdk.__path__ = []
25 | sdk.ReachySDK = MagicMock(name="ReachySDK")
26 |
27 | media = types.ModuleType("reachy2_sdk.media")
28 | media.__path__ = []
29 | camera = types.ModuleType("reachy2_sdk.media.camera")
30 | camera.CameraView = MagicMock(name="CameraView")
31 | camera_manager = types.ModuleType("reachy2_sdk.media.camera_manager")
32 | camera_manager.CameraManager = MagicMock(name="CameraManager")
33 |
34 | sdk.media = media
35 | media.camera = camera
36 | media.camera_manager = camera_manager
37 |
38 | # Register in sys.modules
39 | sys.modules.setdefault("reachy2_sdk", sdk)
40 | sys.modules.setdefault("reachy2_sdk.media", media)
41 | sys.modules.setdefault("reachy2_sdk.media.camera", camera)
42 | sys.modules.setdefault("reachy2_sdk.media.camera_manager", camera_manager)
43 |
44 |
45 | def pytest_sessionstart(session):
46 | _install_reachy2_sdk_stub()
47 |
--------------------------------------------------------------------------------
/src/lerobot/optim/factory.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 |
18 | from torch.optim import Optimizer
19 | from torch.optim.lr_scheduler import LRScheduler
20 |
21 | from lerobot.configs.train import TrainPipelineConfig
22 | from lerobot.policies.pretrained import PreTrainedPolicy
23 |
24 |
25 | def make_optimizer_and_scheduler(
26 | cfg: TrainPipelineConfig, policy: PreTrainedPolicy
27 | ) -> tuple[Optimizer, LRScheduler | None]:
28 | """Generates the optimizer and scheduler based on configs.
29 |
30 | Args:
31 | cfg (TrainPipelineConfig): The training config that contains optimizer and scheduler configs
32 | policy (PreTrainedPolicy): The policy config from which parameters and presets must be taken from.
33 |
34 | Returns:
35 | tuple[Optimizer, LRScheduler | None]: The couple (Optimizer, Scheduler). Scheduler can be `None`.
36 | """
37 | params = policy.get_optim_params() if cfg.use_policy_training_preset else policy.parameters()
38 | optimizer = cfg.optimizer.build(params)
39 | lr_scheduler = cfg.scheduler.build(optimizer, cfg.steps) if cfg.scheduler is not None else None
40 | return optimizer, lr_scheduler
41 |
--------------------------------------------------------------------------------
/src/lerobot/policies/groot/utils.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 | from shutil import copytree
3 |
4 | from huggingface_hub import hf_hub_download
5 |
6 |
7 | def ensure_eagle_cache_ready(vendor_dir: Path, cache_dir: Path, assets_repo: str) -> None:
8 | """Populate the Eagle processor directory in cache and ensure tokenizer assets exist.
9 |
10 | - Copies the vendored Eagle files into cache_dir (overwriting when needed).
11 | - Downloads vocab.json and merges.txt into the same cache_dir if missing.
12 | """
13 | cache_dir = Path(cache_dir)
14 | vendor_dir = Path(vendor_dir)
15 |
16 | try:
17 | # Populate/refresh cache with vendor files to ensure a complete processor directory
18 | print(f"[GROOT] Copying vendor Eagle files to cache: {vendor_dir} -> {cache_dir}")
19 | copytree(vendor_dir, cache_dir, dirs_exist_ok=True)
20 | except Exception as exc: # nosec: B110
21 | print(f"[GROOT] Warning: Failed to copy vendor Eagle files to cache: {exc}")
22 |
23 | required_assets = [
24 | "vocab.json",
25 | "merges.txt",
26 | "added_tokens.json",
27 | "chat_template.json",
28 | "special_tokens_map.json",
29 | "config.json",
30 | "generation_config.json",
31 | "preprocessor_config.json",
32 | "processor_config.json",
33 | "tokenizer_config.json",
34 | ]
35 |
36 | print(f"[GROOT] Assets repo: {assets_repo} \n Cache dir: {cache_dir}")
37 |
38 | for fname in required_assets:
39 | dst = cache_dir / fname
40 | if not dst.exists():
41 | print(f"[GROOT] Fetching {fname}")
42 | hf_hub_download(
43 | repo_id=assets_repo,
44 | filename=fname,
45 | repo_type="model",
46 | local_dir=str(cache_dir),
47 | )
48 |
--------------------------------------------------------------------------------
/src/lerobot/rl/queue.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | import platform
18 | from contextlib import suppress
19 | from queue import Empty
20 | from typing import Any
21 |
22 | from torch.multiprocessing import Queue
23 |
24 |
25 | def get_last_item_from_queue(queue: Queue, block=True, timeout: float = 0.1) -> Any:
26 | if block:
27 | try:
28 | item = queue.get(timeout=timeout)
29 | except Empty:
30 | return None
31 | else:
32 | item = None
33 |
34 | # Drain queue and keep only the most recent parameters
35 | if platform.system() == "Darwin":
36 | # On Mac, avoid using `qsize` due to unreliable implementation.
37 | # There is a comment on `qsize` code in the Python source:
38 | # Raises NotImplementedError on Mac OSX because of broken sem_getvalue()
39 | try:
40 | while True:
41 | item = queue.get_nowait()
42 | except Empty:
43 | pass
44 |
45 | return item
46 |
47 | # Details about using qsize in https://github.com/huggingface/lerobot/issues/1523
48 | while queue.qsize() > 0:
49 | with suppress(Empty):
50 | item = queue.get_nowait()
51 |
52 | return item
53 |
--------------------------------------------------------------------------------
/tests/mocks/mock_serial_patch.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | import threading
18 | import time
19 |
20 | from mock_serial.mock_serial import Stub
21 |
22 |
23 | class WaitableStub(Stub):
24 | """
25 | In some situations, a test might be checking if a stub has been called before `MockSerial` thread had time
26 | to read, match, and call the stub. In these situations, the test can fail randomly.
27 |
28 | Use `wait_called()` or `wait_calls()` to block until the stub is called, avoiding race conditions.
29 |
30 | Proposed fix:
31 | https://github.com/benthorner/mock_serial/pull/3
32 | """
33 |
34 | def __init__(self, **kwargs):
35 | super().__init__(**kwargs)
36 | self._event = threading.Event()
37 |
38 | def call(self):
39 | self._event.set()
40 | return super().call()
41 |
42 | def wait_called(self, timeout: float = 1.0):
43 | return self._event.wait(timeout)
44 |
45 | def wait_calls(self, min_calls: int = 1, timeout: float = 1.0):
46 | start = time.perf_counter()
47 | while time.perf_counter() - start < timeout:
48 | if self.calls >= min_calls:
49 | return self.calls
50 | time.sleep(0.005)
51 | raise TimeoutError(f"Stub not called {min_calls} times within {timeout} seconds.")
52 |
--------------------------------------------------------------------------------
/.github/workflows/security.yml:
--------------------------------------------------------------------------------
1 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | # This workflow handles secret scanning using TruffleHog to detect sensitive information in the codebase.
16 | name: Security
17 | permissions:
18 | contents: read
19 |
20 | on:
21 | # Allows running this workflow manually from the Actions tab
22 | workflow_dispatch:
23 |
24 | # Triggers the workflow on push events to main
25 | push:
26 | branches:
27 | - main
28 |
29 | # Triggers the workflow on pull request events targeting main
30 | pull_request:
31 | branches:
32 | - main
33 |
34 | # Ensures that only the latest commit for a PR or branch is built, canceling older runs.
35 | concurrency:
36 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
37 | cancel-in-progress: true
38 |
39 | jobs:
40 | # This job runs TruffleHog to scan the full history of the repository for secrets.
41 | trufflehog:
42 | name: Secret Leaks Scan
43 | runs-on: ubuntu-latest
44 | steps:
45 | - name: Checkout code
46 | uses: actions/checkout@v4 # zizmor: ignore[unpinned-uses]
47 | with:
48 | fetch-depth: 0
49 | persist-credentials: false
50 |
51 | - name: Secret Scanning
52 | uses: trufflesecurity/trufflehog@v3.90.0 # zizmor: ignore[unpinned-uses]
53 | with:
54 | extra_args: --only-verified
55 |
--------------------------------------------------------------------------------
/src/lerobot/processor/core.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from __future__ import annotations
18 |
19 | from enum import Enum
20 | from typing import Any, TypeAlias, TypedDict
21 |
22 | import numpy as np
23 | import torch
24 |
25 |
26 | class TransitionKey(str, Enum):
27 | """Keys for accessing EnvTransition dictionary components."""
28 |
29 | # TODO(Steven): Use consts
30 | OBSERVATION = "observation"
31 | ACTION = "action"
32 | REWARD = "reward"
33 | DONE = "done"
34 | TRUNCATED = "truncated"
35 | INFO = "info"
36 | COMPLEMENTARY_DATA = "complementary_data"
37 |
38 |
39 | PolicyAction: TypeAlias = torch.Tensor
40 | RobotAction: TypeAlias = dict[str, Any]
41 | EnvAction: TypeAlias = np.ndarray
42 | RobotObservation: TypeAlias = dict[str, Any]
43 |
44 |
45 | EnvTransition = TypedDict(
46 | "EnvTransition",
47 | {
48 | TransitionKey.OBSERVATION.value: dict[str, Any] | None,
49 | TransitionKey.ACTION.value: PolicyAction | RobotAction | EnvAction | None,
50 | TransitionKey.REWARD.value: float | torch.Tensor | None,
51 | TransitionKey.DONE.value: bool | torch.Tensor | None,
52 | TransitionKey.TRUNCATED.value: bool | torch.Tensor | None,
53 | TransitionKey.INFO.value: dict[str, Any] | None,
54 | TransitionKey.COMPLEMENTARY_DATA.value: dict[str, Any] | None,
55 | },
56 | )
57 |
--------------------------------------------------------------------------------
/src/lerobot/robots/hope_jr/config_hope_jr.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from dataclasses import dataclass, field
18 |
19 | from lerobot.cameras import CameraConfig
20 |
21 | from ..config import RobotConfig
22 |
23 |
24 | @RobotConfig.register_subclass("hope_jr_hand")
25 | @dataclass
26 | class HopeJrHandConfig(RobotConfig):
27 | port: str # Port to connect to the hand
28 | side: str # "left" / "right"
29 |
30 | disable_torque_on_disconnect: bool = True
31 |
32 | cameras: dict[str, CameraConfig] = field(default_factory=dict)
33 |
34 | def __post_init__(self):
35 | super().__post_init__()
36 | if self.side not in ["right", "left"]:
37 | raise ValueError(self.side)
38 |
39 |
40 | @RobotConfig.register_subclass("hope_jr_arm")
41 | @dataclass
42 | class HopeJrArmConfig(RobotConfig):
43 | port: str # Port to connect to the hand
44 | disable_torque_on_disconnect: bool = True
45 |
46 | # `max_relative_target` limits the magnitude of the relative positional target vector for safety purposes.
47 | # Set this to a positive scalar to have the same value for all motors, or a dictionary that maps motor
48 | # names to the max_relative_target value for that motor.
49 | max_relative_target: float | dict[str, float] | None = None
50 |
51 | cameras: dict[str, CameraConfig] = field(default_factory=dict)
52 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/reachy2_teleoperator/config_reachy2_teleoperator.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from dataclasses import dataclass
18 |
19 | from ..config import TeleoperatorConfig
20 |
21 |
22 | @TeleoperatorConfig.register_subclass("reachy2_teleoperator")
23 | @dataclass
24 | class Reachy2TeleoperatorConfig(TeleoperatorConfig):
25 | # IP address of the Reachy 2 robot used as teleoperator
26 | ip_address: str | None = "localhost"
27 |
28 | # Whether to use the present position of the joints as actions
29 | # if False, the goal position of the joints will be used
30 | use_present_position: bool = False
31 |
32 | # Which parts of the robot to use
33 | with_mobile_base: bool = True
34 | with_l_arm: bool = True
35 | with_r_arm: bool = True
36 | with_neck: bool = True
37 | with_antennas: bool = True
38 |
39 | def __post_init__(self):
40 | if not (
41 | self.with_mobile_base
42 | or self.with_l_arm
43 | or self.with_r_arm
44 | or self.with_neck
45 | or self.with_antennas
46 | ):
47 | raise ValueError(
48 | "No Reachy2Teleoperator part used.\n"
49 | "At least one part of the robot must be set to True "
50 | "(with_mobile_base, with_l_arm, with_r_arm, with_neck, with_antennas)"
51 | )
52 |
--------------------------------------------------------------------------------
/src/lerobot/policies/groot/action_head/action_encoder.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2 | # SPDX-License-Identifier: Apache-2.0
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | import torch
17 | import torch.nn as nn
18 |
19 |
20 | def swish(x):
21 | return x * torch.sigmoid(x)
22 |
23 |
24 | class SinusoidalPositionalEncoding(nn.Module):
25 | """
26 | Produces a sinusoidal encoding of shape (B, T, w)
27 | given timesteps of shape (B, T).
28 | """
29 |
30 | def __init__(self, embedding_dim):
31 | super().__init__()
32 | self.embedding_dim = embedding_dim
33 |
34 | def forward(self, timesteps):
35 | # timesteps: shape (B, T)
36 | # We'll compute sin/cos frequencies across dim T
37 | timesteps = timesteps.float() # ensure float
38 |
39 | b, t = timesteps.shape
40 | device = timesteps.device
41 |
42 | half_dim = self.embedding_dim // 2
43 | # typical log space frequencies for sinusoidal encoding
44 | exponent = -torch.arange(half_dim, dtype=torch.float, device=device) * (
45 | torch.log(torch.tensor(10000.0)) / half_dim
46 | )
47 | # Expand timesteps to (B, T, 1) then multiply
48 | freqs = timesteps.unsqueeze(-1) * exponent.exp() # (B, T, half_dim)
49 |
50 | sin = torch.sin(freqs)
51 | cos = torch.cos(freqs)
52 | enc = torch.cat([sin, cos], dim=-1) # (B, T, w)
53 |
54 | return enc
55 |
--------------------------------------------------------------------------------
/examples/debug/test_input.py:
--------------------------------------------------------------------------------
1 | import os, sys, time, platform
2 | from contextlib import suppress
3 |
4 | print("=== Env Check ===")
5 | print("platform:", platform.platform())
6 | print("XDG_SESSION_TYPE:", os.environ.get("XDG_SESSION_TYPE"))
7 | print("DISPLAY:", os.environ.get("DISPLAY"))
8 | print("WAYLAND_DISPLAY:", os.environ.get("WAYLAND_DISPLAY"))
9 | print("SSH_CONNECTION:", os.environ.get("SSH_CONNECTION"))
10 | print("TTY:", os.ttyname(0) if sys.stdin.isatty() else "not a tty")
11 |
12 | print("\n=== Pynput Keyboard Listener Smoke Test (5s) ===")
13 | fired = {"press": False, "release": False}
14 | err = None
15 |
16 | try:
17 | from pynput import keyboard
18 | def on_press(key):
19 | fired["press"] = True
20 | with suppress(AttributeError):
21 | print(f"[on_press] {getattr(key, 'char', key)}")
22 |
23 | def on_release(key):
24 | fired["release"] = True
25 | print(f"[on_release] {key}")
26 | if key == keyboard.Key.esc:
27 | print("Esc detected → stopping listener")
28 | return False
29 |
30 | listener = keyboard.Listener(on_press=on_press, on_release=on_release)
31 | listener.start()
32 | print("Listening... press some keys (Esc to stop). Waiting up to 5s...")
33 | t0 = time.time()
34 | while time.time() - t0 < 5 and listener.is_alive():
35 | time.sleep(0.05)
36 | if listener.is_alive():
37 | listener.stop()
38 | time.sleep(0.2)
39 |
40 | except Exception as e:
41 | err = e
42 |
43 | print("\n=== Result ===")
44 | print("callbacks fired:", fired, "| error:", repr(err))
45 |
46 | # Heuristics
47 | print("\n=== Diagnosis Hint ===")
48 | if err:
49 | print("• 初始化报错,多为环境/依赖问题。")
50 | elif not fired["press"] and not fired["release"]:
51 | print("• 5 秒内未捕获事件:")
52 | print(" - 若 XDG_SESSION_TYPE=wayland → 请改用“Ubuntu on Xorg”登录,或用 evdev 方案(见下)。")
53 | print(" - 若在 SSH/TTY/tmux 中 → 请在本机图形终端里运行。")
54 | print(" - 若 DISPLAY 为空 → 没有连接到 X Server。")
55 | else:
56 | print("• 已成功捕获事件:pynput 正常,问题可能在你的业务脚本(回调、线程或主循环阻塞)。")
57 |
--------------------------------------------------------------------------------
/examples/alohamini/replay_bi.py:
--------------------------------------------------------------------------------
1 | import time
2 | import argparse
3 |
4 | from lerobot.datasets.lerobot_dataset import LeRobotDataset
5 | from lerobot.robots.alohamini.config_lekiwi import LeKiwiClientConfig
6 | from lerobot.robots.alohamini.lekiwi_client import LeKiwiClient
7 | from lerobot.utils.robot_utils import precise_sleep
8 | from lerobot.utils.utils import log_say
9 |
10 | parser = argparse.ArgumentParser(description="Replay a LeRobot dataset episode")
11 | parser.add_argument("--dataset", type=str, required=True,
12 | help="Dataset repo_id, e.g. liyitenga/record_20250914225057")
13 | parser.add_argument("--episode", type=int, default=0,
14 | help="Episode index to replay (default 0)")
15 | parser.add_argument("--remote_ip", type=str, default="127.0.0.1", help="LeKiwi host IP address")
16 | parser.add_argument("--robot_id", type=str, default="lekiwi", help="Robot ID")
17 |
18 |
19 |
20 | args = parser.parse_args()
21 |
22 |
23 | robot_config = LeKiwiClientConfig(remote_ip=args.remote_ip, id=args.robot_id)
24 | robot = LeKiwiClient(robot_config)
25 |
26 |
27 | #dataset = LeRobotDataset("liyitenga/record_20250914225057", episodes=[EPISODE_IDX])
28 | dataset = LeRobotDataset(args.dataset, episodes=[args.episode])
29 | actions = dataset.hf_dataset.select_columns("action")
30 | #print(f"Dataset loaded with id: {dataset.repo_id}, num_frames: {dataset.num_frames}")
31 |
32 | robot.connect()
33 |
34 | if not robot.is_connected:
35 | raise ValueError("Robot is not connected!")
36 |
37 | #log_say(f"Replaying episode {args.episode} from {args.dataset}")
38 | print(f"Replaying episode {args.episode} from {args.dataset}")
39 | for idx in range(dataset.num_frames):
40 | t0 = time.perf_counter()
41 |
42 | action = {
43 | name: float(actions[idx]["action"][i]) for i, name in enumerate(dataset.features["action"]["names"])
44 | }
45 |
46 | print(f"replay_bi.action:{action}")
47 | robot.send_action(action)
48 |
49 | precise_sleep(max(1.0 / dataset.fps - (time.perf_counter() - t0), 0.0))
50 |
51 | robot.disconnect()
52 |
--------------------------------------------------------------------------------
/.github/workflows/quality.yml:
--------------------------------------------------------------------------------
1 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | # This workflow handles linting, formatting, and static analysis checks for the codebase.
16 | name: Quality
17 | permissions:
18 | contents: read
19 |
20 | on:
21 | # Allows running this workflow manually from the Actions tab
22 | workflow_dispatch:
23 |
24 | # Triggers the workflow on push events to main
25 | push:
26 | branches:
27 | - main
28 |
29 | # Triggers the workflow on pull request events targeting main
30 | pull_request:
31 | branches:
32 | - main
33 |
34 | # Ensures that only the latest commit for a PR or branch is built, canceling older runs.
35 | concurrency:
36 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
37 | cancel-in-progress: true
38 |
39 | jobs:
40 | # This job runs pre-commit hooks to check code style and formatting.
41 | pre-commit-checks:
42 | name: Run Pre-commit Hooks (Lint, Format & Static Analysis)
43 | runs-on: ubuntu-latest
44 | steps:
45 | - name: Checkout code
46 | uses: actions/checkout@v4
47 | with:
48 | persist-credentials: false
49 |
50 | - name: Set up Python
51 | uses: actions/setup-python@v5
52 | with:
53 | python-version: '3.10'
54 |
55 | - name: Run pre-commit hooks
56 | uses: pre-commit/action@v3.0.1 # zizmor: ignore[unpinned-uses]
57 | with:
58 | extra_args: --all-files --show-diff-on-failure --color=always
59 |
--------------------------------------------------------------------------------
/src/lerobot/policies/rtc/configuration_rtc.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | """
18 | Real Time Chunking (RTC) and Bidirectional Decoding (BID) configuration classes.
19 |
20 | Based on:
21 | - Real Time Chunking: https://www.physicalintelligence.company/research/real_time_chunking
22 | """
23 |
24 | from dataclasses import dataclass
25 |
26 | from lerobot.configs.types import RTCAttentionSchedule
27 |
28 |
29 | @dataclass
30 | class RTCConfig:
31 | """Configuration for Real Time Chunking (RTC) inference.
32 |
33 | RTC improves real-time inference by treating chunk generation as an inpainting problem,
34 | strategically handling overlapping timesteps between action chunks using prefix attention.
35 | """
36 |
37 | # Infrastructure
38 | enabled: bool = False
39 |
40 | # Core RTC settings
41 | # Todo change to exp
42 | prefix_attention_schedule: RTCAttentionSchedule = RTCAttentionSchedule.LINEAR
43 | max_guidance_weight: float = 10.0
44 | execution_horizon: int = 10
45 |
46 | # Debug settings
47 | debug: bool = False
48 | debug_maxlen: int = 100
49 |
50 | def __post_init__(self):
51 | """Validate RTC configuration parameters."""
52 | if self.max_guidance_weight <= 0:
53 | raise ValueError(f"max_guidance_weight must be positive, got {self.max_guidance_weight}")
54 | if self.debug_maxlen <= 0:
55 | raise ValueError(f"debug_maxlen must be positive, got {self.debug_maxlen}")
56 |
--------------------------------------------------------------------------------
/src/lerobot/robots/unitree_g1/config_unitree_g1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from dataclasses import dataclass, field
18 |
19 | from ..config import RobotConfig
20 |
21 | _GAINS: dict[str, dict[str, list[float]]] = {
22 | "left_leg": {
23 | "kp": [150, 150, 150, 300, 40, 40],
24 | "kd": [2, 2, 2, 4, 2, 2],
25 | }, # pitch, roll, yaw, knee, ankle_pitch, ankle_roll
26 | "right_leg": {"kp": [150, 150, 150, 300, 40, 40], "kd": [2, 2, 2, 4, 2, 2]},
27 | "waist": {"kp": [250, 250, 250], "kd": [5, 5, 5]}, # yaw, roll, pitch
28 | "left_arm": {"kp": [80, 80, 80, 80], "kd": [3, 3, 3, 3]}, # shoulder_pitch/roll/yaw, elbow
29 | "left_wrist": {"kp": [40, 40, 40], "kd": [1.5, 1.5, 1.5]}, # roll, pitch, yaw
30 | "right_arm": {"kp": [80, 80, 80, 80], "kd": [3, 3, 3, 3]},
31 | "right_wrist": {"kp": [40, 40, 40], "kd": [1.5, 1.5, 1.5]},
32 | "other": {"kp": [80, 80, 80, 80, 80, 80], "kd": [3, 3, 3, 3, 3, 3]},
33 | }
34 |
35 |
36 | def _build_gains() -> tuple[list[float], list[float]]:
37 | """Build kp and kd lists from body-part groupings."""
38 | kp = [v for g in _GAINS.values() for v in g["kp"]]
39 | kd = [v for g in _GAINS.values() for v in g["kd"]]
40 | return kp, kd
41 |
42 |
43 | _DEFAULT_KP, _DEFAULT_KD = _build_gains()
44 |
45 |
46 | @RobotConfig.register_subclass("unitree_g1")
47 | @dataclass
48 | class UnitreeG1Config(RobotConfig):
49 | kp: list[float] = field(default_factory=lambda: _DEFAULT_KP.copy())
50 | kd: list[float] = field(default_factory=lambda: _DEFAULT_KD.copy())
51 |
52 | control_dt: float = 1.0 / 250.0 # 250Hz
53 |
54 | # socket config for ZMQ bridge
55 | robot_ip: str = "192.168.123.164"
56 |
--------------------------------------------------------------------------------
/src/lerobot/scripts/lerobot_find_port.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | """
16 | Helper to find the USB port associated with your MotorsBus.
17 |
18 | Example:
19 |
20 | ```shell
21 | lerobot-find-port
22 | ```
23 | """
24 |
25 | import platform
26 | import time
27 | from pathlib import Path
28 |
29 |
30 | def find_available_ports():
31 | from serial.tools import list_ports # Part of pyserial library
32 |
33 | if platform.system() == "Windows":
34 | # List COM ports using pyserial
35 | ports = [port.device for port in list_ports.comports()]
36 | else: # Linux/macOS
37 | # List /dev/tty* ports for Unix-based systems
38 | ports = [str(path) for path in Path("/dev").glob("tty*")]
39 | return ports
40 |
41 |
42 | def find_port():
43 | print("Finding all available ports for the MotorsBus.")
44 | ports_before = find_available_ports()
45 | print("Ports before disconnecting:", ports_before)
46 |
47 | print("Remove the USB cable from your MotorsBus and press Enter when done.")
48 | input() # Wait for user to disconnect the device
49 |
50 | time.sleep(0.5) # Allow some time for port to be released
51 | ports_after = find_available_ports()
52 | ports_diff = list(set(ports_before) - set(ports_after))
53 |
54 | if len(ports_diff) == 1:
55 | port = ports_diff[0]
56 | print(f"The port of this MotorsBus is '{port}'")
57 | print("Reconnect the USB cable.")
58 | elif len(ports_diff) == 0:
59 | raise OSError(f"Could not detect the port. No difference was found ({ports_diff}).")
60 | else:
61 | raise OSError(f"Could not detect the port. More than one port was found ({ports_diff}).")
62 |
63 |
64 | def main():
65 | find_port()
66 |
67 |
68 | if __name__ == "__main__":
69 | main()
70 |
--------------------------------------------------------------------------------
/src/lerobot/scripts/lerobot_setup_motors.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | """
16 | Helper to set motor ids and baudrate.
17 |
18 | Example:
19 |
20 | ```shell
21 | lerobot-setup-motors \
22 | --teleop.type=so100_leader \
23 | --teleop.port=/dev/tty.usbmodem575E0031751
24 | ```
25 | """
26 |
27 | from dataclasses import dataclass
28 |
29 | import draccus
30 |
31 | from lerobot.robots import ( # noqa: F401
32 | RobotConfig,
33 | koch_follower,
34 | lekiwi,
35 | make_robot_from_config,
36 | so100_follower,
37 | so101_follower,
38 | )
39 | from lerobot.teleoperators import ( # noqa: F401
40 | TeleoperatorConfig,
41 | koch_leader,
42 | make_teleoperator_from_config,
43 | so100_leader,
44 | so101_leader,
45 | )
46 |
47 | COMPATIBLE_DEVICES = [
48 | "koch_follower",
49 | "koch_leader",
50 | "so100_follower",
51 | "so100_leader",
52 | "so101_follower",
53 | "so101_leader",
54 | "lekiwi",
55 | ]
56 |
57 |
58 | @dataclass
59 | class SetupConfig:
60 | teleop: TeleoperatorConfig | None = None
61 | robot: RobotConfig | None = None
62 |
63 | def __post_init__(self):
64 | if bool(self.teleop) == bool(self.robot):
65 | raise ValueError("Choose either a teleop or a robot.")
66 |
67 | self.device = self.robot if self.robot else self.teleop
68 |
69 |
70 | @draccus.wrap()
71 | def setup_motors(cfg: SetupConfig):
72 | if cfg.device.type not in COMPATIBLE_DEVICES:
73 | raise NotImplementedError
74 |
75 | if isinstance(cfg.device, RobotConfig):
76 | device = make_robot_from_config(cfg.device)
77 | else:
78 | device = make_teleoperator_from_config(cfg.device)
79 |
80 | device.setup_motors()
81 |
82 |
83 | def main():
84 | setup_motors()
85 |
86 |
87 | if __name__ == "__main__":
88 | main()
89 |
--------------------------------------------------------------------------------
/src/lerobot/robots/unitree_g1/g1_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from enum import IntEnum
18 |
19 | # ruff: noqa: N801, N815
20 |
21 | NUM_MOTORS = 35
22 |
23 |
24 | class G1_29_JointArmIndex(IntEnum):
25 | # Left arm
26 | kLeftShoulderPitch = 15
27 | kLeftShoulderRoll = 16
28 | kLeftShoulderYaw = 17
29 | kLeftElbow = 18
30 | kLeftWristRoll = 19
31 | kLeftWristPitch = 20
32 | kLeftWristyaw = 21
33 |
34 | # Right arm
35 | kRightShoulderPitch = 22
36 | kRightShoulderRoll = 23
37 | kRightShoulderYaw = 24
38 | kRightElbow = 25
39 | kRightWristRoll = 26
40 | kRightWristPitch = 27
41 | kRightWristYaw = 28
42 |
43 |
44 | class G1_29_JointIndex(IntEnum):
45 | # Left leg
46 | kLeftHipPitch = 0
47 | kLeftHipRoll = 1
48 | kLeftHipYaw = 2
49 | kLeftKnee = 3
50 | kLeftAnklePitch = 4
51 | kLeftAnkleRoll = 5
52 |
53 | # Right leg
54 | kRightHipPitch = 6
55 | kRightHipRoll = 7
56 | kRightHipYaw = 8
57 | kRightKnee = 9
58 | kRightAnklePitch = 10
59 | kRightAnkleRoll = 11
60 |
61 | kWaistYaw = 12
62 | kWaistRoll = 13
63 | kWaistPitch = 14
64 |
65 | # Left arm
66 | kLeftShoulderPitch = 15
67 | kLeftShoulderRoll = 16
68 | kLeftShoulderYaw = 17
69 | kLeftElbow = 18
70 | kLeftWristRoll = 19
71 | kLeftWristPitch = 20
72 | kLeftWristyaw = 21
73 |
74 | # Right arm
75 | kRightShoulderPitch = 22
76 | kRightShoulderRoll = 23
77 | kRightShoulderYaw = 24
78 | kRightElbow = 25
79 | kRightWristRoll = 26
80 | kRightWristPitch = 27
81 | kRightWristYaw = 28
82 |
83 | # not used
84 | kNotUsedJoint0 = 29
85 | kNotUsedJoint1 = 30
86 | kNotUsedJoint2 = 31
87 | kNotUsedJoint3 = 32
88 | kNotUsedJoint4 = 33
89 | kNotUsedJoint5 = 34
90 |
--------------------------------------------------------------------------------
/tests/test_available.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | import importlib
17 |
18 | import gymnasium as gym
19 | import pytest
20 |
21 | import lerobot
22 | from lerobot.policies.act.modeling_act import ACTPolicy
23 | from lerobot.policies.diffusion.modeling_diffusion import DiffusionPolicy
24 | from lerobot.policies.tdmpc.modeling_tdmpc import TDMPCPolicy
25 | from lerobot.policies.vqbet.modeling_vqbet import VQBeTPolicy
26 | from tests.utils import require_env
27 |
28 |
29 | @pytest.mark.parametrize("env_name, task_name", lerobot.env_task_pairs)
30 | @require_env
31 | def test_available_env_task(env_name: str, task_name: list):
32 | """
33 | This test verifies that all environments listed in `lerobot/__init__.py` can
34 | be successfully imported — if they're installed — and that their
35 | `available_tasks_per_env` are valid.
36 | """
37 | package_name = f"gym_{env_name}"
38 | importlib.import_module(package_name)
39 | gym_handle = f"{package_name}/{task_name}"
40 | assert gym_handle in gym.envs.registry, gym_handle
41 |
42 |
43 | def test_available_policies():
44 | """
45 | This test verifies that the class attribute `name` for all policies is
46 | consistent with those listed in `lerobot/__init__.py`.
47 | """
48 | policy_classes = [ACTPolicy, DiffusionPolicy, TDMPCPolicy, VQBeTPolicy]
49 | policies = [pol_cls.name for pol_cls in policy_classes]
50 | assert set(policies) == set(lerobot.available_policies), policies
51 |
52 |
53 | def test_print():
54 | print(lerobot.available_envs)
55 | print(lerobot.available_tasks_per_env)
56 | print(lerobot.available_datasets)
57 | print(lerobot.available_datasets_per_env)
58 | print(lerobot.available_real_world_datasets)
59 | print(lerobot.available_policies)
60 | print(lerobot.available_policies_per_env)
61 |
--------------------------------------------------------------------------------
/examples/tutorial/rl/reward_classifier_example.py:
--------------------------------------------------------------------------------
1 | import torch
2 |
3 | from lerobot.datasets.lerobot_dataset import LeRobotDataset
4 | from lerobot.policies.factory import make_policy, make_pre_post_processors
5 | from lerobot.policies.sac.reward_model.configuration_classifier import RewardClassifierConfig
6 |
7 |
8 | def main():
9 | # Device to use for training
10 | device = "mps" # or "cuda", or "cpu"
11 |
12 | # Load the dataset used for training
13 | repo_id = "lerobot/example_hil_serl_dataset"
14 | dataset = LeRobotDataset(repo_id)
15 |
16 | # Configure the policy to extract features from the image frames
17 | camera_keys = dataset.meta.camera_keys
18 |
19 | config = RewardClassifierConfig(
20 | num_cameras=len(camera_keys),
21 | device=device,
22 | # backbone model to extract features from the image frames
23 | model_name="microsoft/resnet-18",
24 | )
25 |
26 | # Make policy, preprocessor, and optimizer
27 | policy = make_policy(config, ds_meta=dataset.meta)
28 | optimizer = config.get_optimizer_preset().build(policy.parameters())
29 | preprocessor, _ = make_pre_post_processors(policy_cfg=config, dataset_stats=dataset.meta.stats)
30 |
31 | classifier_id = "/reward_classifier_hil_serl_example"
32 |
33 | # Instantiate a dataloader
34 | dataloader = torch.utils.data.DataLoader(dataset, batch_size=16, shuffle=True)
35 |
36 | # Training loop
37 | num_epochs = 5
38 | for epoch in range(num_epochs):
39 | total_loss = 0
40 | total_accuracy = 0
41 | for batch in dataloader:
42 | # Preprocess the batch and move it to the correct device.
43 | batch = preprocessor(batch)
44 |
45 | # Forward pass
46 | loss, output_dict = policy.forward(batch)
47 |
48 | # Backward pass and optimization
49 | optimizer.zero_grad()
50 | loss.backward()
51 | optimizer.step()
52 |
53 | total_loss += loss.item()
54 | total_accuracy += output_dict["accuracy"]
55 |
56 | avg_loss = total_loss / len(dataloader)
57 | avg_accuracy = total_accuracy / len(dataloader)
58 | print(f"Epoch {epoch + 1}/{num_epochs}, Loss: {avg_loss:.4f}, Accuracy: {avg_accuracy:.2f}%")
59 |
60 | print("Training finished!")
61 |
62 | # You can now save the trained policy.
63 | policy.push_to_hub(classifier_id)
64 |
65 |
66 | if __name__ == "__main__":
67 | main()
68 |
--------------------------------------------------------------------------------
/src/lerobot/datasets/backward_compatibility.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | import packaging.version
16 |
17 | V30_MESSAGE = """
18 | The dataset you requested ({repo_id}) is in {version} format.
19 |
20 | We introduced a new format since v3.0 which is not backward compatible with v2.1.
21 | Please, update your dataset to the new format using this command:
22 | ```
23 | python -m lerobot.datasets.v30.convert_dataset_v21_to_v30 --repo-id={repo_id}
24 | ```
25 |
26 | If you already have a converted version uploaded to the hub, then this error might be because of
27 | an older version in your local cache. Consider deleting the cached version and retrying.
28 |
29 | If you encounter a problem, contact LeRobot maintainers on [Discord](https://discord.com/invite/s3KuuzsPFb)
30 | or open an [issue on GitHub](https://github.com/huggingface/lerobot/issues/new/choose).
31 | """
32 |
33 | FUTURE_MESSAGE = """
34 | The dataset you requested ({repo_id}) is only available in {version} format.
35 | As we cannot ensure forward compatibility with it, please update your current version of lerobot.
36 | """
37 |
38 |
39 | class CompatibilityError(Exception): ...
40 |
41 |
42 | class BackwardCompatibilityError(CompatibilityError):
43 | def __init__(self, repo_id: str, version: packaging.version.Version):
44 | if version.major == 2 and version.minor == 1:
45 | message = V30_MESSAGE.format(repo_id=repo_id, version=version)
46 | else:
47 | raise NotImplementedError(
48 | "Contact the maintainer on [Discord](https://discord.com/invite/s3KuuzsPFb)."
49 | )
50 | super().__init__(message)
51 |
52 |
53 | class ForwardCompatibilityError(CompatibilityError):
54 | def __init__(self, repo_id: str, version: packaging.version.Version):
55 | message = FUTURE_MESSAGE.format(repo_id=repo_id, version=version)
56 | super().__init__(message)
57 |
--------------------------------------------------------------------------------
/examples/tutorial/async-inf/robot_client.py:
--------------------------------------------------------------------------------
1 | import threading
2 |
3 | from lerobot.async_inference.configs import RobotClientConfig
4 | from lerobot.async_inference.helpers import visualize_action_queue_size
5 | from lerobot.async_inference.robot_client import RobotClient
6 | from lerobot.cameras.opencv.configuration_opencv import OpenCVCameraConfig
7 | from lerobot.robots.so100_follower import SO100FollowerConfig
8 |
9 |
10 | def main():
11 | # these cameras must match the ones expected by the policy - find your cameras with lerobot-find-cameras
12 | # check the config.json on the Hub for the policy you are using to see the expected camera specs
13 | camera_cfg = {
14 | "up": OpenCVCameraConfig(index_or_path=0, width=640, height=480, fps=30),
15 | "side": OpenCVCameraConfig(index_or_path=1, width=640, height=480, fps=30),
16 | }
17 |
18 | # # find ports using lerobot-find-port
19 | follower_port = ... # something like "/dev/tty.usbmodem58760431631"
20 |
21 | # # the robot ids are used the load the right calibration files
22 | follower_id = ... # something like "follower_so100"
23 |
24 | robot_cfg = SO100FollowerConfig(port=follower_port, id=follower_id, cameras=camera_cfg)
25 |
26 | server_address = ... # something like "127.0.0.1:8080" if using localhost
27 |
28 | # 3. Create client configuration
29 | client_cfg = RobotClientConfig(
30 | robot=robot_cfg,
31 | server_address=server_address,
32 | policy_device="mps",
33 | policy_type="act",
34 | pretrained_name_or_path="/robot_learning_tutorial_act",
35 | chunk_size_threshold=0.5, # g
36 | actions_per_chunk=50, # make sure this is less than the max actions of the policy
37 | )
38 |
39 | # 4. Create and start client
40 | client = RobotClient(client_cfg)
41 |
42 | # 5. Provide a textual description of the task
43 | task = ...
44 |
45 | if client.start():
46 | # Start action receiver thread
47 | action_receiver_thread = threading.Thread(target=client.receive_actions, daemon=True)
48 | action_receiver_thread.start()
49 |
50 | try:
51 | # Run the control loop
52 | client.control_loop(task)
53 | except KeyboardInterrupt:
54 | client.stop()
55 | action_receiver_thread.join()
56 | # (Optionally) plot the action queue size
57 | visualize_action_queue_size(client.action_queue_size)
58 |
59 |
60 | if __name__ == "__main__":
61 | main()
62 |
--------------------------------------------------------------------------------
/examples/lekiwi/replay.py:
--------------------------------------------------------------------------------
1 | # !/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | import time
18 |
19 | from lerobot.datasets.lerobot_dataset import LeRobotDataset
20 | from lerobot.robots.lekiwi.config_lekiwi import LeKiwiClientConfig
21 | from lerobot.robots.lekiwi.lekiwi_client import LeKiwiClient
22 | from lerobot.utils.constants import ACTION
23 | from lerobot.utils.robot_utils import precise_sleep
24 | from lerobot.utils.utils import log_say
25 |
26 | EPISODE_IDX = 0
27 |
28 |
29 | def main():
30 | # Initialize the robot config
31 | robot_config = LeKiwiClientConfig(remote_ip="172.18.134.136", id="lekiwi")
32 |
33 | # Initialize the robot
34 | robot = LeKiwiClient(robot_config)
35 |
36 | # Fetch the dataset to replay
37 | dataset = LeRobotDataset("/", episodes=[EPISODE_IDX])
38 | # Filter dataset to only include frames from the specified episode since episodes are chunked in dataset V3.0
39 | episode_frames = dataset.hf_dataset.filter(lambda x: x["episode_index"] == EPISODE_IDX)
40 | actions = episode_frames.select_columns(ACTION)
41 |
42 | # Connect to the robot
43 | robot.connect()
44 |
45 | if not robot.is_connected:
46 | raise ValueError("Robot is not connected!")
47 |
48 | print("Starting replay loop...")
49 | log_say(f"Replaying episode {EPISODE_IDX}")
50 | for idx in range(len(episode_frames)):
51 | t0 = time.perf_counter()
52 |
53 | # Get recorded action from dataset
54 | action = {
55 | name: float(actions[idx][ACTION][i]) for i, name in enumerate(dataset.features[ACTION]["names"])
56 | }
57 |
58 | # Send action to robot
59 | _ = robot.send_action(action)
60 |
61 | precise_sleep(max(1.0 / dataset.fps - (time.perf_counter() - t0), 0.0))
62 |
63 | robot.disconnect()
64 |
65 |
66 | if __name__ == "__main__":
67 | main()
68 |
--------------------------------------------------------------------------------
/src/lerobot/motors/encoding_utils.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 |
16 | def encode_sign_magnitude(value: int, sign_bit_index: int):
17 | """
18 | https://en.wikipedia.org/wiki/Signed_number_representations#Sign%E2%80%93magnitude
19 | """
20 | max_magnitude = (1 << sign_bit_index) - 1
21 | magnitude = abs(value)
22 | if magnitude > max_magnitude:
23 | raise ValueError(f"Magnitude {magnitude} exceeds {max_magnitude} (max for {sign_bit_index=})")
24 |
25 | direction_bit = 1 if value < 0 else 0
26 | return (direction_bit << sign_bit_index) | magnitude
27 |
28 |
29 | def decode_sign_magnitude(encoded_value: int, sign_bit_index: int):
30 | """
31 | https://en.wikipedia.org/wiki/Signed_number_representations#Sign%E2%80%93magnitude
32 | """
33 | direction_bit = (encoded_value >> sign_bit_index) & 1
34 | magnitude_mask = (1 << sign_bit_index) - 1
35 | magnitude = encoded_value & magnitude_mask
36 | return -magnitude if direction_bit else magnitude
37 |
38 |
39 | def encode_twos_complement(value: int, n_bytes: int):
40 | """
41 | https://en.wikipedia.org/wiki/Signed_number_representations#Two%27s_complement
42 | """
43 |
44 | bit_width = n_bytes * 8
45 | min_val = -(1 << (bit_width - 1))
46 | max_val = (1 << (bit_width - 1)) - 1
47 |
48 | if not (min_val <= value <= max_val):
49 | raise ValueError(
50 | f"Value {value} out of range for {n_bytes}-byte two's complement: [{min_val}, {max_val}]"
51 | )
52 |
53 | if value >= 0:
54 | return value
55 |
56 | return (1 << bit_width) + value
57 |
58 |
59 | def decode_twos_complement(value: int, n_bytes: int) -> int:
60 | """
61 | https://en.wikipedia.org/wiki/Signed_number_representations#Two%27s_complement
62 | """
63 | bits = n_bytes * 8
64 | sign_bit = 1 << (bits - 1)
65 | if value & sign_bit:
66 | value -= 1 << bits
67 | return value
68 |
--------------------------------------------------------------------------------
/tests/policies/rtc/test_configuration_rtc.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | """Tests for RTC configuration module."""
18 |
19 | from lerobot.configs.types import RTCAttentionSchedule
20 | from lerobot.policies.rtc.configuration_rtc import RTCConfig
21 |
22 | # ====================== Initialization Tests ======================
23 |
24 |
25 | def test_rtc_config_default_initialization():
26 | """Test RTCConfig initializes with default values."""
27 | config = RTCConfig()
28 |
29 | assert config.enabled is False
30 | assert config.prefix_attention_schedule == RTCAttentionSchedule.LINEAR
31 | assert config.max_guidance_weight == 10.0
32 | assert config.execution_horizon == 10
33 | assert config.debug is False
34 | assert config.debug_maxlen == 100
35 |
36 |
37 | def test_rtc_config_custom_initialization():
38 | """Test RTCConfig initializes with custom values."""
39 | config = RTCConfig(
40 | enabled=True,
41 | prefix_attention_schedule=RTCAttentionSchedule.EXP,
42 | max_guidance_weight=5.0,
43 | execution_horizon=20,
44 | debug=True,
45 | debug_maxlen=200,
46 | )
47 |
48 | assert config.enabled is True
49 | assert config.prefix_attention_schedule == RTCAttentionSchedule.EXP
50 | assert config.max_guidance_weight == 5.0
51 | assert config.execution_horizon == 20
52 | assert config.debug is True
53 | assert config.debug_maxlen == 200
54 |
55 |
56 | def test_rtc_config_partial_initialization():
57 | """Test RTCConfig with partial custom values."""
58 | config = RTCConfig(enabled=True, max_guidance_weight=15.0)
59 |
60 | assert config.enabled is True
61 | assert config.max_guidance_weight == 15.0
62 | # Other values should be defaults
63 | assert config.prefix_attention_schedule == RTCAttentionSchedule.LINEAR
64 | assert config.execution_horizon == 10
65 | assert config.debug is False
66 |
--------------------------------------------------------------------------------
/src/lerobot/utils/robot_utils.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | import platform
16 | import time
17 |
18 |
19 | def precise_sleep(seconds: float, spin_threshold: float = 0.010, sleep_margin: float = 0.003):
20 | """
21 | Wait for `seconds` with better precision than time.sleep alone at the expense of more CPU usage.
22 |
23 | Parameters:
24 | - seconds: duration to wait
25 | - spin_threshold: if remaining <= spin_threshold -> spin; otherwise sleep (seconds). Default 10ms
26 | - sleep_margin: when sleeping leave this much time before deadline to avoid oversleep. Default 3ms
27 |
28 | Note:
29 | The default parameters are chosen to prioritize timing accuracy over CPU usage for the common 30 FPS use case.
30 | """
31 | if seconds <= 0:
32 | return
33 |
34 | system = platform.system()
35 | # On macOS and Windows the scheduler / sleep granularity can make
36 | # short sleeps inaccurate. Instead of burning CPU for the whole
37 | # duration, sleep for most of the time and spin for the final few
38 | # milliseconds to achieve good accuracy with much lower CPU usage.
39 | if system in ("Darwin", "Windows"):
40 | end_time = time.perf_counter() + seconds
41 | while True:
42 | remaining = end_time - time.perf_counter()
43 | if remaining <= 0:
44 | break
45 | # If there's more than a couple milliseconds left, sleep most
46 | # of the remaining time and leave a small margin for the final spin.
47 | if remaining > spin_threshold:
48 | # Sleep but avoid sleeping past the end by leaving a small margin.
49 | time.sleep(max(remaining - sleep_margin, 0))
50 | else:
51 | # Final short spin to hit precise timing without long sleeps.
52 | pass
53 | else:
54 | # On Linux time.sleep is accurate enough for most uses
55 | time.sleep(seconds)
56 |
--------------------------------------------------------------------------------
/tests/processor/test_libero_processor.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | import numpy as np
18 | import torch
19 |
20 | from lerobot.envs.utils import preprocess_observation
21 | from lerobot.processor.env_processor import LiberoProcessorStep
22 | from lerobot.processor.pipeline import PolicyProcessorPipeline
23 |
24 | seed = 42
25 | np.random.seed(seed)
26 |
27 | B = 5
28 | obs1 = {
29 | "pixels": {
30 | "image": (np.random.rand(B, 256, 256, 3) * 255).astype(np.uint8),
31 | "image2": (np.random.rand(B, 256, 256, 3) * 255).astype(np.uint8),
32 | },
33 | "robot_state": {
34 | "eef": {
35 | "pos": np.random.randn(B, 3),
36 | "quat": np.random.randn(B, 4),
37 | "mat": np.random.randn(B, 3, 3),
38 | },
39 | "gripper": {
40 | "qpos": np.random.randn(B, 2),
41 | "qvel": np.random.randn(B, 2),
42 | },
43 | "joints": {
44 | "pos": np.random.randn(B, 7),
45 | "vel": np.random.randn(B, 7),
46 | },
47 | },
48 | }
49 |
50 | observation = preprocess_observation(obs1)
51 | libero_preprocessor = PolicyProcessorPipeline(
52 | steps=[
53 | LiberoProcessorStep(),
54 | ]
55 | )
56 | processed_obs = libero_preprocessor(observation)
57 | assert "observation.state" in processed_obs
58 | state = processed_obs["observation.state"]
59 | assert isinstance(state, torch.Tensor)
60 | assert state.dtype == torch.float32
61 |
62 | assert state.shape[0] == B
63 | assert state.shape[1] == 8
64 |
65 | assert "observation.images.image" in processed_obs
66 | assert "observation.images.image2" in processed_obs
67 |
68 | assert isinstance(processed_obs["observation.images.image"], torch.Tensor)
69 | assert isinstance(processed_obs["observation.images.image2"], torch.Tensor)
70 |
71 | assert processed_obs["observation.images.image"].shape == (B, 3, 256, 256)
72 | assert processed_obs["observation.images.image2"].shape == (B, 3, 256, 256)
73 |
--------------------------------------------------------------------------------
/src/lerobot/policies/rtc/latency_tracker.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | """Latency tracking utilities for Real-Time Chunking (RTC)."""
18 |
19 | from collections import deque
20 |
21 | import numpy as np
22 |
23 |
24 | class LatencyTracker:
25 | """Tracks recent latencies and provides max/percentile queries.
26 |
27 | Args:
28 | maxlen (int | None): Optional sliding window size. If provided, only the
29 | most recent ``maxlen`` latencies are kept. If ``None``, keeps all.
30 | """
31 |
32 | def __init__(self, maxlen: int = 100):
33 | self._values = deque(maxlen=maxlen)
34 | self.reset()
35 |
36 | def reset(self) -> None:
37 | """Clear all recorded latencies."""
38 | self._values.clear()
39 | self.max_latency = 0.0
40 |
41 | def add(self, latency: float) -> None:
42 | """Add a latency sample (seconds)."""
43 | # Ensure numeric and non-negative
44 | val = float(latency)
45 |
46 | if val < 0:
47 | return
48 | self._values.append(val)
49 | self.max_latency = max(self.max_latency, val)
50 |
51 | def __len__(self) -> int:
52 | return len(self._values)
53 |
54 | def max(self) -> float | None:
55 | """Return the maximum latency or None if empty."""
56 | return self.max_latency
57 |
58 | def percentile(self, q: float) -> float | None:
59 | """Return the q-quantile (q in [0,1]) of recorded latencies or None if empty."""
60 | if not self._values:
61 | return 0.0
62 | q = float(q)
63 | if q <= 0.0:
64 | return min(self._values)
65 | if q >= 1.0:
66 | return self.max_latency
67 | vals = np.array(list(self._values), dtype=np.float32)
68 | return float(np.quantile(vals, q))
69 |
70 | def p95(self) -> float | None:
71 | """Return the 95th percentile latency or None if empty."""
72 | return self.percentile(0.95)
73 |
--------------------------------------------------------------------------------
/src/lerobot/policies/pi0/README.md:
--------------------------------------------------------------------------------
1 | # π₀ (pi0)
2 |
3 | This repository contains the Hugging Face port of **π₀**, adapted from [OpenPI](https://github.com/Physical-Intelligence/openpi) by the Physical Intelligence.
4 | It is designed as a **Vision-Language-Action model for general robot control**.
5 |
6 | ---
7 |
8 | ## Model Overview
9 |
10 | | Feature | π₀ | π₀.₅ |
11 | | -------------------- | ------------------------------------------------------ | ----------------------------------------- |
12 | | Time Conditioning | Concatenates time with actions via `action_time_mlp_*` | Uses `time_mlp_*` for AdaRMS conditioning |
13 | | AdaRMS | Not used | Used in action expert |
14 | | Tokenizer Length | 48 tokens | 200 tokens |
15 | | Discrete State Input | False (Uses `state_proj` layer) | True |
16 | | Parameter Count | Higher (includes state embedding) | Lower (no state embedding) |
17 |
18 | ---
19 |
20 | ## Citation
21 |
22 | If you use this work, please cite both **OpenPI** and the π₀ paper:
23 |
24 | ```bibtex
25 | @misc{openpi2024,
26 | author = {Physical Intelligence Lab},
27 | title = {OpenPI: PyTorch Implementation of π0 and π0.5 Policies},
28 | year = {2024},
29 | publisher = {GitHub},
30 | howpublished = {\url{https://github.com/Physical-Intelligence/openpi}},
31 | license = {Apache-2.0}
32 | }
33 |
34 | @misc{black2024pi0visionlanguageactionflowmodel,
35 | title = {π₀: A Vision-Language-Action Flow Model for General Robot Control},
36 | author = {Kevin Black and Noah Brown and Danny Driess and Adnan Esmail and Michael Equi and Chelsea Finn and Niccolo Fusai and Lachy Groom and Karol Hausman and Brian Ichter and Szymon Jakubczak and Tim Jones and Liyiming Ke and Sergey Levine and Adrian Li-Bell and Mohith Mothukuri and Suraj Nair and Karl Pertsch and Lucy Xiaoyang Shi and James Tanner and Quan Vuong and Anna Walling and Haohuan Wang and Ury Zhilinsky},
37 | year = {2024},
38 | eprint = {2410.24164},
39 | archivePrefix= {arXiv},
40 | primaryClass = {cs.LG},
41 | url = {https://arxiv.org/abs/2410.24164},
42 | }
43 | ```
44 |
45 | ---
46 |
47 | ## License
48 |
49 | This port follows the **Apache 2.0 License**, consistent with the original [OpenPI repository](https://github.com/Physical-Intelligence/openpi).
50 |
--------------------------------------------------------------------------------
/src/lerobot/rl/eval_policy.py:
--------------------------------------------------------------------------------
1 | # !/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | import logging
17 |
18 | from lerobot.cameras import opencv # noqa: F401
19 | from lerobot.configs import parser
20 | from lerobot.configs.train import TrainRLServerPipelineConfig
21 | from lerobot.datasets.lerobot_dataset import LeRobotDataset
22 | from lerobot.policies.factory import make_policy
23 | from lerobot.robots import ( # noqa: F401
24 | RobotConfig,
25 | make_robot_from_config,
26 | so100_follower,
27 | )
28 | from lerobot.teleoperators import (
29 | gamepad, # noqa: F401
30 | so101_leader, # noqa: F401
31 | )
32 |
33 | from .gym_manipulator import make_robot_env
34 |
35 | logging.basicConfig(level=logging.INFO)
36 |
37 |
38 | def eval_policy(env, policy, n_episodes):
39 | sum_reward_episode = []
40 | for _ in range(n_episodes):
41 | obs, _ = env.reset()
42 | episode_reward = 0.0
43 | while True:
44 | action = policy.select_action(obs)
45 | obs, reward, terminated, truncated, _ = env.step(action)
46 | episode_reward += reward
47 | if terminated or truncated:
48 | break
49 | sum_reward_episode.append(episode_reward)
50 |
51 | logging.info(f"Success after 20 steps {sum_reward_episode}")
52 | logging.info(f"success rate {sum(sum_reward_episode) / len(sum_reward_episode)}")
53 |
54 |
55 | @parser.wrap()
56 | def main(cfg: TrainRLServerPipelineConfig):
57 | env_cfg = cfg.env
58 | env = make_robot_env(env_cfg)
59 | dataset_cfg = cfg.dataset
60 | dataset = LeRobotDataset(repo_id=dataset_cfg.repo_id)
61 | dataset_meta = dataset.meta
62 |
63 | policy = make_policy(
64 | cfg=cfg.policy,
65 | # env_cfg=cfg.env,
66 | ds_meta=dataset_meta,
67 | )
68 | policy = policy.from_pretrained(env_cfg.pretrained_policy_name_or_path)
69 | policy.eval()
70 |
71 | eval_policy(env, policy=policy, n_episodes=10)
72 |
73 |
74 | if __name__ == "__main__":
75 | main()
76 |
--------------------------------------------------------------------------------
/src/lerobot/datasets/sampler.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | from collections.abc import Iterator
17 |
18 | import torch
19 |
20 |
21 | class EpisodeAwareSampler:
22 | def __init__(
23 | self,
24 | dataset_from_indices: list[int],
25 | dataset_to_indices: list[int],
26 | episode_indices_to_use: list | None = None,
27 | drop_n_first_frames: int = 0,
28 | drop_n_last_frames: int = 0,
29 | shuffle: bool = False,
30 | ):
31 | """Sampler that optionally incorporates episode boundary information.
32 |
33 | Args:
34 | dataset_from_indices: List of indices containing the start of each episode in the dataset.
35 | dataset_to_indices: List of indices containing the end of each episode in the dataset.
36 | episode_indices_to_use: List of episode indices to use. If None, all episodes are used.
37 | Assumes that episodes are indexed from 0 to N-1.
38 | drop_n_first_frames: Number of frames to drop from the start of each episode.
39 | drop_n_last_frames: Number of frames to drop from the end of each episode.
40 | shuffle: Whether to shuffle the indices.
41 | """
42 | indices = []
43 | for episode_idx, (start_index, end_index) in enumerate(
44 | zip(dataset_from_indices, dataset_to_indices, strict=True)
45 | ):
46 | if episode_indices_to_use is None or episode_idx in episode_indices_to_use:
47 | indices.extend(range(start_index + drop_n_first_frames, end_index - drop_n_last_frames))
48 |
49 | self.indices = indices
50 | self.shuffle = shuffle
51 |
52 | def __iter__(self) -> Iterator[int]:
53 | if self.shuffle:
54 | for i in torch.randperm(len(self.indices)):
55 | yield self.indices[i]
56 | else:
57 | for i in self.indices:
58 | yield i
59 |
60 | def __len__(self) -> int:
61 | return len(self.indices)
62 |
--------------------------------------------------------------------------------
/.github/workflows/documentation.yml:
--------------------------------------------------------------------------------
1 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | # This workflow handles building documentation for both main branches and PRs.
16 | name: Documentation
17 |
18 | on:
19 | # Allows running this workflow manually from the Actions tab
20 | workflow_dispatch:
21 |
22 | # Triggers the workflow on push events to main for the docs folder
23 | push:
24 | branches:
25 | - main
26 | paths:
27 | - "docs/**"
28 |
29 | # Triggers the workflow on pull request events targeting main for the docs folder
30 | pull_request:
31 | branches:
32 | - main
33 | paths:
34 | - "docs/**"
35 |
36 | # Ensures that only the latest commit for a PR or branch is built, canceling older runs.
37 | concurrency:
38 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
39 | cancel-in-progress: true
40 |
41 | jobs:
42 | # This job builds and deploys the official documentation.
43 | build_main_docs:
44 | name: Build Main Docs
45 | if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
46 | permissions:
47 | contents: read
48 | uses: huggingface/doc-builder/.github/workflows/build_main_documentation.yml@main
49 | with:
50 | commit_sha: ${{ github.sha }}
51 | package: lerobot
52 | additional_args: --not_python_module
53 | secrets:
54 | token: ${{ secrets.HUGGINGFACE_PUSH }}
55 | hf_token: ${{ secrets.HF_DOC_BUILD_PUSH }}
56 |
57 | # This job builds a preview of the documentation for a pull request.
58 | # The result of this job triggers the 'Upload PR Documentation' workflow.
59 | build_pr_docs:
60 | name: Build PR Docs
61 | if: github.event_name == 'pull_request'
62 | permissions:
63 | contents: read
64 | pull-requests: write
65 | uses: huggingface/doc-builder/.github/workflows/build_pr_documentation.yml@main
66 | with:
67 | commit_sha: ${{ github.event.pull_request.head.sha }}
68 | pr_number: ${{ github.event.number }}
69 | package: lerobot
70 | additional_args: --not_python_module
71 |
--------------------------------------------------------------------------------
/examples/tutorial/act/act_using_example.py:
--------------------------------------------------------------------------------
1 | import torch
2 |
3 | from lerobot.cameras.opencv.configuration_opencv import OpenCVCameraConfig
4 | from lerobot.datasets.lerobot_dataset import LeRobotDatasetMetadata
5 | from lerobot.policies.act.modeling_act import ACTPolicy
6 | from lerobot.policies.factory import make_pre_post_processors
7 | from lerobot.policies.utils import build_inference_frame, make_robot_action
8 | from lerobot.robots.so100_follower.config_so100_follower import SO100FollowerConfig
9 | from lerobot.robots.so100_follower.so100_follower import SO100Follower
10 |
11 | MAX_EPISODES = 5
12 | MAX_STEPS_PER_EPISODE = 20
13 |
14 |
15 | def main():
16 | device = torch.device("mps") # or "cuda" or "cpu"
17 | model_id = "/robot_learning_tutorial_act"
18 | model = ACTPolicy.from_pretrained(model_id)
19 |
20 | dataset_id = "lerobot/svla_so101_pickplace"
21 | # This only downloads the metadata for the dataset, ~10s of MB even for large-scale datasets
22 | dataset_metadata = LeRobotDatasetMetadata(dataset_id)
23 | preprocess, postprocess = make_pre_post_processors(model.config, dataset_stats=dataset_metadata.stats)
24 |
25 | # # find ports using lerobot-find-port
26 | follower_port = ... # something like "/dev/tty.usbmodem58760431631"
27 |
28 | # # the robot ids are used the load the right calibration files
29 | follower_id = ... # something like "follower_so100"
30 |
31 | # Robot and environment configuration
32 | # Camera keys must match the name and resolutions of the ones used for training!
33 | # You can check the camera keys expected by a model in the info.json card on the model card on the Hub
34 | camera_config = {
35 | "side": OpenCVCameraConfig(index_or_path=0, width=640, height=480, fps=30),
36 | "up": OpenCVCameraConfig(index_or_path=1, width=640, height=480, fps=30),
37 | }
38 |
39 | robot_cfg = SO100FollowerConfig(port=follower_port, id=follower_id, cameras=camera_config)
40 | robot = SO100Follower(robot_cfg)
41 | robot.connect()
42 |
43 | for _ in range(MAX_EPISODES):
44 | for _ in range(MAX_STEPS_PER_EPISODE):
45 | obs = robot.get_observation()
46 | obs_frame = build_inference_frame(
47 | observation=obs, ds_features=dataset_metadata.features, device=device
48 | )
49 |
50 | obs = preprocess(obs_frame)
51 |
52 | action = model.select_action(obs)
53 | action = postprocess(action)
54 |
55 | action = make_robot_action(action, dataset_metadata.features)
56 |
57 | robot.send_action(action)
58 |
59 | print("Episode finished! Starting new episode...")
60 |
61 |
62 | if __name__ == "__main__":
63 | main()
64 |
--------------------------------------------------------------------------------
/src/lerobot/processor/factory.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from .converters import (
18 | observation_to_transition,
19 | robot_action_observation_to_transition,
20 | transition_to_observation,
21 | transition_to_robot_action,
22 | )
23 | from .core import RobotAction, RobotObservation
24 | from .pipeline import IdentityProcessorStep, RobotProcessorPipeline
25 |
26 |
27 | def make_default_teleop_action_processor() -> RobotProcessorPipeline[
28 | tuple[RobotAction, RobotObservation], RobotAction
29 | ]:
30 | teleop_action_processor = RobotProcessorPipeline[tuple[RobotAction, RobotObservation], RobotAction](
31 | steps=[IdentityProcessorStep()],
32 | to_transition=robot_action_observation_to_transition,
33 | to_output=transition_to_robot_action,
34 | )
35 | return teleop_action_processor
36 |
37 |
38 | def make_default_robot_action_processor() -> RobotProcessorPipeline[
39 | tuple[RobotAction, RobotObservation], RobotAction
40 | ]:
41 | robot_action_processor = RobotProcessorPipeline[tuple[RobotAction, RobotObservation], RobotAction](
42 | steps=[IdentityProcessorStep()],
43 | to_transition=robot_action_observation_to_transition,
44 | to_output=transition_to_robot_action,
45 | )
46 | return robot_action_processor
47 |
48 |
49 | def make_default_robot_observation_processor() -> RobotProcessorPipeline[RobotObservation, RobotObservation]:
50 | robot_observation_processor = RobotProcessorPipeline[RobotObservation, RobotObservation](
51 | steps=[IdentityProcessorStep()],
52 | to_transition=observation_to_transition,
53 | to_output=transition_to_observation,
54 | )
55 | return robot_observation_processor
56 |
57 |
58 | def make_default_processors():
59 | teleop_action_processor = make_default_teleop_action_processor()
60 | robot_action_processor = make_default_robot_action_processor()
61 | robot_observation_processor = make_default_robot_observation_processor()
62 | return (teleop_action_processor, robot_action_processor, robot_observation_processor)
63 |
--------------------------------------------------------------------------------
/src/lerobot/teleoperators/keyboard/configuration_keyboard.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | """Configuration for keyboard teleoperators."""
17 |
18 | from dataclasses import dataclass
19 |
20 | from ..config import TeleoperatorConfig
21 |
22 |
23 | @TeleoperatorConfig.register_subclass("keyboard")
24 | @dataclass
25 | class KeyboardTeleopConfig(TeleoperatorConfig):
26 | """KeyboardTeleopConfig"""
27 |
28 | # TODO(Steven): Consider setting in here the keys that we want to capture/listen
29 |
30 |
31 | @TeleoperatorConfig.register_subclass("keyboard_ee")
32 | @dataclass
33 | class KeyboardEndEffectorTeleopConfig(KeyboardTeleopConfig):
34 | """Configuration for keyboard end-effector teleoperator.
35 |
36 | Used for controlling robot end-effectors with keyboard inputs.
37 |
38 | Attributes:
39 | use_gripper: Whether to include gripper control in actions
40 | """
41 |
42 | use_gripper: bool = True
43 |
44 |
45 | @TeleoperatorConfig.register_subclass("keyboard_rover")
46 | @dataclass
47 | class KeyboardRoverTeleopConfig(TeleoperatorConfig):
48 | """Configuration for keyboard rover teleoperator.
49 |
50 | Used for controlling mobile robots like EarthRover Mini Plus with WASD controls.
51 |
52 | Attributes:
53 | linear_speed: Default linear velocity magnitude (-1 to 1 range for SDK robots)
54 | angular_speed: Default angular velocity magnitude (-1 to 1 range for SDK robots)
55 | speed_increment: Amount to increase/decrease speed with +/- keys
56 | turn_assist_ratio: Forward motion multiplier when turning with A/D keys (0.0-1.0)
57 | angular_speed_ratio: Ratio of angular to linear speed for synchronized adjustments
58 | min_linear_speed: Minimum linear speed when decreasing (prevents zero speed)
59 | min_angular_speed: Minimum angular speed when decreasing (prevents zero speed)
60 | """
61 |
62 | linear_speed: float = 1.0
63 | angular_speed: float = 1.0
64 | speed_increment: float = 0.1
65 | turn_assist_ratio: float = 0.3
66 | angular_speed_ratio: float = 0.6
67 | min_linear_speed: float = 0.1
68 | min_angular_speed: float = 0.05
69 |
--------------------------------------------------------------------------------
/examples/tutorial/diffusion/diffusion_using_example.py:
--------------------------------------------------------------------------------
1 | import torch
2 |
3 | from lerobot.cameras.opencv.configuration_opencv import OpenCVCameraConfig
4 | from lerobot.datasets.lerobot_dataset import LeRobotDatasetMetadata
5 | from lerobot.policies.diffusion.modeling_diffusion import DiffusionPolicy
6 | from lerobot.policies.factory import make_pre_post_processors
7 | from lerobot.policies.utils import build_inference_frame, make_robot_action
8 | from lerobot.robots.so100_follower.config_so100_follower import SO100FollowerConfig
9 | from lerobot.robots.so100_follower.so100_follower import SO100Follower
10 |
11 | MAX_EPISODES = 5
12 | MAX_STEPS_PER_EPISODE = 20
13 |
14 |
15 | def main():
16 | device = torch.device("mps") # or "cuda" or "cpu"
17 | model_id = "/robot_learning_tutorial_diffusion"
18 |
19 | model = DiffusionPolicy.from_pretrained(model_id)
20 |
21 | dataset_id = "lerobot/svla_so101_pickplace"
22 | # This only downloads the metadata for the dataset, ~10s of MB even for large-scale datasets
23 | dataset_metadata = LeRobotDatasetMetadata(dataset_id)
24 | preprocess, postprocess = make_pre_post_processors(
25 | model.config, model_id, dataset_stats=dataset_metadata.stats
26 | )
27 |
28 | # # find ports using lerobot-find-port
29 | follower_port = ... # something like "/dev/tty.usbmodem58760431631"
30 |
31 | # # the robot ids are used the load the right calibration files
32 | follower_id = ... # something like "follower_so100"
33 |
34 | # Robot and environment configuration
35 | # Camera keys must match the name and resolutions of the ones used for training!
36 | # You can check the camera keys expected by a model in the info.json card on the model card on the Hub
37 | camera_config = {
38 | "side": OpenCVCameraConfig(index_or_path=0, width=640, height=480, fps=30),
39 | "up": OpenCVCameraConfig(index_or_path=1, width=640, height=480, fps=30),
40 | }
41 |
42 | robot_cfg = SO100FollowerConfig(port=follower_port, id=follower_id, cameras=camera_config)
43 | robot = SO100Follower(robot_cfg)
44 | robot.connect()
45 |
46 | for _ in range(MAX_EPISODES):
47 | for _ in range(MAX_STEPS_PER_EPISODE):
48 | obs = robot.get_observation()
49 | obs_frame = build_inference_frame(
50 | observation=obs, ds_features=dataset_metadata.features, device=device
51 | )
52 |
53 | obs = preprocess(obs_frame)
54 |
55 | action = model.select_action(obs)
56 | action = postprocess(action)
57 | action = make_robot_action(action, dataset_metadata.features)
58 | robot.send_action(action)
59 |
60 | print("Episode finished! Starting new episode...")
61 |
62 |
63 | if __name__ == "__main__":
64 | main()
65 |
--------------------------------------------------------------------------------
/src/lerobot/cameras/utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | import platform
18 | from typing import cast
19 |
20 | from lerobot.utils.import_utils import make_device_from_device_class
21 |
22 | from .camera import Camera
23 | from .configs import CameraConfig, Cv2Rotation
24 |
25 |
26 | def make_cameras_from_configs(camera_configs: dict[str, CameraConfig]) -> dict[str, Camera]:
27 | cameras: dict[str, Camera] = {}
28 |
29 | for key, cfg in camera_configs.items():
30 | # TODO(Steven): Consider just using the make_device_from_device_class for all types
31 | if cfg.type == "opencv":
32 | from .opencv import OpenCVCamera
33 |
34 | cameras[key] = OpenCVCamera(cfg)
35 |
36 | elif cfg.type == "intelrealsense":
37 | from .realsense.camera_realsense import RealSenseCamera
38 |
39 | cameras[key] = RealSenseCamera(cfg)
40 |
41 | elif cfg.type == "reachy2_camera":
42 | from .reachy2_camera.reachy2_camera import Reachy2Camera
43 |
44 | cameras[key] = Reachy2Camera(cfg)
45 |
46 | else:
47 | try:
48 | cameras[key] = cast(Camera, make_device_from_device_class(cfg))
49 | except Exception as e:
50 | raise ValueError(f"Error creating camera {key} with config {cfg}: {e}") from e
51 |
52 | return cameras
53 |
54 |
55 | def get_cv2_rotation(rotation: Cv2Rotation) -> int | None:
56 | import cv2 # type: ignore # TODO: add type stubs for OpenCV
57 |
58 | if rotation == Cv2Rotation.ROTATE_90:
59 | return int(cv2.ROTATE_90_CLOCKWISE)
60 | elif rotation == Cv2Rotation.ROTATE_180:
61 | return int(cv2.ROTATE_180)
62 | elif rotation == Cv2Rotation.ROTATE_270:
63 | return int(cv2.ROTATE_90_COUNTERCLOCKWISE)
64 | else:
65 | return None
66 |
67 |
68 | def get_cv2_backend() -> int:
69 | import cv2
70 |
71 | if platform.system() == "Windows":
72 | return int(cv2.CAP_MSMF) # Use MSMF for Windows instead of AVFOUNDATION
73 | # elif platform.system() == "Darwin": # macOS
74 | # return cv2.CAP_AVFOUNDATION
75 | else: # Linux and others
76 | return int(cv2.CAP_ANY)
77 |
--------------------------------------------------------------------------------
/src/lerobot/scripts/lerobot_calibrate.py:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | """
16 | Helper to recalibrate your device (robot or teleoperator).
17 |
18 | Example:
19 |
20 | ```shell
21 | lerobot-calibrate \
22 | --teleop.type=so100_leader \
23 | --teleop.port=/dev/tty.usbmodem58760431551 \
24 | --teleop.id=blue
25 | ```
26 | """
27 |
28 | import logging
29 | from dataclasses import asdict, dataclass
30 | from pprint import pformat
31 |
32 | import draccus
33 |
34 | from lerobot.cameras.opencv.configuration_opencv import OpenCVCameraConfig # noqa: F401
35 | from lerobot.cameras.realsense.configuration_realsense import RealSenseCameraConfig # noqa: F401
36 | from lerobot.robots import ( # noqa: F401
37 | Robot,
38 | RobotConfig,
39 | hope_jr,
40 | koch_follower,
41 | lekiwi,
42 | make_robot_from_config,
43 | so100_follower,
44 | so101_follower,
45 | )
46 | from lerobot.teleoperators import ( # noqa: F401
47 | Teleoperator,
48 | TeleoperatorConfig,
49 | homunculus,
50 | koch_leader,
51 | make_teleoperator_from_config,
52 | so100_leader,
53 | so101_leader,
54 | )
55 | from lerobot.utils.import_utils import register_third_party_plugins
56 | from lerobot.utils.utils import init_logging
57 |
58 |
59 | @dataclass
60 | class CalibrateConfig:
61 | teleop: TeleoperatorConfig | None = None
62 | robot: RobotConfig | None = None
63 |
64 | def __post_init__(self):
65 | if bool(self.teleop) == bool(self.robot):
66 | raise ValueError("Choose either a teleop or a robot.")
67 |
68 | self.device = self.robot if self.robot else self.teleop
69 |
70 |
71 | @draccus.wrap()
72 | def calibrate(cfg: CalibrateConfig):
73 | init_logging()
74 | logging.info(pformat(asdict(cfg)))
75 |
76 | if isinstance(cfg.device, RobotConfig):
77 | device = make_robot_from_config(cfg.device)
78 | elif isinstance(cfg.device, TeleoperatorConfig):
79 | device = make_teleoperator_from_config(cfg.device)
80 |
81 | device.connect(calibrate=False)
82 | device.calibrate()
83 | device.disconnect()
84 |
85 |
86 | def main():
87 | register_third_party_plugins()
88 | calibrate()
89 |
90 |
91 | if __name__ == "__main__":
92 | main()
93 |
--------------------------------------------------------------------------------
/examples/port_datasets/display_error_files.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | import argparse
18 | import json
19 | from pathlib import Path
20 |
21 |
22 | def find_missing_workers(completions_dir, world_size):
23 | """Find workers that are not completed and returns their indices."""
24 | full = list(range(world_size))
25 |
26 | completed = []
27 | for path in completions_dir.glob("*"):
28 | if path.name in [".", ".."]:
29 | continue
30 | index = path.name.lstrip("0")
31 | index = 0 if index == "" else int(index)
32 | completed.append(index)
33 |
34 | missing_workers = set(full) - set(completed)
35 | return missing_workers
36 |
37 |
38 | def find_output_files(slurm_dir, worker_indices):
39 | """Find output files associated to worker indices, and return tuples
40 | of (worker index, output file path)
41 | """
42 | out_files = []
43 | for path in slurm_dir.glob("*.out"):
44 | _, worker_id = path.name.replace(".out", "").split("_")
45 | worker_id = int(worker_id)
46 | if worker_id in worker_indices:
47 | out_files.append((worker_id, path))
48 | return out_files
49 |
50 |
51 | def display_error_files(logs_dir, job_name):
52 | executor_path = Path(logs_dir) / job_name / "executor.json"
53 | completions_dir = Path(logs_dir) / job_name / "completions"
54 |
55 | with open(executor_path) as f:
56 | executor = json.load(f)
57 |
58 | missing_workers = find_missing_workers(completions_dir, executor["world_size"])
59 |
60 | for missing in sorted(missing_workers)[::-1]:
61 | print(missing)
62 |
63 |
64 | def main():
65 | parser = argparse.ArgumentParser()
66 |
67 | parser.add_argument(
68 | "--logs-dir",
69 | type=str,
70 | help="Path to logs directory for `datatrove`.",
71 | )
72 | parser.add_argument(
73 | "--job-name",
74 | type=str,
75 | default="port_droid",
76 | help="Job name used in slurm, and name of the directory created inside the provided logs directory.",
77 | )
78 |
79 | args = parser.parse_args()
80 |
81 | display_error_files(**vars(args))
82 |
83 |
84 | if __name__ == "__main__":
85 | main()
86 |
--------------------------------------------------------------------------------
/src/lerobot/policies/pi05/README.md:
--------------------------------------------------------------------------------
1 | # π₀.₅ (pi05)
2 |
3 | This repository contains the Hugging Face port of **π₀.₅**, adapted from [OpenPI](https://github.com/Physical-Intelligence/openpi) by the Physical Intelligence.
4 | It is designed as a **Vision-Language-Action model with open-world generalization**.
5 |
6 | ---
7 |
8 | ## Model Overview
9 |
10 | | Feature | π₀ | π₀.₅ |
11 | | -------------------- | ------------------------------------------------------ | ----------------------------------------- |
12 | | Time Conditioning | Concatenates time with actions via `action_time_mlp_*` | Uses `time_mlp_*` for AdaRMS conditioning |
13 | | AdaRMS | Not used | Used in action expert |
14 | | Tokenizer Length | 48 tokens | 200 tokens |
15 | | Discrete State Input | False (Uses `state_proj` layer) | True |
16 | | Parameter Count | Higher (includes state embedding) | Lower (no state embedding) |
17 |
18 | ---
19 |
20 | ## Citation
21 |
22 | If you use this work, please cite both **OpenPI** and the π₀.₅ paper:
23 |
24 | ```bibtex
25 | @misc{openpi2024,
26 | author = {Physical Intelligence Lab},
27 | title = {OpenPI: PyTorch Implementation of π0 and π0.5 Policies},
28 | year = {2024},
29 | publisher = {GitHub},
30 | howpublished = {\url{https://github.com/Physical-Intelligence/openpi}},
31 | license = {Apache-2.0}
32 | }
33 |
34 | @misc{intelligence2025pi05visionlanguageactionmodelopenworld,
35 | title = {π₀.₅: a Vision-Language-Action Model with Open-World Generalization},
36 | author = {Physical Intelligence and Kevin Black and Noah Brown and James Darpinian and Karan Dhabalia and Danny Driess and Adnan Esmail and Michael Equi and Chelsea Finn and Niccolo Fusai and Manuel Y. Galliker and Dibya Ghosh and Lachy Groom and Karol Hausman and Brian Ichter and Szymon Jakubczak and Tim Jones and Liyiming Ke and Devin LeBlanc and Sergey Levine and Adrian Li-Bell and Mohith Mothukuri and Suraj Nair and Karl Pertsch and Allen Z. Ren and Lucy Xiaoyang Shi and Laura Smith and Jost Tobias Springenberg and Kyle Stachowicz and James Tanner and Quan Vuong and Homer Walke and Anna Walling and Haohuan Wang and Lili Yu and Ury Zhilinsky},
37 | year = {2025},
38 | eprint = {2504.16054},
39 | archivePrefix= {arXiv},
40 | primaryClass = {cs.LG},
41 | url = {https://arxiv.org/abs/2504.16054},
42 | }
43 | ```
44 |
45 | ---
46 |
47 | ## License
48 |
49 | This port follows the **Apache 2.0 License**, consistent with the original [OpenPI repository](https://github.com/Physical-Intelligence/openpi).
50 |
--------------------------------------------------------------------------------
/src/lerobot/processor/policy_robot_bridge.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from dataclasses import asdict, dataclass
18 | from typing import Any
19 |
20 | import torch
21 |
22 | from lerobot.configs.types import FeatureType, PipelineFeatureType, PolicyFeature
23 | from lerobot.processor import ActionProcessorStep, PolicyAction, ProcessorStepRegistry, RobotAction
24 | from lerobot.utils.constants import ACTION
25 |
26 |
27 | @dataclass
28 | @ProcessorStepRegistry.register("robot_action_to_policy_action_processor")
29 | class RobotActionToPolicyActionProcessorStep(ActionProcessorStep):
30 | """Processor step to map a dictionary to a tensor action."""
31 |
32 | motor_names: list[str]
33 |
34 | def action(self, action: RobotAction) -> PolicyAction:
35 | if len(self.motor_names) != len(action):
36 | raise ValueError(f"Action must have {len(self.motor_names)} elements, got {len(action)}")
37 | return torch.tensor([action[f"{name}.pos"] for name in self.motor_names])
38 |
39 | def get_config(self) -> dict[str, Any]:
40 | return asdict(self)
41 |
42 | def transform_features(self, features):
43 | features[PipelineFeatureType.ACTION][ACTION] = PolicyFeature(
44 | type=FeatureType.ACTION, shape=(len(self.motor_names),)
45 | )
46 | return features
47 |
48 |
49 | @dataclass
50 | @ProcessorStepRegistry.register("policy_action_to_robot_action_processor")
51 | class PolicyActionToRobotActionProcessorStep(ActionProcessorStep):
52 | """Processor step to map a policy action to a robot action."""
53 |
54 | motor_names: list[str]
55 |
56 | def action(self, action: PolicyAction) -> RobotAction:
57 | if len(self.motor_names) != len(action):
58 | raise ValueError(f"Action must have {len(self.motor_names)} elements, got {len(action)}")
59 | return {f"{name}.pos": action[i] for i, name in enumerate(self.motor_names)}
60 |
61 | def get_config(self) -> dict[str, Any]:
62 | return asdict(self)
63 |
64 | def transform_features(self, features):
65 | for name in self.motor_names:
66 | features[PipelineFeatureType.ACTION][f"{name}.pos"] = PolicyFeature(
67 | type=FeatureType.ACTION, shape=(1,)
68 | )
69 | return features
70 |
--------------------------------------------------------------------------------
/docs/source/feetech.mdx:
--------------------------------------------------------------------------------
1 | # Feetech Motor Firmware Update
2 |
3 | This tutorial guides you through updating the firmware of Feetech motors using the official Feetech software.
4 |
5 | ## Prerequisites
6 |
7 | - Windows computer (Feetech software is only available for Windows)
8 | - Feetech motor control board
9 | - USB cable to connect the control board to your computer
10 | - Feetech motors connected to the control board
11 |
12 | ## Step 1: Download Feetech Software
13 |
14 | 1. Visit the official Feetech software download page: [https://www.feetechrc.com/software.html](https://www.feetechrc.com/software.html)
15 | 2. Download the latest version of the Feetech debugging software (FD)
16 | 3. Install the software on your Windows computer
17 |
18 | ## Step 2: Hardware Setup
19 |
20 | 1. Connect your Feetech motors to the motor control board
21 | 2. Connect the motor control board to your Windows computer via USB cable
22 | 3. Ensure power is supplied to the motors
23 |
24 | ## Step 3: Configure Connection
25 |
26 | 1. Launch the Feetech debugging software
27 | 2. Select the correct COM port from the port dropdown menu
28 | - If unsure which port to use, check Windows Device Manager under "Ports (COM & LPT)"
29 | 3. Set the appropriate baud rate (typically 1000000 for most Feetech motors)
30 | 4. Click "Open" to establish communication with the control board
31 |
32 | ## Step 4: Scan for Motors
33 |
34 | 1. Once connected, click the "Search" button to detect all connected motors
35 | 2. The software will automatically discover and list all motors on the bus
36 | 3. Each motor will appear with its ID number
37 |
38 | ## Step 5: Update Firmware
39 |
40 | For each motor you want to update:
41 |
42 | 1. **Select the motor** from the list by clicking on it
43 | 2. **Click on Upgrade tab**:
44 | 3. **Click on Online button**:
45 | - If an potential firmware update is found, it will be displayed in the box
46 | 4. **Click on Upgrade button**:
47 | - The update progress will be displayed
48 |
49 | ## Step 6: Verify Update
50 |
51 | 1. After the update completes, the software should automatically refresh the motor information
52 | 2. Verify that the firmware version has been updated to the expected version
53 |
54 | ## Important Notes
55 |
56 | ⚠️ **Warning**: Do not disconnect power or USB during firmware updates, it will potentially brick the motor.
57 |
58 | ## Bonus: Motor Debugging on Linux/macOS
59 |
60 | For debugging purposes only, you can use the open-source Feetech Debug Tool:
61 |
62 | - **Repository**: [FT_SCServo_Debug_Qt](https://github.com/CarolinePascal/FT_SCServo_Debug_Qt/tree/fix/port-search-timer)
63 |
64 | ### Installation Instructions
65 |
66 | Follow the instructions in the repository to install the tool, for Ubuntu you can directly install it, for MacOS you need to build it from source.
67 |
68 | **Limitations:**
69 |
70 | - This tool is for debugging and parameter adjustment only
71 | - Firmware updates must still be done on Windows with official Feetech software
72 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report.yml:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | name: "\U0001F41B Bug Report"
16 | description: Submit a bug report to help us improve LeRobot
17 | body:
18 | - type: markdown
19 | attributes:
20 | value: |
21 | Thanks for taking the time to submit a bug report! 🐛
22 | If this is not a bug related to the LeRobot library directly, but instead a general question about your code or the library specifically please use our [discord](https://discord.gg/s3KuuzsPFb).
23 |
24 | - type: textarea
25 | id: system-info
26 | attributes:
27 | label: System Info
28 | description: Please share your LeRobot configuration by running `lerobot-info` (if installed) or `python -m lerobot.scripts.display_sys_info` (if not installed) and pasting the output below.
29 | render: Shell
30 | placeholder: lerobot version, OS, python version, numpy version, torch version, and lerobot's configuration
31 | validations:
32 | required: true
33 |
34 | - type: checkboxes
35 | id: information-scripts-examples
36 | attributes:
37 | label: Information
38 | description: 'The problem arises when using:'
39 | options:
40 | - label: "One of the scripts in the examples/ folder of LeRobot"
41 | - label: "My own task or dataset (give details below)"
42 |
43 | - type: textarea
44 | id: reproduction
45 | validations:
46 | required: true
47 | attributes:
48 | label: Reproduction
49 | description: |
50 | If needed, provide a simple code sample that reproduces the problem you ran into. It can be a Colab link or just a code snippet.
51 | Sharing error messages or stack traces could be useful as well!
52 | Important! Use code tags to correctly format your code. See https://help.github.com/en/github/writing-on-github/creating-and-highlighting-code-blocks#syntax-highlighting
53 | Try to avoid screenshots, as they are hard to read and don't allow copy-and-pasting.
54 |
55 | placeholder: |
56 | Steps to reproduce the behavior:
57 |
58 | 1.
59 | 2.
60 | 3.
61 |
62 | - type: textarea
63 | id: expected-behavior
64 | validations:
65 | required: true
66 | attributes:
67 | label: Expected behavior
68 | description: "A clear and concise description of what you would expect to happen."
69 |
--------------------------------------------------------------------------------
/src/lerobot/policies/sac/reward_model/configuration_classifier.py:
--------------------------------------------------------------------------------
1 | # !/usr/bin/env python
2 |
3 | # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | from dataclasses import dataclass, field
17 |
18 | from lerobot.configs.policies import PreTrainedConfig
19 | from lerobot.configs.types import NormalizationMode
20 | from lerobot.optim.optimizers import AdamWConfig, OptimizerConfig
21 | from lerobot.optim.schedulers import LRSchedulerConfig
22 | from lerobot.utils.constants import OBS_IMAGE
23 |
24 |
25 | @PreTrainedConfig.register_subclass(name="reward_classifier")
26 | @dataclass
27 | class RewardClassifierConfig(PreTrainedConfig):
28 | """Configuration for the Reward Classifier model."""
29 |
30 | name: str = "reward_classifier"
31 | num_classes: int = 2
32 | hidden_dim: int = 256
33 | latent_dim: int = 256
34 | image_embedding_pooling_dim: int = 8
35 | dropout_rate: float = 0.1
36 | model_name: str = "helper2424/resnet10"
37 | device: str = "cpu"
38 | model_type: str = "cnn" # "transformer" or "cnn"
39 | num_cameras: int = 2
40 | learning_rate: float = 1e-4
41 | weight_decay: float = 0.01
42 | grad_clip_norm: float = 1.0
43 | normalization_mapping: dict[str, NormalizationMode] = field(
44 | default_factory=lambda: {
45 | "VISUAL": NormalizationMode.MEAN_STD,
46 | }
47 | )
48 |
49 | @property
50 | def observation_delta_indices(self) -> list | None:
51 | return None
52 |
53 | @property
54 | def action_delta_indices(self) -> list | None:
55 | return None
56 |
57 | @property
58 | def reward_delta_indices(self) -> list | None:
59 | return None
60 |
61 | def get_optimizer_preset(self) -> OptimizerConfig:
62 | return AdamWConfig(
63 | lr=self.learning_rate,
64 | weight_decay=self.weight_decay,
65 | grad_clip_norm=self.grad_clip_norm,
66 | )
67 |
68 | def get_scheduler_preset(self) -> LRSchedulerConfig | None:
69 | return None
70 |
71 | def validate_features(self) -> None:
72 | """Validate feature configurations."""
73 | has_image = any(key.startswith(OBS_IMAGE) for key in self.input_features)
74 | if not has_image:
75 | raise ValueError(
76 | "You must provide an image observation (key starting with 'observation.image') in the input features"
77 | )
78 |
--------------------------------------------------------------------------------
/docs/source/notebooks.mdx:
--------------------------------------------------------------------------------
1 | # 🤗 LeRobot Notebooks
2 |
3 | This repository contains example notebooks for using LeRobot. These notebooks demonstrate how to train policies on real or simulation datasets using standardized policies.
4 |
5 | ---
6 |
7 | ### Training ACT
8 |
9 | [ACT](https://huggingface.co/papers/2304.13705) (Action Chunking Transformer) is a transformer-based policy architecture for imitation learning that processes robot states and camera inputs to generate smooth, chunked action sequences.
10 |
11 | We provide a ready-to-run Google Colab notebook to help you train ACT policies using datasets from the Hugging Face Hub, with optional logging to Weights & Biases.
12 |
13 | | Notebook | Colab |
14 | | :------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
15 | | [Train ACT with LeRobot](https://github.com/huggingface/notebooks/blob/main/lerobot/training-act.ipynb) | [](https://colab.research.google.com/github/huggingface/notebooks/blob/main/lerobot/training-act.ipynb) |
16 |
17 | Expected training time for 100k steps: ~1.5 hours on an NVIDIA A100 GPU with batch size of `64`.
18 |
19 | ### Training SmolVLA
20 |
21 | [SmolVLA](https://huggingface.co/papers/2506.01844) is a small but efficient Vision-Language-Action model. It is compact in size with 450 M-parameter and is developed by Hugging Face.
22 |
23 | We provide a ready-to-run Google Colab notebook to help you train SmolVLA policies using datasets from the Hugging Face Hub, with optional logging to Weights & Biases.
24 |
25 | | Notebook | Colab |
26 | | :-------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
27 | | [Train SmolVLA with LeRobot](https://github.com/huggingface/notebooks/blob/main/lerobot/training-smolvla.ipynb) | [](https://colab.research.google.com/github/huggingface/notebooks/blob/main/lerobot/training-smolvla.ipynb) |
28 |
29 | Expected training time for 20k steps: ~5 hours on an NVIDIA A100 GPU with batch size of `64`.
30 |
--------------------------------------------------------------------------------
/tests/artifacts/image_transforms/save_image_transforms_to_safetensors.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2024 The HuggingFace Inc. team. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | from pathlib import Path
17 |
18 | import torch
19 | from safetensors.torch import save_file
20 |
21 | from lerobot.datasets.lerobot_dataset import LeRobotDataset
22 | from lerobot.datasets.transforms import (
23 | ImageTransformConfig,
24 | ImageTransforms,
25 | ImageTransformsConfig,
26 | make_transform_from_config,
27 | )
28 | from lerobot.utils.random_utils import seeded_context
29 |
30 | ARTIFACT_DIR = Path("tests/artifacts/image_transforms")
31 | DATASET_REPO_ID = "lerobot/aloha_static_cups_open"
32 |
33 |
34 | def save_default_config_transform(original_frame: torch.Tensor, output_dir: Path):
35 | cfg = ImageTransformsConfig(enable=True)
36 | default_tf = ImageTransforms(cfg)
37 |
38 | with seeded_context(1337):
39 | img_tf = default_tf(original_frame)
40 |
41 | save_file({"default": img_tf}, output_dir / "default_transforms.safetensors")
42 |
43 |
44 | def save_single_transforms(original_frame: torch.Tensor, output_dir: Path):
45 | transforms = {
46 | ("ColorJitter", "brightness", [(0.5, 0.5), (2.0, 2.0)]),
47 | ("ColorJitter", "contrast", [(0.5, 0.5), (2.0, 2.0)]),
48 | ("ColorJitter", "saturation", [(0.5, 0.5), (2.0, 2.0)]),
49 | ("ColorJitter", "hue", [(-0.25, -0.25), (0.25, 0.25)]),
50 | ("SharpnessJitter", "sharpness", [(0.5, 0.5), (2.0, 2.0)]),
51 | }
52 |
53 | frames = {"original_frame": original_frame}
54 | for tf_type, tf_name, min_max_values in transforms.items():
55 | for min_max in min_max_values:
56 | tf_cfg = ImageTransformConfig(type=tf_type, kwargs={tf_name: min_max})
57 | tf = make_transform_from_config(tf_cfg)
58 | key = f"{tf_name}_{min_max[0]}_{min_max[1]}"
59 | frames[key] = tf(original_frame)
60 |
61 | save_file(frames, output_dir / "single_transforms.safetensors")
62 |
63 |
64 | def main():
65 | dataset = LeRobotDataset(DATASET_REPO_ID, episodes=[0], image_transforms=None)
66 | output_dir = Path(ARTIFACT_DIR)
67 | output_dir.mkdir(parents=True, exist_ok=True)
68 | original_frame = dataset[0][dataset.meta.camera_keys[0]]
69 |
70 | save_single_transforms(original_frame, output_dir)
71 | save_default_config_transform(original_frame, output_dir)
72 |
73 |
74 | if __name__ == "__main__":
75 | main()
76 |
--------------------------------------------------------------------------------