reachy-mini 1.2.5rc1__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.
- reachy_mini/__init__.py +4 -0
- reachy_mini/apps/__init__.py +25 -0
- reachy_mini/apps/app.py +258 -0
- reachy_mini/apps/assistant.py +787 -0
- reachy_mini/apps/manager.py +312 -0
- reachy_mini/apps/sources/__init__.py +4 -0
- reachy_mini/apps/sources/hf_space.py +106 -0
- reachy_mini/apps/sources/local_common_venv.py +567 -0
- reachy_mini/apps/templates/README.md.j2 +12 -0
- reachy_mini/apps/templates/gitignore.j2 +3 -0
- reachy_mini/apps/templates/index.html.j2 +40 -0
- reachy_mini/apps/templates/main.py.j2 +74 -0
- reachy_mini/apps/templates/pyproject.toml.j2 +28 -0
- reachy_mini/apps/templates/static/index.html.j2 +27 -0
- reachy_mini/apps/templates/static/main.js.j2 +47 -0
- reachy_mini/apps/templates/static/style.css.j2 +25 -0
- reachy_mini/apps/templates/style.css.j2 +411 -0
- reachy_mini/apps/utils.py +30 -0
- reachy_mini/assets/config/hardware_config.yaml +164 -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/firmware/CHANGELOG.md +15 -0
- reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.2.bin +0 -0
- reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.3.bin +0 -0
- reachy_mini/assets/firmware/update.sh +9 -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 +0 -0
- reachy_mini/assets/models/fknetwork.onnx +0 -0
- reachy_mini/assets/models/fknetwork_int8.onnx +0 -0
- reachy_mini/assets/models/iknetwork.onnx +0 -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/bg_job_register.py +142 -0
- reachy_mini/daemon/app/dashboard/static/assets/KO-cartoon-static.svg +40 -0
- reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon-static.svg +42 -0
- reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/go-to-sleep-cartoon.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon-static.svg +50 -0
- reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-awake.svg +18 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-connection-lost-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-go-to-sleep-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko.svg +22 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping-static.svg +41 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping.svg +18 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-wake-up-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/css/app.css +1433 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Archivo-Italic-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Archivo-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Asap-Italic-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Asap-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/js/3rdparty/gstwebrtc-api-2.0.0.min.js +5 -0
- reachy_mini/daemon/app/dashboard/static/js/apps.js +323 -0
- reachy_mini/daemon/app/dashboard/static/js/appstore.js +167 -0
- reachy_mini/daemon/app/dashboard/static/js/daemon.js +169 -0
- reachy_mini/daemon/app/dashboard/static/js/health_check.js +9 -0
- reachy_mini/daemon/app/dashboard/static/js/move_player.js +132 -0
- reachy_mini/daemon/app/dashboard/static/js/notification.js +41 -0
- reachy_mini/daemon/app/dashboard/static/js/update.js +132 -0
- reachy_mini/daemon/app/dashboard/static/js/volume_control.js +252 -0
- reachy_mini/daemon/app/dashboard/static/js/wifi.js +176 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/README.md +15 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/package-lock.json +1127 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/package.json +6 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/pnpm-lock.yaml +608 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/postcss.config.js +6 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/styles/app.css +185 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/tailwind.config.js +11 -0
- reachy_mini/daemon/app/dashboard/templates/base.html +21 -0
- reachy_mini/daemon/app/dashboard/templates/index.html +20 -0
- reachy_mini/daemon/app/dashboard/templates/sections/apps.html +8 -0
- reachy_mini/daemon/app/dashboard/templates/sections/appstore.html +35 -0
- reachy_mini/daemon/app/dashboard/templates/sections/daemon.html +45 -0
- reachy_mini/daemon/app/dashboard/templates/sections/move_player.html +121 -0
- reachy_mini/daemon/app/dashboard/templates/sections/notification.html +19 -0
- reachy_mini/daemon/app/dashboard/templates/sections/update.html +54 -0
- reachy_mini/daemon/app/dashboard/templates/sections/wifi.html +46 -0
- reachy_mini/daemon/app/dashboard/templates/settings.html +14 -0
- reachy_mini/daemon/app/dependencies.py +41 -0
- reachy_mini/daemon/app/main.py +469 -0
- reachy_mini/daemon/app/models.py +149 -0
- reachy_mini/daemon/app/routers/apps.py +114 -0
- reachy_mini/daemon/app/routers/daemon.py +85 -0
- reachy_mini/daemon/app/routers/kinematics.py +57 -0
- reachy_mini/daemon/app/routers/motors.py +41 -0
- reachy_mini/daemon/app/routers/move.py +265 -0
- reachy_mini/daemon/app/routers/state.py +146 -0
- reachy_mini/daemon/app/routers/update.py +86 -0
- reachy_mini/daemon/app/routers/volume.py +423 -0
- reachy_mini/daemon/app/routers/wifi_config.py +219 -0
- reachy_mini/daemon/app/services/__init__.py +1 -0
- reachy_mini/daemon/app/services/bluetooth/bluetooth_service.py +796 -0
- reachy_mini/daemon/app/services/bluetooth/commands/HOTSPOT.sh +7 -0
- reachy_mini/daemon/app/services/bluetooth/commands/RESTART_DAEMON.sh +4 -0
- reachy_mini/daemon/app/services/bluetooth/commands/SOFTWARE_RESET.sh +7 -0
- reachy_mini/daemon/app/services/bluetooth/install_service_bluetooth.sh +39 -0
- reachy_mini/daemon/app/services/gpio_shutdown/__init__.py +1 -0
- reachy_mini/daemon/app/services/gpio_shutdown/install_service.sh +33 -0
- reachy_mini/daemon/app/services/gpio_shutdown/launcher.sh +3 -0
- reachy_mini/daemon/app/services/gpio_shutdown/shutdown_monitor.py +28 -0
- reachy_mini/daemon/app/services/wireless/generate_asoundrc.sh +70 -0
- reachy_mini/daemon/app/services/wireless/install_service.sh +33 -0
- reachy_mini/daemon/app/services/wireless/launcher.sh +5 -0
- reachy_mini/daemon/backend/__init__.py +1 -0
- reachy_mini/daemon/backend/abstract.py +815 -0
- reachy_mini/daemon/backend/mujoco/__init__.py +36 -0
- reachy_mini/daemon/backend/mujoco/backend.py +395 -0
- reachy_mini/daemon/backend/mujoco/utils.py +59 -0
- reachy_mini/daemon/backend/mujoco/video_udp.py +57 -0
- reachy_mini/daemon/backend/robot/__init__.py +8 -0
- reachy_mini/daemon/backend/robot/backend.py +597 -0
- reachy_mini/daemon/daemon.py +651 -0
- reachy_mini/daemon/utils.py +116 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna.stl +0 -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 +0 -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 +0 -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 +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/arducam.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/arducam.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh.stl +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bearing_85x110x13.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bearing_85x110x13.stl +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/README.m +1 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/croissant_1k.blend/croissant_1k.obj +5021 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/croissant_1k.blend/textures/croissant_diff_1k.png +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/food_apple_01_1k.blend/food_apple_01_1k.obj +18045 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/food_apple_01_1k.blend/textures/food_apple_01_diff_1k.png +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/rubber_duck_toy_1k.blend/rubber_duck_toy_1k.obj +8940 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/rubber_duck_toy_1k.blend/textures/rubber_duck_toy_diff_1k.png +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/wooden_table_02_1k.blend/textures/wooden_table_02_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/wooden_table_02_1k.blend/wooden_table_02_1k.obj +485 -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 +642 -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 +132 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.stl +0 -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 +0 -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 +0 -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 +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arm.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.stl +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/ball.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/ball.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.stl +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.stl +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/README.m +1 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/head_one_3dprint_collider_0.stl +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/foot.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/foot.stl +0 -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 +0 -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 +0 -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 +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.stl +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.stl +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.stl +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.stl +0 -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 +0 -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 +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/shape.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/shape.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.stl +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.stl +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.stl +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -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 +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/config.json +18 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot.urdf +3281 -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/descriptions/reachy_mini/urdf/robot_simple_collision.urdf +2399 -0
- reachy_mini/io/__init__.py +15 -0
- reachy_mini/io/abstract.py +70 -0
- reachy_mini/io/audio_ws.py +242 -0
- reachy_mini/io/protocol.py +44 -0
- reachy_mini/io/video_ws.py +135 -0
- reachy_mini/io/ws_controller.py +120 -0
- reachy_mini/io/zenoh_client.py +273 -0
- reachy_mini/io/zenoh_server.py +230 -0
- reachy_mini/kinematics/__init__.py +68 -0
- reachy_mini/kinematics/analytical_kinematics.py +151 -0
- reachy_mini/kinematics/nn_kinematics.py +109 -0
- reachy_mini/kinematics/placo_kinematics.py +656 -0
- reachy_mini/media/__init__.py +1 -0
- reachy_mini/media/audio_base.py +111 -0
- reachy_mini/media/audio_control_utils.py +455 -0
- reachy_mini/media/audio_gstreamer.py +272 -0
- reachy_mini/media/audio_sounddevice.py +321 -0
- reachy_mini/media/audio_utils.py +114 -0
- reachy_mini/media/camera_base.py +100 -0
- reachy_mini/media/camera_constants.py +152 -0
- reachy_mini/media/camera_gstreamer.py +231 -0
- reachy_mini/media/camera_opencv.py +89 -0
- reachy_mini/media/camera_utils.py +111 -0
- reachy_mini/media/media_manager.py +281 -0
- reachy_mini/media/webrtc_client_gstreamer.py +288 -0
- reachy_mini/media/webrtc_daemon.py +334 -0
- reachy_mini/motion/__init__.py +4 -0
- reachy_mini/motion/goto.py +71 -0
- reachy_mini/motion/move.py +43 -0
- reachy_mini/motion/recorded_move.py +146 -0
- reachy_mini/reachy_mini.py +753 -0
- reachy_mini/tools/reflash_motors.py +116 -0
- reachy_mini/tools/setup_motor.py +422 -0
- reachy_mini/tools/setup_motor_rpi.py +108 -0
- reachy_mini/utils/__init__.py +46 -0
- reachy_mini/utils/constants.py +9 -0
- reachy_mini/utils/hardware_config/__init__.py +1 -0
- reachy_mini/utils/hardware_config/parser.py +65 -0
- reachy_mini/utils/interpolation.py +227 -0
- reachy_mini/utils/parse_urdf_for_kinematics.py +110 -0
- reachy_mini/utils/rerun.py +320 -0
- reachy_mini/utils/wireless_version/__init__.py +1 -0
- reachy_mini/utils/wireless_version/startup_check.py +144 -0
- reachy_mini/utils/wireless_version/update.py +20 -0
- reachy_mini/utils/wireless_version/update_available.py +61 -0
- reachy_mini/utils/wireless_version/utils.py +39 -0
- reachy_mini-1.2.5rc1.dist-info/METADATA +161 -0
- reachy_mini-1.2.5rc1.dist-info/RECORD +479 -0
- reachy_mini-1.2.5rc1.dist-info/WHEEL +5 -0
- reachy_mini-1.2.5rc1.dist-info/entry_points.txt +4 -0
- reachy_mini-1.2.5rc1.dist-info/licenses/LICENSE +201 -0
- reachy_mini-1.2.5rc1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""IO module."""
|
|
2
|
+
|
|
3
|
+
from .audio_ws import AsyncWebSocketAudioStreamer
|
|
4
|
+
from .video_ws import AsyncWebSocketFrameSender
|
|
5
|
+
from .ws_controller import AsyncWebSocketController
|
|
6
|
+
from .zenoh_client import ZenohClient
|
|
7
|
+
from .zenoh_server import ZenohServer
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"AsyncWebSocketAudioStreamer",
|
|
11
|
+
"AsyncWebSocketFrameSender",
|
|
12
|
+
"AsyncWebSocketController",
|
|
13
|
+
"ZenohClient",
|
|
14
|
+
"ZenohServer",
|
|
15
|
+
]
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Base classes for server and client implementations.
|
|
2
|
+
|
|
3
|
+
These abstract classes define the interface for server and client components
|
|
4
|
+
in the Reachy Mini project. They provide methods for starting and stopping
|
|
5
|
+
the server, handling commands, and managing client connections.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from threading import Event
|
|
10
|
+
from uuid import UUID
|
|
11
|
+
|
|
12
|
+
from reachy_mini.io.protocol import AnyTaskRequest
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AbstractServer(ABC):
|
|
16
|
+
"""Base class for server implementations."""
|
|
17
|
+
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def start(self) -> None:
|
|
20
|
+
"""Start the server."""
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def stop(self) -> None:
|
|
25
|
+
"""Stop the server."""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def command_received_event(self) -> Event:
|
|
30
|
+
"""Wait for a new command and return it."""
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AbstractClient(ABC):
|
|
35
|
+
"""Base class for client implementations."""
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def wait_for_connection(self) -> None:
|
|
39
|
+
"""Wait for the client to connect to the server."""
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
@abstractmethod
|
|
43
|
+
def is_connected(self) -> bool:
|
|
44
|
+
"""Check if the client is connected to the server."""
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def disconnect(self) -> None:
|
|
49
|
+
"""Disconnect the client from the server."""
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def send_command(self, command: str) -> None:
|
|
54
|
+
"""Send a command to the server."""
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
@abstractmethod
|
|
58
|
+
def get_current_joints(self) -> tuple[list[float], list[float]]:
|
|
59
|
+
"""Get the current joint positions."""
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
@abstractmethod
|
|
63
|
+
def send_task_request(self, task_req: AnyTaskRequest) -> UUID:
|
|
64
|
+
"""Send a task request to the server and return a unique task identifier."""
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
def wait_for_task_completion(self, task_uid: UUID, timeout: float = 5.0) -> None:
|
|
69
|
+
"""Wait for the specified task to complete."""
|
|
70
|
+
pass
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""Async WebSocket Audio Streamer."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
import threading
|
|
6
|
+
import time
|
|
7
|
+
from queue import Empty, Queue
|
|
8
|
+
from typing import Optional, Union
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
import numpy.typing as npt
|
|
12
|
+
from websockets.asyncio.client import ClientConnection, connect
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger("reachy_mini.io.audio_ws")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AsyncWebSocketAudioStreamer:
|
|
18
|
+
"""Async WebSocket audio streamer with send and receive support."""
|
|
19
|
+
|
|
20
|
+
ws_uri: str
|
|
21
|
+
send_queue: "Queue[bytes]"
|
|
22
|
+
recv_queue: "Queue[bytes]"
|
|
23
|
+
loop: asyncio.AbstractEventLoop
|
|
24
|
+
thread: threading.Thread
|
|
25
|
+
connected: threading.Event
|
|
26
|
+
stop_flag: bool
|
|
27
|
+
keep_alive_interval: float
|
|
28
|
+
|
|
29
|
+
# --- CONFIGURATION ---
|
|
30
|
+
# Target ~2048 samples per packet (approx 128ms)
|
|
31
|
+
# 2048 samples * 2 bytes (int16) = 4096 bytes
|
|
32
|
+
BATCH_SIZE_BYTES = 4096
|
|
33
|
+
# Don't hold audio longer than 200ms even if buffer isn't full
|
|
34
|
+
BATCH_TIMEOUT = 0.2
|
|
35
|
+
|
|
36
|
+
def __init__(self, ws_uri: str, keep_alive_interval: float = 2.0) -> None:
|
|
37
|
+
"""Initialize the WebSocket audio streamer.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
ws_uri: WebSocket URI to connect to.
|
|
41
|
+
keep_alive_interval: Interval in seconds to send keep-alive pings
|
|
42
|
+
when no audio is flowing.
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
self.ws_uri = ws_uri
|
|
46
|
+
self.send_queue = Queue()
|
|
47
|
+
self.recv_queue = Queue()
|
|
48
|
+
self.loop = asyncio.new_event_loop()
|
|
49
|
+
self.thread = threading.Thread(target=self._run_loop, daemon=True)
|
|
50
|
+
self.connected = threading.Event()
|
|
51
|
+
self.stop_flag = False
|
|
52
|
+
self.keep_alive_interval = keep_alive_interval
|
|
53
|
+
self.thread.start()
|
|
54
|
+
|
|
55
|
+
def _run_loop(self) -> None:
|
|
56
|
+
"""Run the WebSocket streamer loop in a background thread."""
|
|
57
|
+
asyncio.set_event_loop(self.loop)
|
|
58
|
+
self.loop.run_until_complete(self._run())
|
|
59
|
+
|
|
60
|
+
async def _run(self) -> None:
|
|
61
|
+
"""Run the main reconnect loop."""
|
|
62
|
+
while not self.stop_flag:
|
|
63
|
+
try:
|
|
64
|
+
async with connect(self.ws_uri) as ws:
|
|
65
|
+
logger.info("[WS-AUDIO] Connected to Space")
|
|
66
|
+
self.connected.set()
|
|
67
|
+
|
|
68
|
+
send_task = asyncio.create_task(self._send_loop(ws))
|
|
69
|
+
recv_task = asyncio.create_task(self._recv_loop(ws))
|
|
70
|
+
|
|
71
|
+
done, pending = await asyncio.wait(
|
|
72
|
+
{send_task, recv_task},
|
|
73
|
+
return_when=asyncio.FIRST_EXCEPTION,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Cancel the other task if one fails or finishes
|
|
77
|
+
for task in pending:
|
|
78
|
+
task.cancel()
|
|
79
|
+
try:
|
|
80
|
+
await task
|
|
81
|
+
except Exception:
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
except Exception as e:
|
|
85
|
+
logger.info(f"[WS-AUDIO] Connection failed: {e}")
|
|
86
|
+
await asyncio.sleep(1.0)
|
|
87
|
+
|
|
88
|
+
self.connected.clear()
|
|
89
|
+
|
|
90
|
+
async def _send_loop(self, ws: ClientConnection) -> None:
|
|
91
|
+
"""Send outgoing audio chunks and keep-alive pings.
|
|
92
|
+
|
|
93
|
+
To avoid audible artifacts, this method aggregates small chunks into larger batches before sending.
|
|
94
|
+
"""
|
|
95
|
+
last_activity = time.time()
|
|
96
|
+
|
|
97
|
+
# Buffer to hold small chunks
|
|
98
|
+
batch_buffer = bytearray()
|
|
99
|
+
# Track when we started filling this specific batch
|
|
100
|
+
batch_start_time = time.time()
|
|
101
|
+
|
|
102
|
+
while not self.stop_flag:
|
|
103
|
+
try:
|
|
104
|
+
# 1. Try to pull data from the queue
|
|
105
|
+
# Use a short timeout so we can check time-based conditions frequently
|
|
106
|
+
chunk = self.send_queue.get(timeout=0.01)
|
|
107
|
+
|
|
108
|
+
# If this is the first chunk in the buffer, reset timer
|
|
109
|
+
if len(batch_buffer) == 0:
|
|
110
|
+
batch_start_time = time.time()
|
|
111
|
+
|
|
112
|
+
batch_buffer.extend(chunk)
|
|
113
|
+
|
|
114
|
+
except Empty:
|
|
115
|
+
pass
|
|
116
|
+
except Exception as e:
|
|
117
|
+
logger.info(f"[WS-AUDIO] Queue error: {e}")
|
|
118
|
+
break
|
|
119
|
+
|
|
120
|
+
# 2. Check if we should send the batch
|
|
121
|
+
now = time.time()
|
|
122
|
+
|
|
123
|
+
# Condition A: Buffer is full enough (Size based)
|
|
124
|
+
is_full = len(batch_buffer) >= self.BATCH_SIZE_BYTES
|
|
125
|
+
|
|
126
|
+
# Condition B: Buffer has data, but it's getting too old (Time based)
|
|
127
|
+
# This ensures that if the robot says a short word, it sends it
|
|
128
|
+
# after 100ms instead of waiting forever for more data.
|
|
129
|
+
is_timed_out = (len(batch_buffer) > 0) and (
|
|
130
|
+
(now - batch_start_time) > self.BATCH_TIMEOUT
|
|
131
|
+
)
|
|
132
|
+
if is_full or is_timed_out:
|
|
133
|
+
try:
|
|
134
|
+
# Send the aggregated buffer
|
|
135
|
+
await ws.send(batch_buffer) # type: ignore
|
|
136
|
+
|
|
137
|
+
# Reset
|
|
138
|
+
batch_buffer = bytearray()
|
|
139
|
+
last_activity = now
|
|
140
|
+
except Exception as e:
|
|
141
|
+
logger.info(f"[WS-AUDIO] Send error: {e}")
|
|
142
|
+
break
|
|
143
|
+
|
|
144
|
+
# 3. Keep-Alive Ping (Only if completely idle)
|
|
145
|
+
# We only ping if the buffer is empty AND we haven't sent anything recently
|
|
146
|
+
if (
|
|
147
|
+
len(batch_buffer) == 0
|
|
148
|
+
and (now - last_activity) > self.keep_alive_interval
|
|
149
|
+
):
|
|
150
|
+
try:
|
|
151
|
+
await ws.send("ping")
|
|
152
|
+
last_activity = now
|
|
153
|
+
logger.debug("[WS-AUDIO] Sent keep-alive ping")
|
|
154
|
+
except Exception as e:
|
|
155
|
+
logger.info(f"[WS-AUDIO] Ping failed: {e}")
|
|
156
|
+
break
|
|
157
|
+
|
|
158
|
+
# Tiny sleep to yield control if we are just spinning
|
|
159
|
+
if len(batch_buffer) == 0:
|
|
160
|
+
await asyncio.sleep(0.001)
|
|
161
|
+
|
|
162
|
+
async def _recv_loop(self, ws: ClientConnection) -> None:
|
|
163
|
+
"""Receive incoming audio chunks."""
|
|
164
|
+
while not self.stop_flag:
|
|
165
|
+
try:
|
|
166
|
+
msg = await ws.recv()
|
|
167
|
+
except Exception as e:
|
|
168
|
+
logger.info(f"[WS-AUDIO] Receive error: {e}")
|
|
169
|
+
break
|
|
170
|
+
|
|
171
|
+
if isinstance(msg, bytes):
|
|
172
|
+
try:
|
|
173
|
+
self.recv_queue.put_nowait(msg)
|
|
174
|
+
except Exception as e:
|
|
175
|
+
logger.debug(f"[WS-AUDIO] Failed to enqueue received audio: {e}")
|
|
176
|
+
else:
|
|
177
|
+
logger.debug(f"[WS-AUDIO] Received non-binary message: {msg}")
|
|
178
|
+
|
|
179
|
+
# ------------------------
|
|
180
|
+
# Public API
|
|
181
|
+
# ------------------------
|
|
182
|
+
|
|
183
|
+
def send_audio_chunk(
|
|
184
|
+
self,
|
|
185
|
+
audio: Union[bytes, npt.NDArray[np.int16], npt.NDArray[np.float32]],
|
|
186
|
+
) -> None:
|
|
187
|
+
"""Queue an audio chunk to be sent.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
audio: Either raw bytes or a numpy array of int16 or float32.
|
|
191
|
+
Float32 arrays are assumed to be in [-1, 1] and will
|
|
192
|
+
be converted to int16 PCM.
|
|
193
|
+
|
|
194
|
+
"""
|
|
195
|
+
if self.stop_flag:
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
if isinstance(audio, bytes):
|
|
199
|
+
data = audio
|
|
200
|
+
else:
|
|
201
|
+
# Convert only if needed
|
|
202
|
+
arr = np.asarray(audio)
|
|
203
|
+
# Handle stereo if accidentally passed (take channel 0)
|
|
204
|
+
if arr.ndim > 1:
|
|
205
|
+
arr = arr[:, 0]
|
|
206
|
+
|
|
207
|
+
if arr.dtype == np.float32 or arr.dtype == np.float64:
|
|
208
|
+
# If any value is above 1 or below -1, scale the entire array so the max abs value is 1 or less
|
|
209
|
+
max_abs = np.max(np.abs(arr))
|
|
210
|
+
if max_abs > 1.0:
|
|
211
|
+
arr = arr / max_abs
|
|
212
|
+
# Convert float audio [-1,1] to int16 PCM
|
|
213
|
+
arr = np.clip(arr, -1.0, 1.0)
|
|
214
|
+
arr = (arr * 32767.0).astype(np.int16) # type: ignore
|
|
215
|
+
elif arr.dtype != np.int16:
|
|
216
|
+
arr = arr.astype(np.int16)
|
|
217
|
+
|
|
218
|
+
data = arr.tobytes()
|
|
219
|
+
|
|
220
|
+
self.send_queue.put(data)
|
|
221
|
+
|
|
222
|
+
def get_audio_chunk(
|
|
223
|
+
self, timeout: Optional[float] = 0.01
|
|
224
|
+
) -> Optional[npt.NDArray[np.float32]]:
|
|
225
|
+
"""Retrieve a received audio chunk, if any."""
|
|
226
|
+
try:
|
|
227
|
+
if timeout == 0:
|
|
228
|
+
audio_bytes = self.recv_queue.get_nowait()
|
|
229
|
+
else:
|
|
230
|
+
audio_bytes = self.recv_queue.get(timeout=timeout)
|
|
231
|
+
# bytes -> int16 -> float32 in [-1, 1]
|
|
232
|
+
int16_arr = np.frombuffer(audio_bytes, dtype=np.int16)
|
|
233
|
+
float_arr = int16_arr.astype(np.float32) / 32767.0
|
|
234
|
+
return float_arr
|
|
235
|
+
except Empty:
|
|
236
|
+
return None
|
|
237
|
+
|
|
238
|
+
def close(self) -> None:
|
|
239
|
+
"""Close the WebSocket audio streamer."""
|
|
240
|
+
self.stop_flag = True
|
|
241
|
+
if self.loop.is_running():
|
|
242
|
+
self.loop.call_soon_threadsafe(self.loop.stop)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Protocol definitions for Reachy Mini client/server communication."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from uuid import UUID
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
from reachy_mini.utils.interpolation import InterpolationTechnique
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GotoTaskRequest(BaseModel):
|
|
12
|
+
"""Class to represent a goto target task."""
|
|
13
|
+
|
|
14
|
+
head: list[float] | None # 4x4 flatten pose matrix
|
|
15
|
+
antennas: list[float] | None # [right_angle, left_angle] (in rads)
|
|
16
|
+
duration: float
|
|
17
|
+
method: InterpolationTechnique
|
|
18
|
+
body_yaw: float | None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PlayMoveTaskRequest(BaseModel):
|
|
22
|
+
"""Class to represent a play move task."""
|
|
23
|
+
|
|
24
|
+
move_name: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
AnyTaskRequest = GotoTaskRequest | PlayMoveTaskRequest
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TaskRequest(BaseModel):
|
|
31
|
+
"""Class to represent any task request."""
|
|
32
|
+
|
|
33
|
+
uuid: UUID
|
|
34
|
+
req: AnyTaskRequest
|
|
35
|
+
timestamp: datetime
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class TaskProgress(BaseModel):
|
|
39
|
+
"""Class to represent task progress."""
|
|
40
|
+
|
|
41
|
+
uuid: UUID
|
|
42
|
+
finished: bool = False
|
|
43
|
+
error: str | None = None
|
|
44
|
+
timestamp: datetime
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Async WebSocket Frame Sender."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
import threading
|
|
6
|
+
from queue import Empty, Full, Queue
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
import cv2
|
|
10
|
+
import numpy as np
|
|
11
|
+
import numpy.typing as npt
|
|
12
|
+
from websockets.asyncio.client import ClientConnection, connect
|
|
13
|
+
from websockets.exceptions import ConnectionClosed
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger("reachy_mini.io.video_ws")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AsyncWebSocketFrameSender:
|
|
19
|
+
"""Async WebSocket frame sender."""
|
|
20
|
+
|
|
21
|
+
ws_uri: str
|
|
22
|
+
queue: "Queue[bytes]"
|
|
23
|
+
loop: asyncio.AbstractEventLoop
|
|
24
|
+
thread: threading.Thread
|
|
25
|
+
connected: threading.Event
|
|
26
|
+
stop_flag: bool
|
|
27
|
+
_last_frame: Optional[npt.NDArray[np.uint8]]
|
|
28
|
+
|
|
29
|
+
def __init__(self, ws_uri: str) -> None:
|
|
30
|
+
"""Initialize the WebSocket frame sender."""
|
|
31
|
+
self.ws_uri = ws_uri
|
|
32
|
+
self.queue = Queue(maxsize=2)
|
|
33
|
+
self.loop = asyncio.new_event_loop()
|
|
34
|
+
self.thread = threading.Thread(target=self._run_loop, daemon=True)
|
|
35
|
+
self.connected = threading.Event()
|
|
36
|
+
self.stop_flag = False
|
|
37
|
+
self._last_frame = None
|
|
38
|
+
self.thread.start()
|
|
39
|
+
|
|
40
|
+
def _run_loop(self) -> None:
|
|
41
|
+
asyncio.set_event_loop(self.loop)
|
|
42
|
+
self.loop.run_until_complete(self._run())
|
|
43
|
+
|
|
44
|
+
def _clear_queue(self) -> None:
|
|
45
|
+
"""Empty the queue so we don't send 10 seconds of old video on reconnect."""
|
|
46
|
+
while not self.queue.empty():
|
|
47
|
+
try:
|
|
48
|
+
self.queue.get_nowait()
|
|
49
|
+
except Empty:
|
|
50
|
+
break
|
|
51
|
+
|
|
52
|
+
async def _run(self) -> None:
|
|
53
|
+
"""Run the WebSocket frame sender loop."""
|
|
54
|
+
while not self.stop_flag:
|
|
55
|
+
try:
|
|
56
|
+
ws: ClientConnection
|
|
57
|
+
async with connect(
|
|
58
|
+
self.ws_uri,
|
|
59
|
+
ping_interval=5, # Every 5 seconds is plenty
|
|
60
|
+
ping_timeout=10, # Give it 10s to respond
|
|
61
|
+
close_timeout=1, # Don't wait long for polite closes
|
|
62
|
+
) as ws:
|
|
63
|
+
logger.info("[WS Video] Connected to Space")
|
|
64
|
+
self.connected.set()
|
|
65
|
+
self._clear_queue() # Ensure we start fresh
|
|
66
|
+
|
|
67
|
+
while not self.stop_flag:
|
|
68
|
+
try:
|
|
69
|
+
frame = self.queue.get_nowait()
|
|
70
|
+
|
|
71
|
+
await ws.send(frame)
|
|
72
|
+
|
|
73
|
+
except Empty:
|
|
74
|
+
# Queue is empty, just yield to event loop
|
|
75
|
+
await asyncio.sleep(0.05)
|
|
76
|
+
continue
|
|
77
|
+
|
|
78
|
+
except Exception as e:
|
|
79
|
+
logger.error(f"[WS Video] Send error: {e}")
|
|
80
|
+
break
|
|
81
|
+
|
|
82
|
+
except ConnectionClosed as e:
|
|
83
|
+
# Common network errors, retry quickly
|
|
84
|
+
logger.error(
|
|
85
|
+
f"[WS Video] Connection lost ({type(e).__name__}). Retrying..."
|
|
86
|
+
)
|
|
87
|
+
self.connected.clear()
|
|
88
|
+
self._last_frame = None
|
|
89
|
+
await asyncio.sleep(0.5) # Wait briefly before reconnecting
|
|
90
|
+
|
|
91
|
+
except Exception as e:
|
|
92
|
+
logger.error(f"[WS Video] Unexpected error: {e}")
|
|
93
|
+
await asyncio.sleep(1)
|
|
94
|
+
|
|
95
|
+
self.connected.clear()
|
|
96
|
+
self._last_frame = None
|
|
97
|
+
|
|
98
|
+
def send_frame(self, frame: npt.NDArray[np.uint8]) -> None:
|
|
99
|
+
"""Send a frame to the WebSocket (Non-blocking)."""
|
|
100
|
+
if not self.stop_flag:
|
|
101
|
+
# 1. Frame Deduplication
|
|
102
|
+
if self._last_frame is not None:
|
|
103
|
+
if np.array_equal(frame, self._last_frame):
|
|
104
|
+
return
|
|
105
|
+
self._last_frame = frame.copy()
|
|
106
|
+
|
|
107
|
+
# 2. Encode
|
|
108
|
+
ok, jpeg_bytes = cv2.imencode(
|
|
109
|
+
".jpg",
|
|
110
|
+
frame,
|
|
111
|
+
[int(cv2.IMWRITE_JPEG_QUALITY), 80],
|
|
112
|
+
)
|
|
113
|
+
if not ok:
|
|
114
|
+
return
|
|
115
|
+
|
|
116
|
+
data = jpeg_bytes.tobytes()
|
|
117
|
+
|
|
118
|
+
# If queue is full (network lagging), remove the old frame and put the new one.
|
|
119
|
+
try:
|
|
120
|
+
self.queue.put_nowait(data)
|
|
121
|
+
except Full:
|
|
122
|
+
# Queue is full, network is likely slower than camera.
|
|
123
|
+
# Drop the OLDEST frame to make room for the NEWEST.
|
|
124
|
+
try:
|
|
125
|
+
self.queue.get_nowait() # Pop old frame
|
|
126
|
+
self.queue.put_nowait(data) # Push new frame
|
|
127
|
+
except Empty:
|
|
128
|
+
logger.error(
|
|
129
|
+
"[WS Video] Queue is full and empty, this should not happen."
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
def close(self) -> None:
|
|
133
|
+
"""Close the WebSocket frame sender."""
|
|
134
|
+
self.stop_flag = True
|
|
135
|
+
self.loop.call_soon_threadsafe(self.loop.stop)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Async WebSocket Controller for remote control and streaming of the robot."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import threading
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
from websockets.asyncio.client import ClientConnection, connect
|
|
12
|
+
|
|
13
|
+
from reachy_mini.daemon.backend.abstract import Backend
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger("reachy_mini.ws_controller")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class Movement:
|
|
20
|
+
"""Movement data for the WebSocket controller."""
|
|
21
|
+
|
|
22
|
+
name: str
|
|
23
|
+
x: float = 0
|
|
24
|
+
y: float = 0
|
|
25
|
+
z: float = 0
|
|
26
|
+
roll: float = 0
|
|
27
|
+
pitch: float = 0
|
|
28
|
+
yaw: float = 0
|
|
29
|
+
body_yaw: float = 0
|
|
30
|
+
left_antenna: Optional[float] = None
|
|
31
|
+
right_antenna: Optional[float] = None
|
|
32
|
+
duration: float = 1.0
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class AsyncWebSocketController:
|
|
36
|
+
"""WebSocket controller for remote control and streaming of the robot."""
|
|
37
|
+
|
|
38
|
+
ws_uri: str
|
|
39
|
+
backend: Backend
|
|
40
|
+
loop: asyncio.AbstractEventLoop
|
|
41
|
+
thread: threading.Thread
|
|
42
|
+
stop_flag: bool
|
|
43
|
+
|
|
44
|
+
def __init__(self, ws_uri: str, backend: Backend) -> None:
|
|
45
|
+
"""Initialize the WebSocket controller."""
|
|
46
|
+
self.ws_uri = ws_uri
|
|
47
|
+
self.backend = backend
|
|
48
|
+
self.loop = asyncio.new_event_loop()
|
|
49
|
+
self.thread = threading.Thread(target=self._run_loop, daemon=True)
|
|
50
|
+
self.stop_flag = False
|
|
51
|
+
self.thread.start()
|
|
52
|
+
|
|
53
|
+
def _run_loop(self) -> None:
|
|
54
|
+
"""Run the WebSocket controller loop."""
|
|
55
|
+
asyncio.set_event_loop(self.loop)
|
|
56
|
+
self.loop.run_until_complete(self._run())
|
|
57
|
+
|
|
58
|
+
async def on_command(self, cmd: Dict[str, Any]) -> None:
|
|
59
|
+
"""Handle a command from the WebSocket."""
|
|
60
|
+
typ = cmd.get("type")
|
|
61
|
+
|
|
62
|
+
if typ == "movement":
|
|
63
|
+
logger.debug("[Daemon] Movement command received")
|
|
64
|
+
mov = cmd.get("movement", {})
|
|
65
|
+
logger.debug("[Daemon] Movement command: %s", mov)
|
|
66
|
+
|
|
67
|
+
head = mov.get("head")
|
|
68
|
+
if head is not None:
|
|
69
|
+
head_arr = np.array(head, dtype=float).reshape(4, 4)
|
|
70
|
+
else:
|
|
71
|
+
head_arr = None
|
|
72
|
+
|
|
73
|
+
antennas = mov.get("antennas")
|
|
74
|
+
if antennas is not None:
|
|
75
|
+
antennas_arr = np.array(antennas, dtype=float)
|
|
76
|
+
else:
|
|
77
|
+
antennas_arr = None
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
await self.backend.goto_target(
|
|
81
|
+
head=head_arr,
|
|
82
|
+
antennas=antennas_arr,
|
|
83
|
+
duration=mov.get("duration", 1.0),
|
|
84
|
+
body_yaw=mov.get("body_yaw", 0.0),
|
|
85
|
+
)
|
|
86
|
+
except Exception as e:
|
|
87
|
+
logger.debug("[Daemon] Error in goto_target: %s", e)
|
|
88
|
+
elif typ == "ping":
|
|
89
|
+
logger.debug("[Daemon] Ping received")
|
|
90
|
+
return
|
|
91
|
+
else:
|
|
92
|
+
logger.debug("[Daemon] Unknown command type: %s", typ)
|
|
93
|
+
|
|
94
|
+
async def _run(self) -> None:
|
|
95
|
+
"""Run the WebSocket controller loop."""
|
|
96
|
+
while not self.stop_flag:
|
|
97
|
+
try:
|
|
98
|
+
ws: ClientConnection
|
|
99
|
+
async with connect(self.ws_uri, ping_interval=5, ping_timeout=10) as ws:
|
|
100
|
+
logger.info("[WS] Connected to Space")
|
|
101
|
+
async for msg in ws:
|
|
102
|
+
try:
|
|
103
|
+
data = json.loads(msg)
|
|
104
|
+
except Exception as e:
|
|
105
|
+
logger.debug("[WS] Bad JSON: %s raw: %s", e, msg)
|
|
106
|
+
continue
|
|
107
|
+
|
|
108
|
+
# Now this is awaited inside the same loop
|
|
109
|
+
await self.on_command(data)
|
|
110
|
+
|
|
111
|
+
except Exception as e:
|
|
112
|
+
logger.info("[WS] Connection failed: %s", e)
|
|
113
|
+
# small backoff before reconnect
|
|
114
|
+
await asyncio.sleep(1)
|
|
115
|
+
|
|
116
|
+
def stop(self) -> None:
|
|
117
|
+
"""Stop the WebSocket controller."""
|
|
118
|
+
self.stop_flag = True
|
|
119
|
+
if self.loop.is_running():
|
|
120
|
+
self.loop.call_soon_threadsafe(lambda: None)
|