reachy-mini 1.0.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of reachy-mini might be problematic. Click here for more details.
- reachy_mini/__init__.py +4 -0
- reachy_mini/apps/__init__.py +23 -0
- reachy_mini/apps/app.py +119 -0
- reachy_mini/apps/manager.py +178 -0
- reachy_mini/apps/sources/__init__.py +4 -0
- reachy_mini/apps/sources/hf_space.py +25 -0
- reachy_mini/apps/sources/local_common_venv.py +38 -0
- reachy_mini/apps/templates/README.md.j2 +1 -0
- reachy_mini/apps/templates/main.py.j2 +46 -0
- reachy_mini/apps/templates/pyproject.toml.j2 +18 -0
- reachy_mini/apps/utils.py +30 -0
- reachy_mini/assets/config/hardware_config.yaml +119 -0
- reachy_mini/assets/confused1.wav +0 -0
- reachy_mini/assets/count.wav +0 -0
- reachy_mini/assets/dance1.wav +0 -0
- reachy_mini/assets/go_sleep.wav +0 -0
- reachy_mini/assets/impatient1.wav +0 -0
- reachy_mini/assets/kinematics_data.json +253 -0
- reachy_mini/assets/models/fknetwork.dynamic.onnx +3 -0
- reachy_mini/assets/models/fknetwork.onnx +3 -0
- reachy_mini/assets/models/fknetwork_int8.onnx +3 -0
- reachy_mini/assets/models/iknetwork.onnx +3 -0
- reachy_mini/assets/wake_up.wav +0 -0
- reachy_mini/daemon/__init__.py +1 -0
- reachy_mini/daemon/app/__init__.py +1 -0
- reachy_mini/daemon/app/dashboard/apps_manager.html +202 -0
- reachy_mini/daemon/app/dashboard/js/3rdparty/gstwebrtc-api-2.0.0.min.js +5 -0
- reachy_mini/daemon/app/dashboard/js/dashboard.js +121 -0
- reachy_mini/daemon/app/dashboard/live_robot_state.html +242 -0
- reachy_mini/daemon/app/dashboard/style.css +37 -0
- reachy_mini/daemon/app/dependencies.py +36 -0
- reachy_mini/daemon/app/main.py +271 -0
- reachy_mini/daemon/app/models.py +146 -0
- reachy_mini/daemon/app/routers/apps.py +220 -0
- reachy_mini/daemon/app/routers/daemon.py +53 -0
- reachy_mini/daemon/app/routers/kinematics.py +36 -0
- reachy_mini/daemon/app/routers/motors.py +41 -0
- reachy_mini/daemon/app/routers/move.py +200 -0
- reachy_mini/daemon/app/routers/state.py +137 -0
- reachy_mini/daemon/app/templates/dashboard.html +40 -0
- reachy_mini/daemon/backend/__init__.py +1 -0
- reachy_mini/daemon/backend/abstract.py +717 -0
- reachy_mini/daemon/backend/mujoco/__init__.py +36 -0
- reachy_mini/daemon/backend/mujoco/backend.py +304 -0
- reachy_mini/daemon/backend/mujoco/utils.py +59 -0
- reachy_mini/daemon/backend/mujoco/video_udp.py +53 -0
- reachy_mini/daemon/backend/robot/__init__.py +8 -0
- reachy_mini/daemon/backend/robot/backend.py +490 -0
- reachy_mini/daemon/daemon.py +442 -0
- reachy_mini/daemon/utils.py +114 -0
- reachy_mini/descriptions/reachy_mini/mjcf/additional.xml +6 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/5w_speaker.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/5w_speaker.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_body_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_body_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_l_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_l_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_r_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_r_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_interface_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_interface_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/arducam.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/arducam.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh_1.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bearing_85x110x13.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bearing_85x110x13.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/big_lens_d40.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/big_lens_d40.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_down_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_down_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_foot_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_foot_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_top_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_top_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_turning_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_turning_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bts2_m2_6x8.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bts2_m2_6x8.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/croissant_1k.blend/croissant_1k.obj +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/croissant_1k.blend/textures/croissant_diff_1k.png +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_b_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_b_dummy.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_f_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_f_dummy.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_m_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_m_dummy.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_horn_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_horn_dummy.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_led_cap2_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_led_cap2_dummy.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/food_apple_01_1k.blend/food_apple_01_1k.obj +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/food_apple_01_1k.blend/textures/food_apple_01_diff_1k.png +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/glasses_dolder_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/glasses_dolder_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_back_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_back_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_front_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_front_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_mic_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_mic_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d30_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d30_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d40_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d40_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/m12_fisheye_lens_1_8mm.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/m12_fisheye_lens_1_8mm.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/mp01062_stewart_arm_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/mp01062_stewart_arm_3.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/neck_reference_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/neck_reference_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_1.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_2.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_2.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_3.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/pp01102_arducam_carter.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/pp01102_arducam_carter.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/rubber_duck_toy_1k.blend/rubber_duck_toy_1k.obj +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/rubber_duck_toy_1k.blend/textures/rubber_duck_toy_diff_1k.png +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/small_lens_d30.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/small_lens_d30.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball__2.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball__2.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_rod.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_main_plate_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_main_plate_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_tricap_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_tricap_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/wooden_table_02_1k.blend/textures/wooden_table_02_diff_1k.png +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/wooden_table_02_1k.blend/wooden_table_02_1k.obj +3 -0
- reachy_mini/descriptions/reachy_mini/mjcf/config.json +21 -0
- reachy_mini/descriptions/reachy_mini/mjcf/joints_properties.xml +29 -0
- reachy_mini/descriptions/reachy_mini/mjcf/reachy_mini.xml +613 -0
- reachy_mini/descriptions/reachy_mini/mjcf/reachy_mini.xml.bak +442 -0
- reachy_mini/descriptions/reachy_mini/mjcf/scene.xml +24 -0
- reachy_mini/descriptions/reachy_mini/mjcf/scenes/empty.xml +27 -0
- reachy_mini/descriptions/reachy_mini/mjcf/scenes/minimal.xml +131 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arm.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arm.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/ball.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/ball.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/foot.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/foot.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/rod.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/shape.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/shape.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.stl +3 -0
- reachy_mini/descriptions/reachy_mini/urdf/config.json +18 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot.urdf +3282 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot.urdf.bak +3282 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot_no_collision.urdf +2316 -0
- reachy_mini/io/__init__.py +1 -0
- reachy_mini/io/abstract.py +70 -0
- reachy_mini/io/protocol.py +44 -0
- reachy_mini/io/zenoh_client.py +258 -0
- reachy_mini/io/zenoh_server.py +183 -0
- reachy_mini/kinematics/__init__.py +68 -0
- reachy_mini/kinematics/analytical_kinematics.py +102 -0
- reachy_mini/kinematics/nn_kinematics.py +100 -0
- reachy_mini/kinematics/placo_kinematics.py +662 -0
- reachy_mini/media/__init__.py +1 -0
- reachy_mini/media/audio_base.py +75 -0
- reachy_mini/media/audio_gstreamer.py +194 -0
- reachy_mini/media/audio_sounddevice.py +207 -0
- reachy_mini/media/audio_utils.py +27 -0
- reachy_mini/media/camera_base.py +58 -0
- reachy_mini/media/camera_constants.py +13 -0
- reachy_mini/media/camera_gstreamer.py +144 -0
- reachy_mini/media/camera_opencv.py +61 -0
- reachy_mini/media/camera_utils.py +60 -0
- reachy_mini/media/media_manager.py +186 -0
- reachy_mini/motion/__init__.py +4 -0
- reachy_mini/motion/goto.py +71 -0
- reachy_mini/motion/move.py +36 -0
- reachy_mini/motion/recorded_move.py +132 -0
- reachy_mini/reachy_mini.py +705 -0
- reachy_mini/utils/__init__.py +46 -0
- reachy_mini/utils/constants.py +9 -0
- reachy_mini/utils/interpolation.py +227 -0
- reachy_mini/utils/parse_urdf_for_kinematics.py +85 -0
- reachy_mini-1.0.0rc1.dist-info/METADATA +261 -0
- reachy_mini-1.0.0rc1.dist-info/RECORD +359 -0
- reachy_mini-1.0.0rc1.dist-info/WHEEL +5 -0
- reachy_mini-1.0.0rc1.dist-info/entry_points.txt +3 -0
- reachy_mini-1.0.0rc1.dist-info/licenses/LICENSE +201 -0
- reachy_mini-1.0.0rc1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""Common pydantic models definitions."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from numpy.typing import NDArray
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
from scipy.spatial.transform import Rotation as R
|
|
9
|
+
|
|
10
|
+
from reachy_mini.daemon.backend.abstract import MotorControlMode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Matrix4x4Pose(BaseModel):
|
|
14
|
+
"""Represent a 3D pose by its 4x4 transformation matrix (translation is expressed in meters)."""
|
|
15
|
+
|
|
16
|
+
m: tuple[
|
|
17
|
+
float,
|
|
18
|
+
float,
|
|
19
|
+
float,
|
|
20
|
+
float,
|
|
21
|
+
float,
|
|
22
|
+
float,
|
|
23
|
+
float,
|
|
24
|
+
float,
|
|
25
|
+
float,
|
|
26
|
+
float,
|
|
27
|
+
float,
|
|
28
|
+
float,
|
|
29
|
+
float,
|
|
30
|
+
float,
|
|
31
|
+
float,
|
|
32
|
+
float,
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def from_pose_array(cls, arr: NDArray[np.float64]) -> "Matrix4x4Pose":
|
|
37
|
+
"""Create a Matrix4x4 pose representation from a 4x4 pose array."""
|
|
38
|
+
assert arr.shape == (4, 4), "Array must be of shape (4, 4)"
|
|
39
|
+
m: tuple[
|
|
40
|
+
float,
|
|
41
|
+
float,
|
|
42
|
+
float,
|
|
43
|
+
float,
|
|
44
|
+
float,
|
|
45
|
+
float,
|
|
46
|
+
float,
|
|
47
|
+
float,
|
|
48
|
+
float,
|
|
49
|
+
float,
|
|
50
|
+
float,
|
|
51
|
+
float,
|
|
52
|
+
float,
|
|
53
|
+
float,
|
|
54
|
+
float,
|
|
55
|
+
float,
|
|
56
|
+
] = tuple(arr.flatten().tolist()) # type: ignore [assignment]
|
|
57
|
+
return cls(m=m)
|
|
58
|
+
|
|
59
|
+
def to_pose_array(self) -> NDArray[np.float64]:
|
|
60
|
+
"""Convert the Matrix4x4Pose to a 4x4 numpy array."""
|
|
61
|
+
return np.array(self.m).reshape((4, 4))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class XYZRPYPose(BaseModel):
|
|
65
|
+
"""Represent a 3D pose using position (x, y, z) in meters and orientation (roll, pitch, yaw) angles in radians."""
|
|
66
|
+
|
|
67
|
+
x: float = 0.0
|
|
68
|
+
y: float = 0.0
|
|
69
|
+
z: float = 0.0
|
|
70
|
+
roll: float = 0.0
|
|
71
|
+
pitch: float = 0.0
|
|
72
|
+
yaw: float = 0.0
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def from_pose_array(cls, arr: NDArray[np.float64]) -> "XYZRPYPose":
|
|
76
|
+
"""Create an XYZRPYPose representation from a 4x4 pose array."""
|
|
77
|
+
assert arr.shape == (4, 4), "Array must be of shape (4, 4)"
|
|
78
|
+
|
|
79
|
+
x, y, z = arr[0, 3], arr[1, 3], arr[2, 3]
|
|
80
|
+
roll, pitch, yaw = R.from_matrix(arr[:3, :3]).as_euler("xyz")
|
|
81
|
+
|
|
82
|
+
return cls(
|
|
83
|
+
x=x,
|
|
84
|
+
y=y,
|
|
85
|
+
z=z,
|
|
86
|
+
roll=roll,
|
|
87
|
+
pitch=pitch,
|
|
88
|
+
yaw=yaw,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def to_pose_array(self) -> NDArray[np.float64]:
|
|
92
|
+
"""Convert the XYZRPYPose to a 4x4 numpy array."""
|
|
93
|
+
rotation = R.from_euler("xyz", [self.roll, self.pitch, self.yaw])
|
|
94
|
+
pose_matrix = np.eye(4)
|
|
95
|
+
pose_matrix[:3, 3] = [self.x, self.y, self.z]
|
|
96
|
+
pose_matrix[:3, :3] = rotation.as_matrix()
|
|
97
|
+
return pose_matrix
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
AnyPose = XYZRPYPose | Matrix4x4Pose
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def as_any_pose(pose: NDArray[np.float64], use_matrix: bool) -> AnyPose:
|
|
104
|
+
"""Convert a numpy array to an AnyPose representation."""
|
|
105
|
+
return (
|
|
106
|
+
Matrix4x4Pose.from_pose_array(pose)
|
|
107
|
+
if use_matrix
|
|
108
|
+
else XYZRPYPose.from_pose_array(pose)
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class FullBodyTarget(BaseModel):
|
|
113
|
+
"""Represent the full body including the head pose and the joints for antennas."""
|
|
114
|
+
|
|
115
|
+
target_head_pose: AnyPose | None = None
|
|
116
|
+
target_antennas: tuple[float, float] | None = None
|
|
117
|
+
timestamp: datetime | None = None
|
|
118
|
+
|
|
119
|
+
model_config = {
|
|
120
|
+
"json_schema_extra": {
|
|
121
|
+
"examples": [
|
|
122
|
+
{
|
|
123
|
+
"target_head_pose": {
|
|
124
|
+
"x": 0.0,
|
|
125
|
+
"y": 0.0,
|
|
126
|
+
"z": 0.0,
|
|
127
|
+
"roll": 0.0,
|
|
128
|
+
"pitch": 0.0,
|
|
129
|
+
"yaw": 0.0,
|
|
130
|
+
},
|
|
131
|
+
"target_antennas": [0.0, 0.0],
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class FullState(BaseModel):
|
|
139
|
+
"""Represent the full state of the robot including all joint positions and poses."""
|
|
140
|
+
|
|
141
|
+
control_mode: MotorControlMode | None = None
|
|
142
|
+
head_pose: AnyPose | None = None
|
|
143
|
+
head_joints: list[float] | None = None
|
|
144
|
+
body_yaw: float | None = None
|
|
145
|
+
antennas_position: list[float] | None = None
|
|
146
|
+
timestamp: datetime | None = None
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""Apps router for apps management."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
import uuid
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Any, Awaitable, Callable
|
|
8
|
+
|
|
9
|
+
from fastapi import (
|
|
10
|
+
APIRouter,
|
|
11
|
+
BackgroundTasks,
|
|
12
|
+
Depends,
|
|
13
|
+
HTTPException,
|
|
14
|
+
WebSocket,
|
|
15
|
+
WebSocketDisconnect,
|
|
16
|
+
)
|
|
17
|
+
from pydantic import BaseModel
|
|
18
|
+
|
|
19
|
+
from reachy_mini.apps import AppInfo, SourceKind
|
|
20
|
+
from reachy_mini.apps.manager import AppManager, AppStatus
|
|
21
|
+
from reachy_mini.daemon.app.dependencies import get_app_manager
|
|
22
|
+
|
|
23
|
+
router = APIRouter(prefix="/apps")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class JobStatus(BaseModel):
|
|
27
|
+
"""Pydantic model for install job status."""
|
|
28
|
+
|
|
29
|
+
command: str
|
|
30
|
+
status: str
|
|
31
|
+
logs: list[str]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class JobHandler:
|
|
36
|
+
"""Handler for background jobs."""
|
|
37
|
+
|
|
38
|
+
status: JobStatus
|
|
39
|
+
new_log_evt: dict[str, asyncio.Event]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
jobs: dict[str, JobHandler] = {}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@router.get("/list-available/{source_kind}")
|
|
46
|
+
async def list_available_apps(
|
|
47
|
+
source_kind: SourceKind,
|
|
48
|
+
app_manager: "AppManager" = Depends(get_app_manager),
|
|
49
|
+
) -> list[AppInfo]:
|
|
50
|
+
"""List available apps (including not installed)."""
|
|
51
|
+
return await app_manager.list_available_apps(source_kind)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@router.get("/list-available")
|
|
55
|
+
async def list_all_available_apps(
|
|
56
|
+
app_manager: "AppManager" = Depends(get_app_manager),
|
|
57
|
+
) -> list[AppInfo]:
|
|
58
|
+
"""List all available apps (including not installed)."""
|
|
59
|
+
return await app_manager.list_all_available_apps()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@router.post("/install")
|
|
63
|
+
async def install_app(
|
|
64
|
+
app_info: AppInfo,
|
|
65
|
+
background_tasks: BackgroundTasks,
|
|
66
|
+
app_manager: "AppManager" = Depends(get_app_manager),
|
|
67
|
+
) -> dict[str, str]:
|
|
68
|
+
"""Install a new app by its info (background, returns job_id)."""
|
|
69
|
+
job_id = start_bg_job(
|
|
70
|
+
"install", background_tasks, app_manager.install_new_app, app_info
|
|
71
|
+
)
|
|
72
|
+
return {"job_id": job_id}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@router.post("/remove/{app_name}")
|
|
76
|
+
async def remove_app(
|
|
77
|
+
app_name: str,
|
|
78
|
+
background_tasks: BackgroundTasks,
|
|
79
|
+
app_manager: "AppManager" = Depends(get_app_manager),
|
|
80
|
+
) -> dict[str, str]:
|
|
81
|
+
"""Remove an installed app by its name (background, returns job_id)."""
|
|
82
|
+
job_id = start_bg_job("remove", background_tasks, app_manager.remove_app, app_name)
|
|
83
|
+
return {"job_id": job_id}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@router.get("/job-status/{job_id}")
|
|
87
|
+
async def job_status(job_id: str) -> JobStatus:
|
|
88
|
+
"""Get status/logs for a job."""
|
|
89
|
+
job = jobs.get(job_id)
|
|
90
|
+
if not job:
|
|
91
|
+
raise HTTPException(status_code=404, detail="Job not found")
|
|
92
|
+
return job.status
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# WebSocket route for live job status/logs
|
|
96
|
+
@router.websocket("/ws/apps-manager/{job_id}")
|
|
97
|
+
async def ws_apps_manager(websocket: WebSocket, job_id: str) -> None:
|
|
98
|
+
"""WebSocket route to stream live job status/logs for a job, sending updates as soon as new logs are available."""
|
|
99
|
+
await websocket.accept()
|
|
100
|
+
last_log_len = 0
|
|
101
|
+
|
|
102
|
+
job = jobs.get(job_id)
|
|
103
|
+
if not job:
|
|
104
|
+
await websocket.send_json({"error": "Job not found"})
|
|
105
|
+
await websocket.close()
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
assert job is not None # for mypy
|
|
109
|
+
|
|
110
|
+
ws_uuid = str(uuid.uuid4())
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
job.new_log_evt[ws_uuid] = asyncio.Event()
|
|
114
|
+
while True:
|
|
115
|
+
await job.new_log_evt[ws_uuid].wait()
|
|
116
|
+
job.new_log_evt[ws_uuid].clear()
|
|
117
|
+
new_logs = job.status.logs[last_log_len:]
|
|
118
|
+
last_log_len = len(job.status.logs)
|
|
119
|
+
await websocket.send_json(
|
|
120
|
+
{
|
|
121
|
+
"command": job.status.command,
|
|
122
|
+
"status": job.status.model_dump_json(),
|
|
123
|
+
"logs": new_logs,
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
if job.status.status.startswith("done") or job.status.status.startswith(
|
|
127
|
+
"error"
|
|
128
|
+
):
|
|
129
|
+
await websocket.close()
|
|
130
|
+
break
|
|
131
|
+
except WebSocketDisconnect:
|
|
132
|
+
pass
|
|
133
|
+
|
|
134
|
+
finally:
|
|
135
|
+
if ws_uuid in job.new_log_evt:
|
|
136
|
+
del job.new_log_evt[ws_uuid]
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@router.post("/start-app/{app_name}")
|
|
140
|
+
async def start_app(
|
|
141
|
+
app_name: str,
|
|
142
|
+
app_manager: "AppManager" = Depends(get_app_manager),
|
|
143
|
+
) -> AppStatus:
|
|
144
|
+
"""Start an app by its name."""
|
|
145
|
+
try:
|
|
146
|
+
return await app_manager.start_app(app_name)
|
|
147
|
+
except RuntimeError as e:
|
|
148
|
+
raise HTTPException(status_code=400, detail=str(e))
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@router.post("/restart-current-app")
|
|
152
|
+
async def restart_app(
|
|
153
|
+
app_manager: "AppManager" = Depends(get_app_manager),
|
|
154
|
+
) -> AppStatus:
|
|
155
|
+
"""Restart the currently running app."""
|
|
156
|
+
try:
|
|
157
|
+
return await app_manager.restart_current_app()
|
|
158
|
+
except RuntimeError as e:
|
|
159
|
+
raise HTTPException(status_code=400, detail=str(e))
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@router.post("/stop-current-app")
|
|
163
|
+
async def stop_app(
|
|
164
|
+
app_manager: "AppManager" = Depends(get_app_manager),
|
|
165
|
+
) -> None:
|
|
166
|
+
"""Stop the currently running app."""
|
|
167
|
+
try:
|
|
168
|
+
return await app_manager.stop_current_app()
|
|
169
|
+
except RuntimeError as e:
|
|
170
|
+
raise HTTPException(status_code=400, detail=str(e))
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@router.get("/current-app-status")
|
|
174
|
+
async def current_app_status(
|
|
175
|
+
app_manager: "AppManager" = Depends(get_app_manager),
|
|
176
|
+
) -> AppStatus | None:
|
|
177
|
+
"""Get the status of the currently running app, if any."""
|
|
178
|
+
return await app_manager.current_app_status()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def start_bg_job(
|
|
182
|
+
command: str,
|
|
183
|
+
background_tasks: BackgroundTasks,
|
|
184
|
+
coro_func: Callable[..., Awaitable[None]],
|
|
185
|
+
*args: Any,
|
|
186
|
+
) -> str:
|
|
187
|
+
"""Start a background job, with a custom logger and return its job_id."""
|
|
188
|
+
job_id = str(uuid.uuid4())
|
|
189
|
+
jobs[job_id] = JobHandler(
|
|
190
|
+
status=JobStatus(
|
|
191
|
+
command=command,
|
|
192
|
+
status="pending",
|
|
193
|
+
logs=[],
|
|
194
|
+
),
|
|
195
|
+
new_log_evt={},
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
async def run_command() -> None:
|
|
199
|
+
jobs[job_id].status.status = "running"
|
|
200
|
+
|
|
201
|
+
class JobLogger(logging.Handler):
|
|
202
|
+
def emit(self, record: logging.LogRecord) -> None:
|
|
203
|
+
jobs[job_id].status.logs.append(self.format(record))
|
|
204
|
+
for ws in jobs[job_id].new_log_evt.values():
|
|
205
|
+
ws.set()
|
|
206
|
+
|
|
207
|
+
logger = logging.getLogger(f"logs_job_{job_id}")
|
|
208
|
+
logger.setLevel(logging.INFO)
|
|
209
|
+
logger.handlers.clear()
|
|
210
|
+
logger.addHandler(JobLogger())
|
|
211
|
+
try:
|
|
212
|
+
await coro_func(*args, logger=logger)
|
|
213
|
+
jobs[job_id].status.status = "done"
|
|
214
|
+
logger.info(f"Job '{command}' completed successfully")
|
|
215
|
+
except Exception as e:
|
|
216
|
+
jobs[job_id].status.status = f"error: {e}"
|
|
217
|
+
logger.error(f"Job '{command}' failed with error: {e}")
|
|
218
|
+
|
|
219
|
+
background_tasks.add_task(run_command)
|
|
220
|
+
return job_id
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Daemon-related API routes."""
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, Depends, Request
|
|
4
|
+
|
|
5
|
+
from ...daemon import Daemon, DaemonStatus
|
|
6
|
+
from ..dependencies import get_daemon
|
|
7
|
+
|
|
8
|
+
router = APIRouter(
|
|
9
|
+
prefix="/daemon",
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@router.post("/start")
|
|
14
|
+
async def start_daemon(
|
|
15
|
+
request: Request,
|
|
16
|
+
wake_up: bool,
|
|
17
|
+
daemon: Daemon = Depends(get_daemon),
|
|
18
|
+
) -> DaemonStatus:
|
|
19
|
+
"""Start the daemon."""
|
|
20
|
+
await daemon.start(
|
|
21
|
+
sim=request.app.state.args.sim,
|
|
22
|
+
scene=request.app.state.args.scene,
|
|
23
|
+
headless=request.app.state.args.headless,
|
|
24
|
+
wake_up_on_start=wake_up,
|
|
25
|
+
)
|
|
26
|
+
return daemon.status()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@router.post("/stop")
|
|
30
|
+
async def stop_daemon(
|
|
31
|
+
goto_sleep: bool, daemon: Daemon = Depends(get_daemon)
|
|
32
|
+
) -> DaemonStatus:
|
|
33
|
+
"""Stop the daemon, optionally putting the robot to sleep."""
|
|
34
|
+
await daemon.stop(goto_sleep_on_stop=goto_sleep)
|
|
35
|
+
return daemon.status()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@router.post("/restart")
|
|
39
|
+
async def restart_daemon(
|
|
40
|
+
request: Request, daemon: Daemon = Depends(get_daemon)
|
|
41
|
+
) -> DaemonStatus:
|
|
42
|
+
"""Restart the daemon."""
|
|
43
|
+
await daemon.restart(
|
|
44
|
+
sim=request.app.state.args.sim,
|
|
45
|
+
scene=request.app.state.args.scene,
|
|
46
|
+
)
|
|
47
|
+
return daemon.status()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@router.get("/status")
|
|
51
|
+
async def get_daemon_status(daemon: Daemon = Depends(get_daemon)) -> DaemonStatus:
|
|
52
|
+
"""Get the current status of the daemon."""
|
|
53
|
+
return daemon.status()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Kinematics router for handling kinematics-related requests.
|
|
2
|
+
|
|
3
|
+
This module defines the API endpoints for interacting with the kinematics
|
|
4
|
+
subsystem of the robot. It provides endpoints for retrieving URDF representation,
|
|
5
|
+
and other kinematics-related information.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from fastapi import APIRouter, Depends
|
|
11
|
+
|
|
12
|
+
from ....daemon.backend.abstract import Backend
|
|
13
|
+
from ..dependencies import get_backend
|
|
14
|
+
|
|
15
|
+
router = APIRouter(
|
|
16
|
+
prefix="/kinematics",
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@router.get("/info")
|
|
21
|
+
async def get_kinematics_info(
|
|
22
|
+
backend: Backend = Depends(get_backend),
|
|
23
|
+
) -> dict[str, Any]:
|
|
24
|
+
"""Get the current information of the kinematics."""
|
|
25
|
+
return {
|
|
26
|
+
"info": {
|
|
27
|
+
"engine": backend.kinematics_engine,
|
|
28
|
+
"collision check": backend.check_collision,
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@router.get("/urdf")
|
|
34
|
+
async def get_urdf(backend: Backend = Depends(get_backend)) -> dict[str, str]:
|
|
35
|
+
"""Get the URDF representation of the robot."""
|
|
36
|
+
return {"urdf": backend.get_urdf()}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Motors router.
|
|
2
|
+
|
|
3
|
+
Provides endpoints to get and set the motor control mode.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from fastapi import APIRouter, Depends
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
from ....daemon.backend.abstract import Backend, MotorControlMode
|
|
10
|
+
from ..dependencies import get_backend
|
|
11
|
+
|
|
12
|
+
router = APIRouter(
|
|
13
|
+
prefix="/motors",
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MotorStatus(BaseModel):
|
|
18
|
+
"""Represents the status of the motors.
|
|
19
|
+
|
|
20
|
+
Exposes
|
|
21
|
+
- mode: The current motor control mode (enabled, disabled, gravity_compensation).
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
mode: MotorControlMode
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@router.get("/status")
|
|
28
|
+
async def get_motor_status(backend: Backend = Depends(get_backend)) -> MotorStatus:
|
|
29
|
+
"""Get the current status of the motors."""
|
|
30
|
+
return MotorStatus(mode=backend.get_motor_control_mode())
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@router.post("/set_mode/{mode}")
|
|
34
|
+
async def set_motor_mode(
|
|
35
|
+
mode: MotorControlMode,
|
|
36
|
+
backend: Backend = Depends(get_backend),
|
|
37
|
+
) -> dict[str, str]:
|
|
38
|
+
"""Set the motor control mode."""
|
|
39
|
+
backend.set_motor_control_mode(mode)
|
|
40
|
+
|
|
41
|
+
return {"status": f"motors changed to {mode} mode"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"""Movement-related API routes.
|
|
2
|
+
|
|
3
|
+
This exposes:
|
|
4
|
+
- goto
|
|
5
|
+
- play (wake_up, goto_sleep)
|
|
6
|
+
- stop running moves
|
|
7
|
+
- set_target and streaming set_target
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import json
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from typing import Any, Coroutine
|
|
14
|
+
from uuid import UUID, uuid4
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
from fastapi import APIRouter, Depends, WebSocket, WebSocketDisconnect
|
|
18
|
+
from pydantic import BaseModel
|
|
19
|
+
|
|
20
|
+
from reachy_mini.motion.recorded_move import RecordedMoves
|
|
21
|
+
|
|
22
|
+
from ....daemon.backend.abstract import Backend
|
|
23
|
+
from ..dependencies import get_backend, ws_get_backend
|
|
24
|
+
from ..models import AnyPose, FullBodyTarget
|
|
25
|
+
|
|
26
|
+
router = APIRouter(
|
|
27
|
+
prefix="/move",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class InterpolationMode(str, Enum):
|
|
32
|
+
"""Interpolation modes for movement."""
|
|
33
|
+
|
|
34
|
+
# TODO: This should be the same as for the backend
|
|
35
|
+
|
|
36
|
+
LINEAR = "linear"
|
|
37
|
+
MINJERK = "minjerk"
|
|
38
|
+
EASE = "ease"
|
|
39
|
+
CARTOON = "cartoon"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class GotoModelRequest(BaseModel):
|
|
43
|
+
"""Request model for the goto endpoint."""
|
|
44
|
+
|
|
45
|
+
head_pose: AnyPose | None = None
|
|
46
|
+
antennas: tuple[float, float] | None = None
|
|
47
|
+
duration: float
|
|
48
|
+
interpolation: InterpolationMode = InterpolationMode.MINJERK
|
|
49
|
+
|
|
50
|
+
model_config = {
|
|
51
|
+
"json_schema_extra": {
|
|
52
|
+
"examples": [
|
|
53
|
+
{
|
|
54
|
+
"head_pose": {
|
|
55
|
+
"x": 0.0,
|
|
56
|
+
"y": 0.0,
|
|
57
|
+
"z": 0.0,
|
|
58
|
+
"roll": 0.0,
|
|
59
|
+
"pitch": 0.0,
|
|
60
|
+
"yaw": 0.0,
|
|
61
|
+
},
|
|
62
|
+
"antennas": [0.0, 0.0],
|
|
63
|
+
"duration": 2.0,
|
|
64
|
+
"interpolation": "minjerk",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"antennas": [0.0, 0.0],
|
|
68
|
+
"duration": 1.0,
|
|
69
|
+
"interpolation": "linear",
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class MoveUUID(BaseModel):
|
|
77
|
+
"""Model representing a unique identifier for a move task."""
|
|
78
|
+
|
|
79
|
+
uuid: UUID
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
move_tasks: dict[UUID, asyncio.Task[None]] = {}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def create_move_task(coro: Coroutine[Any, Any, None]) -> MoveUUID:
|
|
86
|
+
"""Create a new move task using async task coroutine."""
|
|
87
|
+
uuid = uuid4()
|
|
88
|
+
|
|
89
|
+
task = asyncio.create_task(coro)
|
|
90
|
+
|
|
91
|
+
task.add_done_callback(lambda t: move_tasks.pop(uuid, None))
|
|
92
|
+
move_tasks[uuid] = task
|
|
93
|
+
|
|
94
|
+
return MoveUUID(uuid=uuid)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
async def stop_move_task(uuid: UUID) -> dict[str, str]:
|
|
98
|
+
"""Stop a running move task by cancelling it."""
|
|
99
|
+
if uuid not in move_tasks:
|
|
100
|
+
raise KeyError(f"Running move with UUID {uuid} not found")
|
|
101
|
+
|
|
102
|
+
task = move_tasks.pop(uuid, None)
|
|
103
|
+
assert task is not None
|
|
104
|
+
|
|
105
|
+
if task:
|
|
106
|
+
if task.cancel():
|
|
107
|
+
try:
|
|
108
|
+
await task
|
|
109
|
+
except asyncio.CancelledError:
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
"message": f"Stopped move with UUID: {uuid}",
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@router.get("/running")
|
|
118
|
+
async def get_running_moves() -> list[MoveUUID]:
|
|
119
|
+
"""Get a list of currently running move tasks."""
|
|
120
|
+
return [MoveUUID(uuid=uuid) for uuid in move_tasks.keys()]
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@router.post("/goto")
|
|
124
|
+
async def goto(
|
|
125
|
+
goto_req: GotoModelRequest, backend: Backend = Depends(get_backend)
|
|
126
|
+
) -> MoveUUID:
|
|
127
|
+
"""Request a movement to a specific target."""
|
|
128
|
+
return create_move_task(
|
|
129
|
+
backend.goto_target(
|
|
130
|
+
head=goto_req.head_pose.to_pose_array() if goto_req.head_pose else None,
|
|
131
|
+
antennas=np.array(goto_req.antennas) if goto_req.antennas else None,
|
|
132
|
+
duration=goto_req.duration,
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@router.post("/play/wake_up")
|
|
138
|
+
async def play_wake_up(backend: Backend = Depends(get_backend)) -> MoveUUID:
|
|
139
|
+
"""Request the robot to wake up."""
|
|
140
|
+
return create_move_task(backend.wake_up())
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@router.post("/play/goto_sleep")
|
|
144
|
+
async def play_goto_sleep(backend: Backend = Depends(get_backend)) -> MoveUUID:
|
|
145
|
+
"""Request the robot to go to sleep."""
|
|
146
|
+
return create_move_task(backend.goto_sleep())
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@router.post("/play/recorded-move-dataset/{dataset_name:path}/{move_name}")
|
|
150
|
+
async def play_recorded_move_dataset(
|
|
151
|
+
dataset_name: str,
|
|
152
|
+
move_name: str,
|
|
153
|
+
backend: Backend = Depends(get_backend),
|
|
154
|
+
) -> MoveUUID:
|
|
155
|
+
"""Request the robot to play a predefined recorded move from a dataset."""
|
|
156
|
+
move = RecordedMoves(dataset_name).get(move_name)
|
|
157
|
+
return create_move_task(backend.play_move(move))
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@router.post("/stop")
|
|
161
|
+
async def stop_move(uuid: MoveUUID) -> dict[str, str]:
|
|
162
|
+
"""Stop a running move task."""
|
|
163
|
+
return await stop_move_task(uuid.uuid)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
# --- FullBodyTarget streaming and single set_target ---
|
|
167
|
+
@router.post("/set_target")
|
|
168
|
+
async def set_target(
|
|
169
|
+
target: FullBodyTarget,
|
|
170
|
+
backend: Backend = Depends(get_backend),
|
|
171
|
+
) -> dict[str, str]:
|
|
172
|
+
"""POST route to set a single FullBodyTarget."""
|
|
173
|
+
backend.set_target(
|
|
174
|
+
head=target.target_head_pose.to_pose_array()
|
|
175
|
+
if target.target_head_pose
|
|
176
|
+
else None,
|
|
177
|
+
antennas=np.array(target.target_antennas) if target.target_antennas else None,
|
|
178
|
+
)
|
|
179
|
+
return {"status": "ok"}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@router.websocket("/ws/set_target")
|
|
183
|
+
async def ws_set_target(
|
|
184
|
+
websocket: WebSocket, backend: Backend = Depends(ws_get_backend)
|
|
185
|
+
) -> None:
|
|
186
|
+
"""WebSocket route to stream FullBodyTarget set_target calls."""
|
|
187
|
+
await websocket.accept()
|
|
188
|
+
try:
|
|
189
|
+
while True:
|
|
190
|
+
data = await websocket.receive_text()
|
|
191
|
+
try:
|
|
192
|
+
target = FullBodyTarget.model_validate_json(data)
|
|
193
|
+
await set_target(target, backend)
|
|
194
|
+
|
|
195
|
+
except Exception as e:
|
|
196
|
+
await websocket.send_text(
|
|
197
|
+
json.dumps({"status": "error", "detail": str(e)})
|
|
198
|
+
)
|
|
199
|
+
except WebSocketDisconnect:
|
|
200
|
+
pass
|