Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +93 -0
- aloha_robot_project/Miniconda3-latest-Linux-x86_64.sh +3 -0
- aloha_robot_project/calvin/.flake8 +15 -0
- aloha_robot_project/calvin/.github/workflows/codeql.yml +41 -0
- aloha_robot_project/calvin/.gitignore +146 -0
- aloha_robot_project/calvin/.gitmodules +3 -0
- aloha_robot_project/calvin/.pre-commit-config.yaml +33 -0
- aloha_robot_project/calvin/LICENSE +21 -0
- aloha_robot_project/calvin/README.md +377 -0
- aloha_robot_project/calvin/RL_with_CALVIN.ipynb +325 -0
- aloha_robot_project/calvin/calvin_env/.flake8 +15 -0
- aloha_robot_project/calvin/calvin_env/.gitignore +161 -0
- aloha_robot_project/calvin/calvin_env/.gitmodules +3 -0
- aloha_robot_project/calvin/calvin_env/.pre-commit-config.yaml +32 -0
- aloha_robot_project/calvin/calvin_env/LICENSE +21 -0
- aloha_robot_project/calvin/calvin_env/README.md +12 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/__init__.py +10 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/camera/camera.py +91 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/camera/gripper_camera.py +46 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/camera/static_camera.py +72 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/camera/tactile_sensor.py +41 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/datarenderer.py +299 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/envs/play_lmp_wrapper.py +106 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/envs/play_table_env.py +304 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/envs/tasks.py +306 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/io_utils/data_recorder.py +135 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/io_utils/vr_input.py +187 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/robot/IKfast.py +72 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/robot/mixed_ik.py +118 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/robot/robot.py +412 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/scene/objects/base_object.py +13 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/scene/objects/button.py +76 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/scene/objects/door.py +40 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/scene/objects/fixed_object.py +41 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/scene/objects/light.py +52 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/scene/objects/movable_object.py +95 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/scene/objects/switch.py +73 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/scene/play_table_scene.py +234 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/scripts/check_tasks.py +103 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/scripts/convert_gripper_actions.py +16 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/scripts/dataset_to_euler.py +34 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/scripts/record_video_icra.py +112 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/scripts/render_low_freq.py +74 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/scripts/reset_env_rendered_episode.py +84 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/scripts/unnormalize_depth.py +24 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/utils/utils.py +233 -0
- aloha_robot_project/calvin/calvin_env/calvin_env/vrdatacollector.py +52 -0
- aloha_robot_project/calvin/calvin_env/conf/cameras/cameras/gripper.yaml +8 -0
- aloha_robot_project/calvin/calvin_env/conf/cameras/cameras/opposing.yaml +10 -0
- aloha_robot_project/calvin/calvin_env/conf/cameras/cameras/static.yaml +11 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,96 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
aloha_robot_project/Miniconda3-latest-Linux-x86_64.sh filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_A/meshes/light_link.STL filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_A/textures/dark_wood.png filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_A/textures/dark_wood__black_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_A/textures/dark_wood__gray_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_A/textures/light_wood.png filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_A/textures/light_wood__black_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_A/textures/light_wood__gray_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_A/textures/wood.png filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_A/textures/wood__black_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_A/textures/wood__gray_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_B/meshes/light_link.STL filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_B/textures/dark_wood.png filter=lfs diff=lfs merge=lfs -text
|
| 49 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_B/textures/dark_wood__black_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 50 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_B/textures/dark_wood__gray_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 51 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_B/textures/light_wood.png filter=lfs diff=lfs merge=lfs -text
|
| 52 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_B/textures/light_wood__black_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 53 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_B/textures/light_wood__gray_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 54 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_B/textures/wood.png filter=lfs diff=lfs merge=lfs -text
|
| 55 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_B/textures/wood__black_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 56 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_B/textures/wood__gray_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 57 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_C/meshes/light_link.STL filter=lfs diff=lfs merge=lfs -text
|
| 58 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_C/textures/dark_wood.png filter=lfs diff=lfs merge=lfs -text
|
| 59 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_C/textures/dark_wood__black_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 60 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_C/textures/dark_wood__gray_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 61 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_C/textures/light_wood.png filter=lfs diff=lfs merge=lfs -text
|
| 62 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_C/textures/light_wood__black_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 63 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_C/textures/light_wood__gray_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 64 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_C/textures/wood.png filter=lfs diff=lfs merge=lfs -text
|
| 65 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_C/textures/wood__black_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 66 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_C/textures/wood__gray_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 67 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_D/meshes/light_link.STL filter=lfs diff=lfs merge=lfs -text
|
| 68 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_D/textures/dark_wood.png filter=lfs diff=lfs merge=lfs -text
|
| 69 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_D/textures/dark_wood__black_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 70 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_D/textures/dark_wood__gray_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 71 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_D/textures/light_wood.png filter=lfs diff=lfs merge=lfs -text
|
| 72 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_D/textures/light_wood__black_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 73 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_D/textures/light_wood__gray_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 74 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_D/textures/wood.png filter=lfs diff=lfs merge=lfs -text
|
| 75 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_D/textures/wood__black_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 76 |
+
aloha_robot_project/calvin/calvin_env/data/calvin_table_D/textures/wood__gray_handle.png filter=lfs diff=lfs merge=lfs -text
|
| 77 |
+
aloha_robot_project/calvin/calvin_env/data/franka_panda/meshes/visual/Assem1.SLDASM filter=lfs diff=lfs merge=lfs -text
|
| 78 |
+
aloha_robot_project/calvin/calvin_env/data/franka_panda/meshes/visual/FRANKA_Finger.SLDPRT filter=lfs diff=lfs merge=lfs -text
|
| 79 |
+
aloha_robot_project/calvin/calvin_env/data/franka_panda/meshes/visual/digit.STL filter=lfs diff=lfs merge=lfs -text
|
| 80 |
+
aloha_robot_project/calvin/calvin_env/data/franka_panda/meshes/visual/digit_gel_only.STL filter=lfs diff=lfs merge=lfs -text
|
| 81 |
+
aloha_robot_project/calvin/calvin_env/data/franka_panda/meshes/visual/finger.SLDPRT filter=lfs diff=lfs merge=lfs -text
|
| 82 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/allegro_hand_description/meshes/base_link.STL filter=lfs diff=lfs merge=lfs -text
|
| 83 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/allegro_hand_description/meshes/base_link_left.STL filter=lfs diff=lfs merge=lfs -text
|
| 84 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/allegro_hand_description/meshes/digit.STL filter=lfs diff=lfs merge=lfs -text
|
| 85 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/allegro_hand_description/meshes/link_12.0_left.STL filter=lfs diff=lfs merge=lfs -text
|
| 86 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/allegro_hand_description/meshes/link_12.0_right.STL filter=lfs diff=lfs merge=lfs -text
|
| 87 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/sawyer_robot/sawyer_description/meshes/sawyer_mp1/l6.STL filter=lfs diff=lfs merge=lfs -text
|
| 88 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/sawyer_robot/sawyer_description/meshes/sawyer_mp3/l0.STL filter=lfs diff=lfs merge=lfs -text
|
| 89 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/sawyer_robot/sawyer_description/meshes/sawyer_mp3/l1.STL filter=lfs diff=lfs merge=lfs -text
|
| 90 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/sawyer_robot/sawyer_description/meshes/sawyer_pv/base.STL filter=lfs diff=lfs merge=lfs -text
|
| 91 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/sawyer_robot/sawyer_description/meshes/sawyer_pv/head.STL filter=lfs diff=lfs merge=lfs -text
|
| 92 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/sawyer_robot/sawyer_description/meshes/sawyer_pv/l0.STL filter=lfs diff=lfs merge=lfs -text
|
| 93 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/sawyer_robot/sawyer_description/meshes/sawyer_pv/l1.STL filter=lfs diff=lfs merge=lfs -text
|
| 94 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/sawyer_robot/sawyer_description/meshes/sawyer_pv/l2.STL filter=lfs diff=lfs merge=lfs -text
|
| 95 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/sawyer_robot/sawyer_description/meshes/sawyer_pv/l3.STL filter=lfs diff=lfs merge=lfs -text
|
| 96 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/sawyer_robot/sawyer_description/meshes/sawyer_pv/l4.STL filter=lfs diff=lfs merge=lfs -text
|
| 97 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/sawyer_robot/sawyer_description/meshes/sawyer_pv/l5.STL filter=lfs diff=lfs merge=lfs -text
|
| 98 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/sawyer_robot/sawyer_description/meshes/sawyer_pv/l6.STL filter=lfs diff=lfs merge=lfs -text
|
| 99 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/sawyer_robot/sawyer_description/meshes/sawyer_pv/pedestal.STL filter=lfs diff=lfs merge=lfs -text
|
| 100 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/wsg50/WSG-FMF.stl filter=lfs diff=lfs merge=lfs -text
|
| 101 |
+
aloha_robot_project/calvin/calvin_env/tacto/examples/wsg50/digit.STL filter=lfs diff=lfs merge=lfs -text
|
| 102 |
+
aloha_robot_project/calvin/calvin_env/tacto/experiments/grasp_stability/setup/sawyer_robot/sawyer_description/meshes/sawyer_mp1/l6.STL filter=lfs diff=lfs merge=lfs -text
|
| 103 |
+
aloha_robot_project/calvin/calvin_env/tacto/experiments/grasp_stability/setup/sawyer_robot/sawyer_description/meshes/sawyer_mp3/l0.STL filter=lfs diff=lfs merge=lfs -text
|
| 104 |
+
aloha_robot_project/calvin/calvin_env/tacto/experiments/grasp_stability/setup/sawyer_robot/sawyer_description/meshes/sawyer_mp3/l1.STL filter=lfs diff=lfs merge=lfs -text
|
| 105 |
+
aloha_robot_project/calvin/calvin_env/tacto/experiments/grasp_stability/setup/sawyer_robot/sawyer_description/meshes/sawyer_pv/base.STL filter=lfs diff=lfs merge=lfs -text
|
| 106 |
+
aloha_robot_project/calvin/calvin_env/tacto/experiments/grasp_stability/setup/sawyer_robot/sawyer_description/meshes/sawyer_pv/head.STL filter=lfs diff=lfs merge=lfs -text
|
| 107 |
+
aloha_robot_project/calvin/calvin_env/tacto/experiments/grasp_stability/setup/sawyer_robot/sawyer_description/meshes/sawyer_pv/l0.STL filter=lfs diff=lfs merge=lfs -text
|
| 108 |
+
aloha_robot_project/calvin/calvin_env/tacto/experiments/grasp_stability/setup/sawyer_robot/sawyer_description/meshes/sawyer_pv/l1.STL filter=lfs diff=lfs merge=lfs -text
|
| 109 |
+
aloha_robot_project/calvin/calvin_env/tacto/experiments/grasp_stability/setup/sawyer_robot/sawyer_description/meshes/sawyer_pv/l2.STL filter=lfs diff=lfs merge=lfs -text
|
| 110 |
+
aloha_robot_project/calvin/calvin_env/tacto/experiments/grasp_stability/setup/sawyer_robot/sawyer_description/meshes/sawyer_pv/l3.STL filter=lfs diff=lfs merge=lfs -text
|
| 111 |
+
aloha_robot_project/calvin/calvin_env/tacto/experiments/grasp_stability/setup/sawyer_robot/sawyer_description/meshes/sawyer_pv/l4.STL filter=lfs diff=lfs merge=lfs -text
|
| 112 |
+
aloha_robot_project/calvin/calvin_env/tacto/experiments/grasp_stability/setup/sawyer_robot/sawyer_description/meshes/sawyer_pv/l5.STL filter=lfs diff=lfs merge=lfs -text
|
| 113 |
+
aloha_robot_project/calvin/calvin_env/tacto/experiments/grasp_stability/setup/sawyer_robot/sawyer_description/meshes/sawyer_pv/l6.STL filter=lfs diff=lfs merge=lfs -text
|
| 114 |
+
aloha_robot_project/calvin/calvin_env/tacto/experiments/grasp_stability/setup/sawyer_robot/sawyer_description/meshes/sawyer_pv/pedestal.STL filter=lfs diff=lfs merge=lfs -text
|
| 115 |
+
aloha_robot_project/calvin/calvin_env/tacto/experiments/grasp_stability/setup/wsg50/WSG-FMF.stl filter=lfs diff=lfs merge=lfs -text
|
| 116 |
+
aloha_robot_project/calvin/calvin_env/tacto/experiments/grasp_stability/setup/wsg50/digit.STL filter=lfs diff=lfs merge=lfs -text
|
| 117 |
+
aloha_robot_project/calvin/calvin_env/tacto/experiments/rolling/setup/sensors/digit.STL filter=lfs diff=lfs merge=lfs -text
|
| 118 |
+
aloha_robot_project/calvin/calvin_env/tacto/meshes/digit.STL filter=lfs diff=lfs merge=lfs -text
|
| 119 |
+
aloha_robot_project/calvin/calvin_env/tacto/meshes/omnitact.STL filter=lfs diff=lfs merge=lfs -text
|
| 120 |
+
aloha_robot_project/calvin/calvin_env/tacto/meshes/omnitact_mod_pybullet.STL filter=lfs diff=lfs merge=lfs -text
|
| 121 |
+
aloha_robot_project/calvin/calvin_env/tacto/website/static/img/demo_allegro.gif filter=lfs diff=lfs merge=lfs -text
|
| 122 |
+
aloha_robot_project/calvin/calvin_env/tacto/website/static/img/demo_digit.gif filter=lfs diff=lfs merge=lfs -text
|
| 123 |
+
aloha_robot_project/calvin/calvin_env/tacto/website/static/img/demo_grasp.gif filter=lfs diff=lfs merge=lfs -text
|
| 124 |
+
aloha_robot_project/calvin/calvin_env/tacto/website/static/img/demo_omnitact.gif filter=lfs diff=lfs merge=lfs -text
|
| 125 |
+
aloha_robot_project/calvin/calvin_env/tacto/website/static/img/demo_rolling.gif filter=lfs diff=lfs merge=lfs -text
|
| 126 |
+
aloha_robot_project/calvin/calvin_env/tacto/website/static/img/demo_shadow.gif filter=lfs diff=lfs merge=lfs -text
|
| 127 |
+
aloha_robot_project/calvin/media/sensors.png filter=lfs diff=lfs merge=lfs -text
|
| 128 |
+
aloha_robot_project/calvin/media/teaser.png filter=lfs diff=lfs merge=lfs -text
|
aloha_robot_project/Miniconda3-latest-Linux-x86_64.sh
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e0b10e050e8928e2eb9aad2c522ee3b5d31d30048b8a9997663a8a460d538cef
|
| 3 |
+
size 156772981
|
aloha_robot_project/calvin/.flake8
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[flake8]
|
| 2 |
+
exclude = .git
|
| 3 |
+
# Default is 79 in PEP 8
|
| 4 |
+
max-line-length = 120
|
| 5 |
+
select = E,F,W,C
|
| 6 |
+
ignore=W503, # line break before binary operator, need for black
|
| 7 |
+
E203, # whitespace before ':'. Opposite convention enforced by black
|
| 8 |
+
E731, # do not assign a lambda expression, use a def
|
| 9 |
+
E722,
|
| 10 |
+
F401,
|
| 11 |
+
F841,
|
| 12 |
+
E402, # module level import not at top of file
|
| 13 |
+
E741, # ambiguous variable name
|
| 14 |
+
E501, # line too long. Handled by black
|
| 15 |
+
C406, # Unnecessary list literal - rewrite as a dict literal
|
aloha_robot_project/calvin/.github/workflows/codeql.yml
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "CodeQL"
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches: [ "main" ]
|
| 6 |
+
pull_request:
|
| 7 |
+
branches: [ "main" ]
|
| 8 |
+
schedule:
|
| 9 |
+
- cron: "33 22 * * 4"
|
| 10 |
+
|
| 11 |
+
jobs:
|
| 12 |
+
analyze:
|
| 13 |
+
name: Analyze
|
| 14 |
+
runs-on: ubuntu-latest
|
| 15 |
+
permissions:
|
| 16 |
+
actions: read
|
| 17 |
+
contents: read
|
| 18 |
+
security-events: write
|
| 19 |
+
|
| 20 |
+
strategy:
|
| 21 |
+
fail-fast: false
|
| 22 |
+
matrix:
|
| 23 |
+
language: [ python ]
|
| 24 |
+
|
| 25 |
+
steps:
|
| 26 |
+
- name: Checkout
|
| 27 |
+
uses: actions/checkout@v3
|
| 28 |
+
|
| 29 |
+
- name: Initialize CodeQL
|
| 30 |
+
uses: github/codeql-action/init@v2
|
| 31 |
+
with:
|
| 32 |
+
languages: ${{ matrix.language }}
|
| 33 |
+
queries: +security-and-quality
|
| 34 |
+
|
| 35 |
+
- name: Autobuild
|
| 36 |
+
uses: github/codeql-action/autobuild@v2
|
| 37 |
+
|
| 38 |
+
- name: Perform CodeQL Analysis
|
| 39 |
+
uses: github/codeql-action/analyze@v2
|
| 40 |
+
with:
|
| 41 |
+
category: "/language:${{ matrix.language }}"
|
aloha_robot_project/calvin/.gitignore
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# C extensions
|
| 7 |
+
*.so
|
| 8 |
+
|
| 9 |
+
# Distribution / packaging
|
| 10 |
+
.Python
|
| 11 |
+
build/
|
| 12 |
+
develop-eggs/
|
| 13 |
+
dist/
|
| 14 |
+
downloads/
|
| 15 |
+
eggs/
|
| 16 |
+
.eggs/
|
| 17 |
+
lib/
|
| 18 |
+
lib64/
|
| 19 |
+
parts/
|
| 20 |
+
sdist/
|
| 21 |
+
var/
|
| 22 |
+
wheels/
|
| 23 |
+
pip-wheel-metadata/
|
| 24 |
+
share/python-wheels/
|
| 25 |
+
*.egg-info/
|
| 26 |
+
.installed.cfg
|
| 27 |
+
*.egg
|
| 28 |
+
MANIFEST
|
| 29 |
+
|
| 30 |
+
# PyInstaller
|
| 31 |
+
# Usually these files are written by a python script from a template
|
| 32 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 33 |
+
*.manifest
|
| 34 |
+
*.spec
|
| 35 |
+
|
| 36 |
+
# Installer logs
|
| 37 |
+
pip-log.txt
|
| 38 |
+
pip-delete-this-directory.txt
|
| 39 |
+
|
| 40 |
+
# Unit test / coverage reports
|
| 41 |
+
htmlcov/
|
| 42 |
+
.tox/
|
| 43 |
+
.nox/
|
| 44 |
+
.coverage
|
| 45 |
+
.coverage.*
|
| 46 |
+
.cache
|
| 47 |
+
nosetests.xml
|
| 48 |
+
coverage.xml
|
| 49 |
+
*.cover
|
| 50 |
+
*.py,cover
|
| 51 |
+
.hypothesis/
|
| 52 |
+
.pytest_cache/
|
| 53 |
+
|
| 54 |
+
# Translations
|
| 55 |
+
*.mo
|
| 56 |
+
*.pot
|
| 57 |
+
|
| 58 |
+
# Django stuff:
|
| 59 |
+
*.log
|
| 60 |
+
local_settings.py
|
| 61 |
+
db.sqlite3
|
| 62 |
+
db.sqlite3-journal
|
| 63 |
+
|
| 64 |
+
# Flask stuff:
|
| 65 |
+
instance/
|
| 66 |
+
.webassets-cache
|
| 67 |
+
|
| 68 |
+
# Scrapy stuff:
|
| 69 |
+
.scrapy
|
| 70 |
+
|
| 71 |
+
# Sphinx documentation
|
| 72 |
+
docs/_build/
|
| 73 |
+
|
| 74 |
+
# PyBuilder
|
| 75 |
+
target/
|
| 76 |
+
|
| 77 |
+
# Jupyter Notebook
|
| 78 |
+
.ipynb_checkpoints
|
| 79 |
+
|
| 80 |
+
# IPython
|
| 81 |
+
profile_default/
|
| 82 |
+
ipython_config.py
|
| 83 |
+
|
| 84 |
+
# pyenv
|
| 85 |
+
.python-version
|
| 86 |
+
|
| 87 |
+
# pipenv
|
| 88 |
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
| 89 |
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
| 90 |
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
| 91 |
+
# install all needed dependencies.
|
| 92 |
+
#Pipfile.lock
|
| 93 |
+
|
| 94 |
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
| 95 |
+
__pypackages__/
|
| 96 |
+
|
| 97 |
+
# Celery stuff
|
| 98 |
+
celerybeat-schedule
|
| 99 |
+
celerybeat.pid
|
| 100 |
+
|
| 101 |
+
# SageMath parsed files
|
| 102 |
+
*.sage.py
|
| 103 |
+
|
| 104 |
+
# Environments
|
| 105 |
+
.env
|
| 106 |
+
.venv
|
| 107 |
+
env/
|
| 108 |
+
venv/
|
| 109 |
+
ENV/
|
| 110 |
+
env.bak/
|
| 111 |
+
venv.bak/
|
| 112 |
+
|
| 113 |
+
# Spyder project settings
|
| 114 |
+
.spyderproject
|
| 115 |
+
.spyproject
|
| 116 |
+
|
| 117 |
+
# Rope project settings
|
| 118 |
+
.ropeproject
|
| 119 |
+
|
| 120 |
+
# mkdocs documentation
|
| 121 |
+
/site
|
| 122 |
+
|
| 123 |
+
# mypy
|
| 124 |
+
.mypy_cache/
|
| 125 |
+
.dmypy.json
|
| 126 |
+
dmypy.json
|
| 127 |
+
|
| 128 |
+
# Pyre type checker
|
| 129 |
+
.pyre/
|
| 130 |
+
|
| 131 |
+
# vscode
|
| 132 |
+
.vscode
|
| 133 |
+
|
| 134 |
+
*/runs/*
|
| 135 |
+
|
| 136 |
+
dataset/task_D_D/*
|
| 137 |
+
dataset/task_ABCD_D/*
|
| 138 |
+
dataset/task_ABC_D/*
|
| 139 |
+
|
| 140 |
+
.idea/*
|
| 141 |
+
|
| 142 |
+
# Ignore venv created as part of installation instructions
|
| 143 |
+
calvin_venv/*
|
| 144 |
+
|
| 145 |
+
# Ignore datasets as downloaded according to instructions
|
| 146 |
+
dataset/*
|
aloha_robot_project/calvin/.gitmodules
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[submodule "calvin_env"]
|
| 2 |
+
path = calvin_env
|
| 3 |
+
url = https://github.com/mees/calvin_env.git
|
aloha_robot_project/calvin/.pre-commit-config.yaml
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
default_language_version:
|
| 2 |
+
python: python3.8
|
| 3 |
+
repos:
|
| 4 |
+
- repo: https://github.com/psf/black
|
| 5 |
+
rev: 22.3.0
|
| 6 |
+
hooks:
|
| 7 |
+
- id: black
|
| 8 |
+
language_version: python3.8
|
| 9 |
+
|
| 10 |
+
- repo: https://gitlab.com/pycqa/flake8
|
| 11 |
+
rev: 3.8.4
|
| 12 |
+
hooks:
|
| 13 |
+
- id: flake8
|
| 14 |
+
additional_dependencies: [-e, "git+git://github.com/pycqa/pyflakes.git@c72d6cf#egg=pyflakes"]
|
| 15 |
+
|
| 16 |
+
- repo: https://github.com/pycqa/isort
|
| 17 |
+
rev: 5.7.0
|
| 18 |
+
hooks:
|
| 19 |
+
- id: isort
|
| 20 |
+
|
| 21 |
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
| 22 |
+
rev: v0.812
|
| 23 |
+
hooks:
|
| 24 |
+
- id: mypy
|
| 25 |
+
args: [--ignore-missing-imports, --warn-no-return, --warn-redundant-casts, --disallow-incomplete-defs]
|
| 26 |
+
additional_dependencies: [pytorch-lightning==1.4.9, torch==1.10.0, numpy]
|
| 27 |
+
|
| 28 |
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
| 29 |
+
rev: v4.0.1
|
| 30 |
+
hooks:
|
| 31 |
+
- id: check-yaml
|
| 32 |
+
- id: trailing-whitespace
|
| 33 |
+
- id: end-of-file-fixer
|
aloha_robot_project/calvin/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2021 Oier Mees
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
aloha_robot_project/calvin/README.md
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CALVIN
|
| 2 |
+
[](https://github.com/psf/black)
|
| 3 |
+
[](https://lgtm.com/projects/g/mees/calvin/context:python)
|
| 4 |
+
[](https://lgtm.com/projects/g/mees/calvin/alerts/)
|
| 5 |
+
[](https://opensource.org/licenses/MIT)
|
| 6 |
+
|
| 7 |
+
[<b>CALVIN - A benchmark for Language-Conditioned Policy Learning for Long-Horizon Robot Manipulation Tasks</b>](https://arxiv.org/pdf/2112.03227.pdf)
|
| 8 |
+
|
| 9 |
+
[Oier Mees](https://www.oiermees.com/), [Lukas Hermann](https://lukashermann.github.io/), [Erick Rosete](https://www.erickrosete.com/), [Wolfram Burgard](http://www2.informatik.uni-freiburg.de/~burgard)
|
| 10 |
+
|
| 11 |
+
#### CALVIN won the 2022 IEEE Robotics and Automation Letters (RA-L) Best Paper Award!
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
We present **CALVIN** (**C**omposing **A**ctions from **L**anguage and **Vi**sio**n**), an open-source simulated benchmark to learn long-horizon language-conditioned tasks.
|
| 15 |
+
Our aim is to make it possible to develop agents that can solve many robotic manipulation tasks over a long horizon, from onboard sensors, and specified only via human language. CALVIN tasks are more complex in terms of sequence length, action space, and language than existing vision-and-language task datasets and supports flexible specification of sensor
|
| 16 |
+
suites.
|
| 17 |
+
|
| 18 |
+

|
| 19 |
+
|
| 20 |
+
# :computer: Quick Start
|
| 21 |
+
To begin, clone this repository locally
|
| 22 |
+
```bash
|
| 23 |
+
git clone --recurse-submodules https://github.com/mees/calvin.git
|
| 24 |
+
$ export CALVIN_ROOT=$(pwd)/calvin
|
| 25 |
+
|
| 26 |
+
```
|
| 27 |
+
Install requirements:
|
| 28 |
+
```bash
|
| 29 |
+
$ cd $CALVIN_ROOT
|
| 30 |
+
$ conda create -n calvin_venv python=3.8 # or use virtualenv
|
| 31 |
+
$ conda activate calvin_venv
|
| 32 |
+
$ sh install.sh
|
| 33 |
+
```
|
| 34 |
+
If you encounter problems installing pyhash, you might have to downgrade setuptools to a version below 58.
|
| 35 |
+
|
| 36 |
+
Download dataset (choose which split you want to download with the argument `D`, `ABC` or `ABCD`): \
|
| 37 |
+
If you want to get started without downloading the whole dataset, use the argument `debug` to download a small debug dataset (1.3 GB).
|
| 38 |
+
```bash
|
| 39 |
+
$ cd $CALVIN_ROOT/dataset
|
| 40 |
+
$ sh download_data.sh D | ABC | ABCD | debug
|
| 41 |
+
```
|
| 42 |
+
## :weight_lifting_man: Train Baseline Agent
|
| 43 |
+
Train baseline models:
|
| 44 |
+
```bash
|
| 45 |
+
$ cd $CALVIN_ROOT/calvin_models/calvin_agent
|
| 46 |
+
$ python training.py datamodule.root_data_dir=/path/to/dataset/ datamodule/datasets=vision_lang_shm
|
| 47 |
+
```
|
| 48 |
+
The `vision_lang_shm` option loads the CALVIN dataset into shared memory at the beginning of the training,
|
| 49 |
+
speeding up the data loading during training.
|
| 50 |
+
The preparation of the shared memory cache will take some time
|
| 51 |
+
(approx. 20 min at our SLURM cluster). \
|
| 52 |
+
If you want to use the original data loader (e.g. for debugging) just override the command with `datamodule/datasets=vision_lang`. \
|
| 53 |
+
For an additional speed up, you can disable the evaluation callbacks during training by adding `~callbacks/rollout` and `~callbacks/rollout_lh`
|
| 54 |
+
|
| 55 |
+
You want to scale your training to a multi-gpu setup? Just specify the [number of GPUs](https://pytorch-lightning.readthedocs.io/en/latest/advanced/multi_gpu.html#select-gpu-devices) and DDP will automatically be used
|
| 56 |
+
for training thanks to [Pytorch Lightning](https://www.pytorchlightning.ai/).
|
| 57 |
+
To train on all available GPUs:
|
| 58 |
+
```bash
|
| 59 |
+
$ python training.py trainer.gpus=-1
|
| 60 |
+
```
|
| 61 |
+
If you have access to a Slurm cluster, follow this [guide](https://github.com/mees/calvin/blob/main/slurm_scripts/README.md).
|
| 62 |
+
|
| 63 |
+
You can use [Hydra's](https://hydra.cc/) flexible overriding system for changing hyperparameters.
|
| 64 |
+
For example, to train a model with rgb images from both static camera and the gripper camera with relative actions:
|
| 65 |
+
```bash
|
| 66 |
+
$ python training.py datamodule/observation_space=lang_rgb_static_gripper_rel_act model/perceptual_encoder=gripper_cam
|
| 67 |
+
```
|
| 68 |
+
To train a model with RGB-D from both cameras:
|
| 69 |
+
```bash
|
| 70 |
+
$ python training.py datamodule/observation_space=lang_rgbd_both model/perceptual_encoder=RGBD_both
|
| 71 |
+
```
|
| 72 |
+
To train a model with rgb images from the static camera and visual tactile observations with absolute actions:
|
| 73 |
+
```bash
|
| 74 |
+
$ python training.py datamodule/observation_space=lang_rgb_static_tactile_abs_act model/perceptual_encoder=static_RGB_tactile
|
| 75 |
+
```
|
| 76 |
+
|
| 77 |
+
To see all available hyperparameters:
|
| 78 |
+
```console
|
| 79 |
+
$ python training.py --help
|
| 80 |
+
```
|
| 81 |
+
To resume a training, just override the hydra working directory :
|
| 82 |
+
```console
|
| 83 |
+
$ python training.py hydra.run.dir=runs/my_dir
|
| 84 |
+
```
|
| 85 |
+
|
| 86 |
+
## :framed_picture: Sensory Observations
|
| 87 |
+
CALVIN supports a range of sensors commonly utilized for visuomotor control:
|
| 88 |
+
1. **Static camera RGB images** - with shape `200x200x3`.
|
| 89 |
+
2. **Static camera Depth maps** - with shape `200x200`.
|
| 90 |
+
3. **Gripper camera RGB images** - with shape `84x84x3`.
|
| 91 |
+
4. **Gripper camera Depth maps** - with shape `84x84`.
|
| 92 |
+
5. **Tactile image** - with shape `120x160x6`.
|
| 93 |
+
6. **Proprioceptive state** - EE position (3), EE orientation in euler angles (3), gripper width (1), joint positions (7), gripper action (1).
|
| 94 |
+
|
| 95 |
+
<p align="center">
|
| 96 |
+
<img src="media/sensors.png" alt="" width="50%">
|
| 97 |
+
</p>
|
| 98 |
+
|
| 99 |
+
## :joystick: Action Space
|
| 100 |
+
In CALVIN, the agent must perform closed-loop continuous control to follow unconstrained language instructions characterizing complex robot manipulation tasks, sending continuous actions to the robot at 30hz.
|
| 101 |
+
In order to give researchers and practitioners the freedom to experiment with different action spaces, CALVIN supports the following actions spaces:
|
| 102 |
+
1. **Absolute cartesian pose** - EE position (3), EE orientation in euler angles (3), gripper action (1).
|
| 103 |
+
2. **Relative cartesian displacement** - EE position (3), EE orientation in euler angles (3), gripper action (1).
|
| 104 |
+
3. **Joint action** - Joint positions (7), gripper action (1).
|
| 105 |
+
|
| 106 |
+
For more information, please refer to this more detailed [README](https://github.com/mees/calvin/blob/main/dataset/README.md).
|
| 107 |
+
|
| 108 |
+
## :muscle: Evaluation: The Calvin Challenge
|
| 109 |
+
### Long-horizon Multi-task Language Control (LH-MTLC)
|
| 110 |
+
The aim of the CALVIN benchmark is to evaluate the learning of long-horizon language-conditioned continuous control policies. In this setting, a single agent must solve complex manipulation tasks by understanding a series of unconstrained language expressions in a row, e.g., “open the drawer. . . pick up the blue block. . . now push the block into the drawer. . . now open the sliding door”.
|
| 111 |
+
We provide an evaluation protocol with evaluation modes of varying difficulty by choosing different combinations of sensor suites and amounts of training environments.
|
| 112 |
+
To avoid a biased initial position, the robot is reset to a neutral position before every multi-step sequence.
|
| 113 |
+
|
| 114 |
+
To evaluate a trained calvin baseline agent, run the following command:
|
| 115 |
+
|
| 116 |
+
```
|
| 117 |
+
$ cd $CALVIN_ROOT/calvin_models/calvin_agent
|
| 118 |
+
$ python evaluation/evaluate_policy.py --dataset_path <PATH/TO/DATASET> --train_folder <PATH/TO/TRAINING/FOLDER>
|
| 119 |
+
```
|
| 120 |
+
Optional arguments:
|
| 121 |
+
|
| 122 |
+
- `--checkpoint <PATH/TO/CHECKPOINT>`: by default, the evaluation loads the last checkpoint in the training log directory.
|
| 123 |
+
You can instead specify the path to another checkpoint by adding this to the evaluation command.
|
| 124 |
+
- `--debug`: print debug information and visualize environment.
|
| 125 |
+
|
| 126 |
+
If you want to evaluate your own model architecture on the CALVIN challenge, you can implement the `CustomModel` class in `evaluate_policy.py`
|
| 127 |
+
as an interface to your agent. You need to implement the following methods:
|
| 128 |
+
|
| 129 |
+
- \_\_init__():
|
| 130 |
+
gets called once at the beginning of the evaluation.
|
| 131 |
+
- reset(): gets called at the beginning of each evaluation sequence.
|
| 132 |
+
- step(obs, goal): gets called every step and returns the predicted action.
|
| 133 |
+
|
| 134 |
+
Then evaluate the model by running:
|
| 135 |
+
```
|
| 136 |
+
$ python evaluation/evaluate_policy.py --dataset_path <PATH/TO/DATASET> --custom_model
|
| 137 |
+
```
|
| 138 |
+
|
| 139 |
+
You are also free to use your own language model instead of using the precomputed language embeddings provided by CALVIN.
|
| 140 |
+
For this, implement `CustomLangEmbeddings` in `evaluate_policy.py` and add `--custom_lang_embeddings` to the evaluation command.
|
| 141 |
+
|
| 142 |
+
### Multi-task Language Control (MTLC)
|
| 143 |
+
Alternatively, you can evaluate the policy on single tasks and without resetting the robot to a neutral position.
|
| 144 |
+
Note that this evaluation is currently only available for our baseline agent.
|
| 145 |
+
```
|
| 146 |
+
$ python evaluation/evaluate_policy_singlestep.py --dataset_path <PATH/TO/DATASET> --train_folder <PATH/TO/TRAINING/FOLDER> [--checkpoint <PATH/TO/CHECKPOINT>] [--debug]
|
| 147 |
+
```
|
| 148 |
+
|
| 149 |
+
### Pre-trained Model
|
| 150 |
+
Download the [MCIL](http://calvin.cs.uni-freiburg.de/model_weights/D_D_static_rgb_baseline.zip) model checkpoint trained on the static camera rgb images on environment D.
|
| 151 |
+
```
|
| 152 |
+
$ wget http://calvin.cs.uni-freiburg.de/model_weights/D_D_static_rgb_baseline.zip
|
| 153 |
+
$ unzip D_D_static_rgb_baseline.zip
|
| 154 |
+
```
|
| 155 |
+
## :speech_balloon: Relabeling Raw Language Annotations
|
| 156 |
+
You want to try learning language conditioned policies in CALVIN with a new awesome language model?
|
| 157 |
+
|
| 158 |
+
We provide an [example script](https://github.com/mees/calvin/blob/main/calvin_models/calvin_agent/utils/relabel_with_new_lang_model.py) to relabel the annotations with different language model provided in [SBert](https://www.sbert.net/docs/pretrained_models.html), such as the larger MPNet (paraphrase-mpnet-base-v2) or its corresponding multilingual model (paraphrase-multilingual-mpnet-base-v2).
|
| 159 |
+
The supported options are "mini", "mpnet" and "multi". If you want to try different SBert models, just change the model name [here](https://github.com/mees/calvin/blob/main/calvin_models/calvin_agent/models/encoders/language_network.py#L18).
|
| 160 |
+
```
|
| 161 |
+
cd $CALVIN_ROOT/calvin_models/calvin_agent
|
| 162 |
+
python utils/relabel_with_new_lang_model.py +path=$CALVIN_ROOT/dataset/task_D_D/ +name_folder=new_lang_model_folder model.nlp_model=mpnet
|
| 163 |
+
```
|
| 164 |
+
If you additionally want to sample different language annotations for each sequence (from the same task annotations) in the training split run the same command with the parameter `reannotate=true`.
|
| 165 |
+
|
| 166 |
+
## :chart_with_upwards_trend: SOTA Models
|
| 167 |
+
Open-source models that outperform the MCIL baselines from CALVIN:
|
| 168 |
+
|
| 169 |
+
For a detailed overview of the evaluation performances, have a look at our **[LEADERBOARD](http://calvin.cs.uni-freiburg.de/)**.
|
| 170 |
+
|
| 171 |
+
<br>
|
| 172 |
+
<b> Grounding Language with Visual Affordances over Unstructured Data</b>
|
| 173 |
+
<br>
|
| 174 |
+
Oier Mees, Jessica Borja-Diaz, Wolfram Burgard
|
| 175 |
+
<br>
|
| 176 |
+
<a href="https://arxiv.org/pdf/2210.01911.pdf"> Paper</a>, <a href="https://github.com/mees/hulc2"> Code </a>
|
| 177 |
+
|
| 178 |
+
<b> FLOWER: Democratizing Generalist Robot Policies with Efficient Vision-Language-Action Flow Policies </b>
|
| 179 |
+
<br>
|
| 180 |
+
Moritz Reuss, Hongyi Zhou, Marcel Rühle, Ömer Erdinç Yağmurlu, Fabian Otto, Rudolf Lioutikov
|
| 181 |
+
<br>
|
| 182 |
+
<a href="https://arxiv.org/pdf/2509.04996"> Paper</a>, <a href="https://intuitive-robots.github.io/flower_vla/"> Code </a>
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
<b> Unified Vision-Language-Action Model </b>
|
| 186 |
+
<br>
|
| 187 |
+
Yuqi Wang, Xinghang Li, Wenxuan Wang, Junbo Zhang, Yingyan Li, Yuntao Chen, Xinlong Wang, Zhaoxiang Zhang
|
| 188 |
+
<br>
|
| 189 |
+
<a href="https://arxiv.org/pdf/2506.19850"> Paper</a>, <a href="https://robertwyq.github.io/univla.github.io/"> Code </a>
|
| 190 |
+
|
| 191 |
+
<b> Predictive Inverse Dynamics Models are Scalable Learners for Robotic Manipulation </b>
|
| 192 |
+
<br>
|
| 193 |
+
Yang Tian, Sizhe Yang, Jia Zeng, Ping Wang, Dahua Lin, Hao Dong, Jiangmiao Pang
|
| 194 |
+
<br>
|
| 195 |
+
<a href="https://arxiv.org/pdf/2412.15109"> Paper</a>, <a href="https://github.com/OpenRobotLab/Seer/"> Code </a>
|
| 196 |
+
|
| 197 |
+
<b> Diffusion Transformer Policy: Scaling Diffusion Transformer for Generalist Vision-Language-Action Learning </b>
|
| 198 |
+
<br>
|
| 199 |
+
Zhi Hou, Tianyi Zhang, Yuwen Xiong, Hengjun Pu, Chengyang Zhao, Ronglei Tong, Yu Qiao, Jifeng Dai, Yuntao Chen
|
| 200 |
+
<br>
|
| 201 |
+
<a href="https://arxiv.org/pdf/2410.15959"> Paper</a>, <a href="https://github.com/zhihou7/dit_policy_vla"> Code </a>
|
| 202 |
+
|
| 203 |
+
<b> GR-MG: Leveraging Partially Annotated Data via Multi-Modal Goal Conditioned Policy </b>
|
| 204 |
+
<br>
|
| 205 |
+
Peiyan Li, Hongtao Wu, Yan Huang, Chilam Cheang, Liang Wang, Tao Kong
|
| 206 |
+
<br>
|
| 207 |
+
<a href="https://arxiv.org/pdf/2408.14368"> Paper</a>, <a href="https://github.com/bytedance/GR-MG/"> Code </a>
|
| 208 |
+
|
| 209 |
+
<b> GHIL-Glue: Hierarchical Control with Filtered Subgoal Images </b>
|
| 210 |
+
<br>
|
| 211 |
+
Kyle B Hatch, Ashwin Balakrishna, Oier Mees, Suraj Nair, Seohong Park, Blake Wulfe, Masha Itkina, Benjamin Eysenbach, Sergey Levine, Thomas Kollar, Benjamin Burchfiel
|
| 212 |
+
<br>
|
| 213 |
+
<a href="https://arxiv.org/pdf/2410.20018"> Paper</a>, <a href="https://github.com/kyle-hatch-tri/ghil-glue"> Code </a>
|
| 214 |
+
|
| 215 |
+
<b> Efficient Diffusion Transformer Policies with Mixture of Expert Denoisers for Multitask Learning </b>
|
| 216 |
+
<br>
|
| 217 |
+
Moritz Reuss, Jyothish Pari, Pulkit Agrawal, Rudolf Lioutikov
|
| 218 |
+
<br>
|
| 219 |
+
<a href="https://arxiv.org/pdf/2412.12953"> Paper</a>, <a href="https://github.com/intuitive-robots/MoDE_Diffusion_Policy"> Code </a>
|
| 220 |
+
|
| 221 |
+
<b> Incorporating Task Progress Knowledge for Subgoal Generation in Robotic Manipulation through Image Edits </b>
|
| 222 |
+
<br>
|
| 223 |
+
Xuhui Kang, Yen-Ling Kuo
|
| 224 |
+
<br>
|
| 225 |
+
<a href="https://arxiv.org/pdf/2410.11013"> Paper</a>, <a href="https://github.com/Shua-Kang/TaKSIE"> Code </a>
|
| 226 |
+
|
| 227 |
+
<b> Closed-Loop Visuomotor Control with Generative Expectation for Robotic Manipulation </b>
|
| 228 |
+
<br>
|
| 229 |
+
Qingwen Bu, Jia Zeng, Li Chen, Yanchao Yang, Guyue Zhou, Junchi Yan, Ping Luo, Heming Cui, Yi Ma, Hongyang Li
|
| 230 |
+
<br>
|
| 231 |
+
<a href="https://arxiv.org/pdf/2409.09016"> Paper</a>, <a href="https://github.com/OpenDriveLab/CLOVER"> Code </a>
|
| 232 |
+
|
| 233 |
+
<b> DeeR-VLA: Dynamic Inference of Multimodal Large Language Models for Efficient Robot Execution </b>
|
| 234 |
+
<br>
|
| 235 |
+
Yang Yue, Yulin Wang, Bingyi Kang, Yizeng Han, Shenzhi Wang, Shiji Song, Jiashi Feng, Gao Huang
|
| 236 |
+
<br>
|
| 237 |
+
<a href="https://arxiv.org/pdf/2411.02359"> Paper</a>, <a href="https://github.com/yueyang130/DeeR-VLA"> Code </a>
|
| 238 |
+
|
| 239 |
+
<b> RoboUniView: Visual-Language Model with Unified View Representation for Robotic Manipulation </b>
|
| 240 |
+
<br>
|
| 241 |
+
Fanfan Liu, Feng Yan, Liming Zheng, Yiyang Huang, Chengjian Feng, Lin Ma
|
| 242 |
+
<br>
|
| 243 |
+
<a href="https://arxiv.org/pdf/2406.18977v2"> Paper</a>, <a href="https://github.com/liufanfanlff/RoboUniview"> Code </a>
|
| 244 |
+
|
| 245 |
+
<b> Multimodal Diffusion Transformer: Learning Versatile Behavior from Multimodal Goals </b>
|
| 246 |
+
<br>
|
| 247 |
+
Moritz Reuss, Ömer Erdinç Yağmurlu, Fabian Wenzel, Rudolf Lioutikov
|
| 248 |
+
<br>
|
| 249 |
+
<a href="https://arxiv.org/pdf/2407.05996"> Paper</a>, <a href="https://github.com/intuitive-robots/mdt_policy"> Code </a>
|
| 250 |
+
|
| 251 |
+
<b> 3D Diffuser Actor: Policy Diffusion with 3D Scene Representations</b>
|
| 252 |
+
<br>
|
| 253 |
+
Tsung-Wei Ke, Nikolaos Gkanatsios, Katerina Fragkiadaki
|
| 254 |
+
<br>
|
| 255 |
+
<a href="https://arxiv.org/pdf/2402.10885.pdf"> Paper</a>, <a href="https://github.com/nickgkan/3d_diffuser_actor"> Code </a>
|
| 256 |
+
|
| 257 |
+
<b> Unleashing Large-Scale Video Generative Pre-training for Visual Robot Manipulation</b>
|
| 258 |
+
<br>
|
| 259 |
+
Hongtao Wu, Ya Jing, Chilam Cheang, Guangzeng Chen, Jiafeng Xu, Xinghang Li, Minghuan Liu, Hang Li, Tao Kong
|
| 260 |
+
<br>
|
| 261 |
+
<a href="https://arxiv.org/pdf/2312.13139.pdf"> Paper</a>, <a href="https://github.com/bytedance/GR-1"> Code </a>
|
| 262 |
+
|
| 263 |
+
<b> Vision-Language Foundation Models as Effective Robot Imitators</b>
|
| 264 |
+
<br>
|
| 265 |
+
Xinghang Li, Minghuan Liu, Hanbo Zhang, Cunjun Yu, Jie Xu, Hongtao Wu, Chilam Cheang, Ya Jing, Weinan Zhang, Huaping Liu, Hang Li, and Tao Kong
|
| 266 |
+
<br>
|
| 267 |
+
<a href="https://arxiv.org/pdf/2311.01378.pdf"> Paper</a>, <a href="https://github.com/RoboFlamingo/RoboFlamingo"> Code </a>
|
| 268 |
+
|
| 269 |
+
<b> Zero-Shot Robotic Manipulation With Pretrained Image-Editing Diffusion Models</b>
|
| 270 |
+
<br>
|
| 271 |
+
Kevin Black, Mitsuhiko Nakamoto, Pranav Atreya, Homer Walke, Chelsea Finn, Aviral Kumar, Sergey Levine
|
| 272 |
+
<br>
|
| 273 |
+
<a href="https://arxiv.org/pdf/2310.10639.pdf"> Paper</a>, <a href="https://github.com/kvablack/susie"> Code </a>
|
| 274 |
+
|
| 275 |
+
<b> Language Control Diffusion: Efficiently Scaling through Space, Time, and Tasks</b>
|
| 276 |
+
<br>
|
| 277 |
+
Eddie Zhang, Yujie Lu, William Wang, Amy Zhang
|
| 278 |
+
<br>
|
| 279 |
+
<a href="https://arxiv.org/pdf/2210.15629.pdf"> Paper</a>, <a href="https://github.com/ezhang7423/language-control-diffusion"> Code </a>
|
| 280 |
+
|
| 281 |
+
<b> What Matters in Language Conditioned Robotic Imitation Learning over Unstructured Data</b>
|
| 282 |
+
<br>
|
| 283 |
+
Oier Mees, Lukas Hermann, Wolfram Burgard
|
| 284 |
+
<br>
|
| 285 |
+
<a href="https://arxiv.org/pdf/2204.06252.pdf"> Paper</a>, <a href="https://github.com/lukashermann/hulc"> Code </a>
|
| 286 |
+
|
| 287 |
+
<b> Language-Conditioned Imitation Learning with Base Skill Priors under Unstructured Data</b>
|
| 288 |
+
<br>
|
| 289 |
+
Hongkuan Zhou, Zhenshan Bing, Xiangtong Yao, Xiaojie Su, Chenguang Yang, Kai Huang, Alios Knoll
|
| 290 |
+
<br>
|
| 291 |
+
<a href="https://arxiv.org/pdf/2305.19075.pdf"> Paper</a>, <a href="https://github.com/hk-zh/spil"> Code
|
| 292 |
+
|
| 293 |
+
Contact [Oier](https://www.oiermees.com/) to add your model here.
|
| 294 |
+
|
| 295 |
+
## Reinforcement Learning with CALVIN
|
| 296 |
+
Are you interested in trying reinforcement learning agents for the different manipulation tasks in the CALVIN environment?
|
| 297 |
+
We provide a [google colab](https://github.com/mees/calvin/blob/main/RL_with_CALVIN.ipynb) to showcase how to leverage the CALVIN task indicators to learn RL agents with a sparse reward.
|
| 298 |
+
|
| 299 |
+
## FAQ
|
| 300 |
+
|
| 301 |
+
#### Why do you use EGL rendering?
|
| 302 |
+
We use EGL to move the bullet rendering from cpu (which is the default) to gpu, which is much faster.
|
| 303 |
+
This way, we can also do rollouts during the training of the agent to track its performance.
|
| 304 |
+
By changing from cpu to gpu, the rendered textures change slightly, so be aware of this if you plan on testing pretrained models.
|
| 305 |
+
#### I am training with multiple GPUs and why am I get OOM errors during rollouts?
|
| 306 |
+
PyBullet only recently added an option to select which GPU to use for rendering when using EGL (fix was commited in 3c4cb80
|
| 307 |
+
on Oct 22, 2021, see [here](https://github.com/bulletphysics/bullet3/blob/master/examples/OpenGLWindow/EGLOpenGLWindow.cpp#L134).
|
| 308 |
+
If you have an old version of PyBullet, there is no way to choose the GPU, which can lead to problems on cluster nodes with multiple GPUs, because all instances would be placed on the same GPU, slowing down the rendering and potentially leading to OOM erros.
|
| 309 |
+
|
| 310 |
+
The fix introduced an environment variable EGL_VISIBLE_DEVICES (similar to CUDA_VISIBLE_DEVICES) which lets you specify the GPU device to render on.
|
| 311 |
+
However, there is one catch: On some machines, the device ids of CUDA and EGL do not match (e.g. CUDA device 0 could be EGL device 3).
|
| 312 |
+
We automatically handle this in our wrapper in calvin_env and find the corresponding egl device id, so you don't have to set EGL_VISIBLE_DEVICES yourself, see [here](https://github.com/mees/calvin_env/blob/main/calvin_env/envs/play_lmp_wrapper.py#L31).
|
| 313 |
+
|
| 314 |
+
#### I am not interested in the manipulation tasks recorded, can I record different demonstration with teleop?
|
| 315 |
+
Yes, although it is not documented right now, all the code to record data with a VR headset is present in
|
| 316 |
+
calvin_env in [https://github.com/mees/calvin_env/blob/main/calvin_env/vrdatacollector.py](https://github.com/mees/calvin_env/blob/main/calvin_env/vrdatacollector.py)
|
| 317 |
+
|
| 318 |
+
|
| 319 |
+
## Changelog
|
| 320 |
+
### 24 Feb 2023
|
| 321 |
+
- Wrong `scene_info.npy` in D dataset. Note that we have updated the corresponding checksum. Please replace as follows:
|
| 322 |
+
```
|
| 323 |
+
cd task_D_D
|
| 324 |
+
wget http://calvin.cs.uni-freiburg.de/scene_info_fix/task_D_D_scene_info.zip
|
| 325 |
+
unzip task_D_D_scene_info.zip && rm task_D_D_scene_info.zip
|
| 326 |
+
```
|
| 327 |
+
|
| 328 |
+
### 16 Sep 2022
|
| 329 |
+
- **MAJOR BUG IN ABC and ABCD dataset:** If you downloaded these datasets before this date you have to do these fixes:
|
| 330 |
+
- Wrong language annotations in ABC and ABCD dataset. You can download the corrected language embeddings [here](https://github.com/mees/calvin/blob/main/dataset/README.md#language-embeddings).
|
| 331 |
+
- Bug in `calvin_env` that only affects the generation of language embeddings.
|
| 332 |
+
- Wrong `scene_info.npy` in ABC and ABCD dataset. Please replace as follows:
|
| 333 |
+
```
|
| 334 |
+
cd task_ABCD_D
|
| 335 |
+
wget http://calvin.cs.uni-freiburg.de/scene_info_fix/task_ABCD_D_scene_info.zip
|
| 336 |
+
unzip task_ABCD_D_scene_info.zip && rm task_ABCD_D_scene_info.zip
|
| 337 |
+
```
|
| 338 |
+
```
|
| 339 |
+
cd task_ABC_D
|
| 340 |
+
wget http://calvin.cs.uni-freiburg.de/scene_info_fix/task_ABC_D_scene_info.zip
|
| 341 |
+
unzip task_ABC_D_scene_info.zip && rm task_ABC_D_scene_info.zip
|
| 342 |
+
```
|
| 343 |
+
- Added additional language embeddings to dataset.
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
### 15 May 2022
|
| 347 |
+
- Added shared memory dataset loader for faster training. Refactored data loading classes.
|
| 348 |
+
|
| 349 |
+
### 7 Feb 2022
|
| 350 |
+
- Minor changes to the distribution of tasks in the long-horizon multi-step sequences.
|
| 351 |
+
- Changes to the task success criteria of pushing and lifting.
|
| 352 |
+
- Set `use_nullspace: true` for robot in hydra cfg of dataset. If you downloaded one of the datasets prior to this date,
|
| 353 |
+
edit this line in <PATH_TO_DATASET>/training/.hydra/merged_config.yaml and <PATH_TO_DATASET>/validation/.hydra/merged_config.yaml.
|
| 354 |
+
- Renaming `model.decoder` to `model.action_decoder`.
|
| 355 |
+
|
| 356 |
+
### 10 Jan 2022
|
| 357 |
+
- Breaking change to evaluation, using different intitial states for environment.
|
| 358 |
+
|
| 359 |
+
## Citation
|
| 360 |
+
|
| 361 |
+
If you find the dataset or code useful, please cite:
|
| 362 |
+
|
| 363 |
+
```bibtex
|
| 364 |
+
@article{mees2022calvin,
|
| 365 |
+
author = {Oier Mees and Lukas Hermann and Erick Rosete-Beas and Wolfram Burgard},
|
| 366 |
+
title = {CALVIN: A Benchmark for Language-Conditioned Policy Learning for Long-Horizon Robot Manipulation Tasks},
|
| 367 |
+
journal={IEEE Robotics and Automation Letters (RA-L)},
|
| 368 |
+
volume={7},
|
| 369 |
+
number={3},
|
| 370 |
+
pages={7327-7334},
|
| 371 |
+
year={2022}
|
| 372 |
+
}
|
| 373 |
+
```
|
| 374 |
+
|
| 375 |
+
## License
|
| 376 |
+
|
| 377 |
+
MIT License
|
aloha_robot_project/calvin/RL_with_CALVIN.ipynb
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"nbformat": 4,
|
| 3 |
+
"nbformat_minor": 0,
|
| 4 |
+
"metadata": {
|
| 5 |
+
"colab": {
|
| 6 |
+
"name": "RL_with_CALVIN.ipynb",
|
| 7 |
+
"provenance": [],
|
| 8 |
+
"collapsed_sections": [],
|
| 9 |
+
"authorship_tag": "ABX9TyNwSDlhfoXLR2ORP+8/+3zP",
|
| 10 |
+
"include_colab_link": true
|
| 11 |
+
},
|
| 12 |
+
"kernelspec": {
|
| 13 |
+
"display_name": "Python 3",
|
| 14 |
+
"name": "python3"
|
| 15 |
+
},
|
| 16 |
+
"language_info": {
|
| 17 |
+
"name": "python"
|
| 18 |
+
}
|
| 19 |
+
},
|
| 20 |
+
"cells": [
|
| 21 |
+
{
|
| 22 |
+
"cell_type": "markdown",
|
| 23 |
+
"metadata": {
|
| 24 |
+
"id": "view-in-github",
|
| 25 |
+
"colab_type": "text"
|
| 26 |
+
},
|
| 27 |
+
"source": [
|
| 28 |
+
"<a href=\"https://colab.research.google.com/github/mees/calvin/blob/main/RL_with_CALVIN.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
| 29 |
+
]
|
| 30 |
+
},
|
| 31 |
+
{
|
| 32 |
+
"cell_type": "markdown",
|
| 33 |
+
"metadata": {
|
| 34 |
+
"id": "MDAC0SvBi1-9"
|
| 35 |
+
},
|
| 36 |
+
"source": [
|
| 37 |
+
"<h1>Reinforcement Learning with CALVIN</h1>\n",
|
| 38 |
+
"\n",
|
| 39 |
+
"The **CALVIN** simulated benchmark is perfectly suited for training agents with reinforcement learning, in this notebook we will demonstrate how to integrate your agents to these environments."
|
| 40 |
+
]
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"cell_type": "markdown",
|
| 44 |
+
"metadata": {
|
| 45 |
+
"id": "Gk7gEiqXizze"
|
| 46 |
+
},
|
| 47 |
+
"source": [
|
| 48 |
+
"## Installation\n",
|
| 49 |
+
"The first step is to install the CALVIN github repository such that we have access to the packages"
|
| 50 |
+
]
|
| 51 |
+
},
|
| 52 |
+
{
|
| 53 |
+
"cell_type": "code",
|
| 54 |
+
"metadata": {
|
| 55 |
+
"id": "K1cSDZwLlAhR"
|
| 56 |
+
},
|
| 57 |
+
"source": [
|
| 58 |
+
"# Download repo\n",
|
| 59 |
+
"%mkdir /content/calvin\n",
|
| 60 |
+
"%cd /content/calvin\n",
|
| 61 |
+
"!git clone https://github.com/mees/calvin_env.git\n",
|
| 62 |
+
"%cd /content/calvin/calvin_env\n",
|
| 63 |
+
"!git clone https://github.com/lukashermann/tacto.git\n",
|
| 64 |
+
"# Install packages \n",
|
| 65 |
+
"%cd /content/calvin/calvin_env/tacto/\n",
|
| 66 |
+
"!pip3 install -e .\n",
|
| 67 |
+
"%cd /content/calvin/calvin_env\n",
|
| 68 |
+
"!pip3 install -e .\n",
|
| 69 |
+
"!pip3 install -U numpy"
|
| 70 |
+
],
|
| 71 |
+
"execution_count": null,
|
| 72 |
+
"outputs": []
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
"cell_type": "code",
|
| 76 |
+
"metadata": {
|
| 77 |
+
"id": "hPiKJ7mY58o3"
|
| 78 |
+
},
|
| 79 |
+
"source": [
|
| 80 |
+
"# Run this to check if the installation was succesful\n",
|
| 81 |
+
"from calvin_env.envs.play_table_env import PlayTableSimEnv"
|
| 82 |
+
],
|
| 83 |
+
"execution_count": 2,
|
| 84 |
+
"outputs": []
|
| 85 |
+
},
|
| 86 |
+
{
|
| 87 |
+
"cell_type": "markdown",
|
| 88 |
+
"metadata": {
|
| 89 |
+
"id": "JSmDkjlx0FOW"
|
| 90 |
+
},
|
| 91 |
+
"source": [
|
| 92 |
+
"## Loading the environment\n",
|
| 93 |
+
"After the installation has finished successfully, we can start using the environment for reinforcement Learning.\n",
|
| 94 |
+
"To be able to use the environment we need to have the appropriate configuration that define the desired features, for this example, we will load the static and gripper camera."
|
| 95 |
+
]
|
| 96 |
+
},
|
| 97 |
+
{
|
| 98 |
+
"cell_type": "code",
|
| 99 |
+
"metadata": {
|
| 100 |
+
"id": "vp1-mgIgekvY"
|
| 101 |
+
},
|
| 102 |
+
"source": [
|
| 103 |
+
"%cd /content/calvin\n",
|
| 104 |
+
"from hydra import initialize, compose\n",
|
| 105 |
+
"\n",
|
| 106 |
+
"with initialize(config_path=\"./calvin_env/conf/\"):\n",
|
| 107 |
+
" cfg = compose(config_name=\"config_data_collection.yaml\", overrides=[\"cameras=static_and_gripper\"])\n",
|
| 108 |
+
" cfg.env[\"use_egl\"] = False\n",
|
| 109 |
+
" cfg.env[\"show_gui\"] = False\n",
|
| 110 |
+
" cfg.env[\"use_vr\"] = False\n",
|
| 111 |
+
" cfg.env[\"use_scene_info\"] = True\n",
|
| 112 |
+
" print(cfg.env)"
|
| 113 |
+
],
|
| 114 |
+
"execution_count": null,
|
| 115 |
+
"outputs": []
|
| 116 |
+
},
|
| 117 |
+
{
|
| 118 |
+
"cell_type": "markdown",
|
| 119 |
+
"metadata": {
|
| 120 |
+
"id": "onN9sssj1JV6"
|
| 121 |
+
},
|
| 122 |
+
"source": [
|
| 123 |
+
"The environment has similar structure to traditional OpenAI Gym environments.\n",
|
| 124 |
+
"\n",
|
| 125 |
+
"* We can restart the simulation with the *reset* function.\n",
|
| 126 |
+
"* We can perform an action in the environment with the *step* function.\n",
|
| 127 |
+
"* We can visualize images taken from the cameras in the environment by using the *render* function.\n",
|
| 128 |
+
"\n",
|
| 129 |
+
"\n",
|
| 130 |
+
"\n"
|
| 131 |
+
]
|
| 132 |
+
},
|
| 133 |
+
{
|
| 134 |
+
"cell_type": "code",
|
| 135 |
+
"metadata": {
|
| 136 |
+
"id": "MwIuxueazYOh"
|
| 137 |
+
},
|
| 138 |
+
"source": [
|
| 139 |
+
"import time\n",
|
| 140 |
+
"import hydra\n",
|
| 141 |
+
"import numpy as np\n",
|
| 142 |
+
"from google.colab.patches import cv2_imshow\n",
|
| 143 |
+
"\n",
|
| 144 |
+
"env = hydra.utils.instantiate(cfg.env)\n",
|
| 145 |
+
"observation = env.reset()\n",
|
| 146 |
+
"#The observation is given as a dictionary with different values\n",
|
| 147 |
+
"print(observation.keys())\n",
|
| 148 |
+
"for i in range(5):\n",
|
| 149 |
+
" # The action consists in a pose displacement (position and orientation)\n",
|
| 150 |
+
" action_displacement = np.random.uniform(low=-1, high=1, size=6)\n",
|
| 151 |
+
" # And a binary gripper action, -1 for closing and 1 for oppening\n",
|
| 152 |
+
" action_gripper = np.random.choice([-1, 1], size=1)\n",
|
| 153 |
+
" action = np.concatenate((action_displacement, action_gripper), axis=-1)\n",
|
| 154 |
+
" observation, reward, done, info = env.step(action)\n",
|
| 155 |
+
" rgb = env.render(mode=\"rgb_array\")[:,:,::-1]\n",
|
| 156 |
+
" cv2_imshow(rgb)"
|
| 157 |
+
],
|
| 158 |
+
"execution_count": null,
|
| 159 |
+
"outputs": []
|
| 160 |
+
},
|
| 161 |
+
{
|
| 162 |
+
"cell_type": "markdown",
|
| 163 |
+
"metadata": {
|
| 164 |
+
"id": "_0yYw5pHC0eE"
|
| 165 |
+
},
|
| 166 |
+
"source": [
|
| 167 |
+
"## Custom environment for Reinforcement Learning\n",
|
| 168 |
+
"There are some aspects that needs to be defined to be able to use it for reinforcement learning, including:\n",
|
| 169 |
+
"\n",
|
| 170 |
+
"1. Observation space\n",
|
| 171 |
+
"2. Action space\n",
|
| 172 |
+
"3. Reward function\n",
|
| 173 |
+
"\n",
|
| 174 |
+
"We are going to create a Custom environment that extends the **PlaytableSimEnv** to add these requirements. <br/>\n",
|
| 175 |
+
"The specific task that will be solved is called \"move_slider_left\", here you can find a [list of possible tasks](https://github.com/mees/calvin_env/blob/main/conf/tasks/new_playtable_tasks.yaml) that can be evaluated using CALVIN.\n",
|
| 176 |
+
"\n"
|
| 177 |
+
]
|
| 178 |
+
},
|
| 179 |
+
{
|
| 180 |
+
"cell_type": "code",
|
| 181 |
+
"metadata": {
|
| 182 |
+
"id": "Bwj-5UQz2vyF"
|
| 183 |
+
},
|
| 184 |
+
"source": [
|
| 185 |
+
"from gym import spaces\n",
|
| 186 |
+
"from calvin_env.envs.play_table_env import PlayTableSimEnv\n",
|
| 187 |
+
"\n",
|
| 188 |
+
"class SlideEnv(PlayTableSimEnv):\n",
|
| 189 |
+
" def __init__(self,\n",
|
| 190 |
+
" tasks: dict = {},\n",
|
| 191 |
+
" **kwargs):\n",
|
| 192 |
+
" super(SlideEnv, self).__init__(**kwargs)\n",
|
| 193 |
+
" # For this example we will modify the observation to\n",
|
| 194 |
+
" # only retrieve the end effector pose\n",
|
| 195 |
+
" self.action_space = spaces.Box(low=-1, high=1, shape=(7,))\n",
|
| 196 |
+
" self.observation_space = spaces.Box(low=-1, high=1, shape=(7,))\n",
|
| 197 |
+
" # We can use the task utility to know if the task was executed correctly\n",
|
| 198 |
+
" self.tasks = hydra.utils.instantiate(tasks)\n",
|
| 199 |
+
"\n",
|
| 200 |
+
" def reset(self):\n",
|
| 201 |
+
" obs = super().reset()\n",
|
| 202 |
+
" self.start_info = self.get_info()\n",
|
| 203 |
+
" return obs\n",
|
| 204 |
+
"\n",
|
| 205 |
+
" def get_obs(self):\n",
|
| 206 |
+
" \"\"\"Overwrite robot obs to only retrieve end effector position\"\"\"\n",
|
| 207 |
+
" robot_obs, robot_info = self.robot.get_observation()\n",
|
| 208 |
+
" return robot_obs[:7]\n",
|
| 209 |
+
"\n",
|
| 210 |
+
" def _success(self):\n",
|
| 211 |
+
" \"\"\" Returns a boolean indicating if the task was performed correctly \"\"\"\n",
|
| 212 |
+
" current_info = self.get_info()\n",
|
| 213 |
+
" task_filter = [\"move_slider_left\"]\n",
|
| 214 |
+
" task_info = self.tasks.get_task_info_for_set(self.start_info, current_info, task_filter)\n",
|
| 215 |
+
" return 'move_slider_left' in task_info\n",
|
| 216 |
+
"\n",
|
| 217 |
+
" def _reward(self):\n",
|
| 218 |
+
" \"\"\" Returns the reward function that will be used \n",
|
| 219 |
+
" for the RL algorithm \"\"\"\n",
|
| 220 |
+
" reward = int(self._success()) * 10\n",
|
| 221 |
+
" r_info = {'reward': reward}\n",
|
| 222 |
+
" return reward, r_info\n",
|
| 223 |
+
"\n",
|
| 224 |
+
" def _termination(self):\n",
|
| 225 |
+
" \"\"\" Indicates if the robot has reached a terminal state \"\"\"\n",
|
| 226 |
+
" success = self._success()\n",
|
| 227 |
+
" done = success\n",
|
| 228 |
+
" d_info = {'success': success} \n",
|
| 229 |
+
" return done, d_info\n",
|
| 230 |
+
"\n",
|
| 231 |
+
" def step(self, action):\n",
|
| 232 |
+
" \"\"\" Performing a relative action in the environment\n",
|
| 233 |
+
" input:\n",
|
| 234 |
+
" action: 7 tuple containing\n",
|
| 235 |
+
" Position x, y, z. \n",
|
| 236 |
+
" Angle in rad x, y, z. \n",
|
| 237 |
+
" Gripper action\n",
|
| 238 |
+
" each value in range (-1, 1)\n",
|
| 239 |
+
"\n",
|
| 240 |
+
" OR\n",
|
| 241 |
+
" 8 tuple containing\n",
|
| 242 |
+
" Relative Joint angles j1 - j7 (in rad)\n",
|
| 243 |
+
" Gripper action\n",
|
| 244 |
+
" output:\n",
|
| 245 |
+
" observation, reward, done info\n",
|
| 246 |
+
" \"\"\"\n",
|
| 247 |
+
" # Transform gripper action to discrete space\n",
|
| 248 |
+
" env_action = action.copy()\n",
|
| 249 |
+
" env_action[-1] = (int(action[-1] >= 0) * 2) - 1\n",
|
| 250 |
+
"\n",
|
| 251 |
+
" # for using actions in joint space\n",
|
| 252 |
+
" if len(env_action) == 8:\n",
|
| 253 |
+
" env_action = {\"action\": env_action, \"type\": \"joint_rel\"}\n",
|
| 254 |
+
"\n",
|
| 255 |
+
" self.robot.apply_action(env_action)\n",
|
| 256 |
+
" for i in range(self.action_repeat):\n",
|
| 257 |
+
" self.p.stepSimulation(physicsClientId=self.cid)\n",
|
| 258 |
+
" obs = self.get_obs()\n",
|
| 259 |
+
" info = self.get_info()\n",
|
| 260 |
+
" reward, r_info = self._reward()\n",
|
| 261 |
+
" done, d_info = self._termination()\n",
|
| 262 |
+
" info.update(r_info)\n",
|
| 263 |
+
" info.update(d_info)\n",
|
| 264 |
+
" return obs, reward, done, info"
|
| 265 |
+
],
|
| 266 |
+
"execution_count": 5,
|
| 267 |
+
"outputs": []
|
| 268 |
+
},
|
| 269 |
+
{
|
| 270 |
+
"cell_type": "markdown",
|
| 271 |
+
"metadata": {
|
| 272 |
+
"id": "IJo-HWRqcJHc"
|
| 273 |
+
},
|
| 274 |
+
"source": [
|
| 275 |
+
"# Training an RL agent\n",
|
| 276 |
+
"After generating the wrapper training a reinforcement learning agent is straightforward, for this example we will use stable baselines 3 agents"
|
| 277 |
+
]
|
| 278 |
+
},
|
| 279 |
+
{
|
| 280 |
+
"cell_type": "code",
|
| 281 |
+
"metadata": {
|
| 282 |
+
"id": "XOkD9S-iMVcj"
|
| 283 |
+
},
|
| 284 |
+
"source": [
|
| 285 |
+
"!pip3 install stable_baselines3"
|
| 286 |
+
],
|
| 287 |
+
"execution_count": null,
|
| 288 |
+
"outputs": []
|
| 289 |
+
},
|
| 290 |
+
{
|
| 291 |
+
"cell_type": "markdown",
|
| 292 |
+
"metadata": {
|
| 293 |
+
"id": "WxWRcJJFcpEF"
|
| 294 |
+
},
|
| 295 |
+
"source": [
|
| 296 |
+
"To train the agent we create an instance of our new environment and send it to the stable baselines agent to learn a policy.\n",
|
| 297 |
+
"\n",
|
| 298 |
+
"\n",
|
| 299 |
+
"> Note: the example uses Soft Actor Critic (SAC) which is one of the state of the art algorithm for off-policy RL.\n",
|
| 300 |
+
"\n"
|
| 301 |
+
]
|
| 302 |
+
},
|
| 303 |
+
{
|
| 304 |
+
"cell_type": "code",
|
| 305 |
+
"metadata": {
|
| 306 |
+
"id": "3BUeaAnqMNLq"
|
| 307 |
+
},
|
| 308 |
+
"source": [
|
| 309 |
+
"import gym\n",
|
| 310 |
+
"import numpy as np\n",
|
| 311 |
+
"from stable_baselines3 import SAC\n",
|
| 312 |
+
"\n",
|
| 313 |
+
"new_env_cfg = {**cfg.env}\n",
|
| 314 |
+
"new_env_cfg[\"tasks\"] = cfg.tasks\n",
|
| 315 |
+
"new_env_cfg.pop('_target_', None)\n",
|
| 316 |
+
"new_env_cfg.pop('_recursive_', None)\n",
|
| 317 |
+
"env = SlideEnv(**new_env_cfg)\n",
|
| 318 |
+
"model = SAC(\"MlpPolicy\", env, verbose=1)\n",
|
| 319 |
+
"model.learn(total_timesteps=10000, log_interval=4)\n"
|
| 320 |
+
],
|
| 321 |
+
"execution_count": null,
|
| 322 |
+
"outputs": []
|
| 323 |
+
}
|
| 324 |
+
]
|
| 325 |
+
}
|
aloha_robot_project/calvin/calvin_env/.flake8
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[flake8]
|
| 2 |
+
exclude = .git
|
| 3 |
+
# Default is 79 in PEP 8
|
| 4 |
+
max-line-length = 120
|
| 5 |
+
select = E,F,W,C
|
| 6 |
+
ignore=W503, # line break before binary operator, need for black
|
| 7 |
+
E203, # whitespace before ':'. Opposite convention enforced by black
|
| 8 |
+
E731, # do not assign a lambda expression, use a def
|
| 9 |
+
E722,
|
| 10 |
+
F401,
|
| 11 |
+
F841,
|
| 12 |
+
E402, # module level import not at top of file
|
| 13 |
+
E741, # ambiguous variable name
|
| 14 |
+
E501, # line too long. Handled by black
|
| 15 |
+
C406, # Unnecessary list literal - rewrite as a dict literal
|
aloha_robot_project/calvin/calvin_env/.gitignore
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# C extensions
|
| 7 |
+
*.so
|
| 8 |
+
|
| 9 |
+
# Distribution / packaging
|
| 10 |
+
.Python
|
| 11 |
+
build/
|
| 12 |
+
develop-eggs/
|
| 13 |
+
dist/
|
| 14 |
+
downloads/
|
| 15 |
+
eggs/
|
| 16 |
+
.eggs/
|
| 17 |
+
lib/
|
| 18 |
+
lib64/
|
| 19 |
+
parts/
|
| 20 |
+
sdist/
|
| 21 |
+
var/
|
| 22 |
+
wheels/
|
| 23 |
+
pip-wheel-metadata/
|
| 24 |
+
share/python-wheels/
|
| 25 |
+
*.egg-info/
|
| 26 |
+
.installed.cfg
|
| 27 |
+
*.egg
|
| 28 |
+
MANIFEST
|
| 29 |
+
|
| 30 |
+
# PyInstaller
|
| 31 |
+
# Usually these files are written by a python script from a template
|
| 32 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 33 |
+
*.manifest
|
| 34 |
+
*.spec
|
| 35 |
+
|
| 36 |
+
# Installer logs
|
| 37 |
+
pip-log.txt
|
| 38 |
+
pip-delete-this-directory.txt
|
| 39 |
+
|
| 40 |
+
# Unit test / coverage reports
|
| 41 |
+
htmlcov/
|
| 42 |
+
.tox/
|
| 43 |
+
.nox/
|
| 44 |
+
.coverage
|
| 45 |
+
.coverage.*
|
| 46 |
+
.cache
|
| 47 |
+
nosetests.xml
|
| 48 |
+
coverage.xml
|
| 49 |
+
*.cover
|
| 50 |
+
*.py,cover
|
| 51 |
+
.hypothesis/
|
| 52 |
+
.pytest_cache/
|
| 53 |
+
|
| 54 |
+
# Translations
|
| 55 |
+
*.mo
|
| 56 |
+
*.pot
|
| 57 |
+
|
| 58 |
+
# Django stuff:
|
| 59 |
+
*.log
|
| 60 |
+
local_settings.py
|
| 61 |
+
db.sqlite3
|
| 62 |
+
db.sqlite3-journal
|
| 63 |
+
|
| 64 |
+
# Flask stuff:
|
| 65 |
+
instance/
|
| 66 |
+
.webassets-cache
|
| 67 |
+
|
| 68 |
+
# Scrapy stuff:
|
| 69 |
+
.scrapy
|
| 70 |
+
|
| 71 |
+
# Sphinx documentation
|
| 72 |
+
docs/_build/
|
| 73 |
+
|
| 74 |
+
# PyBuilder
|
| 75 |
+
target/
|
| 76 |
+
|
| 77 |
+
# Jupyter Notebook
|
| 78 |
+
.ipynb_checkpoints
|
| 79 |
+
|
| 80 |
+
# IPython
|
| 81 |
+
profile_default/
|
| 82 |
+
ipython_config.py
|
| 83 |
+
|
| 84 |
+
# pyenv
|
| 85 |
+
.python-version
|
| 86 |
+
|
| 87 |
+
# pipenv
|
| 88 |
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
| 89 |
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
| 90 |
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
| 91 |
+
# install all needed dependencies.
|
| 92 |
+
#Pipfile.lock
|
| 93 |
+
|
| 94 |
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
| 95 |
+
__pypackages__/
|
| 96 |
+
|
| 97 |
+
# Celery stuff
|
| 98 |
+
celerybeat-schedule
|
| 99 |
+
celerybeat.pid
|
| 100 |
+
|
| 101 |
+
# SageMath parsed files
|
| 102 |
+
*.sage.py
|
| 103 |
+
|
| 104 |
+
# Environments
|
| 105 |
+
.env
|
| 106 |
+
.venv
|
| 107 |
+
env/
|
| 108 |
+
venv/
|
| 109 |
+
ENV/
|
| 110 |
+
env.bak/
|
| 111 |
+
venv.bak/
|
| 112 |
+
|
| 113 |
+
# Spyder project settings
|
| 114 |
+
.spyderproject
|
| 115 |
+
.spyproject
|
| 116 |
+
|
| 117 |
+
# Rope project settings
|
| 118 |
+
.ropeproject
|
| 119 |
+
|
| 120 |
+
# mkdocs documentation
|
| 121 |
+
/site
|
| 122 |
+
|
| 123 |
+
# mypy
|
| 124 |
+
.mypy_cache/
|
| 125 |
+
.dmypy.json
|
| 126 |
+
dmypy.json
|
| 127 |
+
|
| 128 |
+
# Pyre type checker
|
| 129 |
+
.pyre/
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
# NumPy
|
| 133 |
+
*.npy
|
| 134 |
+
*.npz
|
| 135 |
+
|
| 136 |
+
# Pickles
|
| 137 |
+
*.pickle
|
| 138 |
+
|
| 139 |
+
# gzip
|
| 140 |
+
*.gzip
|
| 141 |
+
|
| 142 |
+
# Json
|
| 143 |
+
#*.json
|
| 144 |
+
|
| 145 |
+
# VSCode
|
| 146 |
+
.vscode/*
|
| 147 |
+
|
| 148 |
+
# Own playground area
|
| 149 |
+
**/playground/*
|
| 150 |
+
|
| 151 |
+
# tensorboard
|
| 152 |
+
events.out*
|
| 153 |
+
*.svg
|
| 154 |
+
|
| 155 |
+
# pdfs
|
| 156 |
+
*.pdf
|
| 157 |
+
|
| 158 |
+
*.egg-info/*
|
| 159 |
+
/.idea/
|
| 160 |
+
|
| 161 |
+
egl_check/EGL_options.o
|
aloha_robot_project/calvin/calvin_env/.gitmodules
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[submodule "tacto"]
|
| 2 |
+
path = tacto
|
| 3 |
+
url = https://github.com/lukashermann/tacto.git
|
aloha_robot_project/calvin/calvin_env/.pre-commit-config.yaml
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
default_language_version:
|
| 2 |
+
python: python3.8
|
| 3 |
+
repos:
|
| 4 |
+
- repo: https://github.com/psf/black
|
| 5 |
+
rev: 22.1.0
|
| 6 |
+
hooks:
|
| 7 |
+
- id: black
|
| 8 |
+
language_version: python3.8
|
| 9 |
+
|
| 10 |
+
- repo: https://gitlab.com/pycqa/flake8
|
| 11 |
+
rev: 3.8.4
|
| 12 |
+
hooks:
|
| 13 |
+
- id: flake8
|
| 14 |
+
additional_dependencies: [-e, "git+git://github.com/pycqa/pyflakes.git@c72d6cf#egg=pyflakes"]
|
| 15 |
+
|
| 16 |
+
- repo: https://github.com/pre-commit/mirrors-isort
|
| 17 |
+
rev: v5.6.4
|
| 18 |
+
hooks:
|
| 19 |
+
- id: isort
|
| 20 |
+
|
| 21 |
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
| 22 |
+
rev: v0.790
|
| 23 |
+
hooks:
|
| 24 |
+
- id: mypy
|
| 25 |
+
args: [--ignore-missing-imports, --warn-no-return, --warn-redundant-casts, --disallow-incomplete-defs]
|
| 26 |
+
|
| 27 |
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
| 28 |
+
rev: v3.4.0
|
| 29 |
+
hooks:
|
| 30 |
+
- id: check-yaml
|
| 31 |
+
- id: trailing-whitespace
|
| 32 |
+
- id: end-of-file-fixer
|
aloha_robot_project/calvin/calvin_env/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2021 Oier Mees
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
aloha_robot_project/calvin/calvin_env/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# calvin_env
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
## Installation
|
| 6 |
+
```bash
|
| 7 |
+
git clone --recursive https://github.com/mees/calvin_env.git
|
| 8 |
+
cd calvin_env/tacto
|
| 9 |
+
pip install -e .
|
| 10 |
+
cd ..
|
| 11 |
+
pip install -e .
|
| 12 |
+
```
|
aloha_robot_project/calvin/calvin_env/calvin_env/__init__.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""'VR Data Collection and Rendering
|
| 2 |
+
:copyright: 2019 by Oier Mees, Lukas Hermann, Wolfram Burgard
|
| 3 |
+
:license: GPLv3, see LICENSE for more details.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
__version__ = "0.0.1"
|
| 7 |
+
__project__ = "calvin_env"
|
| 8 |
+
__author__ = "Oier Mees, Lukas Hermann"
|
| 9 |
+
__license__ = "GPLv3"
|
| 10 |
+
__email__ = "meeso@informatik.uni-freiburg.de, hermannl@informatik.uni-freiburg.de,"
|
aloha_robot_project/calvin/calvin_env/calvin_env/camera/camera.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class Camera:
|
| 7 |
+
def __init__(self, *args, **kwargs):
|
| 8 |
+
raise NotImplementedError
|
| 9 |
+
|
| 10 |
+
def render(self):
|
| 11 |
+
raise NotImplementedError
|
| 12 |
+
|
| 13 |
+
def distance_map_to_point_cloud(self, distances, fov, width, height):
|
| 14 |
+
"""Converts from a depth map to a point cloud.
|
| 15 |
+
Args:
|
| 16 |
+
distances: An numpy array which has the shape of (height, width) that
|
| 17 |
+
denotes a distance map. The unit is meter.
|
| 18 |
+
fov: The field of view of the camera in the vertical direction. The unit
|
| 19 |
+
is radian.
|
| 20 |
+
width: The width of the image resolution of the camera.
|
| 21 |
+
height: The height of the image resolution of the camera.
|
| 22 |
+
Returns:
|
| 23 |
+
point_cloud: The converted point cloud from the distance map. It is a numpy
|
| 24 |
+
array of shape (height, width, 3).
|
| 25 |
+
"""
|
| 26 |
+
f = height / (2 * math.tan(fov / 2.0))
|
| 27 |
+
px = np.tile(np.arange(width), [height, 1])
|
| 28 |
+
x = (2 * (px + 0.5) - width) / f * distances / 2
|
| 29 |
+
py = np.tile(np.arange(height), [width, 1]).T
|
| 30 |
+
y = (2 * (py + 0.5) - height) / f * distances / 2
|
| 31 |
+
point_cloud = np.stack((x, y, distances), axis=-1)
|
| 32 |
+
return point_cloud
|
| 33 |
+
|
| 34 |
+
def z_buffer_to_real_distance(self, z_buffer, far, near):
|
| 35 |
+
"""Function to transform depth buffer values to distances in camera space"""
|
| 36 |
+
return 1.0 * far * near / (far - (far - near) * z_buffer)
|
| 37 |
+
|
| 38 |
+
def process_rgbd(self, obs, nearval, farval):
|
| 39 |
+
(width, height, rgbPixels, depthPixels, segmentationMaskBuffer) = obs
|
| 40 |
+
rgb = np.reshape(rgbPixels, (height, width, 4))
|
| 41 |
+
rgb_img = rgb[:, :, :3]
|
| 42 |
+
depth_buffer = np.reshape(depthPixels, [height, width])
|
| 43 |
+
depth = self.z_buffer_to_real_distance(z_buffer=depth_buffer, far=farval, near=nearval)
|
| 44 |
+
return rgb_img, depth
|
| 45 |
+
|
| 46 |
+
# Reference: world2pixel
|
| 47 |
+
# https://github.com/bulletphysics/bullet3/issues/1952
|
| 48 |
+
def project(self, point):
|
| 49 |
+
"""
|
| 50 |
+
Projects a world point in homogeneous coordinates to pixel coordinates
|
| 51 |
+
Args
|
| 52 |
+
point: np.array of len 4; indicates the desired point to project
|
| 53 |
+
Output
|
| 54 |
+
(x, y): tuple (u, v); pixel coordinates of the projected point
|
| 55 |
+
"""
|
| 56 |
+
|
| 57 |
+
# reshape to get homogeneus transform
|
| 58 |
+
persp_m = np.array(self.projectionMatrix).reshape((4, 4)).T
|
| 59 |
+
view_m = np.array(self.viewMatrix).reshape((4, 4)).T
|
| 60 |
+
|
| 61 |
+
# Perspective proj matrix
|
| 62 |
+
world_pix_tran = persp_m @ view_m @ point
|
| 63 |
+
world_pix_tran = world_pix_tran / world_pix_tran[-1] # divide by w
|
| 64 |
+
world_pix_tran[:3] = (world_pix_tran[:3] + 1) / 2
|
| 65 |
+
x, y = world_pix_tran[0] * self.width, (1 - world_pix_tran[1]) * self.height
|
| 66 |
+
x, y = np.floor(x).astype(int), np.floor(y).astype(int)
|
| 67 |
+
return (x, y)
|
| 68 |
+
|
| 69 |
+
def deproject(self, point, depth_img, homogeneous=False):
|
| 70 |
+
"""
|
| 71 |
+
Deprojects a pixel point to 3D coordinates
|
| 72 |
+
Args
|
| 73 |
+
point: tuple (u, v); pixel coordinates of point to deproject
|
| 74 |
+
depth_img: np.array; depth image used as reference to generate 3D coordinates
|
| 75 |
+
homogeneous: bool; if true it returns the 3D point in homogeneous coordinates,
|
| 76 |
+
else returns the world coordinates (x, y, z) position
|
| 77 |
+
Output
|
| 78 |
+
(x, y): np.array; world coordinates of the deprojected point
|
| 79 |
+
"""
|
| 80 |
+
T_world_cam = np.linalg.inv(np.array(self.viewMatrix).reshape((4, 4)).T)
|
| 81 |
+
|
| 82 |
+
u, v = point
|
| 83 |
+
z = depth_img[v, u]
|
| 84 |
+
foc = self.height / (2 * np.tan(np.deg2rad(self.fov) / 2))
|
| 85 |
+
x = (u - self.width // 2) * z / foc
|
| 86 |
+
y = -(v - self.height // 2) * z / foc
|
| 87 |
+
z = -z
|
| 88 |
+
world_pos = T_world_cam @ np.array([x, y, z, 1])
|
| 89 |
+
if not homogeneous:
|
| 90 |
+
world_pos = world_pos[:3]
|
| 91 |
+
return world_pos
|
aloha_robot_project/calvin/calvin_env/calvin_env/camera/gripper_camera.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pybullet as p
|
| 3 |
+
|
| 4 |
+
from calvin_env.camera.camera import Camera
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class GripperCamera(Camera):
|
| 8 |
+
def __init__(self, fov, aspect, nearval, farval, width, height, robot_id, cid, name, objects=None):
|
| 9 |
+
self.cid = cid
|
| 10 |
+
self.robot_uid = robot_id
|
| 11 |
+
links = {
|
| 12 |
+
p.getJointInfo(self.robot_uid, i, physicsClientId=self.cid)[12].decode("utf-8"): i
|
| 13 |
+
for i in range(p.getNumJoints(self.robot_uid, physicsClientId=self.cid))
|
| 14 |
+
}
|
| 15 |
+
self.gripper_cam_link = links["gripper_cam"]
|
| 16 |
+
self.fov = fov
|
| 17 |
+
self.aspect = aspect
|
| 18 |
+
self.nearval = nearval
|
| 19 |
+
self.farval = farval
|
| 20 |
+
self.width = width
|
| 21 |
+
self.height = height
|
| 22 |
+
|
| 23 |
+
self.name = name
|
| 24 |
+
|
| 25 |
+
def render(self):
|
| 26 |
+
camera_ls = p.getLinkState(
|
| 27 |
+
bodyUniqueId=self.robot_uid, linkIndex=self.gripper_cam_link, physicsClientId=self.cid
|
| 28 |
+
)
|
| 29 |
+
camera_pos, camera_orn = camera_ls[:2]
|
| 30 |
+
cam_rot = p.getMatrixFromQuaternion(camera_orn)
|
| 31 |
+
cam_rot = np.array(cam_rot).reshape(3, 3)
|
| 32 |
+
cam_rot_y, cam_rot_z = cam_rot[:, 1], cam_rot[:, 2]
|
| 33 |
+
# camera: eye position, target position, up vector
|
| 34 |
+
self.view_matrix = p.computeViewMatrix(camera_pos, camera_pos + cam_rot_y, -cam_rot_z)
|
| 35 |
+
self.projection_matrix = p.computeProjectionMatrixFOV(
|
| 36 |
+
fov=self.fov, aspect=self.aspect, nearVal=self.nearval, farVal=self.farval
|
| 37 |
+
)
|
| 38 |
+
image = p.getCameraImage(
|
| 39 |
+
width=self.width,
|
| 40 |
+
height=self.height,
|
| 41 |
+
viewMatrix=self.view_matrix,
|
| 42 |
+
projectionMatrix=self.projection_matrix,
|
| 43 |
+
physicsClientId=self.cid,
|
| 44 |
+
)
|
| 45 |
+
rgb_img, depth_img = self.process_rgbd(image, self.nearval, self.farval)
|
| 46 |
+
return rgb_img, depth_img
|
aloha_robot_project/calvin/calvin_env/calvin_env/camera/static_camera.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pybullet as p
|
| 3 |
+
|
| 4 |
+
from calvin_env.camera.camera import Camera
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class StaticCamera(Camera):
|
| 8 |
+
def __init__(
|
| 9 |
+
self,
|
| 10 |
+
fov,
|
| 11 |
+
aspect,
|
| 12 |
+
nearval,
|
| 13 |
+
farval,
|
| 14 |
+
width,
|
| 15 |
+
height,
|
| 16 |
+
look_at,
|
| 17 |
+
look_from,
|
| 18 |
+
up_vector,
|
| 19 |
+
cid,
|
| 20 |
+
name,
|
| 21 |
+
robot_id=None,
|
| 22 |
+
objects=None,
|
| 23 |
+
):
|
| 24 |
+
"""
|
| 25 |
+
Initialize the camera
|
| 26 |
+
Args:
|
| 27 |
+
argument_group: initialize the camera and add needed arguments to argparse
|
| 28 |
+
|
| 29 |
+
Returns:
|
| 30 |
+
None
|
| 31 |
+
"""
|
| 32 |
+
self.nearval = nearval
|
| 33 |
+
self.farval = farval
|
| 34 |
+
self.fov = fov
|
| 35 |
+
self.aspect = aspect
|
| 36 |
+
self.look_from = look_from
|
| 37 |
+
self.look_at = look_at
|
| 38 |
+
self.up_vector = up_vector
|
| 39 |
+
self.width = width
|
| 40 |
+
self.height = height
|
| 41 |
+
self.viewMatrix = p.computeViewMatrix(
|
| 42 |
+
cameraEyePosition=look_from, cameraTargetPosition=look_at, cameraUpVector=self.up_vector
|
| 43 |
+
)
|
| 44 |
+
self.projectionMatrix = p.computeProjectionMatrixFOV(
|
| 45 |
+
fov=fov, aspect=aspect, nearVal=self.nearval, farVal=self.farval
|
| 46 |
+
)
|
| 47 |
+
self.cid = cid
|
| 48 |
+
self.name = name
|
| 49 |
+
|
| 50 |
+
def set_position_from_gui(self):
|
| 51 |
+
info = p.getDebugVisualizerCamera(physicsClientId=self.cid)
|
| 52 |
+
look_at = np.array(info[-1])
|
| 53 |
+
dist = info[-2]
|
| 54 |
+
forward = np.array(info[5])
|
| 55 |
+
look_from = look_at - dist * forward
|
| 56 |
+
self.viewMatrix = p.computeViewMatrix(
|
| 57 |
+
cameraEyePosition=look_from, cameraTargetPosition=look_at, cameraUpVector=self.up_vector
|
| 58 |
+
)
|
| 59 |
+
look_from = [float(x) for x in look_from]
|
| 60 |
+
look_at = [float(x) for x in look_at]
|
| 61 |
+
return look_from, look_at
|
| 62 |
+
|
| 63 |
+
def render(self):
|
| 64 |
+
image = p.getCameraImage(
|
| 65 |
+
width=self.width,
|
| 66 |
+
height=self.height,
|
| 67 |
+
viewMatrix=self.viewMatrix,
|
| 68 |
+
projectionMatrix=self.projectionMatrix,
|
| 69 |
+
physicsClientId=self.cid,
|
| 70 |
+
)
|
| 71 |
+
rgb_img, depth_img = self.process_rgbd(image, self.nearval, self.farval)
|
| 72 |
+
return rgb_img, depth_img
|
aloha_robot_project/calvin/calvin_env/calvin_env/camera/tactile_sensor.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
|
| 5 |
+
from calvin_env.camera.camera import Camera
|
| 6 |
+
import tacto
|
| 7 |
+
|
| 8 |
+
REPO_BASE = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class TactileSensor(Camera):
|
| 12 |
+
def __init__(
|
| 13 |
+
self, width, height, digit_link_ids, visualize_gui, cid, name, config_path, robot_id=None, objects=None
|
| 14 |
+
):
|
| 15 |
+
"""
|
| 16 |
+
Initialize the camera
|
| 17 |
+
Args:
|
| 18 |
+
argument_group: initialize the camera and add needed arguments to argparse
|
| 19 |
+
|
| 20 |
+
Returns:
|
| 21 |
+
None
|
| 22 |
+
"""
|
| 23 |
+
self.cid = cid
|
| 24 |
+
self.name = name
|
| 25 |
+
self.robot_uid = robot_id
|
| 26 |
+
self.digits = tacto.Sensor(
|
| 27 |
+
width=width, height=height, visualize_gui=visualize_gui, config_path=os.path.join(REPO_BASE, config_path)
|
| 28 |
+
)
|
| 29 |
+
self.digits.add_camera(robot_id, digit_link_ids) # env.robot.digit_links()
|
| 30 |
+
for obj in objects:
|
| 31 |
+
# self.digits.add_body(obj)
|
| 32 |
+
self.digits.add_object(obj.file.as_posix(), obj.uid, obj.global_scaling)
|
| 33 |
+
self.visualize_gui = visualize_gui
|
| 34 |
+
|
| 35 |
+
def render(self):
|
| 36 |
+
rgb, depth = self.digits.render()
|
| 37 |
+
if self.visualize_gui:
|
| 38 |
+
self.digits.updateGUI(rgb, depth)
|
| 39 |
+
rgb = np.concatenate(rgb, axis=2)
|
| 40 |
+
depth = np.stack(depth, axis=2)
|
| 41 |
+
return rgb, depth
|
aloha_robot_project/calvin/calvin_env/calvin_env/datarenderer.py
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/python3
|
| 2 |
+
|
| 3 |
+
from collections import deque
|
| 4 |
+
import logging
|
| 5 |
+
from multiprocessing import Process
|
| 6 |
+
import os
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
|
| 9 |
+
import hydra
|
| 10 |
+
import numpy as np
|
| 11 |
+
import omegaconf
|
| 12 |
+
import pybullet as p
|
| 13 |
+
from scipy.spatial.transform.rotation import Rotation as R
|
| 14 |
+
|
| 15 |
+
from calvin_env.utils.utils import count_frames, get_episode_lengths, set_egl_device, to_relative_action
|
| 16 |
+
|
| 17 |
+
# A logger for this file
|
| 18 |
+
log = logging.getLogger(__name__)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
@hydra.main(config_path="../conf", config_name="config_rendering")
|
| 22 |
+
def main(cfg):
|
| 23 |
+
log.info("pyBullet Data Renderer")
|
| 24 |
+
log.info("Determining maximum frame")
|
| 25 |
+
|
| 26 |
+
recording_dir = (Path(hydra.utils.get_original_cwd()) / cfg.load_dir).absolute()
|
| 27 |
+
max_frames = count_frames(recording_dir)
|
| 28 |
+
log.info(f"Found continuous interval of {max_frames} frames without gaps")
|
| 29 |
+
|
| 30 |
+
num_prev_rendered_episodes = num_previously_rendered_episodes()
|
| 31 |
+
if num_prev_rendered_episodes == 0:
|
| 32 |
+
playback_cfg = build_rendering_config(recording_dir, rendering_config=cfg)
|
| 33 |
+
else:
|
| 34 |
+
playback_cfg = load_rendering_config(cfg)
|
| 35 |
+
|
| 36 |
+
log.info("Initialization done!")
|
| 37 |
+
log.info(f"Starting {cfg.processes} processes")
|
| 38 |
+
|
| 39 |
+
if playback_cfg.set_static_cam:
|
| 40 |
+
playback_cfg = set_static_cams_from_gui(playback_cfg, recording_dir, max_frames)
|
| 41 |
+
|
| 42 |
+
if cfg.processes != 1 and playback_cfg.show_gui:
|
| 43 |
+
log.warning("Multiprocess rendering requires headless mode, setting cfg.show_gui = False")
|
| 44 |
+
playback_cfg.show_gui = False
|
| 45 |
+
# in order to distribute the rendering to multiple processes, predetermine the lengths of the
|
| 46 |
+
# (rendered) episodes and to which (recording) file ids the episode start and end correspond
|
| 47 |
+
# a rendered episode does not contain the done frame, thus length(render_episode) = length(recording_episode) -1
|
| 48 |
+
episode_lengths, render_start_end_ids = get_episode_lengths(cfg.load_dir, max_frames)
|
| 49 |
+
# episode_lengths = episode_lengths[:1]
|
| 50 |
+
# render_start_end_ids = render_start_end_ids[:1]
|
| 51 |
+
|
| 52 |
+
if cfg.processes > len(episode_lengths):
|
| 53 |
+
log.warning(f"Trying to use more processes ({cfg.processes}) than episodes ({len(episode_lengths)}).")
|
| 54 |
+
log.warning(f"Reducing number of processes to {len(episode_lengths)}.")
|
| 55 |
+
cfg.processes = len(episode_lengths)
|
| 56 |
+
# distribute the episodes equally to processes
|
| 57 |
+
split_indices = np.array_split(np.array(render_start_end_ids), cfg.processes, axis=0)
|
| 58 |
+
# every process renders the interval [proc_start_ids, proc_end_ids)
|
| 59 |
+
proc_start_ids = [split_indices[proc_num][0][0] for proc_num in range(cfg.processes)]
|
| 60 |
+
proc_end_ids = [split_indices[proc_num][-1][1] for proc_num in range(cfg.processes)]
|
| 61 |
+
# predetermine starting episode indices for multiple processes
|
| 62 |
+
proc_ep_ids = np.cumsum(
|
| 63 |
+
[0] + list(map(np.sum, np.array_split(np.array(episode_lengths), cfg.processes, axis=0)))[:-1]
|
| 64 |
+
)
|
| 65 |
+
proc_ep_ids += num_prev_rendered_episodes
|
| 66 |
+
if cfg.processes > 1:
|
| 67 |
+
processes = [
|
| 68 |
+
Process(
|
| 69 |
+
target=worker_run,
|
| 70 |
+
args=(
|
| 71 |
+
recording_dir,
|
| 72 |
+
playback_cfg,
|
| 73 |
+
proc_num,
|
| 74 |
+
proc_start_ids[proc_num],
|
| 75 |
+
proc_end_ids[proc_num],
|
| 76 |
+
proc_ep_ids[proc_num],
|
| 77 |
+
),
|
| 78 |
+
name=f"Worker {proc_num}",
|
| 79 |
+
)
|
| 80 |
+
for proc_num in range(cfg.processes)
|
| 81 |
+
]
|
| 82 |
+
deque(map(lambda proc: proc.start(), processes))
|
| 83 |
+
deque(map(lambda proc: proc.join(), processes))
|
| 84 |
+
else:
|
| 85 |
+
worker_run(recording_dir, playback_cfg, 0, 0, max_frames, num_prev_rendered_episodes)
|
| 86 |
+
save_ep_lens(episode_lengths, num_prev_rendered_episodes)
|
| 87 |
+
|
| 88 |
+
log.info("All workers done")
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def build_rendering_config(recording_dir, rendering_config):
|
| 92 |
+
merged_conf = omegaconf.OmegaConf.load(Path(recording_dir) / ".hydra" / "config.yaml")
|
| 93 |
+
merged_conf = omegaconf.OmegaConf.merge(merged_conf, rendering_config)
|
| 94 |
+
hydra.core.utils._save_config(merged_conf, "merged_config.yaml", Path(os.getcwd(), ".hydra"))
|
| 95 |
+
return merged_conf
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def load_rendering_config(rendering_config):
|
| 99 |
+
conf = omegaconf.OmegaConf.load(Path(os.getcwd()) / ".hydra" / "merged_config.yaml")
|
| 100 |
+
override_conf = omegaconf.OmegaConf.select(rendering_config, "scene")
|
| 101 |
+
omegaconf.OmegaConf.update(conf, "scene", override_conf, merge=False)
|
| 102 |
+
conf.set_static_cam = False
|
| 103 |
+
return conf
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def num_previously_rendered_episodes():
|
| 107 |
+
return len(list(Path(os.getcwd()).glob("*.npz")))
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def save_ep_lens(episode_lengths, num_prev_episodes):
|
| 111 |
+
if num_prev_episodes > 0:
|
| 112 |
+
previous_ep_lens = np.load("ep_lens.npy")
|
| 113 |
+
episode_lengths = np.concatenate((previous_ep_lens, episode_lengths))
|
| 114 |
+
np.save("ep_lens.npy", episode_lengths)
|
| 115 |
+
end_ids = np.cumsum(episode_lengths) - 1
|
| 116 |
+
start_ids = [0] + list(end_ids + 1)[:-1]
|
| 117 |
+
ep_start_end_ids = list(zip(start_ids, end_ids))
|
| 118 |
+
np.save("ep_start_end_ids.npy", ep_start_end_ids)
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def save_step(counter, rgbs, depths, actions, robot_obs, scene_obs, cam_names, **additional_infos):
|
| 122 |
+
rgb_entries = {f"rgb_{cam_name}": rgbs[f"rgb_{cam_name}"] for i, cam_name in enumerate(cam_names)}
|
| 123 |
+
depths_entries = {f"depth_{cam_name}": depths[f"depth_{cam_name}"] for i, cam_name in enumerate(cam_names)}
|
| 124 |
+
if actions[-1] == 0:
|
| 125 |
+
actions[-1] = -1
|
| 126 |
+
np.savez_compressed(
|
| 127 |
+
f"episode_{counter:07d}.npz",
|
| 128 |
+
actions=actions,
|
| 129 |
+
rel_actions=to_relative_action(actions, robot_obs),
|
| 130 |
+
robot_obs=robot_obs,
|
| 131 |
+
scene_obs=scene_obs,
|
| 132 |
+
**rgb_entries,
|
| 133 |
+
**depths_entries,
|
| 134 |
+
**additional_infos,
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def state_to_action(info):
|
| 139 |
+
"""
|
| 140 |
+
save action as [tcp_pos, tcp_orn_quaternion, gripper_action]
|
| 141 |
+
"""
|
| 142 |
+
tcp_pos = info["robot_info"]["tcp_pos"]
|
| 143 |
+
tcp_orn = info["robot_info"]["tcp_orn"]
|
| 144 |
+
gripper_action = info["robot_info"]["gripper_action"]
|
| 145 |
+
action = np.concatenate([tcp_pos, tcp_orn, [gripper_action]])
|
| 146 |
+
return action
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def set_static_cams_from_gui(cfg, load_dir, max_frames):
|
| 150 |
+
import cv2
|
| 151 |
+
|
| 152 |
+
assert cfg.env.show_gui
|
| 153 |
+
env = hydra.utils.instantiate(cfg.env)
|
| 154 |
+
env.reset()
|
| 155 |
+
frame = 0
|
| 156 |
+
log.info("--------------------------------------------------")
|
| 157 |
+
log.info("Use Debug GUI to change the position of the camera")
|
| 158 |
+
log.info("Use Render_view_window for keyboard input")
|
| 159 |
+
log.info("Press A or D to move through frames")
|
| 160 |
+
log.info("Press Q or E to skip through frames")
|
| 161 |
+
log.info("Press S to set camera position")
|
| 162 |
+
log.info("Press ENTER to save the set camera position")
|
| 163 |
+
log.info("Press ESC to skip setting position for current camera")
|
| 164 |
+
for cam_index, (cam_name, cam) in enumerate(cfg.cameras.items()):
|
| 165 |
+
if "static" in cam._target_:
|
| 166 |
+
# initialize variables
|
| 167 |
+
look_from = cam.look_from
|
| 168 |
+
look_at = cam.look_at
|
| 169 |
+
up_vector = cam.up_vector
|
| 170 |
+
fov = cam.fov
|
| 171 |
+
while True:
|
| 172 |
+
file_path = load_dir / f"{frame:012d}.pickle"
|
| 173 |
+
_, _, _ = env.reset_from_storage(file_path)
|
| 174 |
+
env.p.stepSimulation()
|
| 175 |
+
frame_rgbs, frame_depths = env.get_camera_obs()
|
| 176 |
+
rgb_static = frame_rgbs[cam_index]
|
| 177 |
+
|
| 178 |
+
cv2.imshow("Render_view_window", cv2.resize(rgb_static, (500, 500))[:, :, ::-1])
|
| 179 |
+
k = cv2.waitKey(10) % 256
|
| 180 |
+
if k == ord("a"):
|
| 181 |
+
frame -= 1
|
| 182 |
+
frame = np.clip(frame, 0, max_frames - 1)
|
| 183 |
+
if k == ord("d"):
|
| 184 |
+
frame += 1
|
| 185 |
+
frame = np.clip(frame, 0, max_frames - 1)
|
| 186 |
+
if k == ord("q"):
|
| 187 |
+
frame -= 100
|
| 188 |
+
frame = np.clip(frame, 0, max_frames - 1)
|
| 189 |
+
if k == ord("e"):
|
| 190 |
+
frame += 100
|
| 191 |
+
frame = np.clip(frame, 0, max_frames - 1)
|
| 192 |
+
if k == ord("z"):
|
| 193 |
+
c = env.cameras[cam_index]
|
| 194 |
+
c.fov -= 1
|
| 195 |
+
c.projectionMatrix = p.computeProjectionMatrixFOV(
|
| 196 |
+
fov=c.fov, aspect=c.aspect, nearVal=c.nearval, farVal=c.farval
|
| 197 |
+
)
|
| 198 |
+
print(c.fov)
|
| 199 |
+
fov = c.fov
|
| 200 |
+
if k == ord("x"):
|
| 201 |
+
c = env.cameras[cam_index]
|
| 202 |
+
c.fov += 1
|
| 203 |
+
c.projectionMatrix = p.computeProjectionMatrixFOV(
|
| 204 |
+
fov=c.fov, aspect=c.aspect, nearVal=c.nearval, farVal=c.farval
|
| 205 |
+
)
|
| 206 |
+
print(c.fov)
|
| 207 |
+
fov = c.fov
|
| 208 |
+
if k == ord("r"):
|
| 209 |
+
c = env.cameras[cam_index]
|
| 210 |
+
direction_vector = np.array(c.look_at) - np.array(c.look_from)
|
| 211 |
+
c.up_vector = (
|
| 212 |
+
R.from_rotvec(0.1 * direction_vector / np.linalg.norm(direction_vector)).as_matrix()
|
| 213 |
+
@ c.up_vector
|
| 214 |
+
)
|
| 215 |
+
up_vector = c.up_vector
|
| 216 |
+
if k == ord("f"):
|
| 217 |
+
c = env.cameras[cam_index]
|
| 218 |
+
direction_vector = np.array(c.look_at) - np.array(c.look_from)
|
| 219 |
+
c.up_vector = (
|
| 220 |
+
R.from_rotvec(-0.1 * direction_vector / np.linalg.norm(direction_vector)).as_matrix()
|
| 221 |
+
@ c.up_vector
|
| 222 |
+
)
|
| 223 |
+
up_vector = c.up_vector
|
| 224 |
+
if k == 13: # Enter
|
| 225 |
+
cam.look_from = look_from
|
| 226 |
+
cam.look_at = look_at
|
| 227 |
+
log.info(f"Set look_from of camera {cam_index} to {look_from}")
|
| 228 |
+
log.info(f"Set look_at of camera {cam_index} to {look_at}")
|
| 229 |
+
cam.up_vector = np.array(up_vector).tolist()
|
| 230 |
+
log.info(f"Set up_vector of camera {cam_index} to {up_vector}")
|
| 231 |
+
cam.fov = fov
|
| 232 |
+
log.info(f"Set fov of camera {cam_index} to {fov}")
|
| 233 |
+
break
|
| 234 |
+
if k == 27: # ESC
|
| 235 |
+
log.info(f"Do no change position of camera {cam_index}")
|
| 236 |
+
break
|
| 237 |
+
# if k == ord("s"):
|
| 238 |
+
look_from, look_at = env.cameras[cam_index].set_position_from_gui()
|
| 239 |
+
hydra.core.utils._save_config(cfg, "merged_config.yaml", Path(os.getcwd(), ".hydra"))
|
| 240 |
+
env.close()
|
| 241 |
+
return cfg
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
def worker_run(load_dir, rendering_cfg, proc_num, start_frame, stop_frame, episode_index):
|
| 245 |
+
log.info(f"[{proc_num}] Starting worker {proc_num}")
|
| 246 |
+
set_egl_device(0)
|
| 247 |
+
env = hydra.utils.instantiate(rendering_cfg.env)
|
| 248 |
+
|
| 249 |
+
log.info(f"[{proc_num}] Entering Loop")
|
| 250 |
+
frame_counter = 0
|
| 251 |
+
rgbs, depths, actions, robot_obs, scene_obs, = (
|
| 252 |
+
[],
|
| 253 |
+
[],
|
| 254 |
+
[],
|
| 255 |
+
[],
|
| 256 |
+
[],
|
| 257 |
+
)
|
| 258 |
+
for frame in range(start_frame, stop_frame):
|
| 259 |
+
file_path = load_dir / f"{frame:012d}.pickle"
|
| 260 |
+
state_ob, done, info = env.reset_from_storage(file_path)
|
| 261 |
+
action = state_to_action(info)
|
| 262 |
+
robot_obs.append(state_ob["robot_obs"])
|
| 263 |
+
scene_obs.append(state_ob["scene_obs"])
|
| 264 |
+
|
| 265 |
+
# action is robot state of next frame
|
| 266 |
+
if frame_counter > 0:
|
| 267 |
+
actions.append(action)
|
| 268 |
+
frame_rgbs, frame_depths = env.get_camera_obs()
|
| 269 |
+
rgbs.append(frame_rgbs)
|
| 270 |
+
depths.append(frame_depths)
|
| 271 |
+
# for terminal states save current robot state as action
|
| 272 |
+
frame_counter += 1
|
| 273 |
+
log.debug(f"episode counter {episode_index} frame counter {frame_counter} done {done}")
|
| 274 |
+
|
| 275 |
+
if frame_counter > 1:
|
| 276 |
+
save_step(
|
| 277 |
+
episode_index,
|
| 278 |
+
rgbs.pop(0),
|
| 279 |
+
depths.pop(0),
|
| 280 |
+
actions.pop(0),
|
| 281 |
+
robot_obs.pop(0),
|
| 282 |
+
scene_obs.pop(0),
|
| 283 |
+
cam_names=[cam.name for cam in env.cameras],
|
| 284 |
+
)
|
| 285 |
+
episode_index += 1
|
| 286 |
+
if done:
|
| 287 |
+
frame_counter = 0
|
| 288 |
+
rgbs, depths, actions, robot_obs, scene_obs = [], [], [], [], []
|
| 289 |
+
|
| 290 |
+
log.debug(f"[{proc_num}] Rendered frame {frame}")
|
| 291 |
+
|
| 292 |
+
assert done
|
| 293 |
+
|
| 294 |
+
env.close()
|
| 295 |
+
log.info(f"[{proc_num}] Finishing worker {proc_num}")
|
| 296 |
+
|
| 297 |
+
|
| 298 |
+
if __name__ == "__main__":
|
| 299 |
+
main()
|
aloha_robot_project/calvin/calvin_env/calvin_env/envs/play_lmp_wrapper.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
import os
|
| 3 |
+
from typing import Any, Dict, Tuple, Union
|
| 4 |
+
|
| 5 |
+
from calvin_agent.datasets.utils.episode_utils import process_depth, process_rgb, process_state
|
| 6 |
+
import gym
|
| 7 |
+
import numpy as np
|
| 8 |
+
import torch
|
| 9 |
+
|
| 10 |
+
from calvin_env.envs.play_table_env import get_env
|
| 11 |
+
from calvin_env.utils.utils import EglDeviceNotFoundError, get_egl_device_id
|
| 12 |
+
|
| 13 |
+
logger = logging.getLogger(__name__)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class PlayLMPWrapper(gym.Wrapper):
|
| 17 |
+
def __init__(self, dataset_loader, device, show_gui=False, **kwargs):
|
| 18 |
+
self.set_egl_device(device)
|
| 19 |
+
env = get_env(
|
| 20 |
+
dataset_loader.abs_datasets_dir, show_gui=show_gui, obs_space=dataset_loader.observation_space, **kwargs
|
| 21 |
+
)
|
| 22 |
+
super(PlayLMPWrapper, self).__init__(env)
|
| 23 |
+
self.observation_space_keys = dataset_loader.observation_space
|
| 24 |
+
self.transforms = dataset_loader.transforms
|
| 25 |
+
self.proprio_state = dataset_loader.proprio_state
|
| 26 |
+
self.device = device
|
| 27 |
+
self.relative_actions = "rel_actions" in self.observation_space_keys["actions"]
|
| 28 |
+
logger.info(f"Initialized PlayTableEnv for device {self.device}")
|
| 29 |
+
|
| 30 |
+
@staticmethod
|
| 31 |
+
def set_egl_device(device):
|
| 32 |
+
if "EGL_VISIBLE_DEVICES" in os.environ:
|
| 33 |
+
logger.warning("Environment variable EGL_VISIBLE_DEVICES is already set. Is this intended?")
|
| 34 |
+
cuda_id = device.index if device.type == "cuda" else 0
|
| 35 |
+
try:
|
| 36 |
+
egl_id = get_egl_device_id(cuda_id)
|
| 37 |
+
except EglDeviceNotFoundError:
|
| 38 |
+
logger.warning(
|
| 39 |
+
"Couldn't find correct EGL device. Setting EGL_VISIBLE_DEVICE=0. "
|
| 40 |
+
"When using DDP with many GPUs this can lead to OOM errors. "
|
| 41 |
+
"Did you install PyBullet correctly? Please refer to calvin env README"
|
| 42 |
+
)
|
| 43 |
+
egl_id = 0
|
| 44 |
+
os.environ["EGL_VISIBLE_DEVICES"] = str(egl_id)
|
| 45 |
+
logger.info(f"EGL_DEVICE_ID {egl_id} <==> CUDA_DEVICE_ID {cuda_id}")
|
| 46 |
+
|
| 47 |
+
def transform_observation(self, obs: Dict[str, Any]) -> Dict[str, Union[torch.Tensor, Dict[str, torch.Tensor]]]:
|
| 48 |
+
state_obs = process_state(obs, self.observation_space_keys, self.transforms, self.proprio_state)
|
| 49 |
+
rgb_obs = process_rgb(obs["rgb_obs"], self.observation_space_keys, self.transforms)
|
| 50 |
+
depth_obs = process_depth(obs["depth_obs"], self.observation_space_keys, self.transforms)
|
| 51 |
+
|
| 52 |
+
state_obs["robot_obs"] = state_obs["robot_obs"].to(self.device).unsqueeze(0)
|
| 53 |
+
rgb_obs.update({"rgb_obs": {k: v.to(self.device).unsqueeze(0) for k, v in rgb_obs["rgb_obs"].items()}})
|
| 54 |
+
depth_obs.update({"depth_obs": {k: v.to(self.device).unsqueeze(0) for k, v in depth_obs["depth_obs"].items()}})
|
| 55 |
+
|
| 56 |
+
obs_dict = {**rgb_obs, **state_obs, **depth_obs}
|
| 57 |
+
obs_dict["robot_obs_raw"] = torch.from_numpy(obs["robot_obs"]).to(self.device)
|
| 58 |
+
return obs_dict
|
| 59 |
+
|
| 60 |
+
def step(
|
| 61 |
+
self, action_tensor: torch.Tensor
|
| 62 |
+
) -> Tuple[Dict[str, Union[torch.Tensor, Tuple[torch.Tensor, ...]]], int, bool, Dict]:
|
| 63 |
+
if self.relative_actions:
|
| 64 |
+
action = action_tensor.squeeze().cpu().detach().numpy()
|
| 65 |
+
assert len(action) == 7
|
| 66 |
+
else:
|
| 67 |
+
if action_tensor.shape[-1] == 7:
|
| 68 |
+
slice_ids = [3, 6]
|
| 69 |
+
elif action_tensor.shape[-1] == 8:
|
| 70 |
+
slice_ids = [3, 7]
|
| 71 |
+
else:
|
| 72 |
+
logger.error("actions are required to have length 8 (for euler angles) or 9 (for quaternions)")
|
| 73 |
+
raise NotImplementedError
|
| 74 |
+
action = np.split(action_tensor.squeeze().cpu().detach().numpy(), slice_ids)
|
| 75 |
+
action[-1] = 1 if action[-1] > 0 else -1
|
| 76 |
+
o, r, d, i = self.env.step(action)
|
| 77 |
+
|
| 78 |
+
obs = self.transform_observation(o)
|
| 79 |
+
return obs, r, d, i
|
| 80 |
+
|
| 81 |
+
def reset(
|
| 82 |
+
self,
|
| 83 |
+
reset_info: Dict[str, Any] = None,
|
| 84 |
+
batch_idx: int = 0,
|
| 85 |
+
seq_idx: int = 0,
|
| 86 |
+
scene_obs: Any = None,
|
| 87 |
+
robot_obs: Any = None,
|
| 88 |
+
) -> Dict[str, Union[torch.Tensor, Tuple[torch.Tensor, ...]]]:
|
| 89 |
+
if reset_info is not None:
|
| 90 |
+
obs = self.env.reset(
|
| 91 |
+
robot_obs=reset_info["robot_obs"][batch_idx, seq_idx],
|
| 92 |
+
scene_obs=reset_info["scene_obs"][batch_idx, seq_idx],
|
| 93 |
+
)
|
| 94 |
+
elif scene_obs is not None or robot_obs is not None:
|
| 95 |
+
obs = self.env.reset(scene_obs=scene_obs, robot_obs=robot_obs)
|
| 96 |
+
else:
|
| 97 |
+
obs = self.env.reset()
|
| 98 |
+
|
| 99 |
+
return self.transform_observation(obs)
|
| 100 |
+
|
| 101 |
+
def get_info(self):
|
| 102 |
+
return self.env.get_info()
|
| 103 |
+
|
| 104 |
+
def get_obs(self):
|
| 105 |
+
obs = self.env.get_obs()
|
| 106 |
+
return self.transform_observation(obs)
|
aloha_robot_project/calvin/calvin_env/calvin_env/envs/play_table_env.py
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
from math import pi
|
| 3 |
+
import os
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
import pickle
|
| 6 |
+
import pkgutil
|
| 7 |
+
import re
|
| 8 |
+
import sys
|
| 9 |
+
import time
|
| 10 |
+
|
| 11 |
+
import cv2
|
| 12 |
+
import gym
|
| 13 |
+
import gym.utils
|
| 14 |
+
import gym.utils.seeding
|
| 15 |
+
import hydra
|
| 16 |
+
import numpy as np
|
| 17 |
+
import pybullet as p
|
| 18 |
+
import pybullet_utils.bullet_client as bc
|
| 19 |
+
|
| 20 |
+
import calvin_env
|
| 21 |
+
from calvin_env.utils.utils import FpsController, get_git_commit_hash
|
| 22 |
+
|
| 23 |
+
# A logger for this file
|
| 24 |
+
log = logging.getLogger(__name__)
|
| 25 |
+
from rich.traceback import install
|
| 26 |
+
|
| 27 |
+
install(show_locals=True)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class PlayTableSimEnv(gym.Env):
|
| 31 |
+
def __init__(
|
| 32 |
+
self,
|
| 33 |
+
robot_cfg,
|
| 34 |
+
seed,
|
| 35 |
+
use_vr,
|
| 36 |
+
bullet_time_step,
|
| 37 |
+
cameras,
|
| 38 |
+
show_gui,
|
| 39 |
+
scene_cfg,
|
| 40 |
+
use_scene_info,
|
| 41 |
+
use_egl,
|
| 42 |
+
control_freq=30,
|
| 43 |
+
):
|
| 44 |
+
self.p = p
|
| 45 |
+
# for calculation of FPS
|
| 46 |
+
self.t = time.time()
|
| 47 |
+
self.prev_time = time.time()
|
| 48 |
+
self.fps_controller = FpsController(bullet_time_step)
|
| 49 |
+
self.use_vr = use_vr
|
| 50 |
+
self.show_gui = show_gui
|
| 51 |
+
self.use_scene_info = use_scene_info
|
| 52 |
+
self.cid = -1
|
| 53 |
+
self.ownsPhysicsClient = False
|
| 54 |
+
self.use_egl = use_egl
|
| 55 |
+
self.control_freq = control_freq
|
| 56 |
+
self.action_repeat = int(bullet_time_step // control_freq)
|
| 57 |
+
render_width = max([cameras[cam].width for cam in cameras]) if cameras else None
|
| 58 |
+
render_height = max([cameras[cam].height for cam in cameras]) if cameras else None
|
| 59 |
+
self.initialize_bullet(bullet_time_step, render_width, render_height)
|
| 60 |
+
self.np_random = None
|
| 61 |
+
self.seed(seed)
|
| 62 |
+
self.robot = hydra.utils.instantiate(robot_cfg, cid=self.cid)
|
| 63 |
+
self.scene = hydra.utils.instantiate(scene_cfg, p=self.p, cid=self.cid, np_random=self.np_random)
|
| 64 |
+
|
| 65 |
+
# Load Env
|
| 66 |
+
self.load()
|
| 67 |
+
|
| 68 |
+
# init cameras after scene is loaded to have robot id available
|
| 69 |
+
self.cameras = [
|
| 70 |
+
hydra.utils.instantiate(
|
| 71 |
+
cameras[name], cid=self.cid, robot_id=self.robot.robot_uid, objects=self.scene.get_objects()
|
| 72 |
+
)
|
| 73 |
+
for name in cameras
|
| 74 |
+
]
|
| 75 |
+
log.info(f"Using calvin_env with commit {get_git_commit_hash(Path(calvin_env.__file__))}.")
|
| 76 |
+
|
| 77 |
+
def __del__(self):
|
| 78 |
+
self.close()
|
| 79 |
+
|
| 80 |
+
# From pybullet gym_manipulator_envs code
|
| 81 |
+
# https://github.com/bulletphysics/bullet3/blob/master/examples/pybullet/gym/pybullet_envs/gym_manipulator_envs.py
|
| 82 |
+
def initialize_bullet(self, bullet_time_step, render_width, render_height):
|
| 83 |
+
if self.cid < 0:
|
| 84 |
+
self.ownsPhysicsClient = True
|
| 85 |
+
if self.use_vr:
|
| 86 |
+
self.p = bc.BulletClient(connection_mode=p.SHARED_MEMORY)
|
| 87 |
+
cid = self.p._client
|
| 88 |
+
if cid < 0:
|
| 89 |
+
log.error("Failed to connect to SHARED_MEMORY bullet server.\n" " Is it running?")
|
| 90 |
+
sys.exit(1)
|
| 91 |
+
self.p.setRealTimeSimulation(enableRealTimeSimulation=1, physicsClientId=cid)
|
| 92 |
+
elif self.show_gui:
|
| 93 |
+
self.p = bc.BulletClient(connection_mode=p.GUI)
|
| 94 |
+
cid = self.p._client
|
| 95 |
+
if cid < 0:
|
| 96 |
+
log.error("Failed to connect to GUI.")
|
| 97 |
+
elif self.use_egl:
|
| 98 |
+
options = f"--width={render_width} --height={render_height}"
|
| 99 |
+
self.p = p
|
| 100 |
+
cid = self.p.connect(p.DIRECT, options=options)
|
| 101 |
+
p.configureDebugVisualizer(p.COV_ENABLE_GUI, 0, physicsClientId=cid)
|
| 102 |
+
p.configureDebugVisualizer(p.COV_ENABLE_SEGMENTATION_MARK_PREVIEW, 0, physicsClientId=cid)
|
| 103 |
+
p.configureDebugVisualizer(p.COV_ENABLE_DEPTH_BUFFER_PREVIEW, 0, physicsClientId=cid)
|
| 104 |
+
p.configureDebugVisualizer(p.COV_ENABLE_RGB_BUFFER_PREVIEW, 0, physicsClientId=cid)
|
| 105 |
+
egl = pkgutil.get_loader("eglRenderer")
|
| 106 |
+
log.info("Loading EGL plugin (may segfault on misconfigured systems)...")
|
| 107 |
+
if egl:
|
| 108 |
+
plugin = p.loadPlugin(egl.get_filename(), "_eglRendererPlugin")
|
| 109 |
+
else:
|
| 110 |
+
plugin = p.loadPlugin("eglRendererPlugin")
|
| 111 |
+
if plugin < 0:
|
| 112 |
+
log.error("\nPlugin Failed to load!\n")
|
| 113 |
+
sys.exit()
|
| 114 |
+
# set environment variable for tacto renderer
|
| 115 |
+
os.environ["PYOPENGL_PLATFORM"] = "egl"
|
| 116 |
+
log.info("Successfully loaded egl plugin")
|
| 117 |
+
else:
|
| 118 |
+
self.p = bc.BulletClient(connection_mode=p.DIRECT)
|
| 119 |
+
cid = self.p._client
|
| 120 |
+
if cid < 0:
|
| 121 |
+
log.error("Failed to start DIRECT bullet mode.")
|
| 122 |
+
log.info(f"Connected to server with id: {cid}")
|
| 123 |
+
|
| 124 |
+
self.cid = cid
|
| 125 |
+
self.p.resetSimulation(physicsClientId=self.cid)
|
| 126 |
+
self.p.setPhysicsEngineParameter(deterministicOverlappingPairs=1, physicsClientId=self.cid)
|
| 127 |
+
self.p.configureDebugVisualizer(self.p.COV_ENABLE_GUI, 0)
|
| 128 |
+
log.info(f"Connected to server with id: {self.cid}")
|
| 129 |
+
self.p.setTimeStep(1.0 / bullet_time_step, physicsClientId=self.cid)
|
| 130 |
+
return cid
|
| 131 |
+
|
| 132 |
+
def load(self):
|
| 133 |
+
log.info("Resetting simulation")
|
| 134 |
+
self.p.resetSimulation(physicsClientId=self.cid)
|
| 135 |
+
log.info("Setting gravity")
|
| 136 |
+
self.p.setGravity(0, 0, -9.8, physicsClientId=self.cid)
|
| 137 |
+
|
| 138 |
+
self.robot.load()
|
| 139 |
+
self.scene.load()
|
| 140 |
+
|
| 141 |
+
def close(self):
|
| 142 |
+
if self.ownsPhysicsClient:
|
| 143 |
+
print("disconnecting id %d from server" % self.cid)
|
| 144 |
+
if self.cid >= 0 and self.p is not None:
|
| 145 |
+
try:
|
| 146 |
+
self.p.disconnect(physicsClientId=self.cid)
|
| 147 |
+
except TypeError:
|
| 148 |
+
pass
|
| 149 |
+
|
| 150 |
+
else:
|
| 151 |
+
print("does not own physics client id")
|
| 152 |
+
|
| 153 |
+
def render(self, mode="human"):
|
| 154 |
+
"""render is gym compatibility function"""
|
| 155 |
+
rgb_obs, depth_obs = self.get_camera_obs()
|
| 156 |
+
if mode == "human":
|
| 157 |
+
if "rgb_static" in rgb_obs:
|
| 158 |
+
img = rgb_obs["rgb_static"][:, :, ::-1]
|
| 159 |
+
cv2.imshow("simulation cam", cv2.resize(img, (500, 500)))
|
| 160 |
+
if "rgb_gripper" in rgb_obs:
|
| 161 |
+
img2 = rgb_obs["rgb_gripper"][:, :, ::-1]
|
| 162 |
+
cv2.imshow("gripper cam", cv2.resize(img2, (500, 500)))
|
| 163 |
+
cv2.waitKey(1)
|
| 164 |
+
elif mode == "rgb_array":
|
| 165 |
+
assert "rgb_static" in rgb_obs, "Environment does not have static camera"
|
| 166 |
+
return rgb_obs["rgb_static"]
|
| 167 |
+
else:
|
| 168 |
+
raise NotImplementedError
|
| 169 |
+
|
| 170 |
+
def get_scene_info(self):
|
| 171 |
+
return self.scene.get_info()
|
| 172 |
+
|
| 173 |
+
def reset(self, robot_obs=None, scene_obs=None):
|
| 174 |
+
self.scene.reset(scene_obs)
|
| 175 |
+
self.robot.reset(robot_obs)
|
| 176 |
+
self.p.stepSimulation(physicsClientId=self.cid)
|
| 177 |
+
return self.get_obs()
|
| 178 |
+
|
| 179 |
+
def seed(self, seed=None):
|
| 180 |
+
self.np_random, seed = gym.utils.seeding.np_random(seed)
|
| 181 |
+
# self.robot.np_random = self.np_random # use the same np_randomizer for robot as for env
|
| 182 |
+
return [seed]
|
| 183 |
+
|
| 184 |
+
def get_camera_obs(self):
|
| 185 |
+
assert self.cameras is not None
|
| 186 |
+
rgb_obs = {}
|
| 187 |
+
depth_obs = {}
|
| 188 |
+
for cam in self.cameras:
|
| 189 |
+
rgb, depth = cam.render()
|
| 190 |
+
rgb_obs[f"rgb_{cam.name}"] = rgb
|
| 191 |
+
depth_obs[f"depth_{cam.name}"] = depth
|
| 192 |
+
return rgb_obs, depth_obs
|
| 193 |
+
|
| 194 |
+
def get_obs(self):
|
| 195 |
+
"""Collect camera, robot and scene observations."""
|
| 196 |
+
rgb_obs, depth_obs = self.get_camera_obs()
|
| 197 |
+
obs = {"rgb_obs": rgb_obs, "depth_obs": depth_obs}
|
| 198 |
+
obs.update(self.get_state_obs())
|
| 199 |
+
return obs
|
| 200 |
+
|
| 201 |
+
def get_state_obs(self):
|
| 202 |
+
"""
|
| 203 |
+
Collect state observation dict
|
| 204 |
+
--state_obs
|
| 205 |
+
--robot_obs
|
| 206 |
+
--robot_state_full
|
| 207 |
+
-- [tcp_pos, tcp_orn, gripper_opening_width]
|
| 208 |
+
--gripper_opening_width
|
| 209 |
+
--arm_joint_states
|
| 210 |
+
--gripper_action}
|
| 211 |
+
--scene_obs
|
| 212 |
+
"""
|
| 213 |
+
robot_obs, robot_info = self.robot.get_observation()
|
| 214 |
+
scene_obs = self.scene.get_obs()
|
| 215 |
+
obs = {"robot_obs": robot_obs, "scene_obs": scene_obs}
|
| 216 |
+
return obs
|
| 217 |
+
|
| 218 |
+
def get_info(self):
|
| 219 |
+
_, robot_info = self.robot.get_observation()
|
| 220 |
+
info = {"robot_info": robot_info}
|
| 221 |
+
if self.use_scene_info:
|
| 222 |
+
info["scene_info"] = self.scene.get_info()
|
| 223 |
+
return info
|
| 224 |
+
|
| 225 |
+
def step(self, action):
|
| 226 |
+
# in vr mode real time simulation is enabled, thus p.stepSimulation() does not have to be called manually
|
| 227 |
+
if self.use_vr:
|
| 228 |
+
log.debug(f"SIM FPS: {(1 / (time.time() - self.t)):.0f}")
|
| 229 |
+
self.t = time.time()
|
| 230 |
+
current_time = time.time()
|
| 231 |
+
delta_t = current_time - self.prev_time
|
| 232 |
+
if delta_t >= (1.0 / self.control_freq):
|
| 233 |
+
log.debug(f"Act FPS: {1 / delta_t:.0f}")
|
| 234 |
+
self.prev_time = time.time()
|
| 235 |
+
self.robot.apply_action(action)
|
| 236 |
+
self.fps_controller.step()
|
| 237 |
+
# for RL call step simulation repeat
|
| 238 |
+
else:
|
| 239 |
+
self.robot.apply_action(action)
|
| 240 |
+
for i in range(self.action_repeat):
|
| 241 |
+
self.p.stepSimulation(physicsClientId=self.cid)
|
| 242 |
+
self.scene.step()
|
| 243 |
+
obs = self.get_obs()
|
| 244 |
+
info = self.get_info()
|
| 245 |
+
# obs, reward, done, info
|
| 246 |
+
return obs, 0, False, info
|
| 247 |
+
|
| 248 |
+
def reset_from_storage(self, filename):
|
| 249 |
+
"""
|
| 250 |
+
Args:
|
| 251 |
+
filename: file to load from.
|
| 252 |
+
Returns:
|
| 253 |
+
observation
|
| 254 |
+
"""
|
| 255 |
+
with open(filename, "rb") as file:
|
| 256 |
+
data = pickle.load(file)
|
| 257 |
+
|
| 258 |
+
self.robot.reset_from_storage(data["robot"])
|
| 259 |
+
self.scene.reset_from_storage(data["scene"])
|
| 260 |
+
|
| 261 |
+
self.p.stepSimulation(physicsClientId=self.cid)
|
| 262 |
+
|
| 263 |
+
return data["state_obs"], data["done"], data["info"]
|
| 264 |
+
|
| 265 |
+
def serialize(self):
|
| 266 |
+
data = {"time": time.time_ns() / (10**9), "robot": self.robot.serialize(), "scene": self.scene.serialize()}
|
| 267 |
+
return data
|
| 268 |
+
|
| 269 |
+
|
| 270 |
+
def get_env(dataset_path, obs_space=None, show_gui=True, **kwargs):
|
| 271 |
+
from pathlib import Path
|
| 272 |
+
|
| 273 |
+
from omegaconf import OmegaConf
|
| 274 |
+
|
| 275 |
+
render_conf = OmegaConf.load(Path(dataset_path) / ".hydra" / "merged_config.yaml")
|
| 276 |
+
|
| 277 |
+
if obs_space is not None:
|
| 278 |
+
exclude_keys = set(render_conf.cameras.keys()) - {
|
| 279 |
+
re.split("_", key)[1] for key in obs_space["rgb_obs"] + obs_space["depth_obs"]
|
| 280 |
+
}
|
| 281 |
+
for k in exclude_keys:
|
| 282 |
+
del render_conf.cameras[k]
|
| 283 |
+
if "scene" in kwargs:
|
| 284 |
+
scene_cfg = OmegaConf.load(Path(calvin_env.__file__).parents[1] / "conf/scene" / f"{kwargs['scene']}.yaml")
|
| 285 |
+
OmegaConf.merge(render_conf, scene_cfg)
|
| 286 |
+
if not hydra.core.global_hydra.GlobalHydra.instance().is_initialized():
|
| 287 |
+
hydra.initialize(".")
|
| 288 |
+
env = hydra.utils.instantiate(render_conf.env, show_gui=show_gui, use_vr=False, use_scene_info=True)
|
| 289 |
+
return env
|
| 290 |
+
|
| 291 |
+
|
| 292 |
+
@hydra.main(config_path="../../conf", config_name="config_data_collection")
|
| 293 |
+
def run_env(cfg):
|
| 294 |
+
env = hydra.utils.instantiate(cfg.env, show_gui=True, use_vr=False, use_scene_info=True)
|
| 295 |
+
|
| 296 |
+
env.reset()
|
| 297 |
+
while True:
|
| 298 |
+
env.step(np.array((0.0, 0, 0, 0, 0, 1)))
|
| 299 |
+
# env.render()
|
| 300 |
+
time.sleep(0.01)
|
| 301 |
+
|
| 302 |
+
|
| 303 |
+
if __name__ == "__main__":
|
| 304 |
+
run_env()
|
aloha_robot_project/calvin/calvin_env/calvin_env/envs/tasks.py
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from functools import partial
|
| 2 |
+
from typing import Dict, Set
|
| 3 |
+
|
| 4 |
+
import numpy as np
|
| 5 |
+
from omegaconf import ListConfig
|
| 6 |
+
from scipy.spatial.transform import Rotation as R
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class Tasks:
|
| 10 |
+
def __init__(self, tasks):
|
| 11 |
+
"""
|
| 12 |
+
A task is defined as a specific change between the start_info and end_info dictionaries.
|
| 13 |
+
Use config file in conf/tasks/ to define tasks using the base task functions defined in this class
|
| 14 |
+
"""
|
| 15 |
+
# register task functions from config file
|
| 16 |
+
self.tasks = {name: partial(getattr(self, args[0]), *args[1:]) for name, args in dict(tasks).items()}
|
| 17 |
+
# dictionary mapping from task name to task id
|
| 18 |
+
self.task_to_id = {name: i for i, name in enumerate(self.tasks.keys())}
|
| 19 |
+
# dictionary mapping from task id to task name
|
| 20 |
+
self.id_to_task = {i: name for i, name in enumerate(self.tasks.keys())}
|
| 21 |
+
|
| 22 |
+
def get_task_info(self, start_info: Dict, end_info: Dict) -> Set:
|
| 23 |
+
"""
|
| 24 |
+
start_info: dict with scene info and robot info
|
| 25 |
+
end_info: dict with scene info and robot info
|
| 26 |
+
returns set with achieved tasks
|
| 27 |
+
"""
|
| 28 |
+
# call functions that are registered in self.tasks
|
| 29 |
+
return {
|
| 30 |
+
task_name
|
| 31 |
+
for task_name, function in self.tasks.items()
|
| 32 |
+
if function(start_info=start_info, end_info=end_info)
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
def get_task_info_for_set(self, start_info: Dict, end_info: Dict, task_filter: Set) -> Set:
|
| 36 |
+
"""
|
| 37 |
+
start_info: dict with scene info and robot info
|
| 38 |
+
end_info: dict with scene info and robot info
|
| 39 |
+
task_filter: set with task names to check
|
| 40 |
+
returns set with achieved tasks
|
| 41 |
+
"""
|
| 42 |
+
# call functions that are registered in self.tasks
|
| 43 |
+
return {
|
| 44 |
+
task_name
|
| 45 |
+
for task_name, function in self.tasks.items()
|
| 46 |
+
if task_name in task_filter and function(start_info=start_info, end_info=end_info)
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
@property
|
| 50 |
+
def num_tasks(self):
|
| 51 |
+
return len(self.tasks)
|
| 52 |
+
|
| 53 |
+
@staticmethod
|
| 54 |
+
def rotate_object(
|
| 55 |
+
obj_name, z_degrees, x_y_threshold=30, z_threshold=180, movement_threshold=0.1, start_info=None, end_info=None
|
| 56 |
+
):
|
| 57 |
+
"""
|
| 58 |
+
Returns True if the object with obj_name was rotated more than z_degrees degrees around the z-axis while not
|
| 59 |
+
being rotated more than x_y_threshold degrees around the x or y axis.
|
| 60 |
+
z_degrees is negative for clockwise rotations and positive for counter-clockwise rotations.
|
| 61 |
+
"""
|
| 62 |
+
obj_start_info = start_info["scene_info"]["movable_objects"][obj_name]
|
| 63 |
+
obj_end_info = end_info["scene_info"]["movable_objects"][obj_name]
|
| 64 |
+
start_orn = R.from_quat(obj_start_info["current_orn"])
|
| 65 |
+
end_orn = R.from_quat(obj_end_info["current_orn"])
|
| 66 |
+
rotation = end_orn * start_orn.inv()
|
| 67 |
+
x, y, z = rotation.as_euler("xyz", degrees=True)
|
| 68 |
+
|
| 69 |
+
start_pos = np.array(obj_start_info["current_pos"])
|
| 70 |
+
end_pos = np.array(obj_end_info["current_pos"])
|
| 71 |
+
pos_diff = end_pos - start_pos
|
| 72 |
+
if np.linalg.norm(pos_diff) > movement_threshold:
|
| 73 |
+
return False
|
| 74 |
+
|
| 75 |
+
end_contacts = set(c[2] for c in obj_end_info["contacts"])
|
| 76 |
+
robot_uid = {start_info["robot_info"]["uid"]}
|
| 77 |
+
if len(end_contacts - robot_uid) == 0:
|
| 78 |
+
return False
|
| 79 |
+
|
| 80 |
+
if z_degrees > 0:
|
| 81 |
+
return z_degrees < z < z_threshold and abs(x) < x_y_threshold and abs(y) < x_y_threshold
|
| 82 |
+
else:
|
| 83 |
+
return z_degrees > z > -z_threshold and abs(x) < x_y_threshold and abs(y) < x_y_threshold
|
| 84 |
+
|
| 85 |
+
@staticmethod
|
| 86 |
+
def push_object(obj_name, x_direction, y_direction, start_info, end_info):
|
| 87 |
+
"""
|
| 88 |
+
Returns True if the object with 'obj_name' was moved more than 'x_direction' meters in x direction
|
| 89 |
+
(or 'y_direction' meters in y direction analogously).
|
| 90 |
+
Note that currently x and y pushes are mutually exclusive, meaning that one of the arguments has to be 0.
|
| 91 |
+
The sign matters, e.g. pushing an object to the right when facing the table coincides with a movement in
|
| 92 |
+
positive x-direction.
|
| 93 |
+
"""
|
| 94 |
+
assert x_direction * y_direction == 0 and x_direction + y_direction != 0
|
| 95 |
+
obj_start_info = start_info["scene_info"]["movable_objects"][obj_name]
|
| 96 |
+
obj_end_info = end_info["scene_info"]["movable_objects"][obj_name]
|
| 97 |
+
start_pos = np.array(obj_start_info["current_pos"])
|
| 98 |
+
end_pos = np.array(obj_end_info["current_pos"])
|
| 99 |
+
pos_diff = end_pos - start_pos
|
| 100 |
+
|
| 101 |
+
robot_uid = start_info["robot_info"]["uid"]
|
| 102 |
+
# contacts excluding robot
|
| 103 |
+
start_contacts = set((c[2], c[4]) for c in obj_start_info["contacts"] if c[2] != robot_uid)
|
| 104 |
+
end_contacts = set((c[2], c[4]) for c in obj_end_info["contacts"] if c[2] != robot_uid)
|
| 105 |
+
|
| 106 |
+
# computing set difference to check if object had surface contact (excluding robot) at both times
|
| 107 |
+
surface_contact = len(start_contacts) > 0 and len(end_contacts) > 0 and start_contacts <= end_contacts
|
| 108 |
+
if not surface_contact:
|
| 109 |
+
return False
|
| 110 |
+
|
| 111 |
+
if x_direction > 0:
|
| 112 |
+
return pos_diff[0] > x_direction
|
| 113 |
+
elif x_direction < 0:
|
| 114 |
+
return pos_diff[0] < x_direction
|
| 115 |
+
|
| 116 |
+
if y_direction > 0:
|
| 117 |
+
return pos_diff[1] > y_direction
|
| 118 |
+
elif y_direction < 0:
|
| 119 |
+
return pos_diff[1] < y_direction
|
| 120 |
+
|
| 121 |
+
@staticmethod
|
| 122 |
+
def lift_object(obj_name, z_direction, surface_body=None, surface_link=None, start_info=None, end_info=None):
|
| 123 |
+
"""
|
| 124 |
+
Returns True if the object with 'obj_name' was grasped by the robot and lifted more than 'z_direction' meters.
|
| 125 |
+
"""
|
| 126 |
+
assert z_direction > 0
|
| 127 |
+
obj_start_info = start_info["scene_info"]["movable_objects"][obj_name]
|
| 128 |
+
obj_end_info = end_info["scene_info"]["movable_objects"][obj_name]
|
| 129 |
+
|
| 130 |
+
start_pos = np.array(obj_start_info["current_pos"])
|
| 131 |
+
end_pos = np.array(obj_end_info["current_pos"])
|
| 132 |
+
pos_diff = end_pos - start_pos
|
| 133 |
+
z_diff = pos_diff[2]
|
| 134 |
+
|
| 135 |
+
robot_uid = start_info["robot_info"]["uid"]
|
| 136 |
+
start_contacts = set(c[2] for c in obj_start_info["contacts"])
|
| 137 |
+
end_contacts = set(c[2] for c in obj_end_info["contacts"])
|
| 138 |
+
|
| 139 |
+
surface_criterion = True
|
| 140 |
+
if surface_body and surface_link is None:
|
| 141 |
+
surface_uid = start_info["scene_info"]["fixed_objects"][surface_body]["uid"]
|
| 142 |
+
surface_criterion = surface_uid in start_contacts
|
| 143 |
+
elif surface_body and surface_link:
|
| 144 |
+
surface_uid = start_info["scene_info"]["fixed_objects"][surface_body]["uid"]
|
| 145 |
+
surface_link_id = start_info["scene_info"]["fixed_objects"][surface_body]["links"][surface_link]
|
| 146 |
+
start_contacts_links = set((c[2], c[4]) for c in obj_start_info["contacts"])
|
| 147 |
+
surface_criterion = (surface_uid, surface_link_id) in start_contacts_links
|
| 148 |
+
|
| 149 |
+
return (
|
| 150 |
+
z_diff > z_direction
|
| 151 |
+
# and robot_uid not in start_contacts
|
| 152 |
+
and robot_uid in end_contacts
|
| 153 |
+
and len(end_contacts) == 1
|
| 154 |
+
and surface_criterion
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
@staticmethod
|
| 158 |
+
def place_object(dest_body, dest_link=None, start_info=None, end_info=None):
|
| 159 |
+
"""
|
| 160 |
+
Returns True if the object that the robot has currently lifted is placed on the body 'dest_body'
|
| 161 |
+
(on 'dest_link' if provided).
|
| 162 |
+
The robot may not touch the object after placing.
|
| 163 |
+
"""
|
| 164 |
+
robot_uid = start_info["robot_info"]["uid"]
|
| 165 |
+
|
| 166 |
+
robot_contacts_start = set(c[2] for c in start_info["robot_info"]["contacts"])
|
| 167 |
+
robot_contacts_end = set(c[2] for c in end_info["robot_info"]["contacts"])
|
| 168 |
+
if not len(robot_contacts_start) == 1:
|
| 169 |
+
return False
|
| 170 |
+
obj_uid = list(robot_contacts_start)[0]
|
| 171 |
+
|
| 172 |
+
if obj_uid in robot_contacts_end:
|
| 173 |
+
return False
|
| 174 |
+
_obj_name = [k for k, v in start_info["scene_info"]["movable_objects"].items() if v["uid"] == obj_uid]
|
| 175 |
+
if not len(_obj_name) == 1:
|
| 176 |
+
return False
|
| 177 |
+
obj_name = _obj_name[0]
|
| 178 |
+
|
| 179 |
+
dest_uid = end_info["scene_info"]["fixed_objects"][dest_body]["uid"]
|
| 180 |
+
|
| 181 |
+
object_contacts_start = set(c[2] for c in start_info["scene_info"]["movable_objects"][obj_name]["contacts"])
|
| 182 |
+
if dest_link is None:
|
| 183 |
+
object_contacts_end = set(c[2] for c in end_info["scene_info"]["movable_objects"][obj_name]["contacts"])
|
| 184 |
+
return (
|
| 185 |
+
robot_uid in object_contacts_start
|
| 186 |
+
and len(object_contacts_start) == 1
|
| 187 |
+
and dest_uid in object_contacts_end
|
| 188 |
+
)
|
| 189 |
+
else:
|
| 190 |
+
dest_link_id = end_info["scene_info"]["fixed_objects"][dest_body]["links"][dest_link]
|
| 191 |
+
end_contacts_links = set(
|
| 192 |
+
(c[2], c[4]) for c in end_info["scene_info"]["movable_objects"][obj_name]["contacts"]
|
| 193 |
+
)
|
| 194 |
+
return (
|
| 195 |
+
robot_uid in object_contacts_start
|
| 196 |
+
and len(object_contacts_start) == 1
|
| 197 |
+
and (dest_uid, dest_link_id) in end_contacts_links
|
| 198 |
+
)
|
| 199 |
+
|
| 200 |
+
@staticmethod
|
| 201 |
+
def push_object_into(obj_name, src_body, src_link, dest_body, dest_link, start_info=None, end_info=None):
|
| 202 |
+
"""
|
| 203 |
+
obj_name is either a list of object names or a string
|
| 204 |
+
Returns True if the object / any of the objects changes contact from src_body to dest_body.
|
| 205 |
+
The robot may neither touch the object at start nor end.
|
| 206 |
+
"""
|
| 207 |
+
if isinstance(obj_name, (list, ListConfig)):
|
| 208 |
+
return any(
|
| 209 |
+
Tasks.push_object_into(ob, src_body, src_link, dest_body, dest_link, start_info, end_info)
|
| 210 |
+
for ob in obj_name
|
| 211 |
+
)
|
| 212 |
+
robot_uid = start_info["robot_info"]["uid"]
|
| 213 |
+
|
| 214 |
+
src_uid = start_info["scene_info"]["fixed_objects"][src_body]["uid"]
|
| 215 |
+
src_link_id = start_info["scene_info"]["fixed_objects"][src_body]["links"][src_link]
|
| 216 |
+
dest_uid = end_info["scene_info"]["fixed_objects"][dest_body]["uid"]
|
| 217 |
+
dest_link_id = end_info["scene_info"]["fixed_objects"][dest_body]["links"][dest_link]
|
| 218 |
+
|
| 219 |
+
start_contacts = set((c[2], c[4]) for c in start_info["scene_info"]["movable_objects"][obj_name]["contacts"])
|
| 220 |
+
end_contacts = set((c[2], c[4]) for c in end_info["scene_info"]["movable_objects"][obj_name]["contacts"])
|
| 221 |
+
return (
|
| 222 |
+
robot_uid not in start_contacts | end_contacts
|
| 223 |
+
and len(start_contacts) == 1
|
| 224 |
+
and (src_uid, src_link_id) in start_contacts
|
| 225 |
+
and (dest_uid, dest_link_id) in end_contacts
|
| 226 |
+
)
|
| 227 |
+
|
| 228 |
+
@staticmethod
|
| 229 |
+
def move_door_abs(joint_name, start_threshold, end_threshold, start_info, end_info):
|
| 230 |
+
"""
|
| 231 |
+
Returns True if the joint specified by 'obj_name' and 'joint_name' (e.g. a door or drawer)
|
| 232 |
+
is moved from at least 'start_threshold' to 'end_threshold'.
|
| 233 |
+
"""
|
| 234 |
+
start_joint_state = start_info["scene_info"]["doors"][joint_name]["current_state"][0]
|
| 235 |
+
end_joint_state = end_info["scene_info"]["doors"][joint_name]["current_state"][0]
|
| 236 |
+
|
| 237 |
+
if start_threshold < end_threshold:
|
| 238 |
+
return start_joint_state < start_threshold < end_threshold < end_joint_state
|
| 239 |
+
elif start_threshold > end_threshold:
|
| 240 |
+
return start_joint_state > start_threshold > end_threshold > end_joint_state
|
| 241 |
+
else:
|
| 242 |
+
raise ValueError
|
| 243 |
+
|
| 244 |
+
@staticmethod
|
| 245 |
+
def move_door_rel(joint_name, threshold, start_info, end_info):
|
| 246 |
+
"""
|
| 247 |
+
Returns True if the joint specified by 'obj_name' and 'joint_name' (e.g. a door or drawer)
|
| 248 |
+
is moved from at least 'start_threshold' to 'end_threshold'.
|
| 249 |
+
"""
|
| 250 |
+
start_joint_state = start_info["scene_info"]["doors"][joint_name]["current_state"]
|
| 251 |
+
end_joint_state = end_info["scene_info"]["doors"][joint_name]["current_state"]
|
| 252 |
+
|
| 253 |
+
return (
|
| 254 |
+
0 < threshold < end_joint_state - start_joint_state or 0 > threshold > end_joint_state - start_joint_state
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
@staticmethod
|
| 258 |
+
def toggle_light(light_name, start_state, end_state, start_info, end_info):
|
| 259 |
+
return (
|
| 260 |
+
start_info["scene_info"]["lights"][light_name]["logical_state"] == start_state
|
| 261 |
+
and end_info["scene_info"]["lights"][light_name]["logical_state"] == end_state
|
| 262 |
+
)
|
| 263 |
+
|
| 264 |
+
@staticmethod
|
| 265 |
+
def stack_objects(max_vel=1, start_info=None, end_info=None):
|
| 266 |
+
obj_uids = set(obj["uid"] for obj in start_info["scene_info"]["movable_objects"].values())
|
| 267 |
+
|
| 268 |
+
for obj_name in start_info["scene_info"]["movable_objects"]:
|
| 269 |
+
obj_start_info = start_info["scene_info"]["movable_objects"][obj_name]
|
| 270 |
+
obj_end_info = end_info["scene_info"]["movable_objects"][obj_name]
|
| 271 |
+
obj_start_contacts = set(c[2] for c in obj_start_info["contacts"])
|
| 272 |
+
obj_end_contacts = set(c[2] for c in obj_end_info["contacts"])
|
| 273 |
+
|
| 274 |
+
if (
|
| 275 |
+
not len(obj_uids & obj_start_contacts)
|
| 276 |
+
and len(obj_uids & obj_end_contacts)
|
| 277 |
+
and not len(obj_end_contacts - obj_uids)
|
| 278 |
+
):
|
| 279 |
+
# object velocity may not exceed max_vel for successful stack
|
| 280 |
+
if np.all(np.abs(obj_end_info["current_lin_vel"]) < max_vel) and np.all(
|
| 281 |
+
np.abs(obj_end_info["current_ang_vel"]) < max_vel
|
| 282 |
+
):
|
| 283 |
+
return True
|
| 284 |
+
return False
|
| 285 |
+
|
| 286 |
+
@staticmethod
|
| 287 |
+
def unstack_objects(max_vel=1, start_info=None, end_info=None):
|
| 288 |
+
obj_uids = set(obj["uid"] for obj in start_info["scene_info"]["movable_objects"].values())
|
| 289 |
+
|
| 290 |
+
for obj_name in start_info["scene_info"]["movable_objects"]:
|
| 291 |
+
obj_start_info = start_info["scene_info"]["movable_objects"][obj_name]
|
| 292 |
+
obj_end_info = end_info["scene_info"]["movable_objects"][obj_name]
|
| 293 |
+
obj_start_contacts = set(c[2] for c in obj_start_info["contacts"])
|
| 294 |
+
obj_end_contacts = set(c[2] for c in obj_end_info["contacts"])
|
| 295 |
+
|
| 296 |
+
if (
|
| 297 |
+
len(obj_uids & obj_start_contacts)
|
| 298 |
+
and not len(obj_start_contacts - obj_uids)
|
| 299 |
+
and not len(obj_uids & obj_end_contacts)
|
| 300 |
+
):
|
| 301 |
+
# object velocity may not exceed max_vel for successful stack
|
| 302 |
+
if np.all(np.abs(obj_start_info["current_lin_vel"]) < max_vel) and np.all(
|
| 303 |
+
np.abs(obj_start_info["current_ang_vel"]) < max_vel
|
| 304 |
+
):
|
| 305 |
+
return True
|
| 306 |
+
return False
|
aloha_robot_project/calvin/calvin_env/calvin_env/io_utils/data_recorder.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
import multiprocessing as mp
|
| 3 |
+
import os
|
| 4 |
+
import pickle
|
| 5 |
+
import time
|
| 6 |
+
|
| 7 |
+
# A logger for this file
|
| 8 |
+
log = logging.getLogger(__name__)
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class DataRecorder:
|
| 12 |
+
"""
|
| 13 |
+
Collects frame information to file in output-dir with
|
| 14 |
+
filename: <TIMESTAMP>/<FRAME>.pickle
|
| 15 |
+
Saving facility with separate worker thread.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
def __init__(self, env, record_fps, enable_tts):
|
| 19 |
+
"""
|
| 20 |
+
Setup MultiprocessingStorage
|
| 21 |
+
"""
|
| 22 |
+
self.env = env
|
| 23 |
+
self.queue = mp.Queue()
|
| 24 |
+
self.process = mp.Process(target=self.process_queue, name="MultiprocessingStorageWorker")
|
| 25 |
+
self.process.start()
|
| 26 |
+
self.running = True
|
| 27 |
+
self.save_frame_cnt = 0
|
| 28 |
+
log.info("Starting serialization worker process")
|
| 29 |
+
self.prev_time = time.time()
|
| 30 |
+
self.loop_time = 1.0 / record_fps
|
| 31 |
+
self._unsaved_vr_events = []
|
| 32 |
+
self.enable_tts = enable_tts
|
| 33 |
+
if enable_tts:
|
| 34 |
+
import pyttsx3
|
| 35 |
+
|
| 36 |
+
self.tts = pyttsx3.init()
|
| 37 |
+
self.tts.setProperty("rate", 175)
|
| 38 |
+
self.prev_done = False
|
| 39 |
+
self.current_episode_filenames = []
|
| 40 |
+
|
| 41 |
+
def step(self, prev_vr_event, state_obs, done, info):
|
| 42 |
+
self._unsaved_vr_events.extend(prev_vr_event)
|
| 43 |
+
current_time = time.time()
|
| 44 |
+
delta_t = current_time - self.prev_time
|
| 45 |
+
if delta_t >= self.loop_time or done:
|
| 46 |
+
log.debug(f"Record FPS: {1 / delta_t:.0f}")
|
| 47 |
+
self.prev_time = time.time()
|
| 48 |
+
file_path = f"{str(self.save_frame_cnt).zfill(12)}.pickle"
|
| 49 |
+
self.save(file_path, self._unsaved_vr_events, state_obs, done, info)
|
| 50 |
+
if self.prev_done and not done:
|
| 51 |
+
self.current_episode_filenames = []
|
| 52 |
+
self.current_episode_filenames.append(file_path)
|
| 53 |
+
|
| 54 |
+
# file_path_state = f"{str(self.save_frame_cnt).zfill(12)}_state.pickle"
|
| 55 |
+
# with open(file_path_state, 'wb') as file:
|
| 56 |
+
# pickle.dump(state_obs, file)
|
| 57 |
+
|
| 58 |
+
self._unsaved_vr_events = []
|
| 59 |
+
self.save_frame_cnt += 1
|
| 60 |
+
self.prev_done = done
|
| 61 |
+
|
| 62 |
+
def save(self, filename, vr_events, state_obs, done, info):
|
| 63 |
+
"""
|
| 64 |
+
Extract dataFrame from pybullet and enqueue for worker thread.
|
| 65 |
+
|
| 66 |
+
Args:
|
| 67 |
+
filename: path to file
|
| 68 |
+
vr_events: vrEvents to attach to data
|
| 69 |
+
state_obs: state observations
|
| 70 |
+
done: true if episode ends
|
| 71 |
+
info: info dict
|
| 72 |
+
|
| 73 |
+
Returns:
|
| 74 |
+
None
|
| 75 |
+
"""
|
| 76 |
+
data = self.env.serialize()
|
| 77 |
+
data["vr_events"] = vr_events
|
| 78 |
+
data["state_obs"] = state_obs
|
| 79 |
+
data["done"] = done
|
| 80 |
+
data["info"] = info
|
| 81 |
+
self.queue.put((filename, data))
|
| 82 |
+
|
| 83 |
+
def delete_episode(self):
|
| 84 |
+
num_frames = len(self.current_episode_filenames)
|
| 85 |
+
if self.enable_tts:
|
| 86 |
+
self.tts.say(f"Deleting last episode with {num_frames} frames")
|
| 87 |
+
self.tts.runAndWait()
|
| 88 |
+
for filename in self.current_episode_filenames:
|
| 89 |
+
os.remove(filename)
|
| 90 |
+
if self.enable_tts:
|
| 91 |
+
self.tts.say("Finished deleting")
|
| 92 |
+
self.tts.runAndWait()
|
| 93 |
+
self.save_frame_cnt -= num_frames
|
| 94 |
+
self.current_episode_filenames = []
|
| 95 |
+
|
| 96 |
+
def process_queue(self):
|
| 97 |
+
"""
|
| 98 |
+
Process function for queue.
|
| 99 |
+
Returns:
|
| 100 |
+
None
|
| 101 |
+
"""
|
| 102 |
+
while True:
|
| 103 |
+
msg = self.queue.get()
|
| 104 |
+
if msg == "QUIT":
|
| 105 |
+
self.running = False
|
| 106 |
+
break
|
| 107 |
+
(filename, data) = msg
|
| 108 |
+
with open(filename, "wb") as file:
|
| 109 |
+
pickle.dump(data, file)
|
| 110 |
+
|
| 111 |
+
def close(self):
|
| 112 |
+
"""
|
| 113 |
+
Tell Worker to shut down.
|
| 114 |
+
Returns:
|
| 115 |
+
None
|
| 116 |
+
"""
|
| 117 |
+
if self.running:
|
| 118 |
+
self.queue.put("QUIT")
|
| 119 |
+
self.process.join()
|
| 120 |
+
|
| 121 |
+
def __enter__(self):
|
| 122 |
+
"""
|
| 123 |
+
with ... as ... : logic
|
| 124 |
+
Returns:
|
| 125 |
+
None
|
| 126 |
+
"""
|
| 127 |
+
return self
|
| 128 |
+
|
| 129 |
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
| 130 |
+
"""
|
| 131 |
+
with ... as ... : logic
|
| 132 |
+
Returns:
|
| 133 |
+
None
|
| 134 |
+
"""
|
| 135 |
+
self.close()
|
aloha_robot_project/calvin/calvin_env/calvin_env/io_utils/vr_input.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import collections
|
| 2 |
+
import logging
|
| 3 |
+
|
| 4 |
+
import numpy as np
|
| 5 |
+
import pybullet as p
|
| 6 |
+
import quaternion # noqa
|
| 7 |
+
|
| 8 |
+
import calvin_env.utils.utils as utils
|
| 9 |
+
|
| 10 |
+
# A logger for this file
|
| 11 |
+
log = logging.getLogger(__name__)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
# identical for both controllers
|
| 15 |
+
RELEVANT_BUTTONS = {
|
| 16 |
+
"button_a": 7,
|
| 17 |
+
"button_b": 1,
|
| 18 |
+
"thumb_trigger": 2, # on the left of the (right) controller; also 34 sent
|
| 19 |
+
"index_trigger": 33,
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class ButtonState(collections.namedtuple("ButtonState", ("button_name", "is_down", "was_triggered", "was_released"))):
|
| 24 |
+
__slots__ = ()
|
| 25 |
+
|
| 26 |
+
@classmethod
|
| 27 |
+
def from_flag(cls, button_name, flag):
|
| 28 |
+
return cls(
|
| 29 |
+
button_name,
|
| 30 |
+
bool(flag & p.VR_BUTTON_IS_DOWN),
|
| 31 |
+
bool(flag & p.VR_BUTTON_WAS_TRIGGERED),
|
| 32 |
+
bool(flag & p.VR_BUTTON_WAS_RELEASED),
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class VrEvent(
|
| 37 |
+
collections.namedtuple(
|
| 38 |
+
"VrEvent",
|
| 39 |
+
[
|
| 40 |
+
"controller_id",
|
| 41 |
+
"position",
|
| 42 |
+
"orientation",
|
| 43 |
+
"analog_axis",
|
| 44 |
+
"n_button_events",
|
| 45 |
+
"n_move_events",
|
| 46 |
+
"buttons",
|
| 47 |
+
"device_type_flag",
|
| 48 |
+
],
|
| 49 |
+
)
|
| 50 |
+
):
|
| 51 |
+
__slots__ = ()
|
| 52 |
+
|
| 53 |
+
@property
|
| 54 |
+
def button_dicts(self):
|
| 55 |
+
return {
|
| 56 |
+
button_name: ButtonState.from_flag(button_name, self.buttons[button_idx])
|
| 57 |
+
for button_name, button_idx in RELEVANT_BUTTONS.items()
|
| 58 |
+
}
|
| 59 |
+
# overall 64 buttons according to OpenVR
|
| 60 |
+
# enumerate([f'button_{i}' for i in range(64)])}
|
| 61 |
+
|
| 62 |
+
@property
|
| 63 |
+
def device_type(self):
|
| 64 |
+
return {p.VR_DEVICE_HMD: "hmd", p.VR_DEVICE_CONTROLLER: "controller", p.VR_DEVICE_GENERIC_TRACKER: "generic"}[
|
| 65 |
+
self.device_type_flag
|
| 66 |
+
]
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
class VrInput:
|
| 70 |
+
"""
|
| 71 |
+
pyBullet VR Data Collector
|
| 72 |
+
"""
|
| 73 |
+
|
| 74 |
+
def __init__(self, vr_controller, limit_angle, visualize_vr_pos, reset_button_queue_len):
|
| 75 |
+
self.vr_controller_id = vr_controller.vr_controller_id
|
| 76 |
+
self.vr_controller = vr_controller
|
| 77 |
+
self.POSITION = vr_controller.POSITION
|
| 78 |
+
self.ORIENTATION = vr_controller.ORIENTATION
|
| 79 |
+
self.ANALOG = vr_controller.ANALOG
|
| 80 |
+
self.BUTTONS = vr_controller.BUTTONS
|
| 81 |
+
self.BUTTON_A = vr_controller.BUTTON_A
|
| 82 |
+
self.BUTTON_B = vr_controller.BUTTON_B
|
| 83 |
+
if limit_angle is not None:
|
| 84 |
+
self.limit_angle = limit_angle[0]
|
| 85 |
+
self.limit_vector = limit_angle[1:]
|
| 86 |
+
else:
|
| 87 |
+
self.limit_angle = None
|
| 88 |
+
self.gripper_position_offset = np.array(vr_controller.gripper_position_offset)
|
| 89 |
+
self.gripper_orientation_offset = quaternion.from_euler_angles(vr_controller.gripper_orientation_offset)
|
| 90 |
+
self.visualize_vr_pos = visualize_vr_pos
|
| 91 |
+
self.vr_pos_uid = None
|
| 92 |
+
if visualize_vr_pos:
|
| 93 |
+
self.vr_pos_uid = self.create_vr_pos_visualization_shape()
|
| 94 |
+
log.info("Disable Picking")
|
| 95 |
+
p.configureDebugVisualizer(p.COV_ENABLE_VR_PICKING, 0)
|
| 96 |
+
p.configureDebugVisualizer(p.COV_ENABLE_VR_RENDER_CONTROLLERS, 0)
|
| 97 |
+
self._prev_vr_events = None
|
| 98 |
+
self.prev_action = None
|
| 99 |
+
self._reset_button_pressed = False
|
| 100 |
+
self._start_button_pressed = False
|
| 101 |
+
self._prev_reset_button_pressed = False
|
| 102 |
+
self._prev_start_button_pressed = False
|
| 103 |
+
self.reset_button_press_counter = 0
|
| 104 |
+
self.reset_button_queue_len = reset_button_queue_len
|
| 105 |
+
# wait until first vr action event arrives
|
| 106 |
+
while self.prev_action is None:
|
| 107 |
+
_ = self.get_vr_action()
|
| 108 |
+
|
| 109 |
+
def create_vr_pos_visualization_shape(self):
|
| 110 |
+
visual_shape_id = p.createVisualShape(shapeType=p.GEOM_SPHERE, rgbaColor=[1, 0, 0, 1], radius=0.005)
|
| 111 |
+
return p.createMultiBody(baseMass=0, baseVisualShapeIndex=visual_shape_id, basePosition=[0, 0, 0])
|
| 112 |
+
|
| 113 |
+
def get_vr_action(self):
|
| 114 |
+
# self._reset_button_pressed = False
|
| 115 |
+
self._prev_reset_button_pressed = self._reset_button_pressed
|
| 116 |
+
self._prev_start_button_pressed = self._start_button_pressed
|
| 117 |
+
vr_events = p.getVREvents()
|
| 118 |
+
if vr_events != ():
|
| 119 |
+
self._prev_vr_events = vr_events
|
| 120 |
+
for event in vr_events:
|
| 121 |
+
# Only use one controller
|
| 122 |
+
# if event[0] == self.vr_controller_id:
|
| 123 |
+
action = (event[self.POSITION], event[self.ORIENTATION], event[self.ANALOG])
|
| 124 |
+
|
| 125 |
+
self._reset_button_pressed = event[self.BUTTONS][self.BUTTON_B] & p.VR_BUTTON_IS_DOWN > 0
|
| 126 |
+
self._start_button_pressed = event[self.BUTTONS][self.BUTTON_A] & p.VR_BUTTON_IS_DOWN > 0
|
| 127 |
+
robot_action = self.vr_action_to_robot_action(action)
|
| 128 |
+
|
| 129 |
+
self.update_reset_button_queue()
|
| 130 |
+
self.prev_action = robot_action
|
| 131 |
+
return robot_action
|
| 132 |
+
|
| 133 |
+
return self.prev_action
|
| 134 |
+
|
| 135 |
+
def vr_action_to_robot_action(self, action):
|
| 136 |
+
controller_pos, vr_controller_orientation, controller_analogue_axis = action
|
| 137 |
+
desired_ee_pos = controller_pos + self.gripper_position_offset
|
| 138 |
+
orientation = utils.xyzw_to_wxyz(vr_controller_orientation)
|
| 139 |
+
q1 = quaternion.from_float_array(orientation)
|
| 140 |
+
q2 = self.gripper_orientation_offset
|
| 141 |
+
q12 = q1 * q2
|
| 142 |
+
arr = quaternion.as_float_array(q12)
|
| 143 |
+
desired_ee_orn = utils.wxyz_to_xyzw(arr)
|
| 144 |
+
|
| 145 |
+
# v2 = quaternion.rotate_vectors(q12, [0, 0, 1])
|
| 146 |
+
# if self.limit_angle is not None:
|
| 147 |
+
# if utils.angle_between(self.limit_vector, v2) <= self.limit_angle / 180 * math.pi:
|
| 148 |
+
# self.prev_ee_orn = desired_ee_orn
|
| 149 |
+
# else:
|
| 150 |
+
# desired_ee_orn = self.prev_ee_orn
|
| 151 |
+
|
| 152 |
+
if controller_analogue_axis > 0.1:
|
| 153 |
+
gripper_action = -1
|
| 154 |
+
else:
|
| 155 |
+
gripper_action = 1
|
| 156 |
+
|
| 157 |
+
# change color of vr pos sphere when starting or ending recording
|
| 158 |
+
if self.visualize_vr_pos:
|
| 159 |
+
if self.start_button_pressed:
|
| 160 |
+
p.changeVisualShape(self.vr_pos_uid, -1, rgbaColor=[0, 1, 0, 1])
|
| 161 |
+
if self.reset_button_pressed:
|
| 162 |
+
p.changeVisualShape(self.vr_pos_uid, -1, rgbaColor=[1, 0, 0, 1])
|
| 163 |
+
p.resetBasePositionAndOrientation(self.vr_pos_uid, desired_ee_pos, desired_ee_orn)
|
| 164 |
+
|
| 165 |
+
return desired_ee_pos, desired_ee_orn, gripper_action
|
| 166 |
+
|
| 167 |
+
def update_reset_button_queue(self):
|
| 168 |
+
if self._reset_button_pressed:
|
| 169 |
+
self.reset_button_press_counter += 1
|
| 170 |
+
else:
|
| 171 |
+
self.reset_button_press_counter = 0
|
| 172 |
+
|
| 173 |
+
@property
|
| 174 |
+
def reset_button_hold(self):
|
| 175 |
+
return self.reset_button_press_counter >= self.reset_button_queue_len
|
| 176 |
+
|
| 177 |
+
@property
|
| 178 |
+
def reset_button_pressed(self):
|
| 179 |
+
return self._reset_button_pressed and not self._prev_reset_button_pressed
|
| 180 |
+
|
| 181 |
+
@property
|
| 182 |
+
def start_button_pressed(self):
|
| 183 |
+
return self._start_button_pressed and not self._prev_start_button_pressed
|
| 184 |
+
|
| 185 |
+
@property
|
| 186 |
+
def prev_vr_events(self):
|
| 187 |
+
return self._prev_vr_events
|
aloha_robot_project/calvin/calvin_env/calvin_env/robot/IKfast.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from ikfast_franka_panda import get_ik
|
| 2 |
+
import numpy as np
|
| 3 |
+
from scipy.spatial.transform import Rotation as R
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class IKfast:
|
| 7 |
+
def __init__(
|
| 8 |
+
self,
|
| 9 |
+
robot_uid,
|
| 10 |
+
cid,
|
| 11 |
+
rp,
|
| 12 |
+
ll_real,
|
| 13 |
+
ul_real,
|
| 14 |
+
base_position,
|
| 15 |
+
base_orientation,
|
| 16 |
+
weights=[1, 1, 1, 1, 1, 1, 1],
|
| 17 |
+
num_angles=50,
|
| 18 |
+
):
|
| 19 |
+
self.robot_uid = robot_uid
|
| 20 |
+
self.cid = cid
|
| 21 |
+
self.ll_real = ll_real
|
| 22 |
+
self.ul_real = ul_real
|
| 23 |
+
self.rp = rp
|
| 24 |
+
self.num_dof = len(self.ll_real)
|
| 25 |
+
self.weights = weights
|
| 26 |
+
self.num_angles = num_angles
|
| 27 |
+
T_world_robot = np.eye(4)
|
| 28 |
+
T_world_robot[:3, 3] = base_position
|
| 29 |
+
T_world_robot[:3, :3] = R.from_quat(base_orientation).as_matrix()
|
| 30 |
+
self.T_robot_world = np.linalg.inv(T_world_robot)
|
| 31 |
+
|
| 32 |
+
def world_to_robot(self, pos_w, orn_w):
|
| 33 |
+
"""
|
| 34 |
+
pos, quat -> pos, Rot
|
| 35 |
+
"""
|
| 36 |
+
pose_w = np.eye(4)
|
| 37 |
+
pose_w[:3, 3] = pos_w
|
| 38 |
+
pose_w[:3, :3] = R.from_quat(orn_w).as_matrix()
|
| 39 |
+
pose_r = self.T_robot_world @ pose_w
|
| 40 |
+
pos_r = list(pose_r[:3, 3])
|
| 41 |
+
orn_r = pose_r[:3, :3].tolist()
|
| 42 |
+
return pos_r, orn_r
|
| 43 |
+
|
| 44 |
+
def filter_solutions(self, sol):
|
| 45 |
+
test_sol = np.ones(self.num_dof) * 9999.0
|
| 46 |
+
for i in range(self.num_dof):
|
| 47 |
+
for add_ang in [-2.0 * np.pi, 0, 2.0 * np.pi]:
|
| 48 |
+
test_ang = sol[i] + add_ang
|
| 49 |
+
if self.ul_real[i] >= test_ang >= self.ll_real[i]:
|
| 50 |
+
test_sol[i] = test_ang
|
| 51 |
+
if np.all(test_sol != 9999.0):
|
| 52 |
+
return test_sol
|
| 53 |
+
return None
|
| 54 |
+
|
| 55 |
+
def take_closest_sol(self, sols, last_q, weights):
|
| 56 |
+
best_sol_ind = np.argmin(np.sum((weights * (sols - np.array(last_q))) ** 2, 1))
|
| 57 |
+
return sols[best_sol_ind]
|
| 58 |
+
|
| 59 |
+
def get_ik_solution(self, target_pos, target_orn):
|
| 60 |
+
target_pos_robot, target_orn_robot = self.world_to_robot(target_pos, target_orn)
|
| 61 |
+
sols = []
|
| 62 |
+
feasible_sols = []
|
| 63 |
+
for q_6 in np.linspace(self.ll_real[-1], self.ul_real[-1], self.num_angles):
|
| 64 |
+
sols += get_ik(target_pos_robot, target_orn_robot, [q_6])
|
| 65 |
+
for sol in sols:
|
| 66 |
+
sol = self.filter_solutions(sol)
|
| 67 |
+
if sol is not None:
|
| 68 |
+
feasible_sols.append(sol)
|
| 69 |
+
if len(feasible_sols) < 1:
|
| 70 |
+
return None
|
| 71 |
+
best_sol = self.take_closest_sol(feasible_sols, self.rp[:7], self.weights)
|
| 72 |
+
return best_sol
|
aloha_robot_project/calvin/calvin_env/calvin_env/robot/mixed_ik.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pybullet as p
|
| 3 |
+
from scipy.spatial.transform import Rotation as R
|
| 4 |
+
|
| 5 |
+
from calvin_env.utils.utils import angle_between_quaternions
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class MixedIK:
|
| 9 |
+
def __init__(
|
| 10 |
+
self,
|
| 11 |
+
robot_uid,
|
| 12 |
+
cid,
|
| 13 |
+
ll_real,
|
| 14 |
+
ul_real,
|
| 15 |
+
base_position,
|
| 16 |
+
base_orientation,
|
| 17 |
+
tcp_link_id,
|
| 18 |
+
ll,
|
| 19 |
+
ul,
|
| 20 |
+
jr,
|
| 21 |
+
rp,
|
| 22 |
+
use_ik_fast,
|
| 23 |
+
use_nullspace=True,
|
| 24 |
+
threshold_pos=0.01,
|
| 25 |
+
threshold_orn=0.05,
|
| 26 |
+
weights=(1, 1, 1, 1, 1, 1, 1),
|
| 27 |
+
num_angles=50,
|
| 28 |
+
):
|
| 29 |
+
self.robot_uid = robot_uid
|
| 30 |
+
self.cid = cid
|
| 31 |
+
self.use_nullspace = use_nullspace
|
| 32 |
+
self.use_ik_fast = use_ik_fast
|
| 33 |
+
self.ik_fast = None
|
| 34 |
+
if self.use_ik_fast:
|
| 35 |
+
from ikfast_franka_panda import get_fk
|
| 36 |
+
|
| 37 |
+
from calvin_env.robot.IKfast import IKfast
|
| 38 |
+
|
| 39 |
+
self.get_fk = get_fk
|
| 40 |
+
self.ik_fast = IKfast(
|
| 41 |
+
robot_uid, cid, rp, ll_real, ul_real, base_position, base_orientation, weights, num_angles
|
| 42 |
+
)
|
| 43 |
+
self.tcp_link_id = tcp_link_id
|
| 44 |
+
self.ll = ll
|
| 45 |
+
self.ul = ul
|
| 46 |
+
self.jr = jr
|
| 47 |
+
self.ll_real = ll_real
|
| 48 |
+
self.ul_real = ul_real
|
| 49 |
+
self.rp = rp
|
| 50 |
+
self.num_dof = len(self.ll_real)
|
| 51 |
+
self.threshold_pos = threshold_pos
|
| 52 |
+
self.threshold_orn = threshold_orn
|
| 53 |
+
self.is_using_IK_fast = False
|
| 54 |
+
|
| 55 |
+
def get_bullet_ik(self, desired_ee_pos, desired_ee_orn):
|
| 56 |
+
if self.use_nullspace:
|
| 57 |
+
jnt_ps = p.calculateInverseKinematics(
|
| 58 |
+
self.robot_uid,
|
| 59 |
+
self.tcp_link_id,
|
| 60 |
+
desired_ee_pos,
|
| 61 |
+
desired_ee_orn,
|
| 62 |
+
self.ll,
|
| 63 |
+
self.ul,
|
| 64 |
+
self.jr,
|
| 65 |
+
self.rp,
|
| 66 |
+
physicsClientId=self.cid,
|
| 67 |
+
)
|
| 68 |
+
else:
|
| 69 |
+
jnt_ps = p.calculateInverseKinematics(
|
| 70 |
+
self.robot_uid, self.tcp_link_id, desired_ee_pos, desired_ee_orn, physicsClientId=self.cid
|
| 71 |
+
)
|
| 72 |
+
# clip joint positions outside the joint ranges
|
| 73 |
+
jnt_ps = np.clip(jnt_ps[: self.num_dof], self.ll_real, self.ul_real)
|
| 74 |
+
return jnt_ps
|
| 75 |
+
|
| 76 |
+
def robot_to_world(self, pos_r, orn_r):
|
| 77 |
+
"""
|
| 78 |
+
pos, Rot -> pos, quat
|
| 79 |
+
"""
|
| 80 |
+
pose_r = np.eye(4)
|
| 81 |
+
pose_r[:3, 3] = pos_r
|
| 82 |
+
pose_r[:3, :3] = orn_r
|
| 83 |
+
pose_w = np.linalg.inv(self.ik_fast.T_robot_world) @ pose_r
|
| 84 |
+
pos_r = pose_w[:3, 3]
|
| 85 |
+
orn_r = R.from_matrix(pose_w[:3, :3]).as_quat()
|
| 86 |
+
return pos_r, orn_r
|
| 87 |
+
|
| 88 |
+
def pose_within_threshold(self, target_pos, target_orn, q):
|
| 89 |
+
pos, orn = self.get_fk(q)
|
| 90 |
+
pos, orn = self.robot_to_world(pos, orn)
|
| 91 |
+
angular_diff = angle_between_quaternions(orn, target_orn)
|
| 92 |
+
threshold_pos_exceeded = np.linalg.norm(target_pos - pos) > self.threshold_pos
|
| 93 |
+
threshold_orn_exceeded = angular_diff > self.threshold_orn
|
| 94 |
+
return not (threshold_pos_exceeded or threshold_orn_exceeded)
|
| 95 |
+
|
| 96 |
+
def get_joint_states(self):
|
| 97 |
+
return list(zip(*p.getJointStates(self.robot_uid, range(self.num_dof))))[0]
|
| 98 |
+
|
| 99 |
+
def get_ik(self, target_pos, target_orn):
|
| 100 |
+
if self.is_using_IK_fast and not self.pose_within_threshold(target_pos, target_orn, self.get_joint_states()):
|
| 101 |
+
q_ik_fast = self.ik_fast.get_ik_solution(target_pos, target_orn)
|
| 102 |
+
if q_ik_fast is not None:
|
| 103 |
+
self.is_using_IK_fast = True
|
| 104 |
+
return q_ik_fast
|
| 105 |
+
else:
|
| 106 |
+
self.is_using_IK_fast = False
|
| 107 |
+
q_bullet = self.get_bullet_ik(target_pos, target_orn)
|
| 108 |
+
return q_bullet
|
| 109 |
+
self.is_using_IK_fast = False
|
| 110 |
+
q_bullet = self.get_bullet_ik(target_pos, target_orn)
|
| 111 |
+
if self.use_ik_fast and not self.pose_within_threshold(target_pos, target_orn, q_bullet):
|
| 112 |
+
q_ik_fast = self.ik_fast.get_ik_solution(target_pos, target_orn)
|
| 113 |
+
if q_ik_fast is not None:
|
| 114 |
+
self.is_using_IK_fast = True
|
| 115 |
+
return q_ik_fast
|
| 116 |
+
else:
|
| 117 |
+
return q_bullet
|
| 118 |
+
return q_bullet
|
aloha_robot_project/calvin/calvin_env/calvin_env/robot/robot.py
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pybullet as p
|
| 5 |
+
|
| 6 |
+
from calvin_env.robot.mixed_ik import MixedIK
|
| 7 |
+
|
| 8 |
+
# A logger for this file
|
| 9 |
+
log = logging.getLogger(__name__)
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class Robot:
|
| 13 |
+
def __init__(
|
| 14 |
+
self,
|
| 15 |
+
filename,
|
| 16 |
+
base_position,
|
| 17 |
+
base_orientation,
|
| 18 |
+
initial_joint_positions,
|
| 19 |
+
max_joint_force,
|
| 20 |
+
gripper_force,
|
| 21 |
+
arm_joint_ids,
|
| 22 |
+
gripper_joint_ids,
|
| 23 |
+
gripper_joint_limits,
|
| 24 |
+
tcp_link_id,
|
| 25 |
+
end_effector_link_id,
|
| 26 |
+
cid,
|
| 27 |
+
use_nullspace,
|
| 28 |
+
max_velocity,
|
| 29 |
+
use_ik_fast,
|
| 30 |
+
euler_obs,
|
| 31 |
+
lower_joint_limits=(-2.8973, -1.7628, -2.8973, -3.0718, -2.8973, -0.0175, -2.8973),
|
| 32 |
+
upper_joint_limits=(2.8973, 1.7628, 2.8973, -0.0698, 2.8973, 3.7525, 2.8973),
|
| 33 |
+
max_rel_pos=0.02,
|
| 34 |
+
max_rel_orn=0.05,
|
| 35 |
+
magic_scaling_factor_pos=1,
|
| 36 |
+
magic_scaling_factor_orn=1,
|
| 37 |
+
use_target_pose=True,
|
| 38 |
+
**kwargs,
|
| 39 |
+
):
|
| 40 |
+
log.info("Loading robot")
|
| 41 |
+
self.cid = cid
|
| 42 |
+
self.filename = filename
|
| 43 |
+
self.use_nullspace = use_nullspace
|
| 44 |
+
self.max_velocity = max_velocity
|
| 45 |
+
self.use_ik_fast = use_ik_fast
|
| 46 |
+
self.base_position = base_position
|
| 47 |
+
self.base_orientation = p.getQuaternionFromEuler(base_orientation)
|
| 48 |
+
self.arm_joint_ids = arm_joint_ids
|
| 49 |
+
self.initial_joint_positions = np.array(initial_joint_positions)
|
| 50 |
+
self.gripper_joint_ids = gripper_joint_ids
|
| 51 |
+
self.max_joint_force = max_joint_force
|
| 52 |
+
self.gripper_force = gripper_force
|
| 53 |
+
self.gripper_joint_limits = gripper_joint_limits
|
| 54 |
+
self.tcp_link_id = tcp_link_id
|
| 55 |
+
# Setup constraint
|
| 56 |
+
self.prev_ee_orn = p.getQuaternionFromEuler([0, 0, 0])
|
| 57 |
+
self.robot_uid = None
|
| 58 |
+
self.end_effector_link_id = end_effector_link_id
|
| 59 |
+
self.gripper_action = 1
|
| 60 |
+
self.ll = self.ul = self.jr = self.rp = None
|
| 61 |
+
self.ll_real = np.array(lower_joint_limits)
|
| 62 |
+
self.ul_real = np.array(upper_joint_limits)
|
| 63 |
+
self.mixed_ik = None
|
| 64 |
+
self.euler_obs = euler_obs
|
| 65 |
+
self.max_rel_pos = max_rel_pos
|
| 66 |
+
self.max_rel_orn = max_rel_orn
|
| 67 |
+
self.magic_scaling_factor_pos = magic_scaling_factor_pos
|
| 68 |
+
self.magic_scaling_factor_orn = magic_scaling_factor_orn
|
| 69 |
+
self.target_pos = None
|
| 70 |
+
self.target_orn = None
|
| 71 |
+
self.use_target_pose = use_target_pose
|
| 72 |
+
# self.reconfigure = False
|
| 73 |
+
|
| 74 |
+
def load(self):
|
| 75 |
+
self.robot_uid = p.loadURDF(
|
| 76 |
+
fileName=self.filename,
|
| 77 |
+
basePosition=self.base_position,
|
| 78 |
+
baseOrientation=self.base_orientation,
|
| 79 |
+
useFixedBase=True,
|
| 80 |
+
physicsClientId=self.cid,
|
| 81 |
+
)
|
| 82 |
+
self.add_base_cylinder()
|
| 83 |
+
# create a constraint to keep the fingers centered
|
| 84 |
+
c = p.createConstraint(
|
| 85 |
+
self.robot_uid,
|
| 86 |
+
self.gripper_joint_ids[0],
|
| 87 |
+
self.robot_uid,
|
| 88 |
+
self.gripper_joint_ids[1],
|
| 89 |
+
jointType=p.JOINT_GEAR,
|
| 90 |
+
jointAxis=[1, 0, 0],
|
| 91 |
+
parentFramePosition=[0, 0, 0],
|
| 92 |
+
childFramePosition=[0, 0, 0],
|
| 93 |
+
physicsClientId=self.cid,
|
| 94 |
+
)
|
| 95 |
+
p.changeConstraint(c, gearRatio=-1, erp=0.1, maxForce=50, physicsClientId=self.cid)
|
| 96 |
+
num_dof = p.computeDofCount(self.robot_uid)
|
| 97 |
+
# lower limits for null space (todo: set them to proper range)
|
| 98 |
+
self.ll = [-7] * num_dof
|
| 99 |
+
# upper limits for null space (todo: set them to proper range)
|
| 100 |
+
self.ul = [7] * num_dof
|
| 101 |
+
# joint ranges for null space (todo: set them to proper range)
|
| 102 |
+
self.jr = [7] * num_dof
|
| 103 |
+
# restposes for null space
|
| 104 |
+
self.rp = list(self.initial_joint_positions) + [self.gripper_joint_limits[1]] * 2
|
| 105 |
+
self.reset()
|
| 106 |
+
self.mixed_ik = MixedIK(
|
| 107 |
+
self.robot_uid,
|
| 108 |
+
self.cid,
|
| 109 |
+
self.ll_real,
|
| 110 |
+
self.ul_real,
|
| 111 |
+
self.base_position,
|
| 112 |
+
self.base_orientation,
|
| 113 |
+
self.tcp_link_id,
|
| 114 |
+
self.ll,
|
| 115 |
+
self.ul,
|
| 116 |
+
self.jr,
|
| 117 |
+
self.rp,
|
| 118 |
+
self.use_ik_fast,
|
| 119 |
+
threshold_pos=0.03,
|
| 120 |
+
threshold_orn=0.1,
|
| 121 |
+
weights=(10, 8, 6, 6, 2, 2, 1),
|
| 122 |
+
num_angles=30,
|
| 123 |
+
)
|
| 124 |
+
|
| 125 |
+
def add_base_cylinder(self):
|
| 126 |
+
"""
|
| 127 |
+
TODO: this should happen in load(), but that would break compatibility with old recorded data
|
| 128 |
+
"""
|
| 129 |
+
pos = self.base_position.copy()
|
| 130 |
+
pos[2] /= 2
|
| 131 |
+
angle = p.getEulerFromQuaternion(self.base_orientation)[2]
|
| 132 |
+
pos[0] -= np.cos(angle) * 0.05
|
| 133 |
+
pos[1] -= np.sin(angle) * 0.05
|
| 134 |
+
cylinder = p.createVisualShape(
|
| 135 |
+
shapeType=p.GEOM_CYLINDER,
|
| 136 |
+
rgbaColor=[1, 1, 1, 1],
|
| 137 |
+
radius=0.13,
|
| 138 |
+
length=self.base_position[2],
|
| 139 |
+
visualFramePosition=pos,
|
| 140 |
+
)
|
| 141 |
+
p.createMultiBody(baseVisualShapeIndex=cylinder)
|
| 142 |
+
|
| 143 |
+
def reset(self, robot_state=None):
|
| 144 |
+
if robot_state is None:
|
| 145 |
+
gripper_state = self.gripper_joint_limits[1]
|
| 146 |
+
joint_states = self.initial_joint_positions
|
| 147 |
+
else:
|
| 148 |
+
joint_indices = [i for i, x in enumerate(self.get_observation_labels()) if x.startswith("robot_joint")]
|
| 149 |
+
joint_states = robot_state[joint_indices]
|
| 150 |
+
gripper_state = robot_state[self.get_observation_labels().index("gripper_opening_width")] / 2
|
| 151 |
+
|
| 152 |
+
assert len(joint_states) == len(self.arm_joint_ids)
|
| 153 |
+
for i, _id in enumerate(self.arm_joint_ids):
|
| 154 |
+
p.resetJointState(self.robot_uid, _id, joint_states[i], physicsClientId=self.cid)
|
| 155 |
+
p.setJointMotorControl2(
|
| 156 |
+
bodyIndex=self.robot_uid,
|
| 157 |
+
jointIndex=_id,
|
| 158 |
+
controlMode=p.POSITION_CONTROL,
|
| 159 |
+
force=self.max_joint_force,
|
| 160 |
+
targetPosition=joint_states[i],
|
| 161 |
+
maxVelocity=self.max_velocity,
|
| 162 |
+
physicsClientId=self.cid,
|
| 163 |
+
)
|
| 164 |
+
for i in self.gripper_joint_ids:
|
| 165 |
+
p.resetJointState(self.robot_uid, i, gripper_state, physicsClientId=self.cid)
|
| 166 |
+
p.setJointMotorControl2(
|
| 167 |
+
bodyIndex=self.robot_uid,
|
| 168 |
+
jointIndex=i,
|
| 169 |
+
controlMode=p.POSITION_CONTROL,
|
| 170 |
+
force=self.gripper_force,
|
| 171 |
+
targetPosition=gripper_state,
|
| 172 |
+
maxVelocity=1,
|
| 173 |
+
physicsClientId=self.cid,
|
| 174 |
+
)
|
| 175 |
+
tcp_pos, tcp_orn = p.getLinkState(self.robot_uid, self.tcp_link_id, physicsClientId=self.cid)[:2]
|
| 176 |
+
if self.euler_obs:
|
| 177 |
+
tcp_orn = p.getEulerFromQuaternion(tcp_orn)
|
| 178 |
+
self.target_pos = np.array(tcp_pos)
|
| 179 |
+
self.target_orn = np.array(tcp_orn)
|
| 180 |
+
|
| 181 |
+
def get_observation(self):
|
| 182 |
+
"""
|
| 183 |
+
returns:
|
| 184 |
+
- robot_state: ndarray (16,)
|
| 185 |
+
- tcp_pos: robot_state[:3]
|
| 186 |
+
- tcp_orn: robot_state[3:7] (quat) / [3:6] (euler)
|
| 187 |
+
- gripper_opening_width: robot_state[7:8] (quat) / [6:7] (euler)
|
| 188 |
+
- arm_joint_states: robot_state[8:15] (quat) / [7:14] (euler)
|
| 189 |
+
- gripper_action: robot_state[15:] (quat) / [14:] (euler)
|
| 190 |
+
- robot_info: Dict
|
| 191 |
+
"""
|
| 192 |
+
tcp_pos, tcp_orn = p.getLinkState(self.robot_uid, self.tcp_link_id, physicsClientId=self.cid)[:2]
|
| 193 |
+
if self.euler_obs:
|
| 194 |
+
tcp_orn = p.getEulerFromQuaternion(tcp_orn)
|
| 195 |
+
gripper_opening_width = (
|
| 196 |
+
p.getJointState(self.robot_uid, self.gripper_joint_ids[0], physicsClientId=self.cid)[0]
|
| 197 |
+
+ p.getJointState(self.robot_uid, self.gripper_joint_ids[1], physicsClientId=self.cid)[0]
|
| 198 |
+
)
|
| 199 |
+
arm_joint_states = []
|
| 200 |
+
for i in self.arm_joint_ids:
|
| 201 |
+
arm_joint_states.append(p.getJointState(self.robot_uid, i, physicsClientId=self.cid)[0])
|
| 202 |
+
robot_state = np.array([*tcp_pos, *tcp_orn, gripper_opening_width, *arm_joint_states, self.gripper_action])
|
| 203 |
+
robot_info = {
|
| 204 |
+
"tcp_pos": tcp_pos,
|
| 205 |
+
"tcp_orn": tcp_orn,
|
| 206 |
+
"gripper_opening_width": gripper_opening_width,
|
| 207 |
+
"arm_joint_states": arm_joint_states,
|
| 208 |
+
"gripper_action": self.gripper_action,
|
| 209 |
+
"uid": self.robot_uid,
|
| 210 |
+
"contacts": p.getContactPoints(bodyA=self.robot_uid, physicsClientId=self.cid),
|
| 211 |
+
}
|
| 212 |
+
return robot_state, robot_info
|
| 213 |
+
|
| 214 |
+
def get_observation_labels(self):
|
| 215 |
+
tcp_pos_labels = [f"tcp_pos_{ax}" for ax in ("x", "y", "z")]
|
| 216 |
+
if self.euler_obs:
|
| 217 |
+
tcp_orn_labels = [f"tcp_orn_{ax}" for ax in ("x", "y", "z")]
|
| 218 |
+
else:
|
| 219 |
+
tcp_orn_labels = [f"tcp_orn_{ax}" for ax in ("x", "y", "z", "w")]
|
| 220 |
+
return [
|
| 221 |
+
*tcp_pos_labels,
|
| 222 |
+
*tcp_orn_labels,
|
| 223 |
+
"gripper_opening_width",
|
| 224 |
+
*[f"robot_joint_{i}" for i in self.arm_joint_ids],
|
| 225 |
+
"gripper_action",
|
| 226 |
+
]
|
| 227 |
+
|
| 228 |
+
def relative_to_absolute(self, action):
|
| 229 |
+
assert len(action) == 7
|
| 230 |
+
rel_pos, rel_orn, gripper = np.split(action, [3, 6])
|
| 231 |
+
rel_pos *= self.max_rel_pos * self.magic_scaling_factor_pos
|
| 232 |
+
rel_orn *= self.max_rel_orn * self.magic_scaling_factor_orn
|
| 233 |
+
if self.use_target_pose:
|
| 234 |
+
self.target_pos += rel_pos
|
| 235 |
+
self.target_orn += rel_orn
|
| 236 |
+
return self.target_pos, self.target_orn, gripper
|
| 237 |
+
else:
|
| 238 |
+
tcp_pos, tcp_orn = p.getLinkState(self.robot_uid, self.tcp_link_id, physicsClientId=self.cid)[:2]
|
| 239 |
+
tcp_orn = p.getEulerFromQuaternion(tcp_orn)
|
| 240 |
+
abs_pos = np.array(tcp_pos) + rel_pos
|
| 241 |
+
abs_orn = np.array(tcp_orn) + rel_orn
|
| 242 |
+
return abs_pos, abs_orn, gripper
|
| 243 |
+
|
| 244 |
+
def apply_action(self, action):
|
| 245 |
+
# cv2.imshow("win", np.zeros((300,300)))
|
| 246 |
+
# k = cv2.waitKey(1) % 255
|
| 247 |
+
# if k == ord('w'):
|
| 248 |
+
# self.base_position[1] += 0.01
|
| 249 |
+
# elif k == ord('s'):
|
| 250 |
+
# self.base_position[1] -= 0.01
|
| 251 |
+
# elif k == ord('d'):
|
| 252 |
+
# self.base_position[0] += 0.01
|
| 253 |
+
# elif k == ord('a'):
|
| 254 |
+
# self.base_position[0] -= 0.01
|
| 255 |
+
# elif k == ord('e'):
|
| 256 |
+
# self.base_position[2] += 0.01
|
| 257 |
+
# elif k == ord('q'):
|
| 258 |
+
# self.base_position[2] -= 0.01
|
| 259 |
+
# elif k == ord('r'):
|
| 260 |
+
# self.initial_joint_positions[0] -= 0.1
|
| 261 |
+
# elif k == ord('f'):
|
| 262 |
+
# self.initial_joint_positions[0] += 0.1
|
| 263 |
+
# elif k == ord('t'):
|
| 264 |
+
# self.initial_joint_positions[1] -= 0.1
|
| 265 |
+
# elif k == ord('g'):
|
| 266 |
+
# self.initial_joint_positions[1] += 0.1
|
| 267 |
+
# elif k == ord('y'):
|
| 268 |
+
# self.initial_joint_positions[2] -= 0.1
|
| 269 |
+
# elif k == ord('h'):
|
| 270 |
+
# self.initial_joint_positions[2] += 0.1
|
| 271 |
+
# elif k == ord('u'):
|
| 272 |
+
# self.initial_joint_positions[3] -= 0.1
|
| 273 |
+
# elif k == ord('j'):
|
| 274 |
+
# self.initial_joint_positions[3] += 0.1
|
| 275 |
+
# elif k == ord('i'):
|
| 276 |
+
# self.initial_joint_positions[4] -= 0.1
|
| 277 |
+
# elif k == ord('k'):
|
| 278 |
+
# self.initial_joint_positions[4] += 0.1
|
| 279 |
+
# elif k == ord('o'):
|
| 280 |
+
# self.initial_joint_positions[5] -= 0.1
|
| 281 |
+
# elif k == ord('l'):
|
| 282 |
+
# self.initial_joint_positions[5] += 0.1
|
| 283 |
+
# elif k == ord('p'):
|
| 284 |
+
# self.initial_joint_positions[6] -= 0.1
|
| 285 |
+
# elif k == ord(';'):
|
| 286 |
+
# self.initial_joint_positions[6] += 0.1
|
| 287 |
+
# elif k == ord('z'):
|
| 288 |
+
# self.reconfigure = not self.reconfigure
|
| 289 |
+
# print(f"{self.initial_joint_positions=}")
|
| 290 |
+
# print(f"{self.base_position=}")
|
| 291 |
+
# if k != 254:
|
| 292 |
+
# self.initial_joint_positions = np.clip(self.initial_joint_positions, self.ll_real, self.ul_real)
|
| 293 |
+
# p.resetBasePositionAndOrientation(self.robot_uid, self.base_position, self.base_orientation, physicsClientId=self.cid)
|
| 294 |
+
# self.rp = list(self.initial_joint_positions) + [self.gripper_joint_limits[1]] * 2
|
| 295 |
+
# self.mixed_ik.rp = self.rp
|
| 296 |
+
# for i, _id in enumerate(self.arm_joint_ids):
|
| 297 |
+
# p.resetJointState(self.robot_uid, _id, self.initial_joint_positions[i], physicsClientId=self.cid)
|
| 298 |
+
# p.setJointMotorControl2(
|
| 299 |
+
# bodyIndex=self.robot_uid,
|
| 300 |
+
# jointIndex=_id,
|
| 301 |
+
# controlMode=p.POSITION_CONTROL,
|
| 302 |
+
# force=self.max_joint_force,
|
| 303 |
+
# targetPosition=self.initial_joint_positions[i],
|
| 304 |
+
# maxVelocity=self.max_velocity,
|
| 305 |
+
# physicsClientId=self.cid,
|
| 306 |
+
# )
|
| 307 |
+
# if self.reconfigure:
|
| 308 |
+
# return
|
| 309 |
+
#
|
| 310 |
+
|
| 311 |
+
if not len(action) == 3:
|
| 312 |
+
action = self.relative_to_absolute(action)
|
| 313 |
+
target_ee_pos, target_ee_orn, self.gripper_action = action
|
| 314 |
+
|
| 315 |
+
assert len(target_ee_pos) == 3
|
| 316 |
+
assert len(target_ee_orn) in (3, 4)
|
| 317 |
+
# automatically transform euler actions to quaternion
|
| 318 |
+
if len(target_ee_orn) == 3:
|
| 319 |
+
target_ee_orn = p.getQuaternionFromEuler(target_ee_orn)
|
| 320 |
+
|
| 321 |
+
if not isinstance(self.gripper_action, int) and len(self.gripper_action) == 1:
|
| 322 |
+
self.gripper_action = self.gripper_action[0]
|
| 323 |
+
assert self.gripper_action in (-1, 1)
|
| 324 |
+
|
| 325 |
+
# #
|
| 326 |
+
# cam_rot = p.getMatrixFromQuaternion(target_ee_orn)
|
| 327 |
+
# cam_rot = np.array(cam_rot).reshape(3, 3)
|
| 328 |
+
# cam_rot_x, cam_rot_y, cam_rot_z = cam_rot[:, 0], cam_rot[:, 1], cam_rot[:, 2]
|
| 329 |
+
# p.addUserDebugLine(target_ee_pos, target_ee_pos + cam_rot_x, lineWidth=3, lineColorRGB=[1,0,0])
|
| 330 |
+
# p.addUserDebugLine(target_ee_pos, target_ee_pos +cam_rot_y, lineWidth=3, lineColorRGB=[0,1,0])
|
| 331 |
+
# p.addUserDebugLine(target_ee_pos, target_ee_pos +cam_rot_z, lineWidth=3, lineColorRGB=[0,0,1])
|
| 332 |
+
#
|
| 333 |
+
# tcp_pos, tcp_orn = p.getLinkState(self.robotId, self.tcp_link_id)[:2]
|
| 334 |
+
# tcp_euler = p.getEulerFromQuaternion(tcp_orn)
|
| 335 |
+
# p.addUserDebugLine([0,0,0], target_ee_pos, lineWidth=8, lineColorRGB=[0,1,0])
|
| 336 |
+
# p.addUserDebugLine([0,0,0], p.getLinkState(self.robot_uid, 6)[4], lineWidth=3, lineColorRGB=[1,0,0])
|
| 337 |
+
# p.addUserDebugLine([0,0,0], p.getLinkState(self.robot_uid, 13)[4], lineWidth=3, lineColorRGB=[0,1,0])
|
| 338 |
+
# target_ee_pos, target_ee_orn = self.tcp_to_ee(target_ee_pos, target_ee_orn)
|
| 339 |
+
# p.addUserDebugLine([0,0,0], target_ee_pos, lineWidth=8, lineColorRGB=[1,0,0])
|
| 340 |
+
jnt_ps = self.mixed_ik.get_ik(target_ee_pos, target_ee_orn)
|
| 341 |
+
for i in range(self.end_effector_link_id):
|
| 342 |
+
# p.resetJointState(self.robot_uid, i, jnt_ps[i])
|
| 343 |
+
p.setJointMotorControl2(
|
| 344 |
+
bodyIndex=self.robot_uid,
|
| 345 |
+
jointIndex=i,
|
| 346 |
+
controlMode=p.POSITION_CONTROL,
|
| 347 |
+
force=self.max_joint_force,
|
| 348 |
+
targetPosition=jnt_ps[i],
|
| 349 |
+
maxVelocity=self.max_velocity,
|
| 350 |
+
physicsClientId=self.cid,
|
| 351 |
+
)
|
| 352 |
+
|
| 353 |
+
self.control_gripper(self.gripper_action)
|
| 354 |
+
|
| 355 |
+
def control_gripper(self, gripper_action):
|
| 356 |
+
if gripper_action == 1:
|
| 357 |
+
gripper_finger_position = self.gripper_joint_limits[1]
|
| 358 |
+
gripper_force = self.gripper_force / 100
|
| 359 |
+
else:
|
| 360 |
+
gripper_finger_position = self.gripper_joint_limits[0]
|
| 361 |
+
gripper_force = self.gripper_force
|
| 362 |
+
for id in self.gripper_joint_ids:
|
| 363 |
+
p.setJointMotorControl2(
|
| 364 |
+
bodyIndex=self.robot_uid,
|
| 365 |
+
jointIndex=id,
|
| 366 |
+
controlMode=p.POSITION_CONTROL,
|
| 367 |
+
targetPosition=gripper_finger_position,
|
| 368 |
+
force=gripper_force,
|
| 369 |
+
maxVelocity=1,
|
| 370 |
+
physicsClientId=self.cid,
|
| 371 |
+
)
|
| 372 |
+
|
| 373 |
+
def serialize(self):
|
| 374 |
+
return {
|
| 375 |
+
"uid": self.robot_uid,
|
| 376 |
+
"info": p.getBodyInfo(self.robot_uid, physicsClientId=self.cid),
|
| 377 |
+
"pose": p.getBasePositionAndOrientation(self.robot_uid, physicsClientId=self.cid),
|
| 378 |
+
"joints": p.getJointStates(
|
| 379 |
+
self.robot_uid,
|
| 380 |
+
list(range(p.getNumJoints(self.robot_uid, physicsClientId=self.cid))),
|
| 381 |
+
physicsClientId=self.cid,
|
| 382 |
+
),
|
| 383 |
+
"gripper_action": self.gripper_action,
|
| 384 |
+
}
|
| 385 |
+
|
| 386 |
+
def reset_from_storage(self, data):
|
| 387 |
+
p.resetBasePositionAndOrientation(
|
| 388 |
+
bodyUniqueId=self.robot_uid, posObj=data["pose"][0], ornObj=data["pose"][1], physicsClientId=self.cid
|
| 389 |
+
)
|
| 390 |
+
num_joints = len(data["joints"])
|
| 391 |
+
assert num_joints == p.getNumJoints(self.robot_uid, physicsClientId=self.cid)
|
| 392 |
+
for i, (value, velocity, *_) in enumerate(data["joints"]):
|
| 393 |
+
p.resetJointState(
|
| 394 |
+
bodyUniqueId=self.robot_uid,
|
| 395 |
+
jointIndex=i,
|
| 396 |
+
targetValue=value,
|
| 397 |
+
targetVelocity=velocity,
|
| 398 |
+
physicsClientId=self.cid,
|
| 399 |
+
)
|
| 400 |
+
p.setJointMotorControl2(
|
| 401 |
+
bodyIndex=self.robot_uid,
|
| 402 |
+
jointIndex=i,
|
| 403 |
+
controlMode=p.POSITION_CONTROL,
|
| 404 |
+
force=self.max_joint_force,
|
| 405 |
+
targetPosition=value,
|
| 406 |
+
maxVelocity=self.max_velocity,
|
| 407 |
+
physicsClientId=self.cid,
|
| 408 |
+
)
|
| 409 |
+
self.control_gripper(data["gripper_action"])
|
| 410 |
+
|
| 411 |
+
def __str__(self):
|
| 412 |
+
return f"{self.filename} : {self.__dict__}"
|
aloha_robot_project/calvin/calvin_env/calvin_env/scene/objects/base_object.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class BaseObject:
|
| 2 |
+
def __init__(self, name, obj_cfg, p, cid, data_path, global_scaling):
|
| 3 |
+
self.p = p
|
| 4 |
+
self.cid = cid
|
| 5 |
+
self.name = name
|
| 6 |
+
self.file = data_path / obj_cfg["file"]
|
| 7 |
+
self.global_scaling = global_scaling
|
| 8 |
+
|
| 9 |
+
def reset(self, state):
|
| 10 |
+
pass
|
| 11 |
+
|
| 12 |
+
def get_info(self):
|
| 13 |
+
pass
|
aloha_robot_project/calvin/calvin_env/calvin_env/scene/objects/button.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from enum import Enum
|
| 2 |
+
|
| 3 |
+
MAX_FORCE = 4
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class ButtonState(Enum):
|
| 7 |
+
ON = 1
|
| 8 |
+
OFF = 0
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class Button:
|
| 12 |
+
def __init__(self, name, cfg, uid, p, cid):
|
| 13 |
+
self.name = name
|
| 14 |
+
self.p = p
|
| 15 |
+
self.cid = cid
|
| 16 |
+
# get joint_index by name (to prevent index errors when additional joints are added)
|
| 17 |
+
joint_index = next(
|
| 18 |
+
i
|
| 19 |
+
for i in range(self.p.getNumJoints(uid, physicsClientId=self.cid))
|
| 20 |
+
if self.p.getJointInfo(uid, i, physicsClientId=self.cid)[1].decode("utf-8") == name
|
| 21 |
+
)
|
| 22 |
+
self.joint_index = joint_index
|
| 23 |
+
self.uid = uid
|
| 24 |
+
self.initial_state = cfg["initial_state"]
|
| 25 |
+
self.effect = cfg["effect"]
|
| 26 |
+
self.ll, self.ul = self.p.getJointInfo(uid, joint_index, physicsClientId=self.cid)[8:10]
|
| 27 |
+
self.trigger_threshold = (self.ll + self.ul) / 2
|
| 28 |
+
self.p.setJointMotorControl2(
|
| 29 |
+
self.uid,
|
| 30 |
+
self.joint_index,
|
| 31 |
+
controlMode=self.p.POSITION_CONTROL,
|
| 32 |
+
targetPosition=self.initial_state,
|
| 33 |
+
force=MAX_FORCE,
|
| 34 |
+
physicsClientId=self.cid,
|
| 35 |
+
)
|
| 36 |
+
self.state = ButtonState.OFF
|
| 37 |
+
self.prev_is_pressed = self._is_pressed
|
| 38 |
+
self.light = None
|
| 39 |
+
|
| 40 |
+
def reset(self, state=None):
|
| 41 |
+
_state = self.initial_state if state is None else state
|
| 42 |
+
self.p.resetJointState(
|
| 43 |
+
self.uid,
|
| 44 |
+
self.joint_index,
|
| 45 |
+
_state,
|
| 46 |
+
physicsClientId=self.cid,
|
| 47 |
+
)
|
| 48 |
+
self.state = ButtonState.OFF
|
| 49 |
+
|
| 50 |
+
def step(self):
|
| 51 |
+
if self.state == ButtonState.OFF and not self.prev_is_pressed and self._is_pressed:
|
| 52 |
+
self.state = ButtonState.ON
|
| 53 |
+
if self.light is not None:
|
| 54 |
+
self.light.turn_on()
|
| 55 |
+
elif self.state == ButtonState.ON and not self.prev_is_pressed and self._is_pressed:
|
| 56 |
+
self.state = ButtonState.OFF
|
| 57 |
+
if self.light is not None:
|
| 58 |
+
self.light.turn_off()
|
| 59 |
+
self.prev_is_pressed = self._is_pressed
|
| 60 |
+
|
| 61 |
+
@property
|
| 62 |
+
def _is_pressed(self):
|
| 63 |
+
if self.initial_state <= self.trigger_threshold:
|
| 64 |
+
return self.get_state() > self.trigger_threshold
|
| 65 |
+
elif self.initial_state > self.trigger_threshold:
|
| 66 |
+
return self.get_state() < self.trigger_threshold
|
| 67 |
+
|
| 68 |
+
def get_state(self):
|
| 69 |
+
"""return button joint state"""
|
| 70 |
+
return float(self.p.getJointState(self.uid, self.joint_index, physicsClientId=self.cid)[0])
|
| 71 |
+
|
| 72 |
+
def get_info(self):
|
| 73 |
+
return {"joint_state": self.get_state(), "logical_state": self.state.value}
|
| 74 |
+
|
| 75 |
+
def add_effect(self, light):
|
| 76 |
+
self.light = light
|
aloha_robot_project/calvin/calvin_env/calvin_env/scene/objects/door.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MAX_FORCE = 4
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class Door:
|
| 5 |
+
def __init__(self, name, cfg, uid, p, cid):
|
| 6 |
+
self.name = name
|
| 7 |
+
self.p = p
|
| 8 |
+
self.cid = cid
|
| 9 |
+
# get joint_index by name (to prevent index errors when additional joints are added)
|
| 10 |
+
joint_index = next(
|
| 11 |
+
i
|
| 12 |
+
for i in range(self.p.getNumJoints(uid, physicsClientId=self.cid))
|
| 13 |
+
if self.p.getJointInfo(uid, i, physicsClientId=self.cid)[1].decode("utf-8") == name
|
| 14 |
+
)
|
| 15 |
+
self.joint_index = joint_index
|
| 16 |
+
self.uid = uid
|
| 17 |
+
self.initial_state = cfg["initial_state"]
|
| 18 |
+
self.p.setJointMotorControl2(
|
| 19 |
+
self.uid,
|
| 20 |
+
self.joint_index,
|
| 21 |
+
controlMode=p.VELOCITY_CONTROL,
|
| 22 |
+
force=MAX_FORCE,
|
| 23 |
+
physicsClientId=self.cid,
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
def reset(self, state=None):
|
| 27 |
+
_state = self.initial_state if state is None else state
|
| 28 |
+
self.p.resetJointState(
|
| 29 |
+
self.uid,
|
| 30 |
+
self.joint_index,
|
| 31 |
+
_state,
|
| 32 |
+
physicsClientId=self.cid,
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
def get_state(self):
|
| 36 |
+
joint_state = self.p.getJointState(self.uid, self.joint_index, physicsClientId=self.cid)
|
| 37 |
+
return float(joint_state[0])
|
| 38 |
+
|
| 39 |
+
def get_info(self):
|
| 40 |
+
return {"current_state": self.get_state()}
|
aloha_robot_project/calvin/calvin_env/calvin_env/scene/objects/fixed_object.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from calvin_env.scene.objects.base_object import BaseObject
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class FixedObject(BaseObject):
|
| 5 |
+
def __init__(self, name, obj_cfg, p, cid, data_path, global_scaling):
|
| 6 |
+
super().__init__(name, obj_cfg, p, cid, data_path, global_scaling)
|
| 7 |
+
self.initial_pos = obj_cfg["initial_pos"]
|
| 8 |
+
self.initial_orn = self.p.getQuaternionFromEuler(obj_cfg["initial_orn"])
|
| 9 |
+
|
| 10 |
+
self.uid = self.p.loadURDF(
|
| 11 |
+
self.file.as_posix(),
|
| 12 |
+
self.initial_pos,
|
| 13 |
+
self.initial_orn,
|
| 14 |
+
globalScaling=global_scaling,
|
| 15 |
+
physicsClientId=self.cid,
|
| 16 |
+
)
|
| 17 |
+
self.info_dict = {"uid": self.uid}
|
| 18 |
+
self.num_joints = self.p.getNumJoints(self.uid, physicsClientId=self.cid)
|
| 19 |
+
if self.num_joints > 0:
|
| 20 |
+
# save link names and ids in dictionary
|
| 21 |
+
links = {
|
| 22 |
+
self.p.getJointInfo(self.uid, i, physicsClientId=self.cid)[12].decode("utf-8"): i
|
| 23 |
+
for i in range(self.num_joints)
|
| 24 |
+
}
|
| 25 |
+
links["base_link"] = -1
|
| 26 |
+
self.info_dict["links"] = links
|
| 27 |
+
|
| 28 |
+
def reset(self, state=None):
|
| 29 |
+
pass
|
| 30 |
+
|
| 31 |
+
def get_info(self):
|
| 32 |
+
obj_info = {**self.info_dict, "contacts": self.p.getContactPoints(bodyA=self.uid, physicsClientId=self.cid)}
|
| 33 |
+
return obj_info
|
| 34 |
+
|
| 35 |
+
def serialize(self):
|
| 36 |
+
joints = (
|
| 37 |
+
self.p.getJointStates(self.uid, list(range(self.num_joints)), physicsClientId=self.cid)
|
| 38 |
+
if self.num_joints > 0
|
| 39 |
+
else ()
|
| 40 |
+
)
|
| 41 |
+
return {"uid": self.uid, "info": self.p.getBodyInfo(self.uid, physicsClientId=self.cid), "joints": joints}
|
aloha_robot_project/calvin/calvin_env/calvin_env/scene/objects/light.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from enum import Enum
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class LightState(Enum):
|
| 5 |
+
ON = 1
|
| 6 |
+
OFF = 0
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class Light:
|
| 10 |
+
def __init__(self, name, cfg, uid, p, cid):
|
| 11 |
+
self.name = name
|
| 12 |
+
self.uid = uid
|
| 13 |
+
self.p = p
|
| 14 |
+
self.cid = cid
|
| 15 |
+
self.link = cfg["link"]
|
| 16 |
+
self.link_id = next(
|
| 17 |
+
i
|
| 18 |
+
for i in range(self.p.getNumJoints(uid, physicsClientId=self.cid))
|
| 19 |
+
if self.p.getJointInfo(uid, i, physicsClientId=self.cid)[12].decode("utf-8") == self.link
|
| 20 |
+
)
|
| 21 |
+
self.color_on = cfg["color"]
|
| 22 |
+
self.color_off = [1, 1, 1, 1]
|
| 23 |
+
self.state = LightState.OFF
|
| 24 |
+
|
| 25 |
+
def reset(self, state=None):
|
| 26 |
+
if state is None:
|
| 27 |
+
self.turn_off()
|
| 28 |
+
else:
|
| 29 |
+
if state == LightState.ON.value:
|
| 30 |
+
self.turn_on()
|
| 31 |
+
elif state == LightState.OFF.value:
|
| 32 |
+
self.turn_off()
|
| 33 |
+
else:
|
| 34 |
+
print("Light state can be only 0 or 1.")
|
| 35 |
+
raise ValueError
|
| 36 |
+
|
| 37 |
+
def get_state(self):
|
| 38 |
+
return self.state.value
|
| 39 |
+
|
| 40 |
+
def get_info(self):
|
| 41 |
+
return {"logical_state": self.get_state()}
|
| 42 |
+
|
| 43 |
+
def turn_on(self):
|
| 44 |
+
self.state = LightState.ON
|
| 45 |
+
self.p.changeVisualShape(self.uid, self.link_id, rgbaColor=self.color_on, physicsClientId=self.cid)
|
| 46 |
+
|
| 47 |
+
def turn_off(self):
|
| 48 |
+
self.state = LightState.OFF
|
| 49 |
+
self.p.changeVisualShape(self.uid, self.link_id, rgbaColor=self.color_off, physicsClientId=self.cid)
|
| 50 |
+
|
| 51 |
+
def serialize(self):
|
| 52 |
+
return self.get_info()
|
aloha_robot_project/calvin/calvin_env/calvin_env/scene/objects/movable_object.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
from omegaconf.errors import ConfigKeyError
|
| 5 |
+
|
| 6 |
+
from calvin_env.scene.objects.base_object import BaseObject
|
| 7 |
+
|
| 8 |
+
log = logging.getLogger(__name__)
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class MovableObject(BaseObject):
|
| 12 |
+
def __init__(self, name, obj_cfg, p, cid, data_path, global_scaling, euler_obs, surfaces, np_random):
|
| 13 |
+
super().__init__(name, obj_cfg, p, cid, data_path, global_scaling)
|
| 14 |
+
self.initial_pos = obj_cfg["initial_pos"]
|
| 15 |
+
self.initial_orn = obj_cfg["initial_orn"]
|
| 16 |
+
if isinstance(self.initial_pos, list):
|
| 17 |
+
self.initial_pos = np.array(self.initial_pos)
|
| 18 |
+
if isinstance(self.initial_orn, list):
|
| 19 |
+
self.initial_orn = np.array(p.getQuaternionFromEuler(self.initial_orn))
|
| 20 |
+
self.euler_obs = euler_obs
|
| 21 |
+
self.surfaces = surfaces
|
| 22 |
+
self.np_random = np_random
|
| 23 |
+
|
| 24 |
+
initial_pos, initial_orn = self.sample_initial_pose()
|
| 25 |
+
self.uid = self.p.loadURDF(
|
| 26 |
+
self.file.as_posix(),
|
| 27 |
+
initial_pos,
|
| 28 |
+
initial_orn,
|
| 29 |
+
globalScaling=global_scaling,
|
| 30 |
+
physicsClientId=self.cid,
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
def reset(self, state=None):
|
| 34 |
+
if state is None:
|
| 35 |
+
initial_pos, initial_orn = self.sample_initial_pose()
|
| 36 |
+
else:
|
| 37 |
+
initial_pos, initial_orn = np.split(state, [3])
|
| 38 |
+
if len(initial_orn) == 3:
|
| 39 |
+
initial_orn = self.p.getQuaternionFromEuler(initial_orn)
|
| 40 |
+
self.p.resetBasePositionAndOrientation(
|
| 41 |
+
self.uid,
|
| 42 |
+
initial_pos,
|
| 43 |
+
initial_orn,
|
| 44 |
+
physicsClientId=self.cid,
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
def sample_initial_pose(self):
|
| 48 |
+
initial_pos = self.initial_pos
|
| 49 |
+
if isinstance(self.initial_pos, str):
|
| 50 |
+
if self.initial_pos == "any":
|
| 51 |
+
surface = self.np_random.choice(list(self.surfaces.keys()))
|
| 52 |
+
sampling_range = np.array(self.surfaces[surface])
|
| 53 |
+
else:
|
| 54 |
+
try:
|
| 55 |
+
sampling_range = np.array(self.surfaces[self.initial_pos])
|
| 56 |
+
except ConfigKeyError:
|
| 57 |
+
log.error(f"surface {self.initial_pos} not specified in scene config")
|
| 58 |
+
raise KeyError
|
| 59 |
+
initial_pos = self.np_random.uniform(sampling_range[0], sampling_range[1])
|
| 60 |
+
initial_orn = self.initial_orn
|
| 61 |
+
if isinstance(self.initial_orn, str):
|
| 62 |
+
if self.initial_orn == "any":
|
| 63 |
+
initial_orn = np.array(
|
| 64 |
+
self.p.getQuaternionFromEuler(self.np_random.uniform([0, 0, -np.pi], [0, 0, np.pi]))
|
| 65 |
+
)
|
| 66 |
+
else:
|
| 67 |
+
log.error("Only keyword 'any' supported at the moment")
|
| 68 |
+
raise ValueError
|
| 69 |
+
return initial_pos, initial_orn
|
| 70 |
+
|
| 71 |
+
def get_state(self):
|
| 72 |
+
pos, orn = self.p.getBasePositionAndOrientation(self.uid, physicsClientId=self.cid)
|
| 73 |
+
if self.euler_obs:
|
| 74 |
+
orn = self.p.getEulerFromQuaternion(orn)
|
| 75 |
+
return np.concatenate([pos, orn])
|
| 76 |
+
|
| 77 |
+
def get_info(self):
|
| 78 |
+
pos, orn = self.p.getBasePositionAndOrientation(self.uid, physicsClientId=self.cid)
|
| 79 |
+
lin_vel, ang_vel = self.p.getBaseVelocity(self.uid, physicsClientId=self.cid)
|
| 80 |
+
obj_info = {
|
| 81 |
+
"current_pos": pos,
|
| 82 |
+
"current_orn": orn,
|
| 83 |
+
"current_lin_vel": lin_vel,
|
| 84 |
+
"current_ang_vel": ang_vel,
|
| 85 |
+
"contacts": self.p.getContactPoints(bodyA=self.uid, physicsClientId=self.cid),
|
| 86 |
+
"uid": self.uid,
|
| 87 |
+
}
|
| 88 |
+
return obj_info
|
| 89 |
+
|
| 90 |
+
def serialize(self):
|
| 91 |
+
return {
|
| 92 |
+
"uid": self.uid,
|
| 93 |
+
"info": self.p.getBodyInfo(self.uid, physicsClientId=self.cid),
|
| 94 |
+
"pose": self.p.getBasePositionAndOrientation(self.uid, physicsClientId=self.cid),
|
| 95 |
+
}
|
aloha_robot_project/calvin/calvin_env/calvin_env/scene/objects/switch.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from enum import Enum
|
| 2 |
+
|
| 3 |
+
MAX_FORCE = 4
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class ButtonState(Enum):
|
| 7 |
+
ON = 1
|
| 8 |
+
OFF = 0
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class Switch:
|
| 12 |
+
def __init__(self, name, cfg, uid, p, cid):
|
| 13 |
+
self.name = name
|
| 14 |
+
self.p = p
|
| 15 |
+
self.cid = cid
|
| 16 |
+
# get joint_index by name (to prevent index errors when additional joints are added)
|
| 17 |
+
joint_index = next(
|
| 18 |
+
i
|
| 19 |
+
for i in range(self.p.getNumJoints(uid, physicsClientId=self.cid))
|
| 20 |
+
if self.p.getJointInfo(uid, i, physicsClientId=self.cid)[1].decode("utf-8") == name
|
| 21 |
+
)
|
| 22 |
+
self.joint_index = joint_index
|
| 23 |
+
self.uid = uid
|
| 24 |
+
self.initial_state = cfg["initial_state"]
|
| 25 |
+
self.effect = cfg["effect"]
|
| 26 |
+
self.ll, self.ul = self.p.getJointInfo(uid, joint_index, physicsClientId=self.cid)[8:10]
|
| 27 |
+
self.trigger_threshold = (self.ll + self.ul) / 2
|
| 28 |
+
self.p.setJointMotorControl2(
|
| 29 |
+
self.uid,
|
| 30 |
+
self.joint_index,
|
| 31 |
+
controlMode=p.VELOCITY_CONTROL,
|
| 32 |
+
force=MAX_FORCE,
|
| 33 |
+
physicsClientId=self.cid,
|
| 34 |
+
)
|
| 35 |
+
self.state = ButtonState.OFF
|
| 36 |
+
self.light = None
|
| 37 |
+
|
| 38 |
+
def reset(self, state=None):
|
| 39 |
+
_state = self.initial_state if state is None else state
|
| 40 |
+
self.p.resetJointState(
|
| 41 |
+
self.uid,
|
| 42 |
+
self.joint_index,
|
| 43 |
+
_state,
|
| 44 |
+
physicsClientId=self.cid,
|
| 45 |
+
)
|
| 46 |
+
self.state = ButtonState.OFF
|
| 47 |
+
|
| 48 |
+
def step(self):
|
| 49 |
+
if self.is_pressed:
|
| 50 |
+
if self.light is not None and self.state == ButtonState.OFF:
|
| 51 |
+
self.light.turn_on()
|
| 52 |
+
self.state = ButtonState.ON
|
| 53 |
+
else:
|
| 54 |
+
if self.light is not None and self.state == ButtonState.ON:
|
| 55 |
+
self.light.turn_off()
|
| 56 |
+
self.state = ButtonState.OFF
|
| 57 |
+
|
| 58 |
+
@property
|
| 59 |
+
def is_pressed(self):
|
| 60 |
+
if self.initial_state <= self.trigger_threshold:
|
| 61 |
+
return self.get_state() > self.trigger_threshold
|
| 62 |
+
elif self.initial_state > self.trigger_threshold:
|
| 63 |
+
return self.get_state() < self.trigger_threshold
|
| 64 |
+
|
| 65 |
+
def get_state(self):
|
| 66 |
+
"""return button joint state"""
|
| 67 |
+
return float(self.p.getJointState(self.uid, self.joint_index, physicsClientId=self.cid)[0])
|
| 68 |
+
|
| 69 |
+
def get_info(self):
|
| 70 |
+
return {"joint_state": self.get_state(), "logical_state": self.state.value}
|
| 71 |
+
|
| 72 |
+
def add_effect(self, light):
|
| 73 |
+
self.light = light
|
aloha_robot_project/calvin/calvin_env/calvin_env/scene/play_table_scene.py
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import itertools
|
| 2 |
+
import logging
|
| 3 |
+
import os
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
import numpy as np
|
| 7 |
+
|
| 8 |
+
# A logger for this file
|
| 9 |
+
from omegaconf import OmegaConf
|
| 10 |
+
|
| 11 |
+
from calvin_env.scene.objects.button import Button
|
| 12 |
+
from calvin_env.scene.objects.door import Door
|
| 13 |
+
from calvin_env.scene.objects.fixed_object import FixedObject
|
| 14 |
+
from calvin_env.scene.objects.light import Light
|
| 15 |
+
from calvin_env.scene.objects.movable_object import MovableObject
|
| 16 |
+
from calvin_env.scene.objects.switch import Switch
|
| 17 |
+
|
| 18 |
+
log = logging.getLogger(__name__)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
REPO_BASE = Path(__file__).parents[2]
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class PlayTableScene:
|
| 25 |
+
def __init__(self, objects, data_path, euler_obs, p, cid, global_scaling, surfaces, np_random, **kwargs):
|
| 26 |
+
self.p = p
|
| 27 |
+
self.cid = cid
|
| 28 |
+
self.global_scaling = global_scaling
|
| 29 |
+
self.euler_obs = euler_obs
|
| 30 |
+
self.surfaces = surfaces
|
| 31 |
+
self.np_random = np_random
|
| 32 |
+
if os.path.isabs(data_path):
|
| 33 |
+
self.data_path = Path(data_path)
|
| 34 |
+
else:
|
| 35 |
+
self.data_path = REPO_BASE / data_path
|
| 36 |
+
self.p.setAdditionalSearchPath(self.data_path.as_posix())
|
| 37 |
+
self.object_cfg = OmegaConf.to_container(objects)
|
| 38 |
+
self.fixed_objects, self.movable_objects = [], []
|
| 39 |
+
self.doors, self.buttons, self.switches, self.lights = [], [], [], []
|
| 40 |
+
|
| 41 |
+
def load(self):
|
| 42 |
+
self.fixed_objects, self.movable_objects = [], []
|
| 43 |
+
self.doors, self.buttons, self.switches, self.lights = [], [], [], []
|
| 44 |
+
|
| 45 |
+
for name, obj_cfg in self.object_cfg.get("movable_objects", {}).items():
|
| 46 |
+
self.movable_objects.append(
|
| 47 |
+
MovableObject(
|
| 48 |
+
name,
|
| 49 |
+
obj_cfg,
|
| 50 |
+
self.p,
|
| 51 |
+
self.cid,
|
| 52 |
+
self.data_path,
|
| 53 |
+
self.global_scaling,
|
| 54 |
+
self.euler_obs,
|
| 55 |
+
self.surfaces,
|
| 56 |
+
self.np_random,
|
| 57 |
+
)
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
for name, obj_cfg in self.object_cfg.get("fixed_objects", {}).items():
|
| 61 |
+
fixed_obj = FixedObject(name, obj_cfg, self.p, self.cid, self.data_path, self.global_scaling)
|
| 62 |
+
|
| 63 |
+
if "joints" in obj_cfg:
|
| 64 |
+
for joint_name, cfg in obj_cfg["joints"].items():
|
| 65 |
+
door = Door(joint_name, cfg, fixed_obj.uid, self.p, self.cid)
|
| 66 |
+
self.doors.append(door)
|
| 67 |
+
self.fixed_objects.append(fixed_obj)
|
| 68 |
+
|
| 69 |
+
if "buttons" in obj_cfg:
|
| 70 |
+
for button_name, cfg in obj_cfg["buttons"].items():
|
| 71 |
+
button = Button(button_name, cfg, fixed_obj.uid, self.p, self.cid)
|
| 72 |
+
self.buttons.append(button)
|
| 73 |
+
|
| 74 |
+
if "switches" in obj_cfg:
|
| 75 |
+
for switch_name, cfg in obj_cfg["switches"].items():
|
| 76 |
+
switch = Switch(switch_name, cfg, fixed_obj.uid, self.p, self.cid)
|
| 77 |
+
self.switches.append(switch)
|
| 78 |
+
|
| 79 |
+
if "lights" in obj_cfg:
|
| 80 |
+
for light_name, cfg in obj_cfg["lights"].items():
|
| 81 |
+
light = Light(light_name, cfg, fixed_obj.uid, self.p, self.cid)
|
| 82 |
+
self.lights.append(light)
|
| 83 |
+
|
| 84 |
+
for light in self.lights:
|
| 85 |
+
for button_switch in itertools.chain(self.buttons, self.switches):
|
| 86 |
+
if button_switch.effect == light.name:
|
| 87 |
+
button_switch.add_effect(light)
|
| 88 |
+
|
| 89 |
+
self.p.loadURDF(os.path.join(self.data_path, "plane/plane.urdf"), physicsClientId=self.cid)
|
| 90 |
+
|
| 91 |
+
def reset(self, scene_obs=None):
|
| 92 |
+
"""Reset objects and doors to initial position."""
|
| 93 |
+
if scene_obs is None:
|
| 94 |
+
for obj in itertools.chain(self.doors, self.buttons, self.switches, self.lights):
|
| 95 |
+
obj.reset()
|
| 96 |
+
self.reset_movable_objects()
|
| 97 |
+
else:
|
| 98 |
+
door_info, button_info, switch_info, light_info, obj_info = self.parse_scene_obs(scene_obs)
|
| 99 |
+
|
| 100 |
+
for door, state in zip(self.doors, door_info):
|
| 101 |
+
door.reset(state)
|
| 102 |
+
for button, state in zip(self.buttons, button_info):
|
| 103 |
+
button.reset(state)
|
| 104 |
+
for switch, state in zip(self.switches, switch_info):
|
| 105 |
+
switch.reset(state)
|
| 106 |
+
for light, state in zip(self.lights, light_info):
|
| 107 |
+
light.reset(state)
|
| 108 |
+
for obj, state in zip(self.movable_objects, obj_info):
|
| 109 |
+
obj.reset(state)
|
| 110 |
+
|
| 111 |
+
def parse_scene_obs(self, scene_obs):
|
| 112 |
+
# an object pose is composed of position (3) and orientation (4 for quaternion) / (3 for euler)
|
| 113 |
+
n_obj = len(self.movable_objects)
|
| 114 |
+
n_doors = len(self.doors)
|
| 115 |
+
n_buttons = len(self.buttons)
|
| 116 |
+
n_switches = len(self.switches)
|
| 117 |
+
n_lights = len(self.lights)
|
| 118 |
+
|
| 119 |
+
split_ids = np.cumsum([n_doors, n_buttons, n_switches, n_lights])
|
| 120 |
+
door_info, button_info, switch_info, light_info, obj_info = np.split(scene_obs, split_ids)
|
| 121 |
+
assert len(door_info) == n_doors
|
| 122 |
+
assert len(button_info) == n_buttons
|
| 123 |
+
assert len(switch_info) == n_switches
|
| 124 |
+
assert len(light_info) == n_lights
|
| 125 |
+
assert len(obj_info) // n_obj in [6, 7] # depending on euler angles or quaternions
|
| 126 |
+
|
| 127 |
+
obj_info = np.split(obj_info, n_obj)
|
| 128 |
+
|
| 129 |
+
return door_info, button_info, switch_info, light_info, obj_info
|
| 130 |
+
|
| 131 |
+
def reset_movable_objects(self):
|
| 132 |
+
"""reset movable objects such that there are no pairwise contacts"""
|
| 133 |
+
num_sampling_iterations = 1000
|
| 134 |
+
for i in range(num_sampling_iterations):
|
| 135 |
+
for obj in self.movable_objects:
|
| 136 |
+
obj.reset()
|
| 137 |
+
self.p.stepSimulation()
|
| 138 |
+
contact = False
|
| 139 |
+
for obj_a, obj_b in itertools.combinations(self.movable_objects, 2):
|
| 140 |
+
if np.any(len(self.p.getContactPoints(bodyA=obj_a.uid, bodyB=obj_b.uid, physicsClientId=self.cid))):
|
| 141 |
+
contact = True
|
| 142 |
+
break
|
| 143 |
+
if not contact:
|
| 144 |
+
return
|
| 145 |
+
log.error(f"Could not place objects in {num_sampling_iterations} iterations without contacts")
|
| 146 |
+
return
|
| 147 |
+
|
| 148 |
+
def step(self):
|
| 149 |
+
for button_switch in itertools.chain(self.buttons, self.switches):
|
| 150 |
+
button_switch.step()
|
| 151 |
+
|
| 152 |
+
def get_obs(self):
|
| 153 |
+
"""Return state information of the doors, drawers and shelves."""
|
| 154 |
+
door_states = [door.get_state() for door in self.doors]
|
| 155 |
+
button_states = [button.get_state() for button in self.buttons]
|
| 156 |
+
switch_states = [switch.get_state() for switch in self.switches]
|
| 157 |
+
light_states = [light.get_state() for light in self.lights]
|
| 158 |
+
object_poses = list(itertools.chain(*[obj.get_state() for obj in self.movable_objects]))
|
| 159 |
+
|
| 160 |
+
return np.concatenate([door_states, button_states, switch_states, light_states, object_poses])
|
| 161 |
+
|
| 162 |
+
def get_info(self):
|
| 163 |
+
"""
|
| 164 |
+
get dictionary of information about the objects in the scene
|
| 165 |
+
self.objects:
|
| 166 |
+
obj1:
|
| 167 |
+
joints:
|
| 168 |
+
joint1:
|
| 169 |
+
joint_index: int
|
| 170 |
+
initial_state: float # revolute
|
| 171 |
+
current_state: float
|
| 172 |
+
...
|
| 173 |
+
current_pos: [x, y, z]
|
| 174 |
+
current_orn: [x, y, z, w] # quaternion
|
| 175 |
+
contacts: output of pybullet getContactPoints(...)
|
| 176 |
+
links: # key exists only if object has num_joints > 0
|
| 177 |
+
link1: link_id # name: id
|
| 178 |
+
...
|
| 179 |
+
"""
|
| 180 |
+
info = {}
|
| 181 |
+
info["fixed_objects"] = {}
|
| 182 |
+
info["movable_objects"] = {}
|
| 183 |
+
info["doors"] = {}
|
| 184 |
+
info["buttons"] = {}
|
| 185 |
+
info["switches"] = {}
|
| 186 |
+
info["lights"] = {}
|
| 187 |
+
|
| 188 |
+
for obj in self.fixed_objects:
|
| 189 |
+
info["fixed_objects"][obj.name] = obj.get_info()
|
| 190 |
+
for obj in self.movable_objects:
|
| 191 |
+
info["movable_objects"][obj.name] = obj.get_info()
|
| 192 |
+
for obj in self.doors:
|
| 193 |
+
info["doors"][obj.name] = obj.get_info()
|
| 194 |
+
for obj in self.buttons:
|
| 195 |
+
info["buttons"][obj.name] = obj.get_info()
|
| 196 |
+
for obj in self.switches:
|
| 197 |
+
info["switches"][obj.name] = obj.get_info()
|
| 198 |
+
for obj in self.lights:
|
| 199 |
+
info["lights"][obj.name] = obj.get_info()
|
| 200 |
+
return info
|
| 201 |
+
|
| 202 |
+
def get_scene_obs_labels(self):
|
| 203 |
+
raise NotImplementedError
|
| 204 |
+
|
| 205 |
+
def get_objects(self):
|
| 206 |
+
return itertools.chain(self.fixed_objects, self.movable_objects)
|
| 207 |
+
|
| 208 |
+
def serialize(self):
|
| 209 |
+
data = {
|
| 210 |
+
"fixed_objects": [obj.serialize() for obj in self.fixed_objects],
|
| 211 |
+
"movable_objects": [obj.serialize() for obj in self.movable_objects],
|
| 212 |
+
"lights": [obj.serialize() for obj in self.lights],
|
| 213 |
+
}
|
| 214 |
+
return data
|
| 215 |
+
|
| 216 |
+
def reset_from_storage(self, data):
|
| 217 |
+
for fixed_obj in data["fixed_objects"]:
|
| 218 |
+
for i, (value, velocity, *_) in enumerate(fixed_obj["joints"]):
|
| 219 |
+
self.p.resetJointState(
|
| 220 |
+
bodyUniqueId=fixed_obj["uid"],
|
| 221 |
+
jointIndex=i,
|
| 222 |
+
targetValue=value,
|
| 223 |
+
targetVelocity=velocity,
|
| 224 |
+
physicsClientId=self.cid,
|
| 225 |
+
)
|
| 226 |
+
for movable_obj in data["movable_objects"]:
|
| 227 |
+
self.p.resetBasePositionAndOrientation(
|
| 228 |
+
bodyUniqueId=movable_obj["uid"],
|
| 229 |
+
posObj=movable_obj["pose"][0],
|
| 230 |
+
ornObj=movable_obj["pose"][1],
|
| 231 |
+
physicsClientId=self.cid,
|
| 232 |
+
)
|
| 233 |
+
for light, state in zip(self.lights, data["lights"]):
|
| 234 |
+
light.reset(state["logical_state"])
|
aloha_robot_project/calvin/calvin_env/calvin_env/scripts/check_tasks.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
import cv2
|
| 4 |
+
import hydra
|
| 5 |
+
import numpy as np
|
| 6 |
+
import pybullet as p
|
| 7 |
+
|
| 8 |
+
"""
|
| 9 |
+
This script loads a rendered episode and replays it using the recorded actions.
|
| 10 |
+
Optionally, gaussian noise can be added to the actions.
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def scale_depth(img):
|
| 15 |
+
img = np.clip(img, 0, 1.5)
|
| 16 |
+
img -= img.min()
|
| 17 |
+
img /= img.max()
|
| 18 |
+
return img
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def noise(action, pos_std=0.01, rot_std=1):
|
| 22 |
+
"""
|
| 23 |
+
adds gaussian noise to position and orientation.
|
| 24 |
+
units are m for pos and degree for rot
|
| 25 |
+
"""
|
| 26 |
+
pos, orn, gripper = action
|
| 27 |
+
rot_std = np.radians(rot_std)
|
| 28 |
+
pos_noise = np.random.normal(0, pos_std, 3)
|
| 29 |
+
rot_noise = p.getQuaternionFromEuler(np.random.normal(0, rot_std, 3))
|
| 30 |
+
pos, orn = p.multiplyTransforms(pos, orn, pos_noise, rot_noise)
|
| 31 |
+
return pos, orn, gripper
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
@hydra.main(config_path="../../conf", config_name="config_data_collection")
|
| 35 |
+
def run_env(cfg):
|
| 36 |
+
env = hydra.utils.instantiate(cfg.env, show_gui=True, use_vr=False, use_scene_info=True)
|
| 37 |
+
|
| 38 |
+
root_dir = Path(__file__).parents[3] / "dataset/task_D_D/training"
|
| 39 |
+
|
| 40 |
+
ep_start_end_ids = np.sort(np.load(root_dir / "ep_start_end_ids.npy"), axis=0)
|
| 41 |
+
|
| 42 |
+
seq_len = 32
|
| 43 |
+
tasks = hydra.utils.instantiate(cfg.tasks)
|
| 44 |
+
for s, e in ep_start_end_ids:
|
| 45 |
+
i = s
|
| 46 |
+
while 1:
|
| 47 |
+
|
| 48 |
+
file = root_dir / f"episode_{i:07}.npz"
|
| 49 |
+
data = np.load(file)
|
| 50 |
+
# gripper_img = data["rgb_gripper"]
|
| 51 |
+
# cv2.imshow("gripper", gripper_img[:, :, ::-1])
|
| 52 |
+
# static_img = data["rgb_static"]
|
| 53 |
+
# cv2.imshow("static", static_img[:, :, ::-1])
|
| 54 |
+
# print(data["robot_obs"])
|
| 55 |
+
# env.render()
|
| 56 |
+
env.reset(scene_obs=data["scene_obs"], robot_obs=data["robot_obs"])
|
| 57 |
+
start_info = env.get_info()
|
| 58 |
+
cv2.imshow("keylistener", np.zeros((300, 300)))
|
| 59 |
+
k = cv2.waitKey(0) % 256
|
| 60 |
+
if k == ord("a"):
|
| 61 |
+
i -= 1
|
| 62 |
+
i = np.clip(i, s, e - seq_len)
|
| 63 |
+
if k == ord("d"):
|
| 64 |
+
i += 1
|
| 65 |
+
i = np.clip(i, s, e - seq_len)
|
| 66 |
+
if k == ord("q"):
|
| 67 |
+
i -= 100
|
| 68 |
+
i = np.clip(i, s, e - seq_len)
|
| 69 |
+
if k == ord("z"):
|
| 70 |
+
i -= seq_len
|
| 71 |
+
i = np.clip(i, s, e - seq_len)
|
| 72 |
+
if k == ord("e"):
|
| 73 |
+
i += 100
|
| 74 |
+
i = np.clip(i, s, e - seq_len)
|
| 75 |
+
if k == ord("r"):
|
| 76 |
+
for j in range(i + 1, i + seq_len):
|
| 77 |
+
file = root_dir / f"episode_{j:07d}.npz"
|
| 78 |
+
data = np.load(file)
|
| 79 |
+
env.reset(scene_obs=data["scene_obs"], robot_obs=data["robot_obs"])
|
| 80 |
+
gripper_img = data["rgb_gripper"]
|
| 81 |
+
cv2.imshow("gripper", gripper_img[:, :, ::-1])
|
| 82 |
+
static_img = data["rgb_static"]
|
| 83 |
+
cv2.imshow("static", static_img[:, :, ::-1])
|
| 84 |
+
cv2.waitKey(100)
|
| 85 |
+
end_info = env.get_info()
|
| 86 |
+
task_info = tasks.get_task_info(start_info, end_info)
|
| 87 |
+
if len(task_info):
|
| 88 |
+
print()
|
| 89 |
+
print()
|
| 90 |
+
print()
|
| 91 |
+
print(task_info)
|
| 92 |
+
else:
|
| 93 |
+
print()
|
| 94 |
+
print()
|
| 95 |
+
print()
|
| 96 |
+
print("No task detected.")
|
| 97 |
+
i = j
|
| 98 |
+
if k == ord("n"): # ESC
|
| 99 |
+
break
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
if __name__ == "__main__":
|
| 103 |
+
run_env()
|
aloha_robot_project/calvin/calvin_env/calvin_env/scripts/convert_gripper_actions.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
import sys
|
| 3 |
+
|
| 4 |
+
import numpy as np
|
| 5 |
+
from tqdm import tqdm
|
| 6 |
+
|
| 7 |
+
path = Path(sys.argv[-1])
|
| 8 |
+
|
| 9 |
+
for subdir in ["training", "validation"]:
|
| 10 |
+
for file in tqdm((path / subdir).glob("*.npz")):
|
| 11 |
+
data = np.load(file)
|
| 12 |
+
if data["rel_actions"][-1] == 0:
|
| 13 |
+
data = dict(data)
|
| 14 |
+
data["rel_actions"][-1] = -1
|
| 15 |
+
data["actions"][-1] = -1
|
| 16 |
+
np.savez(file, **data)
|
aloha_robot_project/calvin/calvin_env/calvin_env/scripts/dataset_to_euler.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
from shutil import copyfile, copytree
|
| 3 |
+
|
| 4 |
+
import numpy as np
|
| 5 |
+
import pybullet as p
|
| 6 |
+
from tqdm import tqdm
|
| 7 |
+
|
| 8 |
+
load_path = Path("/home/hermannl/phd/data/banana_dataset_01_29/validation")
|
| 9 |
+
|
| 10 |
+
save_path = Path("/home/hermannl/phd/data/banana_dataset_01_29_euler/validation")
|
| 11 |
+
save_path.mkdir(parents=True, exist_ok=True)
|
| 12 |
+
|
| 13 |
+
for file in tqdm(load_path.glob("*.npz")):
|
| 14 |
+
data = np.load(file)
|
| 15 |
+
robot_obs = data["robot_obs"]
|
| 16 |
+
robot_obs_euler = np.concatenate([robot_obs[:3], p.getEulerFromQuaternion(robot_obs[3:7]), robot_obs[7:]])
|
| 17 |
+
scene_obs = data["scene_obs"]
|
| 18 |
+
scene_obs_euler = scene_obs[:3]
|
| 19 |
+
for i in range(6):
|
| 20 |
+
scene_obs_euler = np.append(scene_obs_euler, scene_obs[3 + i * 7 : 3 + i * 7 + 3])
|
| 21 |
+
scene_obs_euler = np.append(scene_obs_euler, p.getEulerFromQuaternion(scene_obs[3 + i * 7 + 3 : 3 + i * 7 + 7]))
|
| 22 |
+
actions = data["actions"]
|
| 23 |
+
actions_euler = np.concatenate([actions[:3], p.getEulerFromQuaternion(actions[3:7]), actions[7:]])
|
| 24 |
+
data_euler = dict(data.items())
|
| 25 |
+
data_euler["robot_obs"] = robot_obs_euler
|
| 26 |
+
data_euler["scene_obs"] = scene_obs_euler
|
| 27 |
+
data_euler["actions"] = actions_euler
|
| 28 |
+
np.savez(save_path / file.name, **data_euler)
|
| 29 |
+
|
| 30 |
+
for file in set(load_path.glob("*")) - set(load_path.glob("*.npz")):
|
| 31 |
+
if file.is_dir():
|
| 32 |
+
copytree(file, save_path / file.name)
|
| 33 |
+
else:
|
| 34 |
+
copyfile(file, save_path / file.name)
|
aloha_robot_project/calvin/calvin_env/calvin_env/scripts/record_video_icra.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from copy import deepcopy
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import time
|
| 4 |
+
|
| 5 |
+
import cv2
|
| 6 |
+
import hydra
|
| 7 |
+
import matplotlib.pyplot as plt
|
| 8 |
+
import numpy as np
|
| 9 |
+
import pybullet as p
|
| 10 |
+
|
| 11 |
+
from calvin_env.utils import utils
|
| 12 |
+
|
| 13 |
+
"""
|
| 14 |
+
This script loads a rendered episode and replays it using the recorded actions.
|
| 15 |
+
Optionally, gaussian noise can be added to the actions.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def noise(action, pos_std=0.01, rot_std=1):
|
| 20 |
+
"""
|
| 21 |
+
adds gaussian noise to position and orientation.
|
| 22 |
+
units are m for pos and degree for rot
|
| 23 |
+
"""
|
| 24 |
+
pos, orn, gripper = action
|
| 25 |
+
rot_std = np.radians(rot_std)
|
| 26 |
+
pos_noise = np.random.normal(0, pos_std, 3)
|
| 27 |
+
rot_noise = p.getQuaternionFromEuler(np.random.normal(0, rot_std, 3))
|
| 28 |
+
pos, orn = p.multiplyTransforms(pos, orn, pos_noise, rot_noise)
|
| 29 |
+
return pos, orn, gripper
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def depth2rgb(img, minval=0, maxval=1):
|
| 33 |
+
img -= minval
|
| 34 |
+
img /= maxval - minval
|
| 35 |
+
img *= 255
|
| 36 |
+
img = np.clip(img, 0.0, 255.0)
|
| 37 |
+
img = img.astype(np.uint8)
|
| 38 |
+
img = np.tile(np.expand_dims(img, axis=2), 3)
|
| 39 |
+
return img
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
@hydra.main(config_path="../../conf", config_name="config_data_collection")
|
| 43 |
+
def run_env(cfg):
|
| 44 |
+
env = hydra.utils.instantiate(cfg.env, show_gui=False, use_vr=False, use_scene_info=True)
|
| 45 |
+
|
| 46 |
+
root_dir = Path("/home/hermannl/data/calvin_abcd_example")
|
| 47 |
+
|
| 48 |
+
ep_start_end_ids = [[100000, 104999]]
|
| 49 |
+
rel_actions = []
|
| 50 |
+
tasks = hydra.utils.instantiate(cfg.tasks)
|
| 51 |
+
prev_info = None
|
| 52 |
+
t1 = time.time()
|
| 53 |
+
|
| 54 |
+
video = cv2.VideoWriter(
|
| 55 |
+
"/home/hermannl/Documents/calvin/env_A_tactile.avi", cv2.VideoWriter_fourcc(*"XVID"), 30, (240, 160)
|
| 56 |
+
)
|
| 57 |
+
video_static = cv2.VideoWriter(
|
| 58 |
+
"/home/hermannl/Documents/calvin/env_A_static_depth.avi", cv2.VideoWriter_fourcc(*"XVID"), 30, (200, 200)
|
| 59 |
+
)
|
| 60 |
+
video_gripper = cv2.VideoWriter(
|
| 61 |
+
"/home/hermannl/Documents/calvin/env_A_gripper_depth.avi", cv2.VideoWriter_fourcc(*"XVID"), 30, (100, 100)
|
| 62 |
+
)
|
| 63 |
+
for s, e in ep_start_end_ids:
|
| 64 |
+
print("new_episode")
|
| 65 |
+
for i in range(s, e + 1):
|
| 66 |
+
file = root_dir / f"episode_{i:07d}.npz"
|
| 67 |
+
data = np.load(file)
|
| 68 |
+
# img = data["rgb_static"]
|
| 69 |
+
# cv2.imshow("win2", cv2.resize(img[:, :, ::-1], (500, 500)))
|
| 70 |
+
# cv2.waitKey(0)
|
| 71 |
+
|
| 72 |
+
# if (i - s) % 32 == 0:
|
| 73 |
+
# print(f"reset {i}")
|
| 74 |
+
obs = env.reset(scene_obs=data["scene_obs"], robot_obs=data["robot_obs"])
|
| 75 |
+
|
| 76 |
+
im = obs["rgb_obs"][0]
|
| 77 |
+
im = np.concatenate([im[:, :, :3], im[:, :, 3:6]], axis=1)
|
| 78 |
+
cv2.imshow("im", im)
|
| 79 |
+
cv2.waitKey(1)
|
| 80 |
+
video.write(im[:, :, ::-1])
|
| 81 |
+
video_gripper.write(depth2rgb(obs["depth_obs"][1], minval=0.1, maxval=0.5)[:, :, ::-1])
|
| 82 |
+
video_static.write(depth2rgb(obs["depth_obs"][0], minval=3.5, maxval=5)[:, :, ::-1])
|
| 83 |
+
# action = data["rel_actions"]
|
| 84 |
+
action = np.split(data["actions"], [3, 6])
|
| 85 |
+
action = noise(action)
|
| 86 |
+
|
| 87 |
+
rel_actions.append(utils.to_relative_action(data["actions"], data["robot_obs"][:6]))
|
| 88 |
+
# action = utils.to_relative_action(data["actions"], data["robot_obs"], max_pos=0.04, max_orn=0.1)
|
| 89 |
+
# tcp_pos, tcp_orn = p.getLinkState(env.robot.robot_uid, env.robot.tcp_link_id, physicsClientId=env.cid)[:2]
|
| 90 |
+
# tcp_orn = p.getEulerFromQuaternion(tcp_orn)
|
| 91 |
+
# action2 = utils.to_relative_action(data["actions"], np.concatenate([tcp_pos, tcp_orn]))
|
| 92 |
+
o, _, _, info = env.step(action)
|
| 93 |
+
print(info["scene_info"]["lights"]["led"]["logical_state"])
|
| 94 |
+
if (i - s) % 32 != 0:
|
| 95 |
+
print(tasks.get_task_info(prev_info, info))
|
| 96 |
+
else:
|
| 97 |
+
prev_info = deepcopy(info)
|
| 98 |
+
time.sleep(0.01)
|
| 99 |
+
video.release()
|
| 100 |
+
video_static.release()
|
| 101 |
+
video_gripper.release()
|
| 102 |
+
print(time.time() - t1)
|
| 103 |
+
rel_actions = np.array(rel_actions)
|
| 104 |
+
for j in range(rel_actions.shape[1]):
|
| 105 |
+
plt.figure(j)
|
| 106 |
+
plt.hist(rel_actions[:, j], bins=10)
|
| 107 |
+
plt.plot()
|
| 108 |
+
plt.show()
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
if __name__ == "__main__":
|
| 112 |
+
run_env()
|
aloha_robot_project/calvin/calvin_env/calvin_env/scripts/render_low_freq.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import itertools
|
| 3 |
+
import os
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
import shutil
|
| 6 |
+
import sys
|
| 7 |
+
|
| 8 |
+
import numpy as np
|
| 9 |
+
from omegaconf import OmegaConf
|
| 10 |
+
from tqdm import tqdm
|
| 11 |
+
|
| 12 |
+
from calvin_env.utils import utils
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def pairwise(iterable):
|
| 16 |
+
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
|
| 17 |
+
a, b = itertools.tee(iterable)
|
| 18 |
+
next(b, None)
|
| 19 |
+
return zip(a, b)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
parser = argparse.ArgumentParser(description="convert dataset to 15 hz (leave one step out)")
|
| 23 |
+
parser.add_argument("src", type=str)
|
| 24 |
+
parser.add_argument("dest", type=str)
|
| 25 |
+
parser.add_argument("--max_rel_pos", type=float, default=0.04)
|
| 26 |
+
parser.add_argument("--max_rel_orn", type=float, default=0.1)
|
| 27 |
+
args = parser.parse_args()
|
| 28 |
+
src_path = Path(args.src)
|
| 29 |
+
dest_path = Path(args.dest)
|
| 30 |
+
|
| 31 |
+
os.mkdir(dest_path)
|
| 32 |
+
os.mkdir(dest_path / "training")
|
| 33 |
+
os.mkdir(dest_path / "validation")
|
| 34 |
+
|
| 35 |
+
new_i = 0
|
| 36 |
+
|
| 37 |
+
for subdir in ["training", "validation"]:
|
| 38 |
+
ep_lens = np.load(src_path / subdir / "ep_lens.npy")
|
| 39 |
+
ep_start_end_ids = np.load(src_path / subdir / "ep_start_end_ids.npy")
|
| 40 |
+
|
| 41 |
+
new_ep_lens = []
|
| 42 |
+
new_ep_start_end_ids = []
|
| 43 |
+
|
| 44 |
+
for start, end in tqdm(ep_start_end_ids):
|
| 45 |
+
|
| 46 |
+
ep_len = end - start + 1
|
| 47 |
+
for offset in (0, 1):
|
| 48 |
+
new_start = new_i
|
| 49 |
+
for old_i in range(start + offset, end + 1, 2):
|
| 50 |
+
old_data = np.load(src_path / subdir / f"episode_{old_i:06d}.npz")
|
| 51 |
+
data = dict(old_data)
|
| 52 |
+
if old_i < end:
|
| 53 |
+
next_old_data = np.load(src_path / subdir / f"episode_{old_i + 1:06d}.npz")
|
| 54 |
+
next_data = dict(next_old_data)
|
| 55 |
+
data["actions"] = next_data["actions"]
|
| 56 |
+
data["rel_actions"] = utils.to_relative_action(
|
| 57 |
+
data["actions"], data["robot_obs"], max_pos=args.max_rel_pos, max_orn=args.max_rel_orn
|
| 58 |
+
)
|
| 59 |
+
np.savez(dest_path / subdir / f"episode_{new_i:06d}.npz", **data)
|
| 60 |
+
new_i += 1
|
| 61 |
+
new_end = new_i - 1
|
| 62 |
+
new_ep_len = new_end - new_start + 1
|
| 63 |
+
new_ep_start_end_ids.append((new_start, new_end))
|
| 64 |
+
new_ep_lens.append(new_ep_len)
|
| 65 |
+
np.save(dest_path / subdir / "ep_lens.npy", new_ep_lens)
|
| 66 |
+
np.save(dest_path / subdir / "ep_start_end_ids.npy", new_ep_start_end_ids)
|
| 67 |
+
shutil.copy(src_path / subdir / "statistics.yaml", dest_path / subdir)
|
| 68 |
+
os.makedirs(dest_path / subdir / ".hydra")
|
| 69 |
+
shutil.copytree(src_path / subdir / ".hydra", dest_path / subdir / ".hydra", dirs_exist_ok=True)
|
| 70 |
+
cfg = OmegaConf.load(dest_path / subdir / ".hydra/merged_config.yaml")
|
| 71 |
+
cfg.robot.max_rel_pos = args.max_rel_pos
|
| 72 |
+
cfg.robot.max_rel_orn = args.max_rel_orn
|
| 73 |
+
cfg.env.control_freq = 15
|
| 74 |
+
OmegaConf.save(cfg, dest_path / subdir / ".hydra/merged_config.yaml")
|
aloha_robot_project/calvin/calvin_env/calvin_env/scripts/reset_env_rendered_episode.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from copy import deepcopy
|
| 2 |
+
import glob
|
| 3 |
+
import os
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
import time
|
| 6 |
+
|
| 7 |
+
import cv2
|
| 8 |
+
import hydra
|
| 9 |
+
import matplotlib.pyplot as plt
|
| 10 |
+
import numpy as np
|
| 11 |
+
import pybullet as p
|
| 12 |
+
|
| 13 |
+
from calvin_env.envs.tasks import Tasks
|
| 14 |
+
from calvin_env.utils import utils
|
| 15 |
+
|
| 16 |
+
"""
|
| 17 |
+
This script loads a rendered episode and replays it using the recorded actions.
|
| 18 |
+
Optionally, gaussian noise can be added to the actions.
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def noise(action, pos_std=0.01, rot_std=1):
|
| 23 |
+
"""
|
| 24 |
+
adds gaussian noise to position and orientation.
|
| 25 |
+
units are m for pos and degree for rot
|
| 26 |
+
"""
|
| 27 |
+
pos, orn, gripper = action
|
| 28 |
+
rot_std = np.radians(rot_std)
|
| 29 |
+
pos_noise = np.random.normal(0, pos_std, 3)
|
| 30 |
+
rot_noise = p.getQuaternionFromEuler(np.random.normal(0, rot_std, 3))
|
| 31 |
+
pos, orn = p.multiplyTransforms(pos, orn, pos_noise, rot_noise)
|
| 32 |
+
return pos, orn, gripper
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
@hydra.main(config_path="../../conf", config_name="config_data_collection")
|
| 36 |
+
def run_env(cfg):
|
| 37 |
+
env = hydra.utils.instantiate(cfg.env, show_gui=False, use_vr=False, use_scene_info=True)
|
| 38 |
+
|
| 39 |
+
root_dir = Path("/tmp/test_render/2021-10-05/16-51-11")
|
| 40 |
+
|
| 41 |
+
ep_start_end_ids = np.sort(np.load(root_dir / "ep_start_end_ids.npy"), axis=0)
|
| 42 |
+
rel_actions = []
|
| 43 |
+
tasks = hydra.utils.instantiate(cfg.tasks)
|
| 44 |
+
prev_info = None
|
| 45 |
+
t1 = time.time()
|
| 46 |
+
for s, e in ep_start_end_ids:
|
| 47 |
+
print("new_episode")
|
| 48 |
+
for i in range(s, e + 1):
|
| 49 |
+
file = root_dir / f"episode_{i:07d}.npz"
|
| 50 |
+
data = np.load(file)
|
| 51 |
+
img = data["rgb_static"]
|
| 52 |
+
cv2.imshow("win2", cv2.resize(img[:, :, ::-1], (500, 500)))
|
| 53 |
+
cv2.waitKey(1)
|
| 54 |
+
|
| 55 |
+
if (i - s) % 32 == 0:
|
| 56 |
+
print(f"reset {i}")
|
| 57 |
+
env.reset(scene_obs=data["scene_obs"], robot_obs=data["robot_obs"])
|
| 58 |
+
action = data["rel_actions"]
|
| 59 |
+
# action = np.split(data["actions"], [3, 6])
|
| 60 |
+
# action = noise(action)
|
| 61 |
+
|
| 62 |
+
# rel_actions.append(create_relative_action(data["actions"], data["robot_obs"][:6]))
|
| 63 |
+
# action = utils.to_relative_action(data["actions"], data["robot_obs"], max_pos=0.04, max_orn=0.1)
|
| 64 |
+
# tcp_pos, tcp_orn = p.getLinkState(env.robot.robot_uid, env.robot.tcp_link_id, physicsClientId=env.cid)[:2]
|
| 65 |
+
# tcp_orn = p.getEulerFromQuaternion(tcp_orn)
|
| 66 |
+
# action2 = create_relative_action(data["actions"], np.concatenate([tcp_pos, tcp_orn]))
|
| 67 |
+
o, _, _, info = env.step(action)
|
| 68 |
+
print(info["scene_info"]["lights"]["led"]["logical_state"])
|
| 69 |
+
if (i - s) % 32 != 0:
|
| 70 |
+
print(tasks.get_task_info(prev_info, info))
|
| 71 |
+
else:
|
| 72 |
+
prev_info = deepcopy(info)
|
| 73 |
+
time.sleep(0.01)
|
| 74 |
+
print(time.time() - t1)
|
| 75 |
+
# rel_actions = np.array(rel_actions)
|
| 76 |
+
# for j in range(rel_actions.shape[1]):
|
| 77 |
+
# plt.figure(j)
|
| 78 |
+
# plt.hist(rel_actions[:, j], bins=10)
|
| 79 |
+
# plt.plot()
|
| 80 |
+
# plt.show()
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
if __name__ == "__main__":
|
| 84 |
+
run_env()
|
aloha_robot_project/calvin/calvin_env/calvin_env/scripts/unnormalize_depth.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
from shutil import copyfile, copytree
|
| 3 |
+
|
| 4 |
+
import numpy as np
|
| 5 |
+
from tqdm import tqdm
|
| 6 |
+
|
| 7 |
+
load_path = Path("/home/meeso/expert_demos_03_10/training")
|
| 8 |
+
|
| 9 |
+
save_path = Path("/home/meeso/expert_demos_03_10/training_unnormalized_depth")
|
| 10 |
+
save_path.mkdir(parents=True, exist_ok=True)
|
| 11 |
+
|
| 12 |
+
for file in tqdm(load_path.glob("*.npz")):
|
| 13 |
+
data = np.load(file)
|
| 14 |
+
corrected_data = dict(data.items())
|
| 15 |
+
corrected_data["depth_static"] = data["depth_static"] * 2.0
|
| 16 |
+
corrected_data["depth_gripper"] = data["depth_gripper"] * 2.0
|
| 17 |
+
np.savez(save_path / file.name, **corrected_data)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
for file in set(load_path.glob("*")) - set(load_path.glob("*.npz")):
|
| 21 |
+
if file.is_dir():
|
| 22 |
+
copytree(file, save_path / file.name)
|
| 23 |
+
else:
|
| 24 |
+
copyfile(file, save_path / file.name)
|
aloha_robot_project/calvin/calvin_env/calvin_env/utils/utils.py
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import itertools
|
| 2 |
+
import logging
|
| 3 |
+
import os
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
import pickle
|
| 6 |
+
import re
|
| 7 |
+
import subprocess
|
| 8 |
+
import time
|
| 9 |
+
from typing import Union
|
| 10 |
+
|
| 11 |
+
import git
|
| 12 |
+
import numpy as np
|
| 13 |
+
import quaternion
|
| 14 |
+
|
| 15 |
+
# A logger for this file
|
| 16 |
+
logger = logging.getLogger(__name__)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def timeit(method):
|
| 20 |
+
def timed(*args, **kw):
|
| 21 |
+
ts = time.time()
|
| 22 |
+
result = method(*args, **kw)
|
| 23 |
+
te = time.time()
|
| 24 |
+
if "log_time" in kw:
|
| 25 |
+
name = kw.get("log_name", method.__name__.upper())
|
| 26 |
+
kw["log_time"][name] = int((te - ts) * 1000)
|
| 27 |
+
else:
|
| 28 |
+
print("%r %2.2f ms" % (method.__name__, (te - ts) * 1000))
|
| 29 |
+
return result
|
| 30 |
+
|
| 31 |
+
return timed
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class FpsController:
|
| 35 |
+
def __init__(self, freq):
|
| 36 |
+
self.loop_time = (1.0 / freq) * 10**9
|
| 37 |
+
self.prev_time = time.time_ns()
|
| 38 |
+
|
| 39 |
+
def step(self):
|
| 40 |
+
current_time = time.time_ns()
|
| 41 |
+
delta_t = current_time - self.prev_time
|
| 42 |
+
if delta_t < self.loop_time:
|
| 43 |
+
nano_sleep(self.loop_time - delta_t)
|
| 44 |
+
self.prev_time = time.time_ns()
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def xyzw_to_wxyz(arr):
|
| 48 |
+
"""
|
| 49 |
+
Convert quaternions from pyBullet to numpy.
|
| 50 |
+
"""
|
| 51 |
+
return [arr[3], arr[0], arr[1], arr[2]]
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def wxyz_to_xyzw(arr):
|
| 55 |
+
"""
|
| 56 |
+
Convert quaternions from numpy to pyBullet.
|
| 57 |
+
"""
|
| 58 |
+
return [arr[1], arr[2], arr[3], arr[0]]
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def nano_sleep(time_ns):
|
| 62 |
+
"""
|
| 63 |
+
Spinlock style sleep function. Burns cpu power on purpose
|
| 64 |
+
equivalent to time.sleep(time_ns / (10 ** 9)).
|
| 65 |
+
|
| 66 |
+
Should be more precise, especially on Windows.
|
| 67 |
+
|
| 68 |
+
Args:
|
| 69 |
+
time_ns: time to sleep in ns
|
| 70 |
+
|
| 71 |
+
Returns:
|
| 72 |
+
|
| 73 |
+
"""
|
| 74 |
+
wait_until = time.time_ns() + time_ns
|
| 75 |
+
while time.time_ns() < wait_until:
|
| 76 |
+
pass
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def unit_vector(vector):
|
| 80 |
+
"""Returns the unit vector of the vector."""
|
| 81 |
+
return vector / np.linalg.norm(vector)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def angle_between_quaternions(q1, q2):
|
| 85 |
+
"""
|
| 86 |
+
Returns the minimum rotation angle between to orientations expressed as quaternions
|
| 87 |
+
quaternions use X,Y,Z,W convention
|
| 88 |
+
"""
|
| 89 |
+
q1 = xyzw_to_wxyz(q1)
|
| 90 |
+
q2 = xyzw_to_wxyz(q2)
|
| 91 |
+
q1 = quaternion.from_float_array(q1)
|
| 92 |
+
q2 = quaternion.from_float_array(q2)
|
| 93 |
+
|
| 94 |
+
theta = 2 * np.arcsin(np.linalg.norm((q1 * q2.conjugate()).vec))
|
| 95 |
+
return theta
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def angle_between(v1, v2):
|
| 99 |
+
"""Returns the angle in radians between vectors 'v1' and 'v2'::
|
| 100 |
+
|
| 101 |
+
>>> angle_between((1, 0, 0), (0, 1, 0))
|
| 102 |
+
1.5707963267948966
|
| 103 |
+
>>> angle_between((1, 0, 0), (1, 0, 0))
|
| 104 |
+
0.0
|
| 105 |
+
>>> angle_between((1, 0, 0), (-1, 0, 0))
|
| 106 |
+
3.141592653589793
|
| 107 |
+
"""
|
| 108 |
+
v1_u = unit_vector(v1)
|
| 109 |
+
v2_u = unit_vector(v2)
|
| 110 |
+
return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def get_git_commit_hash(repo_path: Path) -> str:
|
| 114 |
+
repo = git.Repo(search_parent_directories=True, path=repo_path.parent)
|
| 115 |
+
assert repo, "not a repo"
|
| 116 |
+
changed_files = [item.a_path for item in repo.index.diff(None)]
|
| 117 |
+
if changed_files:
|
| 118 |
+
print("WARNING uncommitted modified files: {}".format(",".join(changed_files)))
|
| 119 |
+
return repo.head.object.hexsha
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
class EglDeviceNotFoundError(Exception):
|
| 123 |
+
"""Raised when EGL device cannot be found"""
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def get_egl_device_id(cuda_id: int) -> Union[int]:
|
| 127 |
+
"""
|
| 128 |
+
>>> i = get_egl_device_id(0)
|
| 129 |
+
>>> isinstance(i, int)
|
| 130 |
+
True
|
| 131 |
+
"""
|
| 132 |
+
assert isinstance(cuda_id, int), "cuda_id has to be integer"
|
| 133 |
+
dir_path = Path(__file__).absolute().parents[2] / "egl_check"
|
| 134 |
+
if not os.path.isfile(dir_path / "EGL_options.o"):
|
| 135 |
+
if os.environ.get("LOCAL_RANK", "0") == "0":
|
| 136 |
+
print("Building EGL_options.o")
|
| 137 |
+
subprocess.call(["bash", "build.sh"], cwd=dir_path)
|
| 138 |
+
else:
|
| 139 |
+
# In case EGL_options.o has to be built and multiprocessing is used, give rank 0 process time to build
|
| 140 |
+
time.sleep(5)
|
| 141 |
+
result = subprocess.run(["./EGL_options.o"], capture_output=True, cwd=dir_path)
|
| 142 |
+
n = int(result.stderr.decode("utf-8").split(" of ")[1].split(".")[0])
|
| 143 |
+
for egl_id in range(n):
|
| 144 |
+
my_env = os.environ.copy()
|
| 145 |
+
my_env["EGL_VISIBLE_DEVICE"] = str(egl_id)
|
| 146 |
+
result = subprocess.run(["./EGL_options.o"], capture_output=True, cwd=dir_path, env=my_env)
|
| 147 |
+
match = re.search(r"CUDA_DEVICE=[0-9]+", result.stdout.decode("utf-8"))
|
| 148 |
+
if match:
|
| 149 |
+
current_cuda_id = int(match[0].split("=")[1])
|
| 150 |
+
if cuda_id == current_cuda_id:
|
| 151 |
+
return egl_id
|
| 152 |
+
raise EglDeviceNotFoundError
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def angle_between_angles(a, b):
|
| 156 |
+
diff = b - a
|
| 157 |
+
return (diff + np.pi) % (2 * np.pi) - np.pi
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
def to_relative_action(actions, robot_obs, max_pos=0.02, max_orn=0.05):
|
| 161 |
+
assert isinstance(actions, np.ndarray)
|
| 162 |
+
assert isinstance(robot_obs, np.ndarray)
|
| 163 |
+
|
| 164 |
+
rel_pos = actions[:3] - robot_obs[:3]
|
| 165 |
+
rel_pos = np.clip(rel_pos, -max_pos, max_pos) / max_pos
|
| 166 |
+
|
| 167 |
+
rel_orn = angle_between_angles(robot_obs[3:6], actions[3:6])
|
| 168 |
+
rel_orn = np.clip(rel_orn, -max_orn, max_orn) / max_orn
|
| 169 |
+
|
| 170 |
+
gripper = actions[-1:]
|
| 171 |
+
return np.concatenate([rel_pos, rel_orn, gripper])
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
def set_egl_device(device):
|
| 175 |
+
assert "EGL_VISIBLE_DEVICES" not in os.environ, "Do not manually set EGL_VISIBLE_DEVICES"
|
| 176 |
+
try:
|
| 177 |
+
cuda_id = device.index if device.type == "cuda" else 0
|
| 178 |
+
except AttributeError:
|
| 179 |
+
cuda_id = device
|
| 180 |
+
try:
|
| 181 |
+
egl_id = get_egl_device_id(cuda_id)
|
| 182 |
+
except EglDeviceNotFoundError:
|
| 183 |
+
logger.warning(
|
| 184 |
+
"Couldn't find correct EGL device. Setting EGL_VISIBLE_DEVICE=0. "
|
| 185 |
+
"When using DDP with many GPUs this can lead to OOM errors. "
|
| 186 |
+
"Did you install PyBullet correctly? Please refer to VREnv README"
|
| 187 |
+
)
|
| 188 |
+
egl_id = 0
|
| 189 |
+
os.environ["EGL_VISIBLE_DEVICES"] = str(egl_id)
|
| 190 |
+
logger.info(f"EGL_DEVICE_ID {egl_id} <==> CUDA_DEVICE_ID {cuda_id}")
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
def count_frames(directory):
|
| 194 |
+
"""
|
| 195 |
+
counts the number of consecutive pickled frames in directory
|
| 196 |
+
|
| 197 |
+
Args:
|
| 198 |
+
directory: str of directory
|
| 199 |
+
|
| 200 |
+
Returns:
|
| 201 |
+
0 for none, otherwise >0
|
| 202 |
+
"""
|
| 203 |
+
|
| 204 |
+
for i in itertools.count(start=0):
|
| 205 |
+
pickle_file = os.path.join(directory, f"{str(i).zfill(12)}.pickle")
|
| 206 |
+
if not os.path.isfile(pickle_file):
|
| 207 |
+
return i
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
def get_episode_lengths(load_dir, num_frames):
|
| 211 |
+
episode_lengths = []
|
| 212 |
+
render_start_end_ids = [[0]]
|
| 213 |
+
i = 0
|
| 214 |
+
for frame in range(num_frames):
|
| 215 |
+
file_path = os.path.abspath(os.path.join(load_dir, f"{str(frame).zfill(12)}.pickle"))
|
| 216 |
+
with open(file_path, "rb") as file:
|
| 217 |
+
data = pickle.load(file)
|
| 218 |
+
done = data["done"]
|
| 219 |
+
if not done:
|
| 220 |
+
i += 1
|
| 221 |
+
else:
|
| 222 |
+
episode_lengths.append(i)
|
| 223 |
+
render_start_end_ids[-1].append(frame + 1)
|
| 224 |
+
render_start_end_ids.append([frame + 1])
|
| 225 |
+
i = 0
|
| 226 |
+
render_start_end_ids = render_start_end_ids[:-1]
|
| 227 |
+
return episode_lengths, render_start_end_ids
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
if __name__ == "__main__":
|
| 231 |
+
import doctest
|
| 232 |
+
|
| 233 |
+
doctest.testmod()
|
aloha_robot_project/calvin/calvin_env/calvin_env/vrdatacollector.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/python3
|
| 2 |
+
from copy import deepcopy
|
| 3 |
+
import logging
|
| 4 |
+
import os
|
| 5 |
+
import sys
|
| 6 |
+
|
| 7 |
+
import hydra
|
| 8 |
+
import pybullet as p
|
| 9 |
+
import quaternion # noqa
|
| 10 |
+
|
| 11 |
+
from calvin_env.io_utils.data_recorder import DataRecorder
|
| 12 |
+
from calvin_env.io_utils.vr_input import VrInput
|
| 13 |
+
|
| 14 |
+
# A logger for this file
|
| 15 |
+
log = logging.getLogger(__name__)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@hydra.main(config_path="../conf", config_name="config_data_collection")
|
| 19 |
+
def main(cfg):
|
| 20 |
+
# Load Scene
|
| 21 |
+
env = hydra.utils.instantiate(cfg.env)
|
| 22 |
+
vr_input = hydra.utils.instantiate(cfg.vr_input)
|
| 23 |
+
|
| 24 |
+
data_recorder = None
|
| 25 |
+
if cfg.recorder.record:
|
| 26 |
+
data_recorder = DataRecorder(env, cfg.recorder.record_fps, cfg.recorder.enable_tts)
|
| 27 |
+
|
| 28 |
+
log.info("Initialization done!")
|
| 29 |
+
log.info("Entering Loop")
|
| 30 |
+
|
| 31 |
+
record = False
|
| 32 |
+
|
| 33 |
+
while 1:
|
| 34 |
+
# get input events
|
| 35 |
+
action = vr_input.get_vr_action()
|
| 36 |
+
obs, _, _, info = env.step(action)
|
| 37 |
+
done = False
|
| 38 |
+
if vr_input.reset_button_pressed:
|
| 39 |
+
done = True
|
| 40 |
+
if vr_input.start_button_pressed:
|
| 41 |
+
record = True
|
| 42 |
+
if vr_input.reset_button_hold:
|
| 43 |
+
data_recorder.delete_episode()
|
| 44 |
+
if record and cfg.recorder.record:
|
| 45 |
+
data_recorder.step(vr_input.prev_vr_events, obs, done, info)
|
| 46 |
+
if done:
|
| 47 |
+
record = False
|
| 48 |
+
env.reset()
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
if __name__ == "__main__":
|
| 52 |
+
main()
|
aloha_robot_project/calvin/calvin_env/conf/cameras/cameras/gripper.yaml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_target_: calvin_env.camera.gripper_camera.GripperCamera
|
| 2 |
+
name: gripper
|
| 3 |
+
fov: 75
|
| 4 |
+
aspect: 1
|
| 5 |
+
nearval: 0.01
|
| 6 |
+
farval: 2
|
| 7 |
+
width: 84
|
| 8 |
+
height: 84
|
aloha_robot_project/calvin/calvin_env/conf/cameras/cameras/opposing.yaml
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_target_: calvin_env.camera.static_camera.StaticCamera
|
| 2 |
+
name: opposing
|
| 3 |
+
fov: 75
|
| 4 |
+
aspect: 1
|
| 5 |
+
nearval: 0.01
|
| 6 |
+
farval: 2
|
| 7 |
+
width: 200
|
| 8 |
+
height: 200
|
| 9 |
+
look_at: [ 0.4, 0.5, 0.6 ]
|
| 10 |
+
look_from: [ 0.4, 1.5, 0.9 ]
|
aloha_robot_project/calvin/calvin_env/conf/cameras/cameras/static.yaml
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_target_: calvin_env.camera.static_camera.StaticCamera
|
| 2 |
+
name: static
|
| 3 |
+
fov: 10
|
| 4 |
+
aspect: 1
|
| 5 |
+
nearval: 0.01
|
| 6 |
+
farval: 10
|
| 7 |
+
width: 200
|
| 8 |
+
height: 200
|
| 9 |
+
look_at: [ -0.026242351159453392, -0.0302329882979393, 0.3920000493526459]
|
| 10 |
+
look_from: [ 2.871459009488717, -2.166602199425597, 2.555159848480571]
|
| 11 |
+
up_vector: [ 0.4041403970338857, 0.22629790978217404, 0.8862616969685161]
|