├── 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 |
2 | 3 | HuggingFace Expert Acceleration Program 8 | 9 |
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) | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](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) | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](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 | --------------------------------------------------------------------------------