knowlin 0.38.31__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 (548) hide show
  1. duckpilot/__init__.py +3 -0
  2. duckpilot/__main__.py +256 -0
  3. duckpilot/acrobatics_service.py +185 -0
  4. duckpilot/aio.py +33 -0
  5. duckpilot/api.py +387 -0
  6. duckpilot/augen.py +107 -0
  7. duckpilot/backends/__init__.py +61 -0
  8. duckpilot/backends/begleiter.py +78 -0
  9. duckpilot/backends/fake_robotd.py +268 -0
  10. duckpilot/backends/open_duck.py +114 -0
  11. duckpilot/backends/physics.py +724 -0
  12. duckpilot/backends/reachy.py +497 -0
  13. duckpilot/backends/reachy_apps.py +93 -0
  14. duckpilot/backends/reachy_ausdruck.py +169 -0
  15. duckpilot/backends/reachy_kinematik.py +381 -0
  16. duckpilot/backends/robotd.py +675 -0
  17. duckpilot/backends/sim.py +773 -0
  18. duckpilot/backends/world.py +284 -0
  19. duckpilot/brain/__init__.py +0 -0
  20. duckpilot/brain/agent.py +627 -0
  21. duckpilot/brain/anbieter.py +169 -0
  22. duckpilot/brain/erfahrung.py +359 -0
  23. duckpilot/brain/fallback.py +660 -0
  24. duckpilot/brain/handeln.py +138 -0
  25. duckpilot/brain/kopf.py +534 -0
  26. duckpilot/brain/local.py +163 -0
  27. duckpilot/brain/memory.py +457 -0
  28. duckpilot/brain/tools.py +1235 -0
  29. duckpilot/brain/tools_lab.py +187 -0
  30. duckpilot/brain/tools_raum.py +142 -0
  31. duckpilot/brain/usage.py +93 -0
  32. duckpilot/brain/zusage.py +105 -0
  33. duckpilot/buddy.py +767 -0
  34. duckpilot/care.py +117 -0
  35. duckpilot/choreo.py +433 -0
  36. duckpilot/config.py +207 -0
  37. duckpilot/desktop.py +293 -0
  38. duckpilot/erfahrung_service.py +219 -0
  39. duckpilot/faces.py +645 -0
  40. duckpilot/figuren.py +223 -0
  41. duckpilot/geplant.py +33 -0
  42. duckpilot/hub.py +173 -0
  43. duckpilot/i18n/__init__.py +120 -0
  44. duckpilot/i18n/de.json +69 -0
  45. duckpilot/i18n/en.json +65 -0
  46. duckpilot/i18n/ui_en.json +3235 -0
  47. duckpilot/joints.py +148 -0
  48. duckpilot/kaggle_training.py +1107 -0
  49. duckpilot/knowlet_hub.py +112 -0
  50. duckpilot/knowlets.py +227 -0
  51. duckpilot/kollision_service.py +281 -0
  52. duckpilot/laser.py +134 -0
  53. duckpilot/lernschleife.py +252 -0
  54. duckpilot/mcp_bridge.py +84 -0
  55. duckpilot/mcp_server.py +147 -0
  56. duckpilot/mdns.py +102 -0
  57. duckpilot/mission.py +420 -0
  58. duckpilot/modell/__init__.py +183 -0
  59. duckpilot/modell/__main__.py +3 -0
  60. duckpilot/modell/ansicht.py +307 -0
  61. duckpilot/modell/hand.py +215 -0
  62. duckpilot/modell/hand_quellen.json +48 -0
  63. duckpilot/modell/open_duck.py +215 -0
  64. duckpilot/modell/physik.py +52 -0
  65. duckpilot/modell/quellen.json +175 -0
  66. duckpilot/modell/reachy.py +293 -0
  67. duckpilot/modell/reachy_bewegungen.py +177 -0
  68. duckpilot/modell/reachy_quellen.json +136 -0
  69. duckpilot/modell/service.py +38 -0
  70. duckpilot/motion/__init__.py +0 -0
  71. duckpilot/motion/apportieren.py +251 -0
  72. duckpilot/motion/ausweichen.py +379 -0
  73. duckpilot/motion/behaviors.py +853 -0
  74. duckpilot/motion/character.py +279 -0
  75. duckpilot/motion/choreografie.py +497 -0
  76. duckpilot/motion/eigenstaendig.py +294 -0
  77. duckpilot/motion/engine.py +1039 -0
  78. duckpilot/motion/erkunden.py +130 -0
  79. duckpilot/motion/faehigkeit.py +103 -0
  80. duckpilot/motion/handeln.py +108 -0
  81. duckpilot/motion/hilfen.py +21 -0
  82. duckpilot/motion/karte.py +281 -0
  83. duckpilot/motion/mood.py +173 -0
  84. duckpilot/motion/purzelbaum.py +150 -0
  85. duckpilot/motion/reachy_gesten.py +185 -0
  86. duckpilot/motion/reachy_spiel.py +99 -0
  87. duckpilot/motion/reachy_tanz.py +376 -0
  88. duckpilot/motion/schuss.py +397 -0
  89. duckpilot/motion/schwarm.py +390 -0
  90. duckpilot/motion/sounds.py +48 -0
  91. duckpilot/motion/stabilizer.py +179 -0
  92. duckpilot/motion/tanz.py +315 -0
  93. duckpilot/motion/team.py +391 -0
  94. duckpilot/motion/umrunden.py +136 -0
  95. duckpilot/musik_struktur.py +176 -0
  96. duckpilot/nfc.py +106 -0
  97. duckpilot/physics/__init__.py +1 -0
  98. duckpilot/physics/acrobatics.py +381 -0
  99. duckpilot/physics/core.py +508 -0
  100. duckpilot/physics/course.py +512 -0
  101. duckpilot/physics/isaaclab.py +297 -0
  102. duckpilot/physics/isaaclab_hf.py +168 -0
  103. duckpilot/physics/kollision.py +1102 -0
  104. duckpilot/physics/lernen.py +1218 -0
  105. duckpilot/physics/params.py +14 -0
  106. duckpilot/physics/policies.py +193 -0
  107. duckpilot/physics/practice.py +225 -0
  108. duckpilot/physics/stossreihe.py +377 -0
  109. duckpilot/physics/twist.py +39 -0
  110. duckpilot/physics/zwilling.py +145 -0
  111. duckpilot/protest.py +245 -0
  112. duckpilot/qr.py +265 -0
  113. duckpilot/remote.py +455 -0
  114. duckpilot/schichten.py +60 -0
  115. duckpilot/server/__init__.py +0 -0
  116. duckpilot/server/app.py +82 -0
  117. duckpilot/server/fehlerbericht.py +138 -0
  118. duckpilot/server/plans.py +23 -0
  119. duckpilot/server/raum.py +83 -0
  120. duckpilot/server/routes/__init__.py +3 -0
  121. duckpilot/server/routes/akrobatik.py +74 -0
  122. duckpilot/server/routes/alltag.py +114 -0
  123. duckpilot/server/routes/augen.py +90 -0
  124. duckpilot/server/routes/basis.py +109 -0
  125. duckpilot/server/routes/beenden.py +51 -0
  126. duckpilot/server/routes/choreo.py +137 -0
  127. duckpilot/server/routes/enten.py +38 -0
  128. duckpilot/server/routes/erfahrung.py +55 -0
  129. duckpilot/server/routes/fehlerbericht.py +33 -0
  130. duckpilot/server/routes/fernzugang.py +79 -0
  131. duckpilot/server/routes/gehirn.py +98 -0
  132. duckpilot/server/routes/handeln.py +49 -0
  133. duckpilot/server/routes/kamera.py +43 -0
  134. duckpilot/server/routes/knowlets.py +219 -0
  135. duckpilot/server/routes/kollision.py +35 -0
  136. duckpilot/server/routes/koppeln.py +62 -0
  137. duckpilot/server/routes/mcp.py +88 -0
  138. duckpilot/server/routes/modell.py +29 -0
  139. duckpilot/server/routes/nfc.py +36 -0
  140. duckpilot/server/routes/parcours.py +105 -0
  141. duckpilot/server/routes/pflege.py +70 -0
  142. duckpilot/server/routes/physik.py +48 -0
  143. duckpilot/server/routes/raum.py +39 -0
  144. duckpilot/server/routes/reachy.py +70 -0
  145. duckpilot/server/routes/sicherung.py +83 -0
  146. duckpilot/server/routes/skills.py +58 -0
  147. duckpilot/server/routes/spiele.py +597 -0
  148. duckpilot/server/routes/spiele_hand.py +61 -0
  149. duckpilot/server/routes/start.py +64 -0
  150. duckpilot/server/routes/stimme.py +137 -0
  151. duckpilot/server/routes/stossreihe.py +40 -0
  152. duckpilot/server/routes/training.py +460 -0
  153. duckpilot/server/routes/verbindung.py +96 -0
  154. duckpilot/server/runtime.py +1057 -0
  155. duckpilot/server/runtime_eigenstaendig.py +176 -0
  156. duckpilot/server/runtime_gespraech.py +249 -0
  157. duckpilot/server/runtime_kern.py +225 -0
  158. duckpilot/server/runtime_lab.py +302 -0
  159. duckpilot/server/runtime_reachy.py +241 -0
  160. duckpilot/server/security.py +149 -0
  161. duckpilot/server/sicherung.py +207 -0
  162. duckpilot/server/static/akrobatik.js +601 -0
  163. duckpilot/server/static/alltag.js +285 -0
  164. duckpilot/server/static/anbieter.js +69 -0
  165. duckpilot/server/static/app.js +1270 -0
  166. duckpilot/server/static/apple-touch-icon.png +0 -0
  167. duckpilot/server/static/augen.js +205 -0
  168. duckpilot/server/static/aussehen.js +61 -0
  169. duckpilot/server/static/auswahl.js +104 -0
  170. duckpilot/server/static/beenden.js +74 -0
  171. duckpilot/server/static/charakter.js +63 -0
  172. duckpilot/server/static/controller.js +407 -0
  173. duckpilot/server/static/dashboard.js +638 -0
  174. duckpilot/server/static/designsystem.js +169 -0
  175. duckpilot/server/static/dinge3d.js +94 -0
  176. duckpilot/server/static/ds/components.css +310 -0
  177. duckpilot/server/static/ds/tokens.css +235 -0
  178. duckpilot/server/static/duck3d.js +1260 -0
  179. duckpilot/server/static/duckmodel.js +261 -0
  180. duckpilot/server/static/einstellungen_ui.js +232 -0
  181. duckpilot/server/static/ente3d.js +183 -0
  182. duckpilot/server/static/enten.js +70 -0
  183. duckpilot/server/static/ereignisse.js +54 -0
  184. duckpilot/server/static/erste.js +120 -0
  185. duckpilot/server/static/fehlerbericht.js +33 -0
  186. duckpilot/server/static/fernbedienung.js +185 -0
  187. duckpilot/server/static/figuren/open-duck-mini.png +0 -0
  188. duckpilot/server/static/flaggen/de.svg +1 -0
  189. duckpilot/server/static/flaggen/en.svg +1 -0
  190. duckpilot/server/static/formen3d.js +103 -0
  191. duckpilot/server/static/gedaechtnis_ui.js +111 -0
  192. duckpilot/server/static/hand.js +210 -0
  193. duckpilot/server/static/handy_start.js +73 -0
  194. duckpilot/server/static/herkunft.js +29 -0
  195. duckpilot/server/static/hirn.js +455 -0
  196. duckpilot/server/static/i18n.js +127 -0
  197. duckpilot/server/static/icon-192.png +0 -0
  198. duckpilot/server/static/icon-512.png +0 -0
  199. duckpilot/server/static/icon-maskable-512.png +0 -0
  200. duckpilot/server/static/index.html +2355 -0
  201. duckpilot/server/static/isaaclab.js +217 -0
  202. duckpilot/server/static/kachel.js +52 -0
  203. duckpilot/server/static/kamera_spiele.js +603 -0
  204. duckpilot/server/static/knowlets.js +202 -0
  205. duckpilot/server/static/knowlin-zeichen.png +0 -0
  206. duckpilot/server/static/knowlin.ico +0 -0
  207. duckpilot/server/static/koerperwahl.js +75 -0
  208. duckpilot/server/static/kollision.js +770 -0
  209. duckpilot/server/static/kopf.js +147 -0
  210. duckpilot/server/static/kopfleiste.js +68 -0
  211. duckpilot/server/static/laser.js +94 -0
  212. duckpilot/server/static/lernen.js +245 -0
  213. duckpilot/server/static/licht3d.js +35 -0
  214. duckpilot/server/static/manifest.webmanifest +13 -0
  215. duckpilot/server/static/mcp.js +35 -0
  216. duckpilot/server/static/mind.js +454 -0
  217. duckpilot/server/static/mitwippen.js +227 -0
  218. duckpilot/server/static/model/LIZENZ-open-duck-mini.md +12 -0
  219. duckpilot/server/static/model/open_duck_mini.dpm +0 -0
  220. duckpilot/server/static/modul.js +37 -0
  221. duckpilot/server/static/moebel3d.js +225 -0
  222. duckpilot/server/static/nfc.js +107 -0
  223. duckpilot/server/static/offenlegung.js +53 -0
  224. duckpilot/server/static/parcours.js +387 -0
  225. duckpilot/server/static/personality.js +242 -0
  226. duckpilot/server/static/pflege.js +110 -0
  227. duckpilot/server/static/pointcloud.js +137 -0
  228. duckpilot/server/static/raum.js +189 -0
  229. duckpilot/server/static/raum3d.js +157 -0
  230. duckpilot/server/static/reachy3d.js +130 -0
  231. duckpilot/server/static/reachy_apps.js +143 -0
  232. duckpilot/server/static/reachy_ui.js +112 -0
  233. duckpilot/server/static/reachymodell.js +193 -0
  234. duckpilot/server/static/settingsnav.js +39 -0
  235. duckpilot/server/static/sicherung.js +181 -0
  236. duckpilot/server/static/skills.js +394 -0
  237. duckpilot/server/static/sounds/emotions/am_boden.ogg +0 -0
  238. duckpilot/server/static/sounds/emotions/aufgeregt.ogg +0 -0
  239. duckpilot/server/static/sounds/emotions/betruebt.ogg +0 -0
  240. duckpilot/server/static/sounds/emotions/grummelig_a.ogg +0 -0
  241. duckpilot/server/static/sounds/emotions/grummelig_b.ogg +0 -0
  242. duckpilot/server/static/sounds/emotions/hmm.ogg +0 -0
  243. duckpilot/server/static/sounds/emotions/index.json +21 -0
  244. duckpilot/server/static/sounds/emotions/jawohl.ogg +0 -0
  245. duckpilot/server/static/sounds/emotions/jep.ogg +0 -0
  246. duckpilot/server/static/sounds/emotions/lachen.ogg +0 -0
  247. duckpilot/server/static/sounds/emotions/naeaeae.ogg +0 -0
  248. duckpilot/server/static/sounds/emotions/noe_a.ogg +0 -0
  249. duckpilot/server/static/sounds/emotions/noe_b.ogg +0 -0
  250. duckpilot/server/static/sounds/emotions/noe_c.ogg +0 -0
  251. duckpilot/server/static/sounds/emotions/tot_stellen.ogg +0 -0
  252. duckpilot/server/static/sounds/emotions/ungeduldig.ogg +0 -0
  253. duckpilot/server/static/sounds/emotions/was_was.ogg +0 -0
  254. duckpilot/server/static/sounds/emotions/wer_sagt_das.ogg +0 -0
  255. duckpilot/server/static/sounds/gesten/aufhorchen_0.ogg +0 -0
  256. duckpilot/server/static/sounds/gesten/aufhorchen_1.ogg +0 -0
  257. duckpilot/server/static/sounds/gesten/aufhorchen_2.ogg +0 -0
  258. duckpilot/server/static/sounds/gesten/aufhorchen_3.ogg +0 -0
  259. duckpilot/server/static/sounds/gesten/begeistert_0.ogg +0 -0
  260. duckpilot/server/static/sounds/gesten/begeistert_1.ogg +0 -0
  261. duckpilot/server/static/sounds/gesten/begeistert_2.ogg +0 -0
  262. duckpilot/server/static/sounds/gesten/begeistert_3.ogg +0 -0
  263. duckpilot/server/static/sounds/gesten/begruessen_0.ogg +0 -0
  264. duckpilot/server/static/sounds/gesten/begruessen_1.ogg +0 -0
  265. duckpilot/server/static/sounds/gesten/begruessen_2.ogg +0 -0
  266. duckpilot/server/static/sounds/gesten/begruessen_3.ogg +0 -0
  267. duckpilot/server/static/sounds/gesten/boden_picken_0.ogg +0 -0
  268. duckpilot/server/static/sounds/gesten/boden_picken_1.ogg +0 -0
  269. duckpilot/server/static/sounds/gesten/boden_picken_2.ogg +0 -0
  270. duckpilot/server/static/sounds/gesten/boden_picken_3.ogg +0 -0
  271. duckpilot/server/static/sounds/gesten/doesen_0.ogg +0 -0
  272. duckpilot/server/static/sounds/gesten/doesen_1.ogg +0 -0
  273. duckpilot/server/static/sounds/gesten/doesen_2.ogg +0 -0
  274. duckpilot/server/static/sounds/gesten/doesen_3.ogg +0 -0
  275. duckpilot/server/static/sounds/gesten/einfuehlsam_0.ogg +0 -0
  276. duckpilot/server/static/sounds/gesten/einfuehlsam_1.ogg +0 -0
  277. duckpilot/server/static/sounds/gesten/einfuehlsam_2.ogg +0 -0
  278. duckpilot/server/static/sounds/gesten/einfuehlsam_3.ogg +0 -0
  279. duckpilot/server/static/sounds/gesten/erleichtert_0.ogg +0 -0
  280. duckpilot/server/static/sounds/gesten/erleichtert_1.ogg +0 -0
  281. duckpilot/server/static/sounds/gesten/erleichtert_2.ogg +0 -0
  282. duckpilot/server/static/sounds/gesten/erleichtert_3.ogg +0 -0
  283. duckpilot/server/static/sounds/gesten/erschrecken_0.ogg +0 -0
  284. duckpilot/server/static/sounds/gesten/erschrecken_1.ogg +0 -0
  285. duckpilot/server/static/sounds/gesten/erschrecken_2.ogg +0 -0
  286. duckpilot/server/static/sounds/gesten/erschrecken_3.ogg +0 -0
  287. duckpilot/server/static/sounds/gesten/freude_0.ogg +0 -0
  288. duckpilot/server/static/sounds/gesten/freude_1.ogg +0 -0
  289. duckpilot/server/static/sounds/gesten/freude_2.ogg +0 -0
  290. duckpilot/server/static/sounds/gesten/freude_3.ogg +0 -0
  291. duckpilot/server/static/sounds/gesten/frustriert_0.ogg +0 -0
  292. duckpilot/server/static/sounds/gesten/frustriert_1.ogg +0 -0
  293. duckpilot/server/static/sounds/gesten/frustriert_2.ogg +0 -0
  294. duckpilot/server/static/sounds/gesten/frustriert_3.ogg +0 -0
  295. duckpilot/server/static/sounds/gesten/gaehnen_0.ogg +0 -0
  296. duckpilot/server/static/sounds/gesten/gaehnen_1.ogg +0 -0
  297. duckpilot/server/static/sounds/gesten/gaehnen_2.ogg +0 -0
  298. duckpilot/server/static/sounds/gesten/gaehnen_3.ogg +0 -0
  299. duckpilot/server/static/sounds/gesten/gefieder_putzen_0.ogg +0 -0
  300. duckpilot/server/static/sounds/gesten/gefieder_putzen_1.ogg +0 -0
  301. duckpilot/server/static/sounds/gesten/gefieder_putzen_2.ogg +0 -0
  302. duckpilot/server/static/sounds/gesten/gefieder_putzen_3.ogg +0 -0
  303. duckpilot/server/static/sounds/gesten/haltung_0.ogg +0 -0
  304. duckpilot/server/static/sounds/gesten/haltung_1.ogg +0 -0
  305. duckpilot/server/static/sounds/gesten/haltung_2.ogg +0 -0
  306. duckpilot/server/static/sounds/gesten/haltung_3.ogg +0 -0
  307. duckpilot/server/static/sounds/gesten/hibbeln_0.ogg +0 -0
  308. duckpilot/server/static/sounds/gesten/hibbeln_1.ogg +0 -0
  309. duckpilot/server/static/sounds/gesten/hibbeln_2.ogg +0 -0
  310. duckpilot/server/static/sounds/gesten/hibbeln_3.ogg +0 -0
  311. duckpilot/server/static/sounds/gesten/himmel_gucken_0.ogg +0 -0
  312. duckpilot/server/static/sounds/gesten/himmel_gucken_1.ogg +0 -0
  313. duckpilot/server/static/sounds/gesten/himmel_gucken_2.ogg +0 -0
  314. duckpilot/server/static/sounds/gesten/himmel_gucken_3.ogg +0 -0
  315. duckpilot/server/static/sounds/gesten/index.json +1 -0
  316. duckpilot/server/static/sounds/gesten/ja_0.ogg +0 -0
  317. duckpilot/server/static/sounds/gesten/ja_1.ogg +0 -0
  318. duckpilot/server/static/sounds/gesten/ja_2.ogg +0 -0
  319. duckpilot/server/static/sounds/gesten/ja_3.ogg +0 -0
  320. duckpilot/server/static/sounds/gesten/muede_0.ogg +0 -0
  321. duckpilot/server/static/sounds/gesten/muede_1.ogg +0 -0
  322. duckpilot/server/static/sounds/gesten/muede_2.ogg +0 -0
  323. duckpilot/server/static/sounds/gesten/muede_3.ogg +0 -0
  324. duckpilot/server/static/sounds/gesten/mustern_0.ogg +0 -0
  325. duckpilot/server/static/sounds/gesten/mustern_1.ogg +0 -0
  326. duckpilot/server/static/sounds/gesten/mustern_2.ogg +0 -0
  327. duckpilot/server/static/sounds/gesten/mustern_3.ogg +0 -0
  328. duckpilot/server/static/sounds/gesten/nein_0.ogg +0 -0
  329. duckpilot/server/static/sounds/gesten/nein_1.ogg +0 -0
  330. duckpilot/server/static/sounds/gesten/nein_2.ogg +0 -0
  331. duckpilot/server/static/sounds/gesten/nein_3.ogg +0 -0
  332. duckpilot/server/static/sounds/gesten/neugierig_0.ogg +0 -0
  333. duckpilot/server/static/sounds/gesten/neugierig_1.ogg +0 -0
  334. duckpilot/server/static/sounds/gesten/neugierig_2.ogg +0 -0
  335. duckpilot/server/static/sounds/gesten/neugierig_3.ogg +0 -0
  336. duckpilot/server/static/sounds/gesten/nicken_0.ogg +0 -0
  337. duckpilot/server/static/sounds/gesten/nicken_1.ogg +0 -0
  338. duckpilot/server/static/sounds/gesten/nicken_2.ogg +0 -0
  339. duckpilot/server/static/sounds/gesten/nicken_3.ogg +0 -0
  340. duckpilot/server/static/sounds/gesten/niesen_0.ogg +0 -0
  341. duckpilot/server/static/sounds/gesten/niesen_1.ogg +0 -0
  342. duckpilot/server/static/sounds/gesten/niesen_2.ogg +0 -0
  343. duckpilot/server/static/sounds/gesten/niesen_3.ogg +0 -0
  344. duckpilot/server/static/sounds/gesten/schluckauf_0.ogg +0 -0
  345. duckpilot/server/static/sounds/gesten/schluckauf_1.ogg +0 -0
  346. duckpilot/server/static/sounds/gesten/schluckauf_2.ogg +0 -0
  347. duckpilot/server/static/sounds/gesten/schluckauf_3.ogg +0 -0
  348. duckpilot/server/static/sounds/gesten/schuetteln_0.ogg +0 -0
  349. duckpilot/server/static/sounds/gesten/schuetteln_1.ogg +0 -0
  350. duckpilot/server/static/sounds/gesten/schuetteln_2.ogg +0 -0
  351. duckpilot/server/static/sounds/gesten/schuetteln_3.ogg +0 -0
  352. duckpilot/server/static/sounds/gesten/schwanz_jagen_0.ogg +0 -0
  353. duckpilot/server/static/sounds/gesten/schwanz_jagen_1.ogg +0 -0
  354. duckpilot/server/static/sounds/gesten/schwanz_jagen_2.ogg +0 -0
  355. duckpilot/server/static/sounds/gesten/schwanz_jagen_3.ogg +0 -0
  356. duckpilot/server/static/sounds/gesten/selbstgespraech_0.ogg +0 -0
  357. duckpilot/server/static/sounds/gesten/selbstgespraech_1.ogg +0 -0
  358. duckpilot/server/static/sounds/gesten/selbstgespraech_2.ogg +0 -0
  359. duckpilot/server/static/sounds/gesten/selbstgespraech_3.ogg +0 -0
  360. duckpilot/server/static/sounds/gesten/seufzen_0.ogg +0 -0
  361. duckpilot/server/static/sounds/gesten/seufzen_1.ogg +0 -0
  362. duckpilot/server/static/sounds/gesten/seufzen_2.ogg +0 -0
  363. duckpilot/server/static/sounds/gesten/seufzen_3.ogg +0 -0
  364. duckpilot/server/static/sounds/gesten/staunen_0.ogg +0 -0
  365. duckpilot/server/static/sounds/gesten/staunen_1.ogg +0 -0
  366. duckpilot/server/static/sounds/gesten/staunen_2.ogg +0 -0
  367. duckpilot/server/static/sounds/gesten/staunen_3.ogg +0 -0
  368. duckpilot/server/static/sounds/gesten/stolpern_0.ogg +0 -0
  369. duckpilot/server/static/sounds/gesten/stolpern_1.ogg +0 -0
  370. duckpilot/server/static/sounds/gesten/stolpern_2.ogg +0 -0
  371. duckpilot/server/static/sounds/gesten/stolpern_3.ogg +0 -0
  372. duckpilot/server/static/sounds/gesten/stolz_0.ogg +0 -0
  373. duckpilot/server/static/sounds/gesten/stolz_1.ogg +0 -0
  374. duckpilot/server/static/sounds/gesten/stolz_2.ogg +0 -0
  375. duckpilot/server/static/sounds/gesten/stolz_3.ogg +0 -0
  376. duckpilot/server/static/sounds/gesten/traellern_0.ogg +0 -0
  377. duckpilot/server/static/sounds/gesten/traellern_1.ogg +0 -0
  378. duckpilot/server/static/sounds/gesten/traellern_2.ogg +0 -0
  379. duckpilot/server/static/sounds/gesten/traellern_3.ogg +0 -0
  380. duckpilot/server/static/sounds/gesten/traurig_0.ogg +0 -0
  381. duckpilot/server/static/sounds/gesten/traurig_1.ogg +0 -0
  382. duckpilot/server/static/sounds/gesten/traurig_2.ogg +0 -0
  383. duckpilot/server/static/sounds/gesten/traurig_3.ogg +0 -0
  384. duckpilot/server/static/sounds/gesten/trotzig_0.ogg +0 -0
  385. duckpilot/server/static/sounds/gesten/trotzig_1.ogg +0 -0
  386. duckpilot/server/static/sounds/gesten/trotzig_2.ogg +0 -0
  387. duckpilot/server/static/sounds/gesten/trotzig_3.ogg +0 -0
  388. duckpilot/server/static/sounds/gesten/ueberrascht_0.ogg +0 -0
  389. duckpilot/server/static/sounds/gesten/ueberrascht_1.ogg +0 -0
  390. duckpilot/server/static/sounds/gesten/ueberrascht_2.ogg +0 -0
  391. duckpilot/server/static/sounds/gesten/ueberrascht_3.ogg +0 -0
  392. duckpilot/server/static/sounds/gesten/umherspaehen_0.ogg +0 -0
  393. duckpilot/server/static/sounds/gesten/umherspaehen_1.ogg +0 -0
  394. duckpilot/server/static/sounds/gesten/umherspaehen_2.ogg +0 -0
  395. duckpilot/server/static/sounds/gesten/umherspaehen_3.ogg +0 -0
  396. duckpilot/server/static/sounds/gesten/verlegen_0.ogg +0 -0
  397. duckpilot/server/static/sounds/gesten/verlegen_1.ogg +0 -0
  398. duckpilot/server/static/sounds/gesten/verlegen_2.ogg +0 -0
  399. duckpilot/server/static/sounds/gesten/verlegen_3.ogg +0 -0
  400. duckpilot/server/static/sounds/gesten/wackeln_0.ogg +0 -0
  401. duckpilot/server/static/sounds/gesten/wackeln_1.ogg +0 -0
  402. duckpilot/server/static/sounds/gesten/wackeln_2.ogg +0 -0
  403. duckpilot/server/static/sounds/gesten/wackeln_3.ogg +0 -0
  404. duckpilot/server/static/sounds/gesten/wippen_0.ogg +0 -0
  405. duckpilot/server/static/sounds/gesten/wippen_1.ogg +0 -0
  406. duckpilot/server/static/sounds/gesten/wippen_2.ogg +0 -0
  407. duckpilot/server/static/sounds/gesten/wippen_3.ogg +0 -0
  408. duckpilot/server/static/sounds/gesten/wuetend_0.ogg +0 -0
  409. duckpilot/server/static/sounds/gesten/wuetend_1.ogg +0 -0
  410. duckpilot/server/static/sounds/gesten/wuetend_2.ogg +0 -0
  411. duckpilot/server/static/sounds/gesten/wuetend_3.ogg +0 -0
  412. duckpilot/server/static/sounds/voice/alarm_0.ogg +0 -0
  413. duckpilot/server/static/sounds/voice/alarm_1.ogg +0 -0
  414. duckpilot/server/static/sounds/voice/alarm_2.ogg +0 -0
  415. duckpilot/server/static/sounds/voice/alarm_3.ogg +0 -0
  416. duckpilot/server/static/sounds/voice/alarm_4.ogg +0 -0
  417. duckpilot/server/static/sounds/voice/alarm_5.ogg +0 -0
  418. duckpilot/server/static/sounds/voice/alarm_6.ogg +0 -0
  419. duckpilot/server/static/sounds/voice/alarm_7.ogg +0 -0
  420. duckpilot/server/static/sounds/voice/alarm_8.ogg +0 -0
  421. duckpilot/server/static/sounds/voice/alarm_9.ogg +0 -0
  422. duckpilot/server/static/sounds/voice/chirp_0.ogg +0 -0
  423. duckpilot/server/static/sounds/voice/chirp_1.ogg +0 -0
  424. duckpilot/server/static/sounds/voice/chirp_10.ogg +0 -0
  425. duckpilot/server/static/sounds/voice/chirp_11.ogg +0 -0
  426. duckpilot/server/static/sounds/voice/chirp_2.ogg +0 -0
  427. duckpilot/server/static/sounds/voice/chirp_3.ogg +0 -0
  428. duckpilot/server/static/sounds/voice/chirp_4.ogg +0 -0
  429. duckpilot/server/static/sounds/voice/chirp_5.ogg +0 -0
  430. duckpilot/server/static/sounds/voice/chirp_6.ogg +0 -0
  431. duckpilot/server/static/sounds/voice/chirp_7.ogg +0 -0
  432. duckpilot/server/static/sounds/voice/chirp_8.ogg +0 -0
  433. duckpilot/server/static/sounds/voice/chirp_9.ogg +0 -0
  434. duckpilot/server/static/sounds/voice/coo_0.ogg +0 -0
  435. duckpilot/server/static/sounds/voice/coo_1.ogg +0 -0
  436. duckpilot/server/static/sounds/voice/coo_2.ogg +0 -0
  437. duckpilot/server/static/sounds/voice/coo_3.ogg +0 -0
  438. duckpilot/server/static/sounds/voice/coo_4.ogg +0 -0
  439. duckpilot/server/static/sounds/voice/coo_5.ogg +0 -0
  440. duckpilot/server/static/sounds/voice/coo_6.ogg +0 -0
  441. duckpilot/server/static/sounds/voice/coo_7.ogg +0 -0
  442. duckpilot/server/static/sounds/voice/coo_8.ogg +0 -0
  443. duckpilot/server/static/sounds/voice/coo_9.ogg +0 -0
  444. duckpilot/server/static/sounds/voice/greet_0.ogg +0 -0
  445. duckpilot/server/static/sounds/voice/greet_1.ogg +0 -0
  446. duckpilot/server/static/sounds/voice/greet_10.ogg +0 -0
  447. duckpilot/server/static/sounds/voice/greet_11.ogg +0 -0
  448. duckpilot/server/static/sounds/voice/greet_2.ogg +0 -0
  449. duckpilot/server/static/sounds/voice/greet_3.ogg +0 -0
  450. duckpilot/server/static/sounds/voice/greet_4.ogg +0 -0
  451. duckpilot/server/static/sounds/voice/greet_5.ogg +0 -0
  452. duckpilot/server/static/sounds/voice/greet_6.ogg +0 -0
  453. duckpilot/server/static/sounds/voice/greet_7.ogg +0 -0
  454. duckpilot/server/static/sounds/voice/greet_8.ogg +0 -0
  455. duckpilot/server/static/sounds/voice/greet_9.ogg +0 -0
  456. duckpilot/server/static/sounds/voice/index.json +86 -0
  457. duckpilot/server/static/sounds/voice/inquire_0.ogg +0 -0
  458. duckpilot/server/static/sounds/voice/inquire_1.ogg +0 -0
  459. duckpilot/server/static/sounds/voice/inquire_2.ogg +0 -0
  460. duckpilot/server/static/sounds/voice/inquire_3.ogg +0 -0
  461. duckpilot/server/static/sounds/voice/inquire_4.ogg +0 -0
  462. duckpilot/server/static/sounds/voice/inquire_5.ogg +0 -0
  463. duckpilot/server/static/sounds/voice/inquire_6.ogg +0 -0
  464. duckpilot/server/static/sounds/voice/inquire_7.ogg +0 -0
  465. duckpilot/server/static/sounds/voice/inquire_8.ogg +0 -0
  466. duckpilot/server/static/sounds/voice/inquire_9.ogg +0 -0
  467. duckpilot/server/static/sounds/voice/peck_0.ogg +0 -0
  468. duckpilot/server/static/sounds/voice/peck_1.ogg +0 -0
  469. duckpilot/server/static/sounds/voice/peck_2.ogg +0 -0
  470. duckpilot/server/static/sounds/voice/peck_3.ogg +0 -0
  471. duckpilot/server/static/sounds/voice/peck_4.ogg +0 -0
  472. duckpilot/server/static/sounds/voice/peck_5.ogg +0 -0
  473. duckpilot/server/static/sounds/voice/peck_6.ogg +0 -0
  474. duckpilot/server/static/sounds/voice/peck_7.ogg +0 -0
  475. duckpilot/server/static/sounds/voice/peck_8.ogg +0 -0
  476. duckpilot/server/static/sounds/voice/peck_9.ogg +0 -0
  477. duckpilot/server/static/sounds/voice/wheee_0.ogg +0 -0
  478. duckpilot/server/static/sounds/voice/wheee_1.ogg +0 -0
  479. duckpilot/server/static/spiele_runden.js +239 -0
  480. duckpilot/server/static/sprachwahl.js +60 -0
  481. duckpilot/server/static/start.js +216 -0
  482. duckpilot/server/static/stimme.js +590 -0
  483. duckpilot/server/static/stossreihe.js +266 -0
  484. duckpilot/server/static/style.css +2426 -0
  485. duckpilot/server/static/tanz.js +757 -0
  486. duckpilot/server/static/touchfahren.js +37 -0
  487. duckpilot/server/static/training.js +707 -0
  488. duckpilot/server/static/trainingsfeld.js +516 -0
  489. duckpilot/server/static/tricks_seite.js +160 -0
  490. duckpilot/server/static/ui.js +19 -0
  491. duckpilot/server/static/vendor/BRICOLAGE-OFL.txt +93 -0
  492. duckpilot/server/static/vendor/BufferGeometryUtils.js +1373 -0
  493. duckpilot/server/static/vendor/OrbitControls.js +1523 -0
  494. duckpilot/server/static/vendor/THREE-LICENSE +21 -0
  495. duckpilot/server/static/vendor/bricolage.woff2 +0 -0
  496. duckpilot/server/static/vendor/jsm/environments/RoomEnvironment.js +144 -0
  497. duckpilot/server/static/vendor/jsm/math/SimplexNoise.js +444 -0
  498. duckpilot/server/static/vendor/jsm/postprocessing/EffectComposer.js +231 -0
  499. duckpilot/server/static/vendor/jsm/postprocessing/GTAOPass.js +582 -0
  500. duckpilot/server/static/vendor/jsm/postprocessing/MaskPass.js +104 -0
  501. duckpilot/server/static/vendor/jsm/postprocessing/OutputPass.js +97 -0
  502. duckpilot/server/static/vendor/jsm/postprocessing/Pass.js +95 -0
  503. duckpilot/server/static/vendor/jsm/postprocessing/RenderPass.js +99 -0
  504. duckpilot/server/static/vendor/jsm/postprocessing/ShaderPass.js +77 -0
  505. duckpilot/server/static/vendor/jsm/shaders/CopyShader.js +45 -0
  506. duckpilot/server/static/vendor/jsm/shaders/GTAOShader.js +424 -0
  507. duckpilot/server/static/vendor/jsm/shaders/OutputShader.js +85 -0
  508. duckpilot/server/static/vendor/jsm/shaders/PoissonDenoiseShader.js +226 -0
  509. duckpilot/server/static/vendor/plex/IBMPlexMono-Regular.woff2 +0 -0
  510. duckpilot/server/static/vendor/plex/IBMPlexSans-Light.woff2 +0 -0
  511. duckpilot/server/static/vendor/plex/IBMPlexSans-Medium.woff2 +0 -0
  512. duckpilot/server/static/vendor/plex/IBMPlexSans-Regular.woff2 +0 -0
  513. duckpilot/server/static/vendor/plex/IBMPlexSans-SemiBold.woff2 +0 -0
  514. duckpilot/server/static/vendor/plex/LICENSE-OFL.txt +93 -0
  515. duckpilot/server/static/vendor/three.module.min.js +6 -0
  516. duckpilot/server/static/verbindungstest.js +47 -0
  517. duckpilot/server/static/voice.js +534 -0
  518. duckpilot/server/static/welt.js +61 -0
  519. duckpilot/settings.py +415 -0
  520. duckpilot/sicherungspunkte.py +306 -0
  521. duckpilot/sichtungen.py +174 -0
  522. duckpilot/skills/__init__.py +257 -0
  523. duckpilot/skills/alltag.py +443 -0
  524. duckpilot/skills/app_moves.py +190 -0
  525. duckpilot/skills/community.py +192 -0
  526. duckpilot/skills/detector.py +57 -0
  527. duckpilot/skills/gaits.py +91 -0
  528. duckpilot/skills/talent.py +200 -0
  529. duckpilot/stossreihe_service.py +218 -0
  530. duckpilot/suite.py +77 -0
  531. duckpilot/tailscale.py +116 -0
  532. duckpilot/training.py +859 -0
  533. duckpilot/verbindungstest.py +129 -0
  534. duckpilot/vision.py +207 -0
  535. duckpilot/voice/__init__.py +0 -0
  536. duckpilot/voice/describe.py +154 -0
  537. duckpilot/voice/install.py +215 -0
  538. duckpilot/voice/qwen_prozess.py +233 -0
  539. duckpilot/voice/speech.py +642 -0
  540. duckpilot/voice/tts.py +673 -0
  541. duckpilot/voice/whisper_stt.py +69 -0
  542. knowlin-0.38.31.dist-info/METADATA +244 -0
  543. knowlin-0.38.31.dist-info/RECORD +548 -0
  544. knowlin-0.38.31.dist-info/WHEEL +5 -0
  545. knowlin-0.38.31.dist-info/entry_points.txt +3 -0
  546. knowlin-0.38.31.dist-info/licenses/LICENSE +202 -0
  547. knowlin-0.38.31.dist-info/licenses/NOTICE +77 -0
  548. knowlin-0.38.31.dist-info/top_level.txt +1 -0
duckpilot/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """duckpilot – Microduck am PC simulieren, mit Claude steuern, später auf den echten Roboter umschalten."""
2
+
3
+ __version__ = "0.38.31"
duckpilot/__main__.py ADDED
@@ -0,0 +1,256 @@
1
+ """Start: `python -m duckpilot` (Simulator) oder `python -m duckpilot --backend robotd`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import asyncio
7
+ import logging
8
+ import sys
9
+ import threading
10
+ import webbrowser
11
+
12
+ from . import __version__
13
+ from .config import load
14
+
15
+
16
+ def _safe_console() -> None:
17
+ """Ausgaben dürfen nie abstürzen: Unter Windows ist die Ausgabe beim Neustart eine Datei in cp1252 – das 🦆 der
18
+ Startmeldung ließ den neuen Prozess sterben (UnicodeEncodeError), und duckpilot kam nicht wieder."""
19
+ for stream in (sys.stdout, sys.stderr):
20
+ reconfigure = getattr(stream, "reconfigure", None)
21
+ if reconfigure is not None:
22
+ try:
23
+ reconfigure(errors="replace")
24
+ except (ValueError, OSError):
25
+ pass
26
+
27
+
28
+ def main(argv: list[str] | None = None) -> None:
29
+ _safe_console()
30
+ ap = argparse.ArgumentParser(prog="knowlin", description="Knowlin – ein Kopf, viele Körper. Startet die Oberfläche im "
31
+ "Browser (Simulator oder echter Roboter). Python-Paket: duckpilot.")
32
+ ap.add_argument("--config", help="Pfad zu duckpilot.toml")
33
+ ap.add_argument("--backend", choices=["sim", "robotd"], help="sim (Standard) oder robotd (echter Roboter / duck-sim)")
34
+ ap.add_argument("--robot", help="robotd-Adresse, z. B. tcp:127.0.0.1:7900 oder unix:/run/robotd.sock")
35
+ ap.add_argument("--tof", help="tofd-Adresse für den Tiefensensor (optional)")
36
+ ap.add_argument("--updater", help="updaterd-Adresse für Tricks vom Hub (optional)")
37
+ ap.add_argument("--port", type=int, help="Port der Oberfläche (Standard 8765)")
38
+ ap.add_argument("--host", help="Adresse der Oberfläche (Standard 127.0.0.1 – nur dieser PC)")
39
+ ap.add_argument("--model", help="Claude-Modell, z. B. claude-sonnet-5-5 oder claude-haiku-5-5")
40
+ ap.add_argument("--stt", choices=["browser", "whisper", "aus"], help="Spracherkennung")
41
+ ap.add_argument("--memory", metavar="DATEI", help="Pfad der Gedächtnisdatei (Standard: lokaler App-Datenordner)")
42
+ ap.add_argument("--no-memory", action="store_true", help="Gedächtnis ausschalten")
43
+ ap.add_argument("--no-browser", action="store_true", help="Browser nicht automatisch öffnen")
44
+ ap.add_argument("--app", action="store_true",
45
+ help="Als eigenes Programmfenster starten (ohne Konsole, beendet sich beim Schließen)")
46
+ ap.add_argument("--modul", choices=["duckpilot", "reachy_mini", "open_duck_mini"],
47
+ help="gleich in einer Verkörperung starten statt auf der Knowlin-Startseite (Verknüpfungen "
48
+ "„Knowlin - Microduck“ & Co.)")
49
+ ap.add_argument("--hintergrund", action="store_true",
50
+ help="Mit --app: ohne Fenster starten (z. B. mit Windows, für die Handy-Fernbedienung)")
51
+ ap.add_argument("--neustart", action="store_true", help=argparse.SUPPRESS) # nach „Neu starten“: kein neues Fenster
52
+ ap.add_argument("--fake-robotd", metavar="ADRESSE",
53
+ help="Statt der Oberfläche: den Simulator als robotd-kompatiblen Server anbieten (zum Testen)")
54
+ ap.add_argument("--verknuepfung", action="store_true",
55
+ help="Windows: Verknüpfungen „Knowlin“ und „Knowlin - Microduck“ auf Desktop und im Startmenü anlegen")
56
+ ap.add_argument("-v", "--verbose", action="store_true")
57
+ ap.add_argument("--version", action="version", version=f"Knowlin {__version__} (Paket duckpilot)")
58
+ args = ap.parse_args(argv)
59
+
60
+ if args.app:
61
+ from .desktop import setup_file_logging
62
+
63
+ setup_file_logging()
64
+ else:
65
+ logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO,
66
+ format="%(asctime)s %(levelname)-7s %(name)s: %(message)s", datefmt="%H:%M:%S")
67
+
68
+ if args.verknuepfung: # seit 0.38.31: nach „pipx install knowlin“ – ohne start.bat
69
+ from .desktop import verknuepfung_anlegen
70
+
71
+ try:
72
+ for modul in ("", "duckpilot"):
73
+ for pfad in verknuepfung_anlegen(modul):
74
+ print(f" Verknüpfung: {pfad}")
75
+ except RuntimeError as exc:
76
+ print(f" {exc}")
77
+ return
78
+
79
+ if args.fake_robotd:
80
+ from .backends.fake_robotd import serve
81
+
82
+ asyncio.run(serve(args.fake_robotd))
83
+ return
84
+
85
+ cfg = load(args.config, backend=args.backend, robot=args.robot, tof=args.tof, updater=args.updater, port=args.port,
86
+ host=args.host, model=args.model, stt=args.stt, memory_path=args.memory)
87
+ if args.no_memory:
88
+ cfg.memory = False
89
+ if args.no_browser:
90
+ cfg.open_browser = False
91
+
92
+ import uvicorn
93
+
94
+ url = f"http://{'127.0.0.1' if cfg.host in ('0.0.0.0', '::') else cfg.host}:{cfg.port}"
95
+ fenster = f"{url}/?modul={args.modul}" if args.modul else url # was das Fenster zeigt (Startseite oder Modul)
96
+ if args.app:
97
+ from .desktop import already_running, open_window, refresh_shortcut_icons
98
+
99
+ cfg.app_mode = True
100
+ threading.Thread(target=refresh_shortcut_icons, name="icons", daemon=True).start()
101
+ cfg.hintergrund = bool(args.hintergrund)
102
+ if args.neustart: # nach „Neu starten“: warten, bis der alte Prozess den Port freigegeben hat
103
+ import time
104
+
105
+ restart_log(f"neuer Prozess {__version__} gestartet, warte auf freien Port {cfg.port}")
106
+ for _ in range(240): # bis 60 s – der alte fährt noch herunter
107
+ if not already_running(url):
108
+ break
109
+ time.sleep(0.25)
110
+ restart_log("Port frei" if not already_running(url) else "alter Prozess antwortet noch – gebe auf")
111
+ if already_running(url): # zweiter Doppelklick: nur ein weiteres Fenster
112
+ if not args.hintergrund:
113
+ open_window(fenster) # dasselbe laufende Knowlin – ein Kern, ein Gedächtnis
114
+ return
115
+
116
+ step = restart_log if args.app else (lambda _t: None) # Programmfenster: Startschritte ins neustart.log
117
+ step(f"Start {__version__}: lade Programm")
118
+ from .server.app import Runtime, create_app
119
+
120
+ step("baue Laufzeit auf (Körper, Stimme, Gedächtnis …)")
121
+ try:
122
+ runtime = Runtime(cfg)
123
+ except ValueError as exc:
124
+ raise SystemExit(str(exc)) from None
125
+ step("Laufzeit bereit")
126
+ if args.hintergrund and not runtime.remote.enabled:
127
+ print(" Handy-Fernbedienung ist aus – im Hintergrund gibt es nichts zu tun.")
128
+ return
129
+ app = create_app(runtime)
130
+ print(f"\n 🧠 Knowlin {__version__} – Backend: {cfg.backend}"
131
+ f"{' (' + cfg.robot + ')' if cfg.backend == 'robotd' else ''}")
132
+ print(f" Claude: {'an (' + cfg.model + ')' if cfg.api_key else 'aus – kein API-Schlüssel, einfache Befehle aktiv'}")
133
+ if runtime.memory is not None:
134
+ print(f" Gedächtnis: {runtime.memory.path}")
135
+ print(f" Oberfläche: {url}\n")
136
+
137
+ server = uvicorn.Server(uvicorn.Config(app, host=cfg.host, port=cfg.port, log_level="warning",
138
+ log_config=None if args.app else uvicorn.config.LOGGING_CONFIG))
139
+
140
+ def stop_server() -> None:
141
+ server.should_exit = True
142
+
143
+ def watchdog() -> None: # hängt das Herunterfahren, nach 15 s hart beenden (Port wird frei)
144
+ import os
145
+ import time
146
+
147
+ lernt = bool(getattr(getattr(runtime, "lernen", None), "running", False))
148
+ time.sleep(75 if lernt else 15) # beim Lernen am PC Zeit lassen, den besten Stand zu speichern
149
+ restart_log("Herunterfahren hing – beende hart")
150
+ os._exit(0)
151
+
152
+ threading.Thread(target=watchdog, daemon=True).start()
153
+
154
+ runtime.on_idle_exit = stop_server
155
+ runtime.spawn_restart = lambda: _restart(argv if argv is not None else sys.argv[1:])
156
+
157
+ def open_when_ready() -> None:
158
+ import time
159
+
160
+ for _ in range(100):
161
+ if server.started:
162
+ break
163
+ time.sleep(0.1)
164
+ if args.app and args.hintergrund:
165
+ print(" Im Hintergrund gestartet – Fenster öffnen die Knowlin-Verknüpfungen.")
166
+ elif args.app and args.neustart:
167
+ # das offene Fenster lädt sich von selbst neu; ist keins mehr da, eins öffnen
168
+ time.sleep(15)
169
+ if not runtime.clients:
170
+ from .desktop import open_window
171
+
172
+ open_window(url)
173
+ elif args.app:
174
+ from .desktop import open_window
175
+
176
+ open_window(fenster)
177
+ elif cfg.open_browser:
178
+ webbrowser.open(url)
179
+
180
+ threading.Thread(target=open_when_ready, daemon=True).start()
181
+ step("starte Server")
182
+ server.run()
183
+ if args.neustart:
184
+ restart_log("Server beendet")
185
+
186
+
187
+ def restart_log(text: str) -> None:
188
+ """Kurzes Protokoll des Neustarts im Programmordner (neustart.log) – zum Nachsehen, falls er hakt."""
189
+ import os
190
+ import time
191
+
192
+ try:
193
+ path = os.path.join(os.getcwd(), "neustart.log")
194
+ if os.path.exists(path) and os.path.getsize(path) > 50_000:
195
+ os.replace(path, path + ".1")
196
+ with open(path, "a", encoding="utf-8") as f:
197
+ f.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} [{os.getpid()}] {text}\n")
198
+ except OSError:
199
+ pass
200
+
201
+
202
+ def restart_command(argv: list[str]) -> list[str]:
203
+ """Dieselben Startoptionen (Port, Konfiguration …) wie vorher, als Programmfenster, ohne „im Hintergrund“."""
204
+ keep = [a for a in argv if a not in ("--hintergrund", "--neustart", "--app")]
205
+ return [sys.executable, "-m", "duckpilot", *keep, "--app", "--neustart"]
206
+
207
+
208
+ def _restart(argv: list[str]) -> None:
209
+ """Nach „Neu starten“: dieselbe Python-Umgebung, derselbe Ordner, dieselben Optionen."""
210
+ import os
211
+ import subprocess
212
+
213
+ cmd = restart_command(list(argv))
214
+ # Neue Pakete nötig (requirements.txt geändert)? Dann über start.bat, das sie vorher installiert
215
+ here = os.getcwd()
216
+ req, have = os.path.join(here, "requirements.txt"), os.path.join(sys.prefix, "requirements.txt")
217
+ bat = os.path.join(here, "start.bat")
218
+ try:
219
+ changed = os.path.exists(req) and (not os.path.exists(have) or open(req, "rb").read() != open(have, "rb").read())
220
+ except OSError:
221
+ changed = False
222
+ if sys.platform == "win32" and changed and os.path.exists(bat):
223
+ cmd = ["cmd", "/c", bat]
224
+ flags = 0
225
+ if sys.platform == "win32":
226
+ flags = 0x00000008 | 0x00000200 # DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP
227
+ restart_log(f"starte neu: {' '.join(cmd)}")
228
+ for extra in (0x01000000, 0): # erst mit CREATE_BREAKAWAY_FROM_JOB (falls in einem Job), sonst ohne
229
+ try:
230
+ p = subprocess.Popen(cmd, cwd=os.getcwd(), creationflags=flags | (extra if sys.platform == "win32" else 0),
231
+ close_fds=True, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
232
+ stderr=subprocess.DEVNULL)
233
+ restart_log(f"neuer Prozess {p.pid} gestartet")
234
+ return
235
+ except OSError as exc:
236
+ restart_log(f"Start fehlgeschlagen ({exc})")
237
+ print(" Neustart ging nicht – bitte über die Verknüpfung starten.")
238
+
239
+ def _run() -> int | None:
240
+ """Start mit Absturz-Protokoll: Im Programmfenster gibt es keine Konsole – ein Fehler beim Start soll trotzdem
241
+ sichtbar werden (neustart.log im Programmordner und duckpilot.log)."""
242
+ try:
243
+ return main()
244
+ except SystemExit:
245
+ raise
246
+ except BaseException:
247
+ import traceback
248
+
249
+ text = traceback.format_exc()
250
+ logging.getLogger("duckpilot").error("Absturz beim Start:\n%s", text)
251
+ restart_log("Absturz: " + text.strip().replace("\n", "\n "))
252
+ raise
253
+
254
+
255
+ if __name__ == "__main__":
256
+ sys.exit(_run())
@@ -0,0 +1,185 @@
1
+ """Akrobatik-Dienst: Gliederpuppe prüfen, üben lassen, Fähigkeit je Übung merken.
2
+
3
+ Die Rechnung steckt in physics/acrobatics.py (MuJoCo). Hier: ein Prüf-Exemplar für schnelle Antworten beim
4
+ Ziehen, ein eigenes Exemplar je Übungslauf (Hintergrund-Thread), und das Fähigkeitsheft `akrobatik.json` im
5
+ Datenordner (beste Fähigkeit je Übung, Verlauf, gespeicherte Haltungen).
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import math
11
+ import threading
12
+ import time
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ from .api import JOINT_NAMES
17
+ from .joints import LABELS
18
+
19
+ MAX_POSES = 30
20
+ MAX_HISTORY = 60
21
+ NAME_MAX = 40
22
+
23
+
24
+ def available() -> tuple[bool, str]:
25
+ import importlib.util
26
+
27
+ if importlib.util.find_spec("mujoco") is None or importlib.util.find_spec("numpy") is None:
28
+ return False, "MuJoCo ist nicht installiert – unter Einstellungen → Körper „Echte Physik“ einrichten."
29
+ return True, ""
30
+
31
+
32
+ def _clean_pose(raw: Any) -> dict[str, float]:
33
+ out: dict[str, float] = {}
34
+ if isinstance(raw, dict):
35
+ for n in JOINT_NAMES:
36
+ v = raw.get(n)
37
+ if isinstance(v, (int, float)) and math.isfinite(v) and abs(v) < 4:
38
+ out[n] = float(v)
39
+ return out
40
+
41
+
42
+ class AcrobaticsService:
43
+ def __init__(self, data_dir: Path) -> None:
44
+ self.path = Path(data_dir) / "akrobatik.json"
45
+ self._lock = threading.Lock()
46
+ self._book_lock = threading.Lock() # Heft: Übungs-Thread und Server schreiben beide
47
+ self._acro = None
48
+ self.job: dict[str, Any] = {}
49
+ self._cancel = threading.Event()
50
+ self.book = self._load()
51
+
52
+ # ── Heft ─────────────────────────────────────────────────────────────
53
+ def _load(self) -> dict[str, Any]:
54
+ try:
55
+ data = json.loads(self.path.read_text(encoding="utf-8"))
56
+ if isinstance(data, dict):
57
+ data.setdefault("uebungen", {})
58
+ data.setdefault("posen", {})
59
+ return data
60
+ except (OSError, ValueError):
61
+ pass
62
+ return {"uebungen": {}, "posen": {}}
63
+
64
+ def _save(self) -> None:
65
+ """Nur mit self._book_lock aufrufen."""
66
+ self.path.parent.mkdir(parents=True, exist_ok=True)
67
+ tmp = self.path.with_name(f"{self.path.stem}.{threading.get_ident()}.tmp")
68
+ tmp.write_text(json.dumps(self.book, ensure_ascii=False, indent=1), encoding="utf-8")
69
+ tmp.replace(self.path)
70
+
71
+ def save_pose(self, name: str, pose: Any) -> dict[str, Any]:
72
+ name = " ".join(str(name or "").replace("/", " ").replace("\\", " ").split())[:NAME_MAX]
73
+ if not name:
74
+ raise ValueError("Bitte einen Namen für die Haltung angeben.")
75
+ clean = _clean_pose(pose)
76
+ if not clean:
77
+ raise ValueError("Die Haltung ist leer.")
78
+ with self._book_lock:
79
+ poses = self.book["posen"]
80
+ if name not in poses and len(poses) >= MAX_POSES:
81
+ raise ValueError(f"Höchstens {MAX_POSES} Haltungen – erst eine löschen.")
82
+ poses[name] = clean
83
+ self._save()
84
+ return {"name": name}
85
+
86
+ def delete_pose(self, name: str) -> bool:
87
+ with self._book_lock:
88
+ if self.book["posen"].pop(name, None) is None:
89
+ return False
90
+ self._save()
91
+ return True
92
+
93
+ # ── Prüfen ───────────────────────────────────────────────────────────
94
+ def _live(self):
95
+ if self._acro is None:
96
+ from .physics.acrobatics import Acrobat
97
+
98
+ self._acro = Acrobat()
99
+ return self._acro
100
+
101
+ def overview(self) -> dict[str, Any]:
102
+ ok, why = available()
103
+ with self._book_lock:
104
+ book = json.loads(json.dumps(self.book))
105
+ res: dict[str, Any] = {"verfuegbar": ok, "grund": why, "posen": book["posen"], "job": self.job}
106
+ if not ok:
107
+ return res
108
+ from .physics.acrobatics import ACTUATORS, EXERCISES
109
+
110
+ with self._lock:
111
+ acro = self._live()
112
+ res["gelenke"] = [{"name": n, "label": LABELS.get(n, n), "min": round(float(acro.lo[i]), 3),
113
+ "max": round(float(acro.hi[i]), 3)} for i, n in enumerate(ACTUATORS)]
114
+ res["grundstellung"] = acro.as_dict(acro.pose_from({}))
115
+ res["uebungen"] = []
116
+ for ex in EXERCISES.values():
117
+ mine = book["uebungen"].get(ex.id, {})
118
+ res["uebungen"].append({"id": ex.id, "name": ex.name, "beschreibung": ex.beschreibung,
119
+ "beste": mine.get("beste"), "start": mine.get("start"),
120
+ "verlauf": mine.get("verlauf", []),
121
+ "pose": mine.get("pose")})
122
+ return res
123
+
124
+ def check(self, pose: Any) -> dict[str, Any]:
125
+ with self._lock:
126
+ acro = self._live()
127
+ return acro.place(acro.pose_from(_clean_pose(pose)))
128
+
129
+ def probe(self, pose: Any) -> dict[str, Any]:
130
+ with self._lock:
131
+ acro = self._live()
132
+ return acro.hold(acro.pose_from(_clean_pose(pose)))
133
+
134
+ # ── Üben (Hintergrund) ───────────────────────────────────────────────
135
+ def start_practice(self, pose: Any, exercise: str) -> dict[str, Any]:
136
+ from .physics.acrobatics import EXERCISES
137
+
138
+ if exercise not in EXERCISES:
139
+ raise ValueError("Unbekannte Übung.")
140
+ if self.job.get("laeuft"):
141
+ raise RuntimeError("Sie übt schon.")
142
+ clean = _clean_pose(pose)
143
+ self._cancel.clear()
144
+ self.job = {"laeuft": True, "uebung": exercise, "generation": 0, "von": 0, "faehigkeit": None,
145
+ "pose": None, "verlauf": [], "ergebnis": None, "fehler": None, "start": time.time()}
146
+ threading.Thread(target=self._run, args=(clean, exercise), daemon=True, name="akrobatik").start()
147
+ return self.job
148
+
149
+ def cancel(self) -> None:
150
+ self._cancel.set()
151
+
152
+ def _run(self, pose: dict[str, float], exercise: str) -> None:
153
+ from .physics.acrobatics import EXERCISES, Acrobat
154
+
155
+ try:
156
+ acro = Acrobat() # eigene Welt – Prüfen beim Ziehen bleibt flüssig
157
+
158
+ def step(p: dict[str, Any]) -> None:
159
+ self.job.update(generation=p["generation"], von=p["von"], faehigkeit=p["faehigkeit"], pose=p["pose"])
160
+ self.job["verlauf"] = self.job["verlauf"] + [p["faehigkeit"]]
161
+
162
+ res = acro.practice(acro.pose_from(pose), EXERCISES[exercise], on_step=step,
163
+ stop=self._cancel.is_set)
164
+ cancelled = self._cancel.is_set()
165
+ if not cancelled: # abgebrochene Runden zählen nicht als Fähigkeit
166
+ self._remember(exercise, res)
167
+ self.job.update(laeuft=False, ergebnis=res, abgebrochen=cancelled)
168
+ except Exception as exc:
169
+ self.job.update(laeuft=False, fehler=f"{type(exc).__name__}: {exc}")
170
+
171
+ def _remember(self, exercise: str, res: dict[str, Any]) -> None:
172
+ with self._book_lock:
173
+ self._remember_locked(exercise, res)
174
+
175
+ def _remember_locked(self, exercise: str, res: dict[str, Any]) -> None:
176
+ entry = self.book["uebungen"].setdefault(exercise, {"beste": None, "verlauf": []})
177
+ score = res["faehigkeit"]
178
+ if entry.get("start") is None and res.get("verlauf"):
179
+ entry["start"] = res["verlauf"][0]["faehigkeit"] # so gut war sie vor dem allerersten Üben
180
+ entry["verlauf"] = (entry.get("verlauf", []) + [{"zeit": time.strftime("%Y-%m-%dT%H:%M:%S"),
181
+ "faehigkeit": score}])[-MAX_HISTORY:]
182
+ if entry.get("beste") is None or score > entry["beste"]:
183
+ entry["beste"] = score
184
+ entry["pose"] = res["pose"]
185
+ self._save()
duckpilot/aio.py ADDED
@@ -0,0 +1,33 @@
1
+ """Hintergrund-Aufgaben sicher starten.
2
+
3
+ `asyncio.ensure_future(...)` ohne gemerkte Referenz kann der Garbage Collector mitten im Lauf einsammeln, und
4
+ Fehler darin verschwinden still („Task exception was never retrieved“). `spawn` merkt sich jede Aufgabe, bis sie
5
+ fertig ist, und schreibt Fehler ins Protokoll.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import asyncio
11
+ import logging
12
+ from collections.abc import Awaitable
13
+ from typing import Any
14
+
15
+ log = logging.getLogger(__name__)
16
+ _RUNNING: set[asyncio.Future[Any]] = set()
17
+
18
+
19
+ def spawn(aw: Awaitable[Any]) -> asyncio.Future[Any]:
20
+ task = asyncio.ensure_future(aw)
21
+ _RUNNING.add(task)
22
+ task.add_done_callback(_finished)
23
+ return task
24
+
25
+
26
+ def _finished(task: asyncio.Future[Any]) -> None:
27
+ _RUNNING.discard(task)
28
+ if not task.cancelled() and task.exception() is not None:
29
+ log.error("Hintergrund-Aufgabe fehlgeschlagen", exc_info=task.exception())
30
+
31
+
32
+ def running() -> int:
33
+ return len(_RUNNING)