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.
Files changed (479) hide show
  1. reachy_mini/__init__.py +4 -0
  2. reachy_mini/apps/__init__.py +25 -0
  3. reachy_mini/apps/app.py +258 -0
  4. reachy_mini/apps/assistant.py +787 -0
  5. reachy_mini/apps/manager.py +312 -0
  6. reachy_mini/apps/sources/__init__.py +4 -0
  7. reachy_mini/apps/sources/hf_space.py +106 -0
  8. reachy_mini/apps/sources/local_common_venv.py +567 -0
  9. reachy_mini/apps/templates/README.md.j2 +12 -0
  10. reachy_mini/apps/templates/gitignore.j2 +3 -0
  11. reachy_mini/apps/templates/index.html.j2 +40 -0
  12. reachy_mini/apps/templates/main.py.j2 +74 -0
  13. reachy_mini/apps/templates/pyproject.toml.j2 +28 -0
  14. reachy_mini/apps/templates/static/index.html.j2 +27 -0
  15. reachy_mini/apps/templates/static/main.js.j2 +47 -0
  16. reachy_mini/apps/templates/static/style.css.j2 +25 -0
  17. reachy_mini/apps/templates/style.css.j2 +411 -0
  18. reachy_mini/apps/utils.py +30 -0
  19. reachy_mini/assets/config/hardware_config.yaml +164 -0
  20. reachy_mini/assets/confused1.wav +0 -0
  21. reachy_mini/assets/count.wav +0 -0
  22. reachy_mini/assets/dance1.wav +0 -0
  23. reachy_mini/assets/firmware/CHANGELOG.md +15 -0
  24. reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.2.bin +0 -0
  25. reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.3.bin +0 -0
  26. reachy_mini/assets/firmware/update.sh +9 -0
  27. reachy_mini/assets/go_sleep.wav +0 -0
  28. reachy_mini/assets/impatient1.wav +0 -0
  29. reachy_mini/assets/kinematics_data.json +253 -0
  30. reachy_mini/assets/models/fknetwork.dynamic.onnx +0 -0
  31. reachy_mini/assets/models/fknetwork.onnx +0 -0
  32. reachy_mini/assets/models/fknetwork_int8.onnx +0 -0
  33. reachy_mini/assets/models/iknetwork.onnx +0 -0
  34. reachy_mini/assets/wake_up.wav +0 -0
  35. reachy_mini/daemon/__init__.py +1 -0
  36. reachy_mini/daemon/app/__init__.py +1 -0
  37. reachy_mini/daemon/app/bg_job_register.py +142 -0
  38. reachy_mini/daemon/app/dashboard/static/assets/KO-cartoon-static.svg +40 -0
  39. reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon-static.svg +42 -0
  40. reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon.svg +6 -0
  41. reachy_mini/daemon/app/dashboard/static/assets/go-to-sleep-cartoon.svg +6 -0
  42. reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon-static.svg +50 -0
  43. reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon.svg +6 -0
  44. reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-awake.svg +18 -0
  45. reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-connection-lost-animation.svg +6 -0
  46. reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-go-to-sleep-animation.svg +6 -0
  47. reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko-animation.svg +6 -0
  48. reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko.svg +22 -0
  49. reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping-static.svg +41 -0
  50. reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping.svg +18 -0
  51. reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-wake-up-animation.svg +6 -0
  52. reachy_mini/daemon/app/dashboard/static/css/app.css +1433 -0
  53. reachy_mini/daemon/app/dashboard/static/fonts/Archivo-Italic-VariableFont_wdth,wght.ttf +0 -0
  54. reachy_mini/daemon/app/dashboard/static/fonts/Archivo-VariableFont_wdth,wght.ttf +0 -0
  55. reachy_mini/daemon/app/dashboard/static/fonts/Asap-Italic-VariableFont_wdth,wght.ttf +0 -0
  56. reachy_mini/daemon/app/dashboard/static/fonts/Asap-VariableFont_wdth,wght.ttf +0 -0
  57. reachy_mini/daemon/app/dashboard/static/js/3rdparty/gstwebrtc-api-2.0.0.min.js +5 -0
  58. reachy_mini/daemon/app/dashboard/static/js/apps.js +323 -0
  59. reachy_mini/daemon/app/dashboard/static/js/appstore.js +167 -0
  60. reachy_mini/daemon/app/dashboard/static/js/daemon.js +169 -0
  61. reachy_mini/daemon/app/dashboard/static/js/health_check.js +9 -0
  62. reachy_mini/daemon/app/dashboard/static/js/move_player.js +132 -0
  63. reachy_mini/daemon/app/dashboard/static/js/notification.js +41 -0
  64. reachy_mini/daemon/app/dashboard/static/js/update.js +132 -0
  65. reachy_mini/daemon/app/dashboard/static/js/volume_control.js +252 -0
  66. reachy_mini/daemon/app/dashboard/static/js/wifi.js +176 -0
  67. reachy_mini/daemon/app/dashboard/tailwindcss/README.md +15 -0
  68. reachy_mini/daemon/app/dashboard/tailwindcss/package-lock.json +1127 -0
  69. reachy_mini/daemon/app/dashboard/tailwindcss/package.json +6 -0
  70. reachy_mini/daemon/app/dashboard/tailwindcss/pnpm-lock.yaml +608 -0
  71. reachy_mini/daemon/app/dashboard/tailwindcss/postcss.config.js +6 -0
  72. reachy_mini/daemon/app/dashboard/tailwindcss/styles/app.css +185 -0
  73. reachy_mini/daemon/app/dashboard/tailwindcss/tailwind.config.js +11 -0
  74. reachy_mini/daemon/app/dashboard/templates/base.html +21 -0
  75. reachy_mini/daemon/app/dashboard/templates/index.html +20 -0
  76. reachy_mini/daemon/app/dashboard/templates/sections/apps.html +8 -0
  77. reachy_mini/daemon/app/dashboard/templates/sections/appstore.html +35 -0
  78. reachy_mini/daemon/app/dashboard/templates/sections/daemon.html +45 -0
  79. reachy_mini/daemon/app/dashboard/templates/sections/move_player.html +121 -0
  80. reachy_mini/daemon/app/dashboard/templates/sections/notification.html +19 -0
  81. reachy_mini/daemon/app/dashboard/templates/sections/update.html +54 -0
  82. reachy_mini/daemon/app/dashboard/templates/sections/wifi.html +46 -0
  83. reachy_mini/daemon/app/dashboard/templates/settings.html +14 -0
  84. reachy_mini/daemon/app/dependencies.py +41 -0
  85. reachy_mini/daemon/app/main.py +469 -0
  86. reachy_mini/daemon/app/models.py +149 -0
  87. reachy_mini/daemon/app/routers/apps.py +114 -0
  88. reachy_mini/daemon/app/routers/daemon.py +85 -0
  89. reachy_mini/daemon/app/routers/kinematics.py +57 -0
  90. reachy_mini/daemon/app/routers/motors.py +41 -0
  91. reachy_mini/daemon/app/routers/move.py +265 -0
  92. reachy_mini/daemon/app/routers/state.py +146 -0
  93. reachy_mini/daemon/app/routers/update.py +86 -0
  94. reachy_mini/daemon/app/routers/volume.py +423 -0
  95. reachy_mini/daemon/app/routers/wifi_config.py +219 -0
  96. reachy_mini/daemon/app/services/__init__.py +1 -0
  97. reachy_mini/daemon/app/services/bluetooth/bluetooth_service.py +796 -0
  98. reachy_mini/daemon/app/services/bluetooth/commands/HOTSPOT.sh +7 -0
  99. reachy_mini/daemon/app/services/bluetooth/commands/RESTART_DAEMON.sh +4 -0
  100. reachy_mini/daemon/app/services/bluetooth/commands/SOFTWARE_RESET.sh +7 -0
  101. reachy_mini/daemon/app/services/bluetooth/install_service_bluetooth.sh +39 -0
  102. reachy_mini/daemon/app/services/gpio_shutdown/__init__.py +1 -0
  103. reachy_mini/daemon/app/services/gpio_shutdown/install_service.sh +33 -0
  104. reachy_mini/daemon/app/services/gpio_shutdown/launcher.sh +3 -0
  105. reachy_mini/daemon/app/services/gpio_shutdown/shutdown_monitor.py +28 -0
  106. reachy_mini/daemon/app/services/wireless/generate_asoundrc.sh +70 -0
  107. reachy_mini/daemon/app/services/wireless/install_service.sh +33 -0
  108. reachy_mini/daemon/app/services/wireless/launcher.sh +5 -0
  109. reachy_mini/daemon/backend/__init__.py +1 -0
  110. reachy_mini/daemon/backend/abstract.py +815 -0
  111. reachy_mini/daemon/backend/mujoco/__init__.py +36 -0
  112. reachy_mini/daemon/backend/mujoco/backend.py +395 -0
  113. reachy_mini/daemon/backend/mujoco/utils.py +59 -0
  114. reachy_mini/daemon/backend/mujoco/video_udp.py +57 -0
  115. reachy_mini/daemon/backend/robot/__init__.py +8 -0
  116. reachy_mini/daemon/backend/robot/backend.py +597 -0
  117. reachy_mini/daemon/daemon.py +651 -0
  118. reachy_mini/daemon/utils.py +116 -0
  119. reachy_mini/descriptions/reachy_mini/mjcf/additional.xml +6 -0
  120. reachy_mini/descriptions/reachy_mini/mjcf/assets/5w_speaker.part +13 -0
  121. reachy_mini/descriptions/reachy_mini/mjcf/assets/5w_speaker.stl +0 -0
  122. reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna.part +13 -0
  123. reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna.stl +0 -0
  124. reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_body_3dprint.part +13 -0
  125. reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_body_3dprint.stl +0 -0
  126. reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_l_3dprint.part +13 -0
  127. reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_l_3dprint.stl +0 -0
  128. reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_r_3dprint.part +13 -0
  129. reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_r_3dprint.stl +0 -0
  130. reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_interface_3dprint.part +13 -0
  131. reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_interface_3dprint.stl +0 -0
  132. reachy_mini/descriptions/reachy_mini/mjcf/assets/arducam.part +13 -0
  133. reachy_mini/descriptions/reachy_mini/mjcf/assets/arducam.stl +0 -0
  134. reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh.part +13 -0
  135. reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh.stl +0 -0
  136. reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh_1.part +13 -0
  137. reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh_1.stl +0 -0
  138. reachy_mini/descriptions/reachy_mini/mjcf/assets/bearing_85x110x13.part +13 -0
  139. reachy_mini/descriptions/reachy_mini/mjcf/assets/bearing_85x110x13.stl +0 -0
  140. reachy_mini/descriptions/reachy_mini/mjcf/assets/big_lens_d40.part +13 -0
  141. reachy_mini/descriptions/reachy_mini/mjcf/assets/big_lens_d40.stl +0 -0
  142. reachy_mini/descriptions/reachy_mini/mjcf/assets/body_down_3dprint.part +13 -0
  143. reachy_mini/descriptions/reachy_mini/mjcf/assets/body_down_3dprint.stl +0 -0
  144. reachy_mini/descriptions/reachy_mini/mjcf/assets/body_foot_3dprint.part +13 -0
  145. reachy_mini/descriptions/reachy_mini/mjcf/assets/body_foot_3dprint.stl +0 -0
  146. reachy_mini/descriptions/reachy_mini/mjcf/assets/body_top_3dprint.part +13 -0
  147. reachy_mini/descriptions/reachy_mini/mjcf/assets/body_top_3dprint.stl +0 -0
  148. reachy_mini/descriptions/reachy_mini/mjcf/assets/body_turning_3dprint.part +13 -0
  149. reachy_mini/descriptions/reachy_mini/mjcf/assets/body_turning_3dprint.stl +0 -0
  150. reachy_mini/descriptions/reachy_mini/mjcf/assets/bts2_m2_6x8.part +13 -0
  151. reachy_mini/descriptions/reachy_mini/mjcf/assets/bts2_m2_6x8.stl +0 -0
  152. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/README.m +1 -0
  153. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_0.stl +0 -0
  154. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_1.stl +0 -0
  155. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_2.stl +0 -0
  156. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_3.stl +0 -0
  157. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_0.stl +0 -0
  158. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_1.stl +0 -0
  159. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_2.stl +0 -0
  160. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/head_one_3dprint_collider_0.stl +0 -0
  161. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_0.stl +0 -0
  162. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_1.stl +0 -0
  163. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_2.stl +0 -0
  164. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_3.stl +0 -0
  165. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_0.stl +0 -0
  166. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_1.stl +0 -0
  167. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_2.stl +0 -0
  168. reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/head_one_3dprint_collider_0.stl +0 -0
  169. reachy_mini/descriptions/reachy_mini/mjcf/assets/croissant_1k.blend/croissant_1k.obj +5021 -0
  170. reachy_mini/descriptions/reachy_mini/mjcf/assets/croissant_1k.blend/textures/croissant_diff_1k.png +0 -0
  171. reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_b_dummy.part +13 -0
  172. reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_b_dummy.stl +0 -0
  173. reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_f_dummy.part +13 -0
  174. reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_f_dummy.stl +0 -0
  175. reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_m_dummy.part +13 -0
  176. reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_m_dummy.stl +0 -0
  177. reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_horn_dummy.part +13 -0
  178. reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_horn_dummy.stl +0 -0
  179. reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_led_cap2_dummy.part +13 -0
  180. reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_led_cap2_dummy.stl +0 -0
  181. reachy_mini/descriptions/reachy_mini/mjcf/assets/food_apple_01_1k.blend/food_apple_01_1k.obj +18045 -0
  182. reachy_mini/descriptions/reachy_mini/mjcf/assets/food_apple_01_1k.blend/textures/food_apple_01_diff_1k.png +0 -0
  183. reachy_mini/descriptions/reachy_mini/mjcf/assets/glasses_dolder_3dprint.part +13 -0
  184. reachy_mini/descriptions/reachy_mini/mjcf/assets/glasses_dolder_3dprint.stl +0 -0
  185. reachy_mini/descriptions/reachy_mini/mjcf/assets/head_back_3dprint.part +13 -0
  186. reachy_mini/descriptions/reachy_mini/mjcf/assets/head_back_3dprint.stl +0 -0
  187. reachy_mini/descriptions/reachy_mini/mjcf/assets/head_front_3dprint.part +13 -0
  188. reachy_mini/descriptions/reachy_mini/mjcf/assets/head_front_3dprint.stl +0 -0
  189. reachy_mini/descriptions/reachy_mini/mjcf/assets/head_mic_3dprint.part +13 -0
  190. reachy_mini/descriptions/reachy_mini/mjcf/assets/head_mic_3dprint.stl +0 -0
  191. reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d30_3dprint.part +13 -0
  192. reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d30_3dprint.stl +0 -0
  193. reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d40_3dprint.part +13 -0
  194. reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d40_3dprint.stl +0 -0
  195. reachy_mini/descriptions/reachy_mini/mjcf/assets/m12_fisheye_lens_1_8mm.part +13 -0
  196. reachy_mini/descriptions/reachy_mini/mjcf/assets/m12_fisheye_lens_1_8mm.stl +0 -0
  197. reachy_mini/descriptions/reachy_mini/mjcf/assets/mp01062_stewart_arm_3.part +13 -0
  198. reachy_mini/descriptions/reachy_mini/mjcf/assets/mp01062_stewart_arm_3.stl +0 -0
  199. reachy_mini/descriptions/reachy_mini/mjcf/assets/neck_reference_3dprint.part +13 -0
  200. reachy_mini/descriptions/reachy_mini/mjcf/assets/neck_reference_3dprint.stl +0 -0
  201. reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10.part +13 -0
  202. reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10.stl +0 -0
  203. reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_1.part +13 -0
  204. reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_1.stl +0 -0
  205. reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_2.part +13 -0
  206. reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_2.stl +0 -0
  207. reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_3.part +13 -0
  208. reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_3.stl +0 -0
  209. reachy_mini/descriptions/reachy_mini/mjcf/assets/pp01102_arducam_carter.part +13 -0
  210. reachy_mini/descriptions/reachy_mini/mjcf/assets/pp01102_arducam_carter.stl +0 -0
  211. reachy_mini/descriptions/reachy_mini/mjcf/assets/rubber_duck_toy_1k.blend/rubber_duck_toy_1k.obj +8940 -0
  212. reachy_mini/descriptions/reachy_mini/mjcf/assets/rubber_duck_toy_1k.blend/textures/rubber_duck_toy_diff_1k.png +0 -0
  213. reachy_mini/descriptions/reachy_mini/mjcf/assets/small_lens_d30.part +13 -0
  214. reachy_mini/descriptions/reachy_mini/mjcf/assets/small_lens_d30.stl +0 -0
  215. reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball.part +13 -0
  216. reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball.stl +0 -0
  217. reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball__2.part +13 -0
  218. reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball__2.stl +0 -0
  219. reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_rod.part +13 -0
  220. reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_rod.stl +0 -0
  221. reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_main_plate_3dprint.part +13 -0
  222. reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_main_plate_3dprint.stl +0 -0
  223. reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_tricap_3dprint.part +13 -0
  224. reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_tricap_3dprint.stl +0 -0
  225. reachy_mini/descriptions/reachy_mini/mjcf/assets/wooden_table_02_1k.blend/textures/wooden_table_02_diff_1k.png +0 -0
  226. reachy_mini/descriptions/reachy_mini/mjcf/assets/wooden_table_02_1k.blend/wooden_table_02_1k.obj +485 -0
  227. reachy_mini/descriptions/reachy_mini/mjcf/config.json +21 -0
  228. reachy_mini/descriptions/reachy_mini/mjcf/joints_properties.xml +29 -0
  229. reachy_mini/descriptions/reachy_mini/mjcf/reachy_mini.xml +642 -0
  230. reachy_mini/descriptions/reachy_mini/mjcf/reachy_mini.xml.bak +442 -0
  231. reachy_mini/descriptions/reachy_mini/mjcf/scene.xml +24 -0
  232. reachy_mini/descriptions/reachy_mini/mjcf/scenes/empty.xml +27 -0
  233. reachy_mini/descriptions/reachy_mini/mjcf/scenes/minimal.xml +132 -0
  234. reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.part +13 -0
  235. reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.stl +0 -0
  236. reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.part +13 -0
  237. reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.stl +0 -0
  238. reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.part +13 -0
  239. reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.stl +0 -0
  240. reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.part +13 -0
  241. reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.stl +0 -0
  242. reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.part +13 -0
  243. reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.stl +0 -0
  244. reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.part +13 -0
  245. reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.stl +0 -0
  246. reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.part +13 -0
  247. reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.stl +0 -0
  248. reachy_mini/descriptions/reachy_mini/urdf/assets/arm.part +13 -0
  249. reachy_mini/descriptions/reachy_mini/urdf/assets/arm.stl +0 -0
  250. reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.part +13 -0
  251. reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.stl +0 -0
  252. reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.part +13 -0
  253. reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.stl +0 -0
  254. reachy_mini/descriptions/reachy_mini/urdf/assets/ball.part +13 -0
  255. reachy_mini/descriptions/reachy_mini/urdf/assets/ball.stl +0 -0
  256. reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.part +13 -0
  257. reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.stl +0 -0
  258. reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.part +13 -0
  259. reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.stl +0 -0
  260. reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.part +13 -0
  261. reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.stl +0 -0
  262. reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.part +13 -0
  263. reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.stl +0 -0
  264. reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.part +13 -0
  265. reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.stl +0 -0
  266. reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.part +13 -0
  267. reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.stl +0 -0
  268. reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.part +13 -0
  269. reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.stl +0 -0
  270. reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.part +13 -0
  271. reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.stl +0 -0
  272. reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.part +13 -0
  273. reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.stl +0 -0
  274. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/README.m +1 -0
  275. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_0.stl +0 -0
  276. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_1.stl +0 -0
  277. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_2.stl +0 -0
  278. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_3.stl +0 -0
  279. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_0.stl +0 -0
  280. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_1.stl +0 -0
  281. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_2.stl +0 -0
  282. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/head_one_3dprint_collider_0.stl +0 -0
  283. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_0.stl +0 -0
  284. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_1.stl +0 -0
  285. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_2.stl +0 -0
  286. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_3.stl +0 -0
  287. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_0.stl +0 -0
  288. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_1.stl +0 -0
  289. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_2.stl +0 -0
  290. reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/head_one_3dprint_collider_0.stl +0 -0
  291. reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.part +13 -0
  292. reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.stl +0 -0
  293. reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.part +13 -0
  294. reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.stl +0 -0
  295. reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.part +13 -0
  296. reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.stl +0 -0
  297. reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.part +13 -0
  298. reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.stl +0 -0
  299. reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.part +13 -0
  300. reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.stl +0 -0
  301. reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.part +14 -0
  302. reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.stl +0 -0
  303. reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.part +14 -0
  304. reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.stl +0 -0
  305. reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.part +13 -0
  306. reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.stl +0 -0
  307. reachy_mini/descriptions/reachy_mini/urdf/assets/foot.part +13 -0
  308. reachy_mini/descriptions/reachy_mini/urdf/assets/foot.stl +0 -0
  309. reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.part +13 -0
  310. reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.stl +0 -0
  311. reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.part +13 -0
  312. reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.stl +0 -0
  313. reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.part +13 -0
  314. reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.stl +0 -0
  315. reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.part +13 -0
  316. reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.stl +0 -0
  317. reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.part +13 -0
  318. reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.stl +0 -0
  319. reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.part +13 -0
  320. reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.stl +0 -0
  321. reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.part +13 -0
  322. reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.stl +0 -0
  323. reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.part +13 -0
  324. reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.stl +0 -0
  325. reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.part +13 -0
  326. reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.stl +0 -0
  327. reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.part +13 -0
  328. reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.stl +0 -0
  329. reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.part +13 -0
  330. reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.stl +0 -0
  331. reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.part +13 -0
  332. reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.stl +0 -0
  333. reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.part +13 -0
  334. reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.stl +0 -0
  335. reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.part +13 -0
  336. reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.stl +0 -0
  337. reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.part +13 -0
  338. reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.stl +0 -0
  339. reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.part +13 -0
  340. reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.stl +0 -0
  341. reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.part +13 -0
  342. reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.stl +0 -0
  343. reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.part +13 -0
  344. reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.stl +0 -0
  345. reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.part +13 -0
  346. reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.stl +0 -0
  347. reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.part +13 -0
  348. reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.stl +0 -0
  349. reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.part +13 -0
  350. reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.stl +0 -0
  351. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.part +13 -0
  352. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.stl +0 -0
  353. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.part +13 -0
  354. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.stl +0 -0
  355. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.part +13 -0
  356. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.stl +0 -0
  357. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.part +13 -0
  358. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.stl +0 -0
  359. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.part +13 -0
  360. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.stl +0 -0
  361. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.part +13 -0
  362. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.stl +0 -0
  363. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.part +13 -0
  364. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.stl +0 -0
  365. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.part +13 -0
  366. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.stl +0 -0
  367. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.part +13 -0
  368. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.stl +0 -0
  369. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.part +13 -0
  370. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.stl +0 -0
  371. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.part +13 -0
  372. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.stl +0 -0
  373. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.part +13 -0
  374. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.stl +0 -0
  375. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.part +13 -0
  376. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.stl +0 -0
  377. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.part +13 -0
  378. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.stl +0 -0
  379. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.part +13 -0
  380. reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.stl +0 -0
  381. reachy_mini/descriptions/reachy_mini/urdf/assets/rod.part +13 -0
  382. reachy_mini/descriptions/reachy_mini/urdf/assets/rod.stl +0 -0
  383. reachy_mini/descriptions/reachy_mini/urdf/assets/shape.part +13 -0
  384. reachy_mini/descriptions/reachy_mini/urdf/assets/shape.stl +0 -0
  385. reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.part +13 -0
  386. reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.stl +0 -0
  387. reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.part +13 -0
  388. reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.stl +0 -0
  389. reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.part +13 -0
  390. reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.stl +0 -0
  391. reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.part +13 -0
  392. reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.stl +0 -0
  393. reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.part +13 -0
  394. reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.stl +0 -0
  395. reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.part +13 -0
  396. reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.stl +0 -0
  397. reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.part +13 -0
  398. reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.stl +0 -0
  399. reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.part +13 -0
  400. reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.stl +0 -0
  401. reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.part +13 -0
  402. reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.stl +0 -0
  403. reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.part +13 -0
  404. reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.stl +0 -0
  405. reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.part +13 -0
  406. reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.stl +0 -0
  407. reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.part +13 -0
  408. reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.stl +0 -0
  409. reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.part +13 -0
  410. reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.stl +0 -0
  411. reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.part +14 -0
  412. reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.stl +0 -0
  413. reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.part +14 -0
  414. reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.stl +0 -0
  415. reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.part +14 -0
  416. reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.stl +0 -0
  417. reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.part +14 -0
  418. reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.stl +0 -0
  419. reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.part +14 -0
  420. reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.stl +0 -0
  421. reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.part +14 -0
  422. reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.stl +0 -0
  423. reachy_mini/descriptions/reachy_mini/urdf/config.json +18 -0
  424. reachy_mini/descriptions/reachy_mini/urdf/robot.urdf +3281 -0
  425. reachy_mini/descriptions/reachy_mini/urdf/robot.urdf.bak +3282 -0
  426. reachy_mini/descriptions/reachy_mini/urdf/robot_no_collision.urdf +2316 -0
  427. reachy_mini/descriptions/reachy_mini/urdf/robot_simple_collision.urdf +2399 -0
  428. reachy_mini/io/__init__.py +15 -0
  429. reachy_mini/io/abstract.py +70 -0
  430. reachy_mini/io/audio_ws.py +242 -0
  431. reachy_mini/io/protocol.py +44 -0
  432. reachy_mini/io/video_ws.py +135 -0
  433. reachy_mini/io/ws_controller.py +120 -0
  434. reachy_mini/io/zenoh_client.py +273 -0
  435. reachy_mini/io/zenoh_server.py +230 -0
  436. reachy_mini/kinematics/__init__.py +68 -0
  437. reachy_mini/kinematics/analytical_kinematics.py +151 -0
  438. reachy_mini/kinematics/nn_kinematics.py +109 -0
  439. reachy_mini/kinematics/placo_kinematics.py +656 -0
  440. reachy_mini/media/__init__.py +1 -0
  441. reachy_mini/media/audio_base.py +111 -0
  442. reachy_mini/media/audio_control_utils.py +455 -0
  443. reachy_mini/media/audio_gstreamer.py +272 -0
  444. reachy_mini/media/audio_sounddevice.py +321 -0
  445. reachy_mini/media/audio_utils.py +114 -0
  446. reachy_mini/media/camera_base.py +100 -0
  447. reachy_mini/media/camera_constants.py +152 -0
  448. reachy_mini/media/camera_gstreamer.py +231 -0
  449. reachy_mini/media/camera_opencv.py +89 -0
  450. reachy_mini/media/camera_utils.py +111 -0
  451. reachy_mini/media/media_manager.py +281 -0
  452. reachy_mini/media/webrtc_client_gstreamer.py +288 -0
  453. reachy_mini/media/webrtc_daemon.py +334 -0
  454. reachy_mini/motion/__init__.py +4 -0
  455. reachy_mini/motion/goto.py +71 -0
  456. reachy_mini/motion/move.py +43 -0
  457. reachy_mini/motion/recorded_move.py +146 -0
  458. reachy_mini/reachy_mini.py +753 -0
  459. reachy_mini/tools/reflash_motors.py +116 -0
  460. reachy_mini/tools/setup_motor.py +422 -0
  461. reachy_mini/tools/setup_motor_rpi.py +108 -0
  462. reachy_mini/utils/__init__.py +46 -0
  463. reachy_mini/utils/constants.py +9 -0
  464. reachy_mini/utils/hardware_config/__init__.py +1 -0
  465. reachy_mini/utils/hardware_config/parser.py +65 -0
  466. reachy_mini/utils/interpolation.py +227 -0
  467. reachy_mini/utils/parse_urdf_for_kinematics.py +110 -0
  468. reachy_mini/utils/rerun.py +320 -0
  469. reachy_mini/utils/wireless_version/__init__.py +1 -0
  470. reachy_mini/utils/wireless_version/startup_check.py +144 -0
  471. reachy_mini/utils/wireless_version/update.py +20 -0
  472. reachy_mini/utils/wireless_version/update_available.py +61 -0
  473. reachy_mini/utils/wireless_version/utils.py +39 -0
  474. reachy_mini-1.2.5rc1.dist-info/METADATA +161 -0
  475. reachy_mini-1.2.5rc1.dist-info/RECORD +479 -0
  476. reachy_mini-1.2.5rc1.dist-info/WHEEL +5 -0
  477. reachy_mini-1.2.5rc1.dist-info/entry_points.txt +4 -0
  478. reachy_mini-1.2.5rc1.dist-info/licenses/LICENSE +201 -0
  479. reachy_mini-1.2.5rc1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,597 @@
1
+ """Robot Backend for Reachy Mini.
2
+
3
+ This module provides the `RobotBackend` class, which interfaces with the Reachy Mini motor controller to control the robot's movements and manage its status.
4
+ It handles the control loop, joint positions, torque enabling/disabling, and provides a status report of the robot's backend.
5
+ It uses the `ReachyMiniMotorController` to communicate with the robot's motors.
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ import struct
11
+ import time
12
+ from dataclasses import dataclass
13
+ from datetime import timedelta
14
+ from multiprocessing import Event # It seems to be more accurate than threading.Event
15
+ from typing import Annotated, Any
16
+
17
+ import log_throttling
18
+ import numpy as np
19
+ import numpy.typing as npt
20
+ from reachy_mini_motor_controller import ReachyMiniPyControlLoop
21
+
22
+ from reachy_mini.utils.hardware_config.parser import parse_yaml_config
23
+
24
+ from ..abstract import Backend, MotorControlMode
25
+
26
+
27
+ class RobotBackend(Backend):
28
+ """Real robot backend for Reachy Mini."""
29
+
30
+ def __init__(
31
+ self,
32
+ serialport: str,
33
+ log_level: str = "INFO",
34
+ check_collision: bool = False,
35
+ kinematics_engine: str = "AnalyticalKinematics",
36
+ hardware_error_check_frequency: float = 1.0,
37
+ use_audio: bool = True,
38
+ wireless_version: bool = False,
39
+ hardware_config_filepath: str | None = None,
40
+ ):
41
+ """Initialize the RobotBackend.
42
+
43
+ Args:
44
+ serialport (str): The serial port to which the Reachy Mini is connected.
45
+ log_level (str): The logging level for the backend. Default is "INFO".
46
+ check_collision (bool): If True, enable collision checking. Default is False.
47
+ kinematics_engine (str): Kinematics engine to use. Defaults to "AnalyticalKinematics".
48
+ hardware_error_check_frequency (float): Frequency in seconds to check for hardware errors. Default is 1.0.
49
+ use_audio (bool): If True, use audio. Default is True.
50
+ wireless_version (bool): If True, indicates that the wireless version of Reachy Mini is used. Default is False.
51
+ hardware_config_filepath (str | None): Path to the hardware configuration YAML file. Default is None.
52
+
53
+ Tries to connect to the Reachy Mini motor controller and initializes the control loop.
54
+
55
+ """
56
+ super().__init__(
57
+ check_collision=check_collision,
58
+ kinematics_engine=kinematics_engine,
59
+ use_audio=use_audio,
60
+ wireless_version=wireless_version,
61
+ )
62
+
63
+ self.logger = logging.getLogger(__name__)
64
+ self.logger.setLevel(log_level)
65
+
66
+ self.control_loop_frequency = 50.0 # Hz
67
+ self.c: ReachyMiniPyControlLoop | None = ReachyMiniPyControlLoop(
68
+ serialport,
69
+ read_position_loop_period=timedelta(
70
+ seconds=1.0 / self.control_loop_frequency
71
+ ),
72
+ allowed_retries=5,
73
+ stats_pub_period=None,
74
+ )
75
+
76
+ self.name2id = self.c.get_motor_name_id()
77
+ if hardware_config_filepath is not None:
78
+ config = parse_yaml_config(hardware_config_filepath)
79
+ for motor_name, motor_conf in config.motors.items():
80
+ if motor_conf.pid is not None:
81
+ motor_id = self.name2id[motor_name]
82
+ p, i, d = motor_conf.pid
83
+ self.logger.info(
84
+ f"Setting PID gains for motor '{motor_name}' (ID: {motor_id}): P={p}, I={i}, D={d}"
85
+ )
86
+ self.c.async_write_pid_gains(motor_id, p, i, d)
87
+
88
+ self.motor_control_mode = self._infer_control_mode()
89
+ self._torque_enabled = self.motor_control_mode != MotorControlMode.Disabled
90
+ self.logger.info(f"Motor control mode: {self.motor_control_mode}")
91
+ self.last_alive: float | None = None
92
+
93
+ self._status = RobotBackendStatus(
94
+ motor_control_mode=self.motor_control_mode,
95
+ ready=False,
96
+ last_alive=None,
97
+ control_loop_stats={},
98
+ )
99
+ self._stats_record_period = 1.0 # seconds
100
+ self._stats: dict[str, Any] = {
101
+ "timestamps": [],
102
+ "nb_error": 0,
103
+ "record_period": self._stats_record_period,
104
+ }
105
+
106
+ self._current_head_operation_mode = -1 # Default to torque control mode
107
+ self._current_antennas_operation_mode = -1 # Default to torque control mode
108
+ self.target_antenna_joint_current = None # Placeholder for antenna joint torque
109
+ self.target_head_joint_current = None # Placeholder for head joint torque
110
+
111
+ self.hardware_error_check_frequency = hardware_error_check_frequency # seconds
112
+
113
+ def run(self) -> None:
114
+ """Run the control loop for the robot backend.
115
+
116
+ This method continuously updates the motor controller at a specified frequency.
117
+ It reads the joint positions, updates the motor controller, and publishes the joint positions.
118
+ It also handles errors and retries if the motor controller is not responding.
119
+ """
120
+ assert self.c is not None, "Motor controller not initialized or already closed."
121
+
122
+ period = 1.0 / self.control_loop_frequency # Control loop period in seconds
123
+
124
+ self.retries = 5
125
+ self.stats_record_t0 = time.time()
126
+
127
+ self.last_hardware_error_check_time = time.time()
128
+
129
+ next_call_event = Event()
130
+
131
+ # Compute the forward kinematics to get the initial head pose
132
+ # IMPORTANT for wake_up
133
+ head_positions, _ = self.get_all_joint_positions()
134
+ # make sure to converge fully (a lot of iterations)
135
+ self.current_head_pose = self.head_kinematics.fk(
136
+ np.array(head_positions),
137
+ no_iterations=20,
138
+ )
139
+ assert self.current_head_pose is not None
140
+
141
+ self.head_kinematics.ik(self.current_head_pose, no_iterations=20)
142
+
143
+ while not self.should_stop.is_set():
144
+ start_t = time.time()
145
+ self._stats["timestamps"].append(time.time())
146
+ self._update()
147
+ took = time.time() - start_t
148
+
149
+ sleep_time = period - took
150
+ if sleep_time < 0:
151
+ self.logger.debug(
152
+ f"Control loop took too long: {took * 1000:.3f} ms, expected {period * 1000:.3f} ms"
153
+ )
154
+ sleep_time = 0.001
155
+
156
+ next_call_event.clear()
157
+ next_call_event.wait(sleep_time)
158
+
159
+ def _update(self) -> None:
160
+ assert self.c is not None, "Motor controller not initialized or already closed."
161
+
162
+ if self._torque_enabled:
163
+ if self._current_head_operation_mode != 0: # if position control mode
164
+ if self.target_head_joint_positions is not None:
165
+ self.c.set_stewart_platform_position(
166
+ self.target_head_joint_positions[1:].tolist()
167
+ )
168
+ self.c.set_body_rotation(self.target_head_joint_positions[0])
169
+ else: # it's in torque control mode
170
+ if self.gravity_compensation_mode:
171
+ # This function will set the head_joint_current
172
+ # to the current necessary to compensate for gravity
173
+ self.compensate_head_gravity()
174
+ if self.target_head_joint_current is not None:
175
+ self.c.set_stewart_platform_goal_current(
176
+ np.round(self.target_head_joint_current[1:], 0)
177
+ .astype(int)
178
+ .tolist()
179
+ )
180
+ # Body rotation torque control is not supported with feetech motors
181
+ # self.c.set_body_rotation_goal_current(int(self.target_head_joint_current[0]))
182
+
183
+ if self._current_antennas_operation_mode != 0: # if position control mode
184
+ if self.target_antenna_joint_positions is not None:
185
+ self.c.set_antennas_positions(
186
+ self.target_antenna_joint_positions.tolist()
187
+ )
188
+ # Antenna torque control is not supported with feetech motors
189
+ # else:
190
+ # if self.target_antenna_joint_current is not None:
191
+ # self.c.set_antennas_goal_current(
192
+ # np.round(self.target_antenna_joint_current, 0).astype(int).tolist()
193
+ # )
194
+
195
+ if (
196
+ self.joint_positions_publisher is not None
197
+ and self.pose_publisher is not None
198
+ ):
199
+ try:
200
+ head_positions, antenna_positions = self.get_all_joint_positions()
201
+
202
+ # Update the head kinematics model with the current head positions
203
+ self.update_head_kinematics_model(
204
+ np.array(head_positions),
205
+ np.array(antenna_positions),
206
+ )
207
+
208
+ # Update the target head joint positions from IK if necessary
209
+ # - does nothing if the targets did not change
210
+ if self.ik_required:
211
+ try:
212
+ self.update_target_head_joints_from_ik(
213
+ self.target_head_pose, self.target_body_yaw
214
+ )
215
+ except ValueError as e:
216
+ log_throttling.by_time(self.logger, interval=0.5).warning(
217
+ f"IK error: {e}"
218
+ )
219
+
220
+ if not self.is_shutting_down:
221
+ self.joint_positions_publisher.put(
222
+ json.dumps(
223
+ {
224
+ "head_joint_positions": head_positions,
225
+ "antennas_joint_positions": antenna_positions,
226
+ }
227
+ )
228
+ )
229
+ self.pose_publisher.put(
230
+ json.dumps(
231
+ {
232
+ "head_pose": self.get_present_head_pose().tolist(),
233
+ }
234
+ )
235
+ )
236
+
237
+ self.last_alive = time.time()
238
+
239
+ self.ready.set() # Mark the backend as ready
240
+ except RuntimeError as e:
241
+ self._stats["nb_error"] += 1
242
+
243
+ assert self.last_alive is not None
244
+
245
+ if self.last_alive + 1 < time.time():
246
+ self.error = (
247
+ "No response from the robot's motor for the last second."
248
+ )
249
+
250
+ self.logger.error(
251
+ "No response from the robot for the last second, stopping."
252
+ )
253
+ raise e
254
+
255
+ if time.time() - self.stats_record_t0 > self._stats_record_period:
256
+ dt = np.diff(self._stats["timestamps"])
257
+ if len(dt) > 1:
258
+ self._status.control_loop_stats["mean_control_loop_frequency"] = (
259
+ float(np.mean(1.0 / dt))
260
+ )
261
+ self._status.control_loop_stats["max_control_loop_interval"] = (
262
+ float(np.max(dt))
263
+ )
264
+ self._status.control_loop_stats["nb_error"] = self._stats[
265
+ "nb_error"
266
+ ]
267
+
268
+ self._stats["timestamps"].clear()
269
+ self._stats["nb_error"] = 0
270
+ self.stats_record_t0 = time.time()
271
+
272
+ if (
273
+ time.time() - self.last_hardware_error_check_time
274
+ > self.hardware_error_check_frequency
275
+ ):
276
+ hardware_errors = self.read_hardware_errors()
277
+ if hardware_errors:
278
+ for motor_name, errors in hardware_errors.items():
279
+ self.logger.error(
280
+ f"Motor '{motor_name}' hardware errors: {errors}"
281
+ )
282
+ self.last_hardware_error_check_time = time.time()
283
+
284
+ def close(self) -> None:
285
+ """Close the motor controller connection."""
286
+ if self.c is not None:
287
+ self.c.close()
288
+ self.c = None
289
+
290
+ def get_status(self) -> "RobotBackendStatus":
291
+ """Get the current status of the robot backend."""
292
+ self._status.error = self.error
293
+ self._status.motor_control_mode = self.motor_control_mode
294
+ return self._status
295
+
296
+ def enable_motors(self) -> None:
297
+ """Enable the motors by turning the torque on."""
298
+ assert self.c is not None, "Motor controller not initialized or already closed."
299
+
300
+ self.c.enable_torque()
301
+ self._torque_enabled = True
302
+
303
+ def disable_motors(self) -> None:
304
+ """Disable the motors by turning the torque off."""
305
+ assert self.c is not None, "Motor controller not initialized or already closed."
306
+
307
+ self.c.disable_torque()
308
+ self._torque_enabled = False
309
+
310
+ def set_head_operation_mode(self, mode: int) -> None:
311
+ """Change the operation mode of the head motors.
312
+
313
+ Args:
314
+ mode (int): The operation mode for the head motors.
315
+
316
+ The operation modes can be:
317
+ 0: torque control
318
+ 3: position control
319
+ 5: current-based position control.
320
+
321
+ Important:
322
+ This method does not work well with the current feetech motors (body rotation), as they do not support torque control.
323
+ So the method disables the antennas when in torque control mode.
324
+ The dynamixel motors used for the head do support torque control, so this method works as expected.
325
+
326
+ Args:
327
+ mode (int): The operation mode for the head motors.
328
+ This could be a specific mode like position control, velocity control, or torque control.
329
+
330
+ """
331
+ assert self.c is not None, "Motor controller not initialized or already closed."
332
+ assert mode in [0, 3, 5], (
333
+ "Invalid operation mode. Must be one of [0 (torque), 3 (position), 5 (current-limiting position)]."
334
+ f" Got {mode} instead"
335
+ )
336
+
337
+ # if motors are enabled, disable them before changing the mode
338
+ if self._torque_enabled:
339
+ self.c.enable_stewart_platform(False)
340
+ # set the new operation mode
341
+ self.c.set_stewart_platform_operating_mode(mode)
342
+
343
+ if mode != 0:
344
+ # if the mode is not torque control, we need to set the head joint positions
345
+ # to the current positions to avoid sudden movements
346
+ motor_pos = self.c.get_last_position()
347
+ self.target_head_joint_positions = np.array(
348
+ [motor_pos.body_yaw] + motor_pos.stewart
349
+ )
350
+
351
+ self.c.set_stewart_platform_position(
352
+ self.target_head_joint_positions[1:].tolist()
353
+ )
354
+ self.c.set_body_rotation(self.target_head_joint_positions[0])
355
+ self.c.enable_body_rotation(True)
356
+ self.c.set_body_rotation_operating_mode(0)
357
+ else:
358
+ self.c.enable_body_rotation(False)
359
+
360
+ if self._torque_enabled:
361
+ self.c.enable_stewart_platform(True)
362
+
363
+ self._current_head_operation_mode = mode
364
+
365
+ def set_antennas_operation_mode(self, mode: int) -> None:
366
+ """Change the operation mode of the antennas motors.
367
+
368
+ Args:
369
+ mode (int): The operation mode for the antennas motors (0: torque control, 3: position control, 5: current-based position control).
370
+
371
+ Important:
372
+ This method does not work well with the current feetech motors, as they do not support torque control.
373
+ So the method disables the antennas when in torque control mode.
374
+
375
+ Args:
376
+ mode (int): The operation mode for the antennas motors.
377
+ This could be a specific mode like position control, velocity control, or torque control.
378
+
379
+ """
380
+ assert self.c is not None, "Motor controller not initialized or already closed."
381
+ assert mode in [0, 3, 5], (
382
+ "Invalid operation mode. Must be one of [0 (torque), 3 (position), 5 (current-limiting position)]."
383
+ )
384
+
385
+ if self._current_antennas_operation_mode != mode:
386
+ if mode != 0:
387
+ # if the mode is not torque control, we need to set the head joint positions
388
+ # to the current positions to avoid sudden movements
389
+ self.target_antenna_joint_positions = np.array(
390
+ self.c.get_last_position().antennas
391
+ )
392
+ self.c.set_antennas_positions(
393
+ self.target_antenna_joint_positions.tolist()
394
+ )
395
+ self.c.enable_antennas(True)
396
+ else:
397
+ self.c.enable_antennas(False)
398
+
399
+ self._current_antennas_operation_mode = mode
400
+
401
+ def get_all_joint_positions(self) -> tuple[list[float], list[float]]:
402
+ """Get the current joint positions of the robot.
403
+
404
+ Returns:
405
+ tuple: A tuple containing two lists - the first list is for the head joint positions,
406
+ and the second list is for the antenna joint positions.
407
+
408
+ """
409
+ assert self.c is not None, "Motor controller not initialized or already closed."
410
+ positions = self.c.get_last_position()
411
+
412
+ yaw = positions.body_yaw
413
+ antennas = positions.antennas
414
+ dofs = positions.stewart
415
+
416
+ return [yaw] + list(dofs), list(antennas)
417
+
418
+ def get_present_head_joint_positions(
419
+ self,
420
+ ) -> Annotated[npt.NDArray[np.float64], (7,)]:
421
+ """Get the current joint positions of the head.
422
+
423
+ Returns:
424
+ list: A list of joint positions for the head, including the body rotation.
425
+
426
+ """
427
+ return np.array(self.get_all_joint_positions()[0])
428
+
429
+ def get_present_antenna_joint_positions(
430
+ self,
431
+ ) -> Annotated[npt.NDArray[np.float64], (2,)]:
432
+ """Get the current joint positions of the antennas.
433
+
434
+ Returns:
435
+ list: A list of joint positions for the antennas.
436
+
437
+ """
438
+ return np.array(self.get_all_joint_positions()[1])
439
+
440
+ def compensate_head_gravity(self) -> None:
441
+ """Calculate the currents necessary to compensate for gravity."""
442
+ assert self.kinematics_engine == "Placo", (
443
+ "Gravity compensation is only supported with the Placo kinematics engine."
444
+ )
445
+
446
+ # Even though in their docs dynamixes says that 1 count is 1 mA, in practice I've found it to be 3mA.
447
+ # I am not sure why this happens
448
+ # Another explanation is that our model is bad and the current is overestimated 3x (but I have not had these issues with other robots)
449
+ # So I am using a magic number to compensate for this.
450
+ # for currents under 30mA the constant is around 1
451
+ from_Nm_to_mA = 1.47 / 0.52 * 1000
452
+ # Conversion factor from Nm to mA for the Stewart platform motors
453
+ # The torque constant is not linear, so we need to use a correction factor
454
+ # This is a magic number that should be determined experimentally
455
+ # For currents under 30mA, the constant is around 4.0
456
+ # Then it drops to 1.0 for currents above 1.5A
457
+ correction_factor = 4.0
458
+ # Get the current head joint positions
459
+ head_joints = self.get_present_head_joint_positions()
460
+ gravity_torque = self.head_kinematics.compute_gravity_torque( # type: ignore [union-attr]
461
+ np.array(head_joints)
462
+ )
463
+ # Convert the torque from Nm to mA
464
+ current = gravity_torque * from_Nm_to_mA / correction_factor
465
+ # Set the head joint current
466
+ self.set_target_head_joint_current(current)
467
+
468
+ def get_motor_control_mode(self) -> MotorControlMode:
469
+ """Get the motor control mode."""
470
+ return self.motor_control_mode
471
+
472
+ def set_motor_control_mode(self, mode: MotorControlMode) -> None:
473
+ """Set the motor control mode."""
474
+ # Check if the mode is already set
475
+ if mode == self.motor_control_mode:
476
+ return
477
+
478
+ if mode == MotorControlMode.Enabled:
479
+ if self.motor_control_mode == MotorControlMode.GravityCompensation:
480
+ # First, make sure we switch to position control
481
+ self.disable_motors()
482
+ self.set_head_operation_mode(3)
483
+ self.set_antennas_operation_mode(3)
484
+
485
+ self.gravity_compensation_mode = False
486
+ self.enable_motors()
487
+
488
+ elif mode == MotorControlMode.Disabled:
489
+ self.gravity_compensation_mode = False
490
+ self.disable_motors()
491
+
492
+ elif mode == MotorControlMode.GravityCompensation:
493
+ if self.kinematics_engine != "Placo":
494
+ raise RuntimeError(
495
+ "Gravity compensation mode is only supported with the Placo kinematics engine."
496
+ )
497
+
498
+ self.disable_motors()
499
+ self.set_head_operation_mode(0)
500
+ self.set_antennas_operation_mode(0)
501
+ self.gravity_compensation_mode = True
502
+ self.enable_motors()
503
+
504
+ self.motor_control_mode = mode
505
+
506
+ def set_motor_torque_ids(self, ids: list[str], on: bool) -> None:
507
+ """Set the torque state for specific motor names.
508
+
509
+ Args:
510
+ ids (list[int]): List of motor IDs to set the torque state for.
511
+ on (bool): True to enable torque, False to disable.
512
+
513
+ """
514
+ assert self.c is not None, "Motor controller not initialized or already closed."
515
+
516
+ assert ids is not None and len(ids) > 0, "IDs list cannot be empty or None."
517
+
518
+ ids_int = [self.name2id[name] for name in ids]
519
+
520
+ if on:
521
+ self.c.enable_torque_on_ids(ids_int)
522
+ else:
523
+ self.c.disable_torque_on_ids(ids_int)
524
+
525
+ def _infer_control_mode(self) -> MotorControlMode:
526
+ assert self.c is not None, "Motor controller not initialized or already closed."
527
+
528
+ torque = self.c.is_torque_enabled()
529
+
530
+ if not torque:
531
+ return MotorControlMode.Disabled
532
+
533
+ mode = self.c.get_stewart_platform_operating_mode()
534
+ if mode == 3:
535
+ return MotorControlMode.Enabled
536
+ elif mode == 1:
537
+ return MotorControlMode.GravityCompensation
538
+ else:
539
+ raise ValueError(f"Unknown motor control mode: {mode}")
540
+
541
+ def read_hardware_errors(self) -> dict[str, list[str]]:
542
+ """Read hardware errors from the motor controller."""
543
+ if self.c is None:
544
+ return {}
545
+
546
+ def decode_hardware_error_byte(err_byte: int) -> list[str]:
547
+ # https://emanual.robotis.com/docs/en/dxl/x/xl330-m288/#hardware-error-status
548
+ bits_to_error = {
549
+ 0: "Input Voltage Error",
550
+ 2: "Overheating Error",
551
+ 4: "Electrical Shock Error",
552
+ 5: "Overload Error",
553
+ }
554
+ err_bits = [i for i in range(8) if (err_byte & (1 << i)) != 0]
555
+ return [bits_to_error[b] for b in err_bits if b in bits_to_error]
556
+
557
+ def voltage_ok(
558
+ id: int,
559
+ allowed_max_voltage: float = 7.8,
560
+ ) -> bool:
561
+ assert self.c is not None, (
562
+ "Motor controller not initialized or already closed."
563
+ )
564
+ # https://emanual.robotis.com/docs/en/dxl/x/xl330-m288/#present-input-voltage
565
+ resp_bytes = self.c.async_read_raw_bytes(id, 144, 2)
566
+ resp = struct.unpack("h", bytes(resp_bytes))[0]
567
+ voltage: float = resp / 10.0 # in Volts
568
+
569
+ return voltage <= allowed_max_voltage
570
+
571
+ errors = {}
572
+ for name, id in self.c.get_motor_name_id().items():
573
+ # https://emanual.robotis.com/docs/en/dxl/x/xl330-m288/#hardware-error-status
574
+ err_byte = self.c.async_read_raw_bytes(id, 70, 1)
575
+ assert len(err_byte) == 1
576
+ err = decode_hardware_error_byte(err_byte[0])
577
+ if err:
578
+ if "Input Voltage Error" in err:
579
+ if voltage_ok(id):
580
+ err.remove("Input Voltage Error")
581
+
582
+ # To avoid logging empty errors like "Motor 1: []"
583
+ if len(err) > 0:
584
+ errors[name] = err
585
+
586
+ return errors
587
+
588
+
589
+ @dataclass
590
+ class RobotBackendStatus:
591
+ """Status of the Robot Backend."""
592
+
593
+ ready: bool
594
+ motor_control_mode: MotorControlMode
595
+ last_alive: float | None
596
+ control_loop_stats: dict[str, Any]
597
+ error: str | None = None