reachy-mini 1.0.0__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 +24 -0
- reachy_mini/apps/app.py +121 -0
- reachy_mini/apps/manager.py +183 -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 +44 -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 +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/js/3rdparty/gstwebrtc-api-2.0.0.min.js +5 -0
- reachy_mini/daemon/app/dashboard/static/js/apps.js +304 -0
- reachy_mini/daemon/app/dashboard/static/js/appstore.js +150 -0
- reachy_mini/daemon/app/dashboard/static/js/daemon.js +163 -0
- reachy_mini/daemon/app/dashboard/static/js/move_player.js +132 -0
- reachy_mini/daemon/app/dashboard/static/js/setup_wifi.js +94 -0
- reachy_mini/daemon/app/dashboard/static/js/update.js +80 -0
- reachy_mini/daemon/app/dashboard/static/style.css +83 -0
- reachy_mini/daemon/app/dashboard/templates/base.html +23 -0
- reachy_mini/daemon/app/dashboard/templates/index.html +17 -0
- reachy_mini/daemon/app/dashboard/templates/sections/apps.html +8 -0
- reachy_mini/daemon/app/dashboard/templates/sections/appstore.html +29 -0
- reachy_mini/daemon/app/dashboard/templates/sections/daemon.html +36 -0
- reachy_mini/daemon/app/dashboard/templates/sections/move_player.html +27 -0
- reachy_mini/daemon/app/dashboard/update.html +22 -0
- reachy_mini/daemon/app/dashboard/wifi_config.html +41 -0
- reachy_mini/daemon/app/dependencies.py +41 -0
- reachy_mini/daemon/app/main.py +262 -0
- reachy_mini/daemon/app/models.py +147 -0
- reachy_mini/daemon/app/routers/apps.py +114 -0
- reachy_mini/daemon/app/routers/daemon.py +80 -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 +257 -0
- reachy_mini/daemon/app/routers/state.py +146 -0
- reachy_mini/daemon/backend/__init__.py +1 -0
- reachy_mini/daemon/backend/abstract.py +750 -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 +535 -0
- reachy_mini/daemon/daemon.py +444 -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 +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/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 +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 +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/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/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 +666 -0
- reachy_mini/media/__init__.py +1 -0
- reachy_mini/media/audio_base.py +163 -0
- reachy_mini/media/audio_gstreamer.py +195 -0
- reachy_mini/media/audio_sounddevice.py +226 -0
- reachy_mini/media/audio_utils.py +27 -0
- reachy_mini/media/camera_base.py +63 -0
- reachy_mini/media/camera_constants.py +13 -0
- reachy_mini/media/camera_gstreamer.py +162 -0
- reachy_mini/media/camera_opencv.py +61 -0
- reachy_mini/media/camera_utils.py +60 -0
- reachy_mini/media/media_manager.py +194 -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 +110 -0
- reachy_mini/utils/rerun.py +546 -0
- reachy_mini-1.0.0.dist-info/METADATA +286 -0
- reachy_mini-1.0.0.dist-info/RECORD +385 -0
- reachy_mini-1.0.0.dist-info/WHEEL +5 -0
- reachy_mini-1.0.0.dist-info/entry_points.txt +3 -0
- reachy_mini-1.0.0.dist-info/licenses/LICENSE +201 -0
- reachy_mini-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""Base classes for audio implementations.
|
|
2
|
+
|
|
3
|
+
The audio implementations support various backends and provide a unified
|
|
4
|
+
interface for audio input/output.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import struct
|
|
9
|
+
from abc import ABC, abstractmethod
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import List, Optional
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
import numpy.typing as npt
|
|
15
|
+
import usb
|
|
16
|
+
from libusb_package import get_libusb1_backend
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AudioBackend(Enum):
|
|
20
|
+
"""Audio backends."""
|
|
21
|
+
|
|
22
|
+
SOUNDDEVICE = "sounddevice"
|
|
23
|
+
GSTREAMER = "gstreamer"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AudioBase(ABC):
|
|
27
|
+
"""Abstract class for opening and managing audio devices."""
|
|
28
|
+
|
|
29
|
+
SAMPLE_RATE = 16000 # respeaker samplerate
|
|
30
|
+
TIMEOUT = 100000
|
|
31
|
+
PARAMETERS = {
|
|
32
|
+
"VERSION": (48, 0, 4, "ro", "uint8"),
|
|
33
|
+
"AEC_AZIMUTH_VALUES": (33, 75, 16 + 1, "ro", "radians"),
|
|
34
|
+
"DOA_VALUE": (20, 18, 4 + 1, "ro", "uint16"),
|
|
35
|
+
"DOA_VALUE_RADIANS": (20, 19, 8 + 1, "ro", "radians"),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
def __init__(self, backend: AudioBackend, log_level: str = "INFO") -> None:
|
|
39
|
+
"""Initialize the audio device."""
|
|
40
|
+
self.logger = logging.getLogger(__name__)
|
|
41
|
+
self.logger.setLevel(log_level)
|
|
42
|
+
self.backend = backend
|
|
43
|
+
self._respeaker = self._init_respeaker_usb()
|
|
44
|
+
# name, resid, cmdid, length, type
|
|
45
|
+
|
|
46
|
+
def __del__(self) -> None:
|
|
47
|
+
"""Destructor to ensure resources are released."""
|
|
48
|
+
if self._respeaker:
|
|
49
|
+
usb.util.dispose_resources(self._respeaker)
|
|
50
|
+
|
|
51
|
+
@abstractmethod
|
|
52
|
+
def start_recording(self) -> None:
|
|
53
|
+
"""Start recording audio."""
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
@abstractmethod
|
|
57
|
+
def get_audio_sample(self) -> Optional[npt.NDArray[np.float32]]:
|
|
58
|
+
"""Read audio data from the device. Returns the data or None if error."""
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def stop_recording(self) -> None:
|
|
63
|
+
"""Close the audio device and release resources."""
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
@abstractmethod
|
|
67
|
+
def start_playing(self) -> None:
|
|
68
|
+
"""Start playing audio."""
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
@abstractmethod
|
|
72
|
+
def push_audio_sample(self, data: npt.NDArray[np.float32]) -> None:
|
|
73
|
+
"""Push audio data to the output device."""
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
@abstractmethod
|
|
77
|
+
def stop_playing(self) -> None:
|
|
78
|
+
"""Stop playing audio and release resources."""
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
@abstractmethod
|
|
82
|
+
def play_sound(self, sound_file: str) -> None:
|
|
83
|
+
"""Play a sound file.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
sound_file (str): Path to the sound file to play.
|
|
87
|
+
|
|
88
|
+
"""
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
def _init_respeaker_usb(self) -> Optional[usb.core.Device]:
|
|
92
|
+
try:
|
|
93
|
+
dev = usb.core.find(
|
|
94
|
+
idVendor=0x2886, idProduct=0x001A, backend=get_libusb1_backend()
|
|
95
|
+
)
|
|
96
|
+
return dev
|
|
97
|
+
except usb.core.NoBackendError:
|
|
98
|
+
self.logger.error(
|
|
99
|
+
"No USB backend was found ! Make sure libusb_package is correctly installed with `pip install libusb_package`."
|
|
100
|
+
)
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
def _read_usb(self, name: str) -> Optional[List[int] | List[float]]:
|
|
104
|
+
try:
|
|
105
|
+
data = self.PARAMETERS[name]
|
|
106
|
+
except KeyError:
|
|
107
|
+
self.logger.error(f"Unknown parameter: {name}")
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
if not self._respeaker:
|
|
111
|
+
self.logger.warning("ReSpeaker device not found.")
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
resid = data[0]
|
|
115
|
+
cmdid = 0x80 | data[1]
|
|
116
|
+
length = data[2]
|
|
117
|
+
|
|
118
|
+
response = self._respeaker.ctrl_transfer(
|
|
119
|
+
usb.util.CTRL_IN
|
|
120
|
+
| usb.util.CTRL_TYPE_VENDOR
|
|
121
|
+
| usb.util.CTRL_RECIPIENT_DEVICE,
|
|
122
|
+
0,
|
|
123
|
+
cmdid,
|
|
124
|
+
resid,
|
|
125
|
+
length,
|
|
126
|
+
self.TIMEOUT,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
self.logger.debug(f"Response for {name}: {response}")
|
|
130
|
+
|
|
131
|
+
result: Optional[List[float] | List[int]] = None
|
|
132
|
+
if data[4] == "uint8":
|
|
133
|
+
result = response.tolist()
|
|
134
|
+
elif data[4] == "radians":
|
|
135
|
+
byte_data = response.tobytes()
|
|
136
|
+
num_values = (data[2] - 1) / 4
|
|
137
|
+
match_str = "<"
|
|
138
|
+
for i in range(int(num_values)):
|
|
139
|
+
match_str += "f"
|
|
140
|
+
result = [
|
|
141
|
+
float(x) for x in struct.unpack(match_str, byte_data[1 : data[2]])
|
|
142
|
+
]
|
|
143
|
+
elif data[4] == "uint16":
|
|
144
|
+
result = response.tolist()
|
|
145
|
+
|
|
146
|
+
return result
|
|
147
|
+
|
|
148
|
+
def get_DoA(self) -> tuple[float, bool] | None:
|
|
149
|
+
"""Get the Direction of Arrival (DoA) value from the ReSpeaker device.
|
|
150
|
+
|
|
151
|
+
0° is left, 90° is front/back, 180° is right
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
tuple: A tuple containing the DoA value as an integer and the speech detection, or None if the device is not found.
|
|
155
|
+
|
|
156
|
+
"""
|
|
157
|
+
if not self._respeaker:
|
|
158
|
+
self.logger.warning("ReSpeaker device not found.")
|
|
159
|
+
return None
|
|
160
|
+
result = self._read_usb("DOA_VALUE_RADIANS")
|
|
161
|
+
if result is None:
|
|
162
|
+
return None
|
|
163
|
+
return float(result[0]), bool(result[1])
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""GStreamer camera backend.
|
|
2
|
+
|
|
3
|
+
This module provides an implementation of the CameraBase class using GStreamer.
|
|
4
|
+
By default the module directly returns JPEG images as output by the camera.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from threading import Thread
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
import numpy.typing as npt
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
import gi
|
|
15
|
+
except ImportError as e:
|
|
16
|
+
raise ImportError(
|
|
17
|
+
"The 'gi' module is required for GStreamerCamera but could not be imported. \
|
|
18
|
+
Please install the GStreamer backend: pip install .[gstreamer]."
|
|
19
|
+
) from e
|
|
20
|
+
|
|
21
|
+
gi.require_version("Gst", "1.0")
|
|
22
|
+
gi.require_version("GstApp", "1.0")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
from gi.repository import GLib, Gst, GstApp # noqa: E402
|
|
26
|
+
|
|
27
|
+
from .audio_base import AudioBackend, AudioBase # noqa: E402
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class GStreamerAudio(AudioBase):
|
|
31
|
+
"""Audio implementation using GStreamer."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, log_level: str = "INFO") -> None:
|
|
34
|
+
"""Initialize the GStreamer audio."""
|
|
35
|
+
super().__init__(backend=AudioBackend.GSTREAMER, log_level=log_level)
|
|
36
|
+
Gst.init(None)
|
|
37
|
+
self._loop = GLib.MainLoop()
|
|
38
|
+
self._thread_bus_calls = Thread(target=lambda: self._loop.run(), daemon=True)
|
|
39
|
+
self._thread_bus_calls.start()
|
|
40
|
+
|
|
41
|
+
self._pipeline_record = Gst.Pipeline.new("audio_recorder")
|
|
42
|
+
self._appsink_audio: Optional[GstApp] = None
|
|
43
|
+
self._init_pipeline_record(self._pipeline_record)
|
|
44
|
+
self._bus_record = self._pipeline_record.get_bus()
|
|
45
|
+
self._bus_record.add_watch(
|
|
46
|
+
GLib.PRIORITY_DEFAULT, self._on_bus_message, self._loop
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
self._pipeline_playback = Gst.Pipeline.new("audio_player")
|
|
50
|
+
self._appsrc: Optional[GstApp] = None
|
|
51
|
+
self._init_pipeline_playback(self._pipeline_playback)
|
|
52
|
+
self._bus_playback = self._pipeline_playback.get_bus()
|
|
53
|
+
self._bus_playback.add_watch(
|
|
54
|
+
GLib.PRIORITY_DEFAULT, self._on_bus_message, self._loop
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def _init_pipeline_record(self, pipeline: Gst.Pipeline) -> None:
|
|
58
|
+
self._appsink_audio = Gst.ElementFactory.make("appsink")
|
|
59
|
+
caps = Gst.Caps.from_string(
|
|
60
|
+
f"audio/x-raw,channels=2,rate={self.SAMPLE_RATE},format=F32LE"
|
|
61
|
+
)
|
|
62
|
+
self._appsink_audio.set_property("caps", caps)
|
|
63
|
+
self._appsink_audio.set_property("drop", True) # avoid overflow
|
|
64
|
+
self._appsink_audio.set_property("max-buffers", 500)
|
|
65
|
+
|
|
66
|
+
autoaudiosrc = Gst.ElementFactory.make("autoaudiosrc") # use default mic
|
|
67
|
+
# caps_respeaker = Gst.Caps.from_string(
|
|
68
|
+
# "audio/x-raw, layout=interleaved, format=S16LE, rate=16000, channels=2"
|
|
69
|
+
# )
|
|
70
|
+
# autoaudiosrc.set_property("filter-caps", caps_respeaker)
|
|
71
|
+
queue = Gst.ElementFactory.make("queue")
|
|
72
|
+
audioconvert = Gst.ElementFactory.make("audioconvert")
|
|
73
|
+
audioresample = Gst.ElementFactory.make("audioresample")
|
|
74
|
+
|
|
75
|
+
if not all(
|
|
76
|
+
[autoaudiosrc, queue, audioconvert, audioresample, self._appsink_audio]
|
|
77
|
+
):
|
|
78
|
+
raise RuntimeError("Failed to create GStreamer elements")
|
|
79
|
+
|
|
80
|
+
pipeline.add(autoaudiosrc)
|
|
81
|
+
pipeline.add(queue)
|
|
82
|
+
pipeline.add(audioconvert)
|
|
83
|
+
pipeline.add(audioresample)
|
|
84
|
+
pipeline.add(self._appsink_audio)
|
|
85
|
+
|
|
86
|
+
autoaudiosrc.link(queue)
|
|
87
|
+
queue.link(audioconvert)
|
|
88
|
+
audioconvert.link(audioresample)
|
|
89
|
+
audioresample.link(self._appsink_audio)
|
|
90
|
+
|
|
91
|
+
def __del__(self) -> None:
|
|
92
|
+
"""Destructor to ensure gstreamer resources are released."""
|
|
93
|
+
super().__del__()
|
|
94
|
+
self._loop.quit()
|
|
95
|
+
self._bus_record.remove_watch()
|
|
96
|
+
self._bus_playback.remove_watch()
|
|
97
|
+
|
|
98
|
+
def _init_pipeline_playback(self, pipeline: Gst.Pipeline) -> None:
|
|
99
|
+
self._appsrc = Gst.ElementFactory.make("appsrc")
|
|
100
|
+
self._appsrc.set_property("format", Gst.Format.TIME)
|
|
101
|
+
self._appsrc.set_property("is-live", True)
|
|
102
|
+
caps = Gst.Caps.from_string(
|
|
103
|
+
f"audio/x-raw,format=F32LE,channels=1,rate={self.SAMPLE_RATE},layout=interleaved"
|
|
104
|
+
)
|
|
105
|
+
self._appsrc.set_property("caps", caps)
|
|
106
|
+
|
|
107
|
+
audioconvert = Gst.ElementFactory.make("audioconvert")
|
|
108
|
+
audioresample = Gst.ElementFactory.make("audioresample")
|
|
109
|
+
|
|
110
|
+
queue = Gst.ElementFactory.make("queue")
|
|
111
|
+
audiosink = Gst.ElementFactory.make("autoaudiosink") # use default speaker
|
|
112
|
+
|
|
113
|
+
pipeline.add(queue)
|
|
114
|
+
pipeline.add(audiosink)
|
|
115
|
+
pipeline.add(self._appsrc)
|
|
116
|
+
pipeline.add(audioconvert)
|
|
117
|
+
pipeline.add(audioresample)
|
|
118
|
+
|
|
119
|
+
self._appsrc.link(queue)
|
|
120
|
+
queue.link(audioconvert)
|
|
121
|
+
audioconvert.link(audioresample)
|
|
122
|
+
audioresample.link(audiosink)
|
|
123
|
+
|
|
124
|
+
def _on_bus_message(self, bus: Gst.Bus, msg: Gst.Message, loop) -> bool: # type: ignore[no-untyped-def]
|
|
125
|
+
t = msg.type
|
|
126
|
+
if t == Gst.MessageType.EOS:
|
|
127
|
+
self.logger.warning("End-of-stream")
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
elif t == Gst.MessageType.ERROR:
|
|
131
|
+
err, debug = msg.parse_error()
|
|
132
|
+
self.logger.error(f"Error: {err} {debug}")
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
return True
|
|
136
|
+
|
|
137
|
+
def start_recording(self) -> None:
|
|
138
|
+
"""Open the audio card using GStreamer."""
|
|
139
|
+
self._pipeline_record.set_state(Gst.State.PLAYING)
|
|
140
|
+
|
|
141
|
+
def _get_sample(self, appsink: GstApp.AppSink) -> Optional[bytes]:
|
|
142
|
+
sample = appsink.try_pull_sample(20_000_000)
|
|
143
|
+
if sample is None:
|
|
144
|
+
return None
|
|
145
|
+
data = None
|
|
146
|
+
if isinstance(sample, Gst.Sample):
|
|
147
|
+
buf = sample.get_buffer()
|
|
148
|
+
if buf is None:
|
|
149
|
+
self.logger.warning("Buffer is None")
|
|
150
|
+
|
|
151
|
+
data = buf.extract_dup(0, buf.get_size())
|
|
152
|
+
return data
|
|
153
|
+
|
|
154
|
+
def get_audio_sample(self) -> Optional[npt.NDArray[np.float32]]:
|
|
155
|
+
"""Read a sample from the audio card. Returns the sample or None if error.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
Optional[npt.NDArray[np.float32]]: The captured sample in raw format, or None if error.
|
|
159
|
+
|
|
160
|
+
"""
|
|
161
|
+
sample = self._get_sample(self._appsink_audio)
|
|
162
|
+
if sample is None:
|
|
163
|
+
return None
|
|
164
|
+
return np.frombuffer(sample, dtype=np.float32).reshape(-1, 2)
|
|
165
|
+
|
|
166
|
+
def stop_recording(self) -> None:
|
|
167
|
+
"""Release the camera resource."""
|
|
168
|
+
self._pipeline_record.set_state(Gst.State.NULL)
|
|
169
|
+
|
|
170
|
+
def start_playing(self) -> None:
|
|
171
|
+
"""Open the audio output using GStreamer."""
|
|
172
|
+
self._pipeline_playback.set_state(Gst.State.PLAYING)
|
|
173
|
+
|
|
174
|
+
def stop_playing(self) -> None:
|
|
175
|
+
"""Stop playing audio and release resources."""
|
|
176
|
+
self._pipeline_playback.set_state(Gst.State.NULL)
|
|
177
|
+
|
|
178
|
+
def push_audio_sample(self, data: npt.NDArray[np.float32]) -> None:
|
|
179
|
+
"""Push audio data to the output device."""
|
|
180
|
+
if self._appsrc is not None:
|
|
181
|
+
buf = Gst.Buffer.new_wrapped(data.tobytes())
|
|
182
|
+
self._appsrc.push_buffer(buf)
|
|
183
|
+
else:
|
|
184
|
+
self.logger.warning(
|
|
185
|
+
"AppSrc is not initialized. Call start_playing() first."
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
def play_sound(self, sound_file: str) -> None:
|
|
189
|
+
"""Play a sound file.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
sound_file (str): Path to the sound file to play.
|
|
193
|
+
|
|
194
|
+
"""
|
|
195
|
+
self.logger.warning("play_sound is not implemented for GStreamerAudio.")
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""Audio implementation using sounddevice backend."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import threading
|
|
5
|
+
import time
|
|
6
|
+
from typing import Any, List, Optional
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import numpy.typing as npt
|
|
10
|
+
import scipy
|
|
11
|
+
import sounddevice as sd
|
|
12
|
+
import soundfile as sf
|
|
13
|
+
|
|
14
|
+
from reachy_mini.utils.constants import ASSETS_ROOT_PATH
|
|
15
|
+
|
|
16
|
+
from .audio_base import AudioBackend, AudioBase
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SoundDeviceAudio(AudioBase):
|
|
20
|
+
"""Audio device implementation using sounddevice."""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
frames_per_buffer: int = 1024,
|
|
25
|
+
log_level: str = "INFO",
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Initialize the SoundDevice audio device."""
|
|
28
|
+
super().__init__(backend=AudioBackend.SOUNDDEVICE, log_level=log_level)
|
|
29
|
+
self.frames_per_buffer = frames_per_buffer
|
|
30
|
+
self.stream = None
|
|
31
|
+
self._output_stream = None
|
|
32
|
+
self._buffer: List[npt.NDArray[np.float32]] = []
|
|
33
|
+
self._output_device_id = self.get_output_device_id("respeaker")
|
|
34
|
+
self._input_device_id = self.get_input_device_id("respeaker")
|
|
35
|
+
|
|
36
|
+
def start_recording(self) -> None:
|
|
37
|
+
"""Open the audio input stream, using ReSpeaker card if available."""
|
|
38
|
+
self.stream = sd.InputStream(
|
|
39
|
+
blocksize=self.frames_per_buffer,
|
|
40
|
+
device=self._input_device_id,
|
|
41
|
+
callback=self._callback,
|
|
42
|
+
samplerate=self.SAMPLE_RATE,
|
|
43
|
+
)
|
|
44
|
+
if self.stream is None:
|
|
45
|
+
raise RuntimeError("Failed to open SoundDevice audio stream.")
|
|
46
|
+
self._buffer.clear()
|
|
47
|
+
self.stream.start()
|
|
48
|
+
self.logger.info("SoundDevice audio stream opened.")
|
|
49
|
+
|
|
50
|
+
def _callback(
|
|
51
|
+
self,
|
|
52
|
+
indata: npt.NDArray[np.float32],
|
|
53
|
+
frames: int,
|
|
54
|
+
time: int,
|
|
55
|
+
status: sd.CallbackFlags,
|
|
56
|
+
) -> None:
|
|
57
|
+
if status:
|
|
58
|
+
self.logger.warning(f"SoundDevice status: {status}")
|
|
59
|
+
|
|
60
|
+
self._buffer.append(indata.copy())
|
|
61
|
+
|
|
62
|
+
def get_audio_sample(self) -> Optional[npt.NDArray[np.float32]]:
|
|
63
|
+
"""Read audio data from the buffer. Returns numpy array or None if empty."""
|
|
64
|
+
if self._buffer and len(self._buffer) > 0:
|
|
65
|
+
data: npt.NDArray[np.float32] = np.concatenate(self._buffer, axis=0)
|
|
66
|
+
self._buffer.clear()
|
|
67
|
+
return data
|
|
68
|
+
self.logger.debug("No audio data available in buffer.")
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
def stop_recording(self) -> None:
|
|
72
|
+
"""Close the audio stream and release resources."""
|
|
73
|
+
if self.stream is not None:
|
|
74
|
+
self.stream.stop()
|
|
75
|
+
self.stream.close()
|
|
76
|
+
self.stream = None
|
|
77
|
+
self.logger.info("SoundDevice audio stream closed.")
|
|
78
|
+
|
|
79
|
+
def push_audio_sample(self, data: npt.NDArray[np.float32]) -> None:
|
|
80
|
+
"""Push audio data to the output device."""
|
|
81
|
+
if self._output_stream is not None:
|
|
82
|
+
self._output_stream.write(data)
|
|
83
|
+
else:
|
|
84
|
+
self.logger.warning(
|
|
85
|
+
"Output stream is not open. Call start_playing() first."
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def start_playing(self) -> None:
|
|
89
|
+
"""Open the audio output stream."""
|
|
90
|
+
self._output_stream = sd.OutputStream(
|
|
91
|
+
samplerate=self.SAMPLE_RATE,
|
|
92
|
+
device=self._output_device_id,
|
|
93
|
+
channels=1,
|
|
94
|
+
)
|
|
95
|
+
if self._output_stream is None:
|
|
96
|
+
raise RuntimeError("Failed to open SoundDevice audio output stream.")
|
|
97
|
+
self._output_stream.start()
|
|
98
|
+
|
|
99
|
+
def stop_playing(self) -> None:
|
|
100
|
+
"""Close the audio output stream."""
|
|
101
|
+
if self._output_stream is not None:
|
|
102
|
+
self._output_stream.stop()
|
|
103
|
+
self._output_stream.close()
|
|
104
|
+
self._output_stream = None
|
|
105
|
+
self.logger.info("SoundDevice audio output stream closed.")
|
|
106
|
+
|
|
107
|
+
def play_sound(self, sound_file: str, autoclean: bool = False) -> None:
|
|
108
|
+
"""Play a sound file from the assets directory or a given path using sounddevice and soundfile."""
|
|
109
|
+
file_path = f"{ASSETS_ROOT_PATH}/{sound_file}"
|
|
110
|
+
if not os.path.exists(file_path):
|
|
111
|
+
raise FileNotFoundError(f"Sound file {file_path} not found.")
|
|
112
|
+
|
|
113
|
+
data, samplerate_in = sf.read(file_path, dtype="float32")
|
|
114
|
+
|
|
115
|
+
if samplerate_in != self.SAMPLE_RATE:
|
|
116
|
+
data = scipy.signal.resample(
|
|
117
|
+
data, int(len(data) * (self.SAMPLE_RATE / samplerate_in))
|
|
118
|
+
)
|
|
119
|
+
if data.ndim > 1: # convert to mono
|
|
120
|
+
data = np.mean(data, axis=1)
|
|
121
|
+
|
|
122
|
+
self.logger.debug(f"Playing sound '{file_path}' at {samplerate_in} Hz")
|
|
123
|
+
|
|
124
|
+
self.stop_playing()
|
|
125
|
+
start = [0] # using list to modify in callback
|
|
126
|
+
length = len(data)
|
|
127
|
+
|
|
128
|
+
def callback(
|
|
129
|
+
outdata: npt.NDArray[np.float32],
|
|
130
|
+
frames: int,
|
|
131
|
+
time: Any, # cdata 'struct PaStreamCallbackTimeInfo *
|
|
132
|
+
status: sd.CallbackFlags,
|
|
133
|
+
) -> None:
|
|
134
|
+
"""Actual playback."""
|
|
135
|
+
if status:
|
|
136
|
+
self.logger.warning(f"SoundDevice output status: {status}")
|
|
137
|
+
|
|
138
|
+
end = start[0] + frames
|
|
139
|
+
if end > length:
|
|
140
|
+
# Fill the output buffer with the audio data, or zeros if finished
|
|
141
|
+
outdata[: length - start[0], 0] = data[start[0] :]
|
|
142
|
+
outdata[length - start[0] :, 0] = 0
|
|
143
|
+
raise sd.CallbackStop()
|
|
144
|
+
else:
|
|
145
|
+
outdata[:, 0] = data[start[0] : end]
|
|
146
|
+
start[0] = end
|
|
147
|
+
|
|
148
|
+
event = threading.Event()
|
|
149
|
+
|
|
150
|
+
self._output_stream = sd.OutputStream(
|
|
151
|
+
samplerate=self.SAMPLE_RATE,
|
|
152
|
+
device=self._output_device_id,
|
|
153
|
+
channels=1,
|
|
154
|
+
callback=callback,
|
|
155
|
+
finished_callback=event.set, # release the device when done
|
|
156
|
+
)
|
|
157
|
+
if self._output_stream is None:
|
|
158
|
+
raise RuntimeError("Failed to open SoundDevice audio output stream.")
|
|
159
|
+
self._output_stream.start()
|
|
160
|
+
|
|
161
|
+
def _clean_up_thread() -> None:
|
|
162
|
+
"""Thread to clean up the output stream after playback.
|
|
163
|
+
|
|
164
|
+
The daemon may play sound but should release the audio device.
|
|
165
|
+
"""
|
|
166
|
+
event.wait()
|
|
167
|
+
timeout = 5 # seconds
|
|
168
|
+
waited = 0
|
|
169
|
+
while (
|
|
170
|
+
self._output_stream is not None
|
|
171
|
+
and self._output_stream.active
|
|
172
|
+
and waited < timeout
|
|
173
|
+
):
|
|
174
|
+
time.sleep(0.1)
|
|
175
|
+
waited += 0.1
|
|
176
|
+
self.stop_playing()
|
|
177
|
+
|
|
178
|
+
if autoclean:
|
|
179
|
+
threading.Thread(
|
|
180
|
+
target=_clean_up_thread,
|
|
181
|
+
daemon=True,
|
|
182
|
+
).start()
|
|
183
|
+
|
|
184
|
+
def get_output_device_id(self, name_contains: str) -> int:
|
|
185
|
+
"""Return the output device id whose name contains the given string (case-insensitive).
|
|
186
|
+
|
|
187
|
+
If not found, return the default output device id.
|
|
188
|
+
"""
|
|
189
|
+
devices = sd.query_devices()
|
|
190
|
+
|
|
191
|
+
for idx, dev in enumerate(devices):
|
|
192
|
+
if (
|
|
193
|
+
name_contains.lower() in dev["name"].lower()
|
|
194
|
+
and dev["max_output_channels"] > 0
|
|
195
|
+
):
|
|
196
|
+
return idx
|
|
197
|
+
# Return default output device if not found
|
|
198
|
+
self.logger.warning(
|
|
199
|
+
f"No output device found containing '{name_contains}', using default."
|
|
200
|
+
)
|
|
201
|
+
return self._safe_query_device('output')
|
|
202
|
+
|
|
203
|
+
def get_input_device_id(self, name_contains: str) -> int:
|
|
204
|
+
"""Return the input device id whose name contains the given string (case-insensitive).
|
|
205
|
+
|
|
206
|
+
If not found, return the default input device id.
|
|
207
|
+
"""
|
|
208
|
+
devices = sd.query_devices()
|
|
209
|
+
|
|
210
|
+
for idx, dev in enumerate(devices):
|
|
211
|
+
if (
|
|
212
|
+
name_contains.lower() in dev["name"].lower()
|
|
213
|
+
and dev["max_input_channels"] > 0
|
|
214
|
+
):
|
|
215
|
+
return idx
|
|
216
|
+
# Return default input device if not found
|
|
217
|
+
self.logger.warning(
|
|
218
|
+
f"No input device found containing '{name_contains}', using default."
|
|
219
|
+
)
|
|
220
|
+
return self._safe_query_device('input')
|
|
221
|
+
|
|
222
|
+
def _safe_query_device(self, kind: str) -> int:
|
|
223
|
+
try:
|
|
224
|
+
return int(sd.query_devices(None, kind)['index'])
|
|
225
|
+
except sd.PortAudioError:
|
|
226
|
+
return int(sd.default.device[1])
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Utility functions for audio handling, specifically for detecting the ReSpeaker sound card."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import subprocess
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_respeaker_card_number() -> int:
|
|
8
|
+
"""Return the card number of the ReSpeaker sound card, or 0 if not found."""
|
|
9
|
+
try:
|
|
10
|
+
result = subprocess.run(
|
|
11
|
+
["arecord", "-l"], capture_output=True, text=True, check=True
|
|
12
|
+
)
|
|
13
|
+
output = result.stdout
|
|
14
|
+
|
|
15
|
+
lines = output.split("\n")
|
|
16
|
+
for line in lines:
|
|
17
|
+
if "ReSpeaker" in line and "card" in line:
|
|
18
|
+
card_number = line.split(":")[0].split("card ")[1].strip()
|
|
19
|
+
logging.debug(f"Found ReSpeaker sound card: {card_number}")
|
|
20
|
+
return int(card_number)
|
|
21
|
+
|
|
22
|
+
logging.warning("ReSpeaker sound card not found. Returning default card")
|
|
23
|
+
return 0 # default sound card
|
|
24
|
+
|
|
25
|
+
except subprocess.CalledProcessError as e:
|
|
26
|
+
logging.error(f"Cannot find sound card: {e}")
|
|
27
|
+
return 0
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Base classes for camera implementations.
|
|
2
|
+
|
|
3
|
+
The camera implementations support various backends and provide a unified
|
|
4
|
+
interface for capturing images.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from enum import Enum
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
import numpy.typing as npt
|
|
14
|
+
|
|
15
|
+
from reachy_mini.media.camera_constants import CameraResolution
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CameraBackend(Enum):
|
|
19
|
+
"""Camera backends."""
|
|
20
|
+
|
|
21
|
+
OPENCV = "opencv"
|
|
22
|
+
GSTREAMER = "gstreamer"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class CameraBase(ABC):
|
|
26
|
+
"""Abstract class for opening and managing a camera."""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
backend: CameraBackend,
|
|
31
|
+
log_level: str = "INFO",
|
|
32
|
+
resolution: CameraResolution = CameraResolution.R1280x720,
|
|
33
|
+
) -> None:
|
|
34
|
+
"""Initialize the camera."""
|
|
35
|
+
self.logger = logging.getLogger(__name__)
|
|
36
|
+
self.logger.setLevel(log_level)
|
|
37
|
+
self.backend = backend
|
|
38
|
+
self._resolution = resolution
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def resolution(self) -> tuple[int, int]:
|
|
42
|
+
"""Get the current camera resolution as a tuple (width, height)."""
|
|
43
|
+
return (self._resolution.value[0], self._resolution.value[1])
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def framerate(self) -> int:
|
|
47
|
+
"""Get the current camera frames per second."""
|
|
48
|
+
return self._resolution.value[2]
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
def open(self) -> None:
|
|
52
|
+
"""Open the camera."""
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
@abstractmethod
|
|
56
|
+
def read(self) -> Optional[npt.NDArray[np.uint8]]:
|
|
57
|
+
"""Read an image from the camera. Returns the image or None if error."""
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
@abstractmethod
|
|
61
|
+
def close(self) -> None:
|
|
62
|
+
"""Close the camera and release resources."""
|
|
63
|
+
pass
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Camera constants for Reachy Mini."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CameraResolution(Enum):
|
|
7
|
+
"""Camera resolutions. Arducam_12MP."""
|
|
8
|
+
|
|
9
|
+
R2304x1296 = (2304, 1296, 30)
|
|
10
|
+
R4608x2592 = (4608, 2592, 10)
|
|
11
|
+
R1920x1080 = (1920, 1080, 30)
|
|
12
|
+
R1600x1200 = (1600, 1200, 30)
|
|
13
|
+
R1280x720 = (1280, 720, 30)
|