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.
- duckpilot/__init__.py +3 -0
- duckpilot/__main__.py +256 -0
- duckpilot/acrobatics_service.py +185 -0
- duckpilot/aio.py +33 -0
- duckpilot/api.py +387 -0
- duckpilot/augen.py +107 -0
- duckpilot/backends/__init__.py +61 -0
- duckpilot/backends/begleiter.py +78 -0
- duckpilot/backends/fake_robotd.py +268 -0
- duckpilot/backends/open_duck.py +114 -0
- duckpilot/backends/physics.py +724 -0
- duckpilot/backends/reachy.py +497 -0
- duckpilot/backends/reachy_apps.py +93 -0
- duckpilot/backends/reachy_ausdruck.py +169 -0
- duckpilot/backends/reachy_kinematik.py +381 -0
- duckpilot/backends/robotd.py +675 -0
- duckpilot/backends/sim.py +773 -0
- duckpilot/backends/world.py +284 -0
- duckpilot/brain/__init__.py +0 -0
- duckpilot/brain/agent.py +627 -0
- duckpilot/brain/anbieter.py +169 -0
- duckpilot/brain/erfahrung.py +359 -0
- duckpilot/brain/fallback.py +660 -0
- duckpilot/brain/handeln.py +138 -0
- duckpilot/brain/kopf.py +534 -0
- duckpilot/brain/local.py +163 -0
- duckpilot/brain/memory.py +457 -0
- duckpilot/brain/tools.py +1235 -0
- duckpilot/brain/tools_lab.py +187 -0
- duckpilot/brain/tools_raum.py +142 -0
- duckpilot/brain/usage.py +93 -0
- duckpilot/brain/zusage.py +105 -0
- duckpilot/buddy.py +767 -0
- duckpilot/care.py +117 -0
- duckpilot/choreo.py +433 -0
- duckpilot/config.py +207 -0
- duckpilot/desktop.py +293 -0
- duckpilot/erfahrung_service.py +219 -0
- duckpilot/faces.py +645 -0
- duckpilot/figuren.py +223 -0
- duckpilot/geplant.py +33 -0
- duckpilot/hub.py +173 -0
- duckpilot/i18n/__init__.py +120 -0
- duckpilot/i18n/de.json +69 -0
- duckpilot/i18n/en.json +65 -0
- duckpilot/i18n/ui_en.json +3235 -0
- duckpilot/joints.py +148 -0
- duckpilot/kaggle_training.py +1107 -0
- duckpilot/knowlet_hub.py +112 -0
- duckpilot/knowlets.py +227 -0
- duckpilot/kollision_service.py +281 -0
- duckpilot/laser.py +134 -0
- duckpilot/lernschleife.py +252 -0
- duckpilot/mcp_bridge.py +84 -0
- duckpilot/mcp_server.py +147 -0
- duckpilot/mdns.py +102 -0
- duckpilot/mission.py +420 -0
- duckpilot/modell/__init__.py +183 -0
- duckpilot/modell/__main__.py +3 -0
- duckpilot/modell/ansicht.py +307 -0
- duckpilot/modell/hand.py +215 -0
- duckpilot/modell/hand_quellen.json +48 -0
- duckpilot/modell/open_duck.py +215 -0
- duckpilot/modell/physik.py +52 -0
- duckpilot/modell/quellen.json +175 -0
- duckpilot/modell/reachy.py +293 -0
- duckpilot/modell/reachy_bewegungen.py +177 -0
- duckpilot/modell/reachy_quellen.json +136 -0
- duckpilot/modell/service.py +38 -0
- duckpilot/motion/__init__.py +0 -0
- duckpilot/motion/apportieren.py +251 -0
- duckpilot/motion/ausweichen.py +379 -0
- duckpilot/motion/behaviors.py +853 -0
- duckpilot/motion/character.py +279 -0
- duckpilot/motion/choreografie.py +497 -0
- duckpilot/motion/eigenstaendig.py +294 -0
- duckpilot/motion/engine.py +1039 -0
- duckpilot/motion/erkunden.py +130 -0
- duckpilot/motion/faehigkeit.py +103 -0
- duckpilot/motion/handeln.py +108 -0
- duckpilot/motion/hilfen.py +21 -0
- duckpilot/motion/karte.py +281 -0
- duckpilot/motion/mood.py +173 -0
- duckpilot/motion/purzelbaum.py +150 -0
- duckpilot/motion/reachy_gesten.py +185 -0
- duckpilot/motion/reachy_spiel.py +99 -0
- duckpilot/motion/reachy_tanz.py +376 -0
- duckpilot/motion/schuss.py +397 -0
- duckpilot/motion/schwarm.py +390 -0
- duckpilot/motion/sounds.py +48 -0
- duckpilot/motion/stabilizer.py +179 -0
- duckpilot/motion/tanz.py +315 -0
- duckpilot/motion/team.py +391 -0
- duckpilot/motion/umrunden.py +136 -0
- duckpilot/musik_struktur.py +176 -0
- duckpilot/nfc.py +106 -0
- duckpilot/physics/__init__.py +1 -0
- duckpilot/physics/acrobatics.py +381 -0
- duckpilot/physics/core.py +508 -0
- duckpilot/physics/course.py +512 -0
- duckpilot/physics/isaaclab.py +297 -0
- duckpilot/physics/isaaclab_hf.py +168 -0
- duckpilot/physics/kollision.py +1102 -0
- duckpilot/physics/lernen.py +1218 -0
- duckpilot/physics/params.py +14 -0
- duckpilot/physics/policies.py +193 -0
- duckpilot/physics/practice.py +225 -0
- duckpilot/physics/stossreihe.py +377 -0
- duckpilot/physics/twist.py +39 -0
- duckpilot/physics/zwilling.py +145 -0
- duckpilot/protest.py +245 -0
- duckpilot/qr.py +265 -0
- duckpilot/remote.py +455 -0
- duckpilot/schichten.py +60 -0
- duckpilot/server/__init__.py +0 -0
- duckpilot/server/app.py +82 -0
- duckpilot/server/fehlerbericht.py +138 -0
- duckpilot/server/plans.py +23 -0
- duckpilot/server/raum.py +83 -0
- duckpilot/server/routes/__init__.py +3 -0
- duckpilot/server/routes/akrobatik.py +74 -0
- duckpilot/server/routes/alltag.py +114 -0
- duckpilot/server/routes/augen.py +90 -0
- duckpilot/server/routes/basis.py +109 -0
- duckpilot/server/routes/beenden.py +51 -0
- duckpilot/server/routes/choreo.py +137 -0
- duckpilot/server/routes/enten.py +38 -0
- duckpilot/server/routes/erfahrung.py +55 -0
- duckpilot/server/routes/fehlerbericht.py +33 -0
- duckpilot/server/routes/fernzugang.py +79 -0
- duckpilot/server/routes/gehirn.py +98 -0
- duckpilot/server/routes/handeln.py +49 -0
- duckpilot/server/routes/kamera.py +43 -0
- duckpilot/server/routes/knowlets.py +219 -0
- duckpilot/server/routes/kollision.py +35 -0
- duckpilot/server/routes/koppeln.py +62 -0
- duckpilot/server/routes/mcp.py +88 -0
- duckpilot/server/routes/modell.py +29 -0
- duckpilot/server/routes/nfc.py +36 -0
- duckpilot/server/routes/parcours.py +105 -0
- duckpilot/server/routes/pflege.py +70 -0
- duckpilot/server/routes/physik.py +48 -0
- duckpilot/server/routes/raum.py +39 -0
- duckpilot/server/routes/reachy.py +70 -0
- duckpilot/server/routes/sicherung.py +83 -0
- duckpilot/server/routes/skills.py +58 -0
- duckpilot/server/routes/spiele.py +597 -0
- duckpilot/server/routes/spiele_hand.py +61 -0
- duckpilot/server/routes/start.py +64 -0
- duckpilot/server/routes/stimme.py +137 -0
- duckpilot/server/routes/stossreihe.py +40 -0
- duckpilot/server/routes/training.py +460 -0
- duckpilot/server/routes/verbindung.py +96 -0
- duckpilot/server/runtime.py +1057 -0
- duckpilot/server/runtime_eigenstaendig.py +176 -0
- duckpilot/server/runtime_gespraech.py +249 -0
- duckpilot/server/runtime_kern.py +225 -0
- duckpilot/server/runtime_lab.py +302 -0
- duckpilot/server/runtime_reachy.py +241 -0
- duckpilot/server/security.py +149 -0
- duckpilot/server/sicherung.py +207 -0
- duckpilot/server/static/akrobatik.js +601 -0
- duckpilot/server/static/alltag.js +285 -0
- duckpilot/server/static/anbieter.js +69 -0
- duckpilot/server/static/app.js +1270 -0
- duckpilot/server/static/apple-touch-icon.png +0 -0
- duckpilot/server/static/augen.js +205 -0
- duckpilot/server/static/aussehen.js +61 -0
- duckpilot/server/static/auswahl.js +104 -0
- duckpilot/server/static/beenden.js +74 -0
- duckpilot/server/static/charakter.js +63 -0
- duckpilot/server/static/controller.js +407 -0
- duckpilot/server/static/dashboard.js +638 -0
- duckpilot/server/static/designsystem.js +169 -0
- duckpilot/server/static/dinge3d.js +94 -0
- duckpilot/server/static/ds/components.css +310 -0
- duckpilot/server/static/ds/tokens.css +235 -0
- duckpilot/server/static/duck3d.js +1260 -0
- duckpilot/server/static/duckmodel.js +261 -0
- duckpilot/server/static/einstellungen_ui.js +232 -0
- duckpilot/server/static/ente3d.js +183 -0
- duckpilot/server/static/enten.js +70 -0
- duckpilot/server/static/ereignisse.js +54 -0
- duckpilot/server/static/erste.js +120 -0
- duckpilot/server/static/fehlerbericht.js +33 -0
- duckpilot/server/static/fernbedienung.js +185 -0
- duckpilot/server/static/figuren/open-duck-mini.png +0 -0
- duckpilot/server/static/flaggen/de.svg +1 -0
- duckpilot/server/static/flaggen/en.svg +1 -0
- duckpilot/server/static/formen3d.js +103 -0
- duckpilot/server/static/gedaechtnis_ui.js +111 -0
- duckpilot/server/static/hand.js +210 -0
- duckpilot/server/static/handy_start.js +73 -0
- duckpilot/server/static/herkunft.js +29 -0
- duckpilot/server/static/hirn.js +455 -0
- duckpilot/server/static/i18n.js +127 -0
- duckpilot/server/static/icon-192.png +0 -0
- duckpilot/server/static/icon-512.png +0 -0
- duckpilot/server/static/icon-maskable-512.png +0 -0
- duckpilot/server/static/index.html +2355 -0
- duckpilot/server/static/isaaclab.js +217 -0
- duckpilot/server/static/kachel.js +52 -0
- duckpilot/server/static/kamera_spiele.js +603 -0
- duckpilot/server/static/knowlets.js +202 -0
- duckpilot/server/static/knowlin-zeichen.png +0 -0
- duckpilot/server/static/knowlin.ico +0 -0
- duckpilot/server/static/koerperwahl.js +75 -0
- duckpilot/server/static/kollision.js +770 -0
- duckpilot/server/static/kopf.js +147 -0
- duckpilot/server/static/kopfleiste.js +68 -0
- duckpilot/server/static/laser.js +94 -0
- duckpilot/server/static/lernen.js +245 -0
- duckpilot/server/static/licht3d.js +35 -0
- duckpilot/server/static/manifest.webmanifest +13 -0
- duckpilot/server/static/mcp.js +35 -0
- duckpilot/server/static/mind.js +454 -0
- duckpilot/server/static/mitwippen.js +227 -0
- duckpilot/server/static/model/LIZENZ-open-duck-mini.md +12 -0
- duckpilot/server/static/model/open_duck_mini.dpm +0 -0
- duckpilot/server/static/modul.js +37 -0
- duckpilot/server/static/moebel3d.js +225 -0
- duckpilot/server/static/nfc.js +107 -0
- duckpilot/server/static/offenlegung.js +53 -0
- duckpilot/server/static/parcours.js +387 -0
- duckpilot/server/static/personality.js +242 -0
- duckpilot/server/static/pflege.js +110 -0
- duckpilot/server/static/pointcloud.js +137 -0
- duckpilot/server/static/raum.js +189 -0
- duckpilot/server/static/raum3d.js +157 -0
- duckpilot/server/static/reachy3d.js +130 -0
- duckpilot/server/static/reachy_apps.js +143 -0
- duckpilot/server/static/reachy_ui.js +112 -0
- duckpilot/server/static/reachymodell.js +193 -0
- duckpilot/server/static/settingsnav.js +39 -0
- duckpilot/server/static/sicherung.js +181 -0
- duckpilot/server/static/skills.js +394 -0
- duckpilot/server/static/sounds/emotions/am_boden.ogg +0 -0
- duckpilot/server/static/sounds/emotions/aufgeregt.ogg +0 -0
- duckpilot/server/static/sounds/emotions/betruebt.ogg +0 -0
- duckpilot/server/static/sounds/emotions/grummelig_a.ogg +0 -0
- duckpilot/server/static/sounds/emotions/grummelig_b.ogg +0 -0
- duckpilot/server/static/sounds/emotions/hmm.ogg +0 -0
- duckpilot/server/static/sounds/emotions/index.json +21 -0
- duckpilot/server/static/sounds/emotions/jawohl.ogg +0 -0
- duckpilot/server/static/sounds/emotions/jep.ogg +0 -0
- duckpilot/server/static/sounds/emotions/lachen.ogg +0 -0
- duckpilot/server/static/sounds/emotions/naeaeae.ogg +0 -0
- duckpilot/server/static/sounds/emotions/noe_a.ogg +0 -0
- duckpilot/server/static/sounds/emotions/noe_b.ogg +0 -0
- duckpilot/server/static/sounds/emotions/noe_c.ogg +0 -0
- duckpilot/server/static/sounds/emotions/tot_stellen.ogg +0 -0
- duckpilot/server/static/sounds/emotions/ungeduldig.ogg +0 -0
- duckpilot/server/static/sounds/emotions/was_was.ogg +0 -0
- duckpilot/server/static/sounds/emotions/wer_sagt_das.ogg +0 -0
- duckpilot/server/static/sounds/gesten/aufhorchen_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/aufhorchen_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/aufhorchen_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/aufhorchen_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/begeistert_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/begeistert_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/begeistert_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/begeistert_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/begruessen_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/begruessen_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/begruessen_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/begruessen_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/boden_picken_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/boden_picken_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/boden_picken_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/boden_picken_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/doesen_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/doesen_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/doesen_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/doesen_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/einfuehlsam_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/einfuehlsam_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/einfuehlsam_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/einfuehlsam_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/erleichtert_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/erleichtert_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/erleichtert_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/erleichtert_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/erschrecken_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/erschrecken_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/erschrecken_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/erschrecken_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/freude_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/freude_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/freude_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/freude_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/frustriert_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/frustriert_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/frustriert_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/frustriert_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/gaehnen_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/gaehnen_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/gaehnen_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/gaehnen_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/gefieder_putzen_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/gefieder_putzen_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/gefieder_putzen_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/gefieder_putzen_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/haltung_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/haltung_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/haltung_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/haltung_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/hibbeln_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/hibbeln_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/hibbeln_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/hibbeln_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/himmel_gucken_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/himmel_gucken_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/himmel_gucken_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/himmel_gucken_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/index.json +1 -0
- duckpilot/server/static/sounds/gesten/ja_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/ja_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/ja_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/ja_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/muede_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/muede_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/muede_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/muede_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/mustern_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/mustern_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/mustern_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/mustern_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/nein_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/nein_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/nein_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/nein_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/neugierig_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/neugierig_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/neugierig_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/neugierig_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/nicken_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/nicken_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/nicken_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/nicken_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/niesen_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/niesen_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/niesen_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/niesen_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/schluckauf_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/schluckauf_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/schluckauf_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/schluckauf_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/schuetteln_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/schuetteln_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/schuetteln_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/schuetteln_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/schwanz_jagen_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/schwanz_jagen_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/schwanz_jagen_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/schwanz_jagen_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/selbstgespraech_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/selbstgespraech_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/selbstgespraech_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/selbstgespraech_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/seufzen_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/seufzen_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/seufzen_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/seufzen_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/staunen_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/staunen_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/staunen_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/staunen_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/stolpern_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/stolpern_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/stolpern_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/stolpern_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/stolz_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/stolz_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/stolz_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/stolz_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/traellern_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/traellern_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/traellern_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/traellern_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/traurig_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/traurig_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/traurig_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/traurig_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/trotzig_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/trotzig_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/trotzig_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/trotzig_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/ueberrascht_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/ueberrascht_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/ueberrascht_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/ueberrascht_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/umherspaehen_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/umherspaehen_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/umherspaehen_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/umherspaehen_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/verlegen_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/verlegen_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/verlegen_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/verlegen_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/wackeln_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/wackeln_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/wackeln_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/wackeln_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/wippen_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/wippen_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/wippen_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/wippen_3.ogg +0 -0
- duckpilot/server/static/sounds/gesten/wuetend_0.ogg +0 -0
- duckpilot/server/static/sounds/gesten/wuetend_1.ogg +0 -0
- duckpilot/server/static/sounds/gesten/wuetend_2.ogg +0 -0
- duckpilot/server/static/sounds/gesten/wuetend_3.ogg +0 -0
- duckpilot/server/static/sounds/voice/alarm_0.ogg +0 -0
- duckpilot/server/static/sounds/voice/alarm_1.ogg +0 -0
- duckpilot/server/static/sounds/voice/alarm_2.ogg +0 -0
- duckpilot/server/static/sounds/voice/alarm_3.ogg +0 -0
- duckpilot/server/static/sounds/voice/alarm_4.ogg +0 -0
- duckpilot/server/static/sounds/voice/alarm_5.ogg +0 -0
- duckpilot/server/static/sounds/voice/alarm_6.ogg +0 -0
- duckpilot/server/static/sounds/voice/alarm_7.ogg +0 -0
- duckpilot/server/static/sounds/voice/alarm_8.ogg +0 -0
- duckpilot/server/static/sounds/voice/alarm_9.ogg +0 -0
- duckpilot/server/static/sounds/voice/chirp_0.ogg +0 -0
- duckpilot/server/static/sounds/voice/chirp_1.ogg +0 -0
- duckpilot/server/static/sounds/voice/chirp_10.ogg +0 -0
- duckpilot/server/static/sounds/voice/chirp_11.ogg +0 -0
- duckpilot/server/static/sounds/voice/chirp_2.ogg +0 -0
- duckpilot/server/static/sounds/voice/chirp_3.ogg +0 -0
- duckpilot/server/static/sounds/voice/chirp_4.ogg +0 -0
- duckpilot/server/static/sounds/voice/chirp_5.ogg +0 -0
- duckpilot/server/static/sounds/voice/chirp_6.ogg +0 -0
- duckpilot/server/static/sounds/voice/chirp_7.ogg +0 -0
- duckpilot/server/static/sounds/voice/chirp_8.ogg +0 -0
- duckpilot/server/static/sounds/voice/chirp_9.ogg +0 -0
- duckpilot/server/static/sounds/voice/coo_0.ogg +0 -0
- duckpilot/server/static/sounds/voice/coo_1.ogg +0 -0
- duckpilot/server/static/sounds/voice/coo_2.ogg +0 -0
- duckpilot/server/static/sounds/voice/coo_3.ogg +0 -0
- duckpilot/server/static/sounds/voice/coo_4.ogg +0 -0
- duckpilot/server/static/sounds/voice/coo_5.ogg +0 -0
- duckpilot/server/static/sounds/voice/coo_6.ogg +0 -0
- duckpilot/server/static/sounds/voice/coo_7.ogg +0 -0
- duckpilot/server/static/sounds/voice/coo_8.ogg +0 -0
- duckpilot/server/static/sounds/voice/coo_9.ogg +0 -0
- duckpilot/server/static/sounds/voice/greet_0.ogg +0 -0
- duckpilot/server/static/sounds/voice/greet_1.ogg +0 -0
- duckpilot/server/static/sounds/voice/greet_10.ogg +0 -0
- duckpilot/server/static/sounds/voice/greet_11.ogg +0 -0
- duckpilot/server/static/sounds/voice/greet_2.ogg +0 -0
- duckpilot/server/static/sounds/voice/greet_3.ogg +0 -0
- duckpilot/server/static/sounds/voice/greet_4.ogg +0 -0
- duckpilot/server/static/sounds/voice/greet_5.ogg +0 -0
- duckpilot/server/static/sounds/voice/greet_6.ogg +0 -0
- duckpilot/server/static/sounds/voice/greet_7.ogg +0 -0
- duckpilot/server/static/sounds/voice/greet_8.ogg +0 -0
- duckpilot/server/static/sounds/voice/greet_9.ogg +0 -0
- duckpilot/server/static/sounds/voice/index.json +86 -0
- duckpilot/server/static/sounds/voice/inquire_0.ogg +0 -0
- duckpilot/server/static/sounds/voice/inquire_1.ogg +0 -0
- duckpilot/server/static/sounds/voice/inquire_2.ogg +0 -0
- duckpilot/server/static/sounds/voice/inquire_3.ogg +0 -0
- duckpilot/server/static/sounds/voice/inquire_4.ogg +0 -0
- duckpilot/server/static/sounds/voice/inquire_5.ogg +0 -0
- duckpilot/server/static/sounds/voice/inquire_6.ogg +0 -0
- duckpilot/server/static/sounds/voice/inquire_7.ogg +0 -0
- duckpilot/server/static/sounds/voice/inquire_8.ogg +0 -0
- duckpilot/server/static/sounds/voice/inquire_9.ogg +0 -0
- duckpilot/server/static/sounds/voice/peck_0.ogg +0 -0
- duckpilot/server/static/sounds/voice/peck_1.ogg +0 -0
- duckpilot/server/static/sounds/voice/peck_2.ogg +0 -0
- duckpilot/server/static/sounds/voice/peck_3.ogg +0 -0
- duckpilot/server/static/sounds/voice/peck_4.ogg +0 -0
- duckpilot/server/static/sounds/voice/peck_5.ogg +0 -0
- duckpilot/server/static/sounds/voice/peck_6.ogg +0 -0
- duckpilot/server/static/sounds/voice/peck_7.ogg +0 -0
- duckpilot/server/static/sounds/voice/peck_8.ogg +0 -0
- duckpilot/server/static/sounds/voice/peck_9.ogg +0 -0
- duckpilot/server/static/sounds/voice/wheee_0.ogg +0 -0
- duckpilot/server/static/sounds/voice/wheee_1.ogg +0 -0
- duckpilot/server/static/spiele_runden.js +239 -0
- duckpilot/server/static/sprachwahl.js +60 -0
- duckpilot/server/static/start.js +216 -0
- duckpilot/server/static/stimme.js +590 -0
- duckpilot/server/static/stossreihe.js +266 -0
- duckpilot/server/static/style.css +2426 -0
- duckpilot/server/static/tanz.js +757 -0
- duckpilot/server/static/touchfahren.js +37 -0
- duckpilot/server/static/training.js +707 -0
- duckpilot/server/static/trainingsfeld.js +516 -0
- duckpilot/server/static/tricks_seite.js +160 -0
- duckpilot/server/static/ui.js +19 -0
- duckpilot/server/static/vendor/BRICOLAGE-OFL.txt +93 -0
- duckpilot/server/static/vendor/BufferGeometryUtils.js +1373 -0
- duckpilot/server/static/vendor/OrbitControls.js +1523 -0
- duckpilot/server/static/vendor/THREE-LICENSE +21 -0
- duckpilot/server/static/vendor/bricolage.woff2 +0 -0
- duckpilot/server/static/vendor/jsm/environments/RoomEnvironment.js +144 -0
- duckpilot/server/static/vendor/jsm/math/SimplexNoise.js +444 -0
- duckpilot/server/static/vendor/jsm/postprocessing/EffectComposer.js +231 -0
- duckpilot/server/static/vendor/jsm/postprocessing/GTAOPass.js +582 -0
- duckpilot/server/static/vendor/jsm/postprocessing/MaskPass.js +104 -0
- duckpilot/server/static/vendor/jsm/postprocessing/OutputPass.js +97 -0
- duckpilot/server/static/vendor/jsm/postprocessing/Pass.js +95 -0
- duckpilot/server/static/vendor/jsm/postprocessing/RenderPass.js +99 -0
- duckpilot/server/static/vendor/jsm/postprocessing/ShaderPass.js +77 -0
- duckpilot/server/static/vendor/jsm/shaders/CopyShader.js +45 -0
- duckpilot/server/static/vendor/jsm/shaders/GTAOShader.js +424 -0
- duckpilot/server/static/vendor/jsm/shaders/OutputShader.js +85 -0
- duckpilot/server/static/vendor/jsm/shaders/PoissonDenoiseShader.js +226 -0
- duckpilot/server/static/vendor/plex/IBMPlexMono-Regular.woff2 +0 -0
- duckpilot/server/static/vendor/plex/IBMPlexSans-Light.woff2 +0 -0
- duckpilot/server/static/vendor/plex/IBMPlexSans-Medium.woff2 +0 -0
- duckpilot/server/static/vendor/plex/IBMPlexSans-Regular.woff2 +0 -0
- duckpilot/server/static/vendor/plex/IBMPlexSans-SemiBold.woff2 +0 -0
- duckpilot/server/static/vendor/plex/LICENSE-OFL.txt +93 -0
- duckpilot/server/static/vendor/three.module.min.js +6 -0
- duckpilot/server/static/verbindungstest.js +47 -0
- duckpilot/server/static/voice.js +534 -0
- duckpilot/server/static/welt.js +61 -0
- duckpilot/settings.py +415 -0
- duckpilot/sicherungspunkte.py +306 -0
- duckpilot/sichtungen.py +174 -0
- duckpilot/skills/__init__.py +257 -0
- duckpilot/skills/alltag.py +443 -0
- duckpilot/skills/app_moves.py +190 -0
- duckpilot/skills/community.py +192 -0
- duckpilot/skills/detector.py +57 -0
- duckpilot/skills/gaits.py +91 -0
- duckpilot/skills/talent.py +200 -0
- duckpilot/stossreihe_service.py +218 -0
- duckpilot/suite.py +77 -0
- duckpilot/tailscale.py +116 -0
- duckpilot/training.py +859 -0
- duckpilot/verbindungstest.py +129 -0
- duckpilot/vision.py +207 -0
- duckpilot/voice/__init__.py +0 -0
- duckpilot/voice/describe.py +154 -0
- duckpilot/voice/install.py +215 -0
- duckpilot/voice/qwen_prozess.py +233 -0
- duckpilot/voice/speech.py +642 -0
- duckpilot/voice/tts.py +673 -0
- duckpilot/voice/whisper_stt.py +69 -0
- knowlin-0.38.31.dist-info/METADATA +244 -0
- knowlin-0.38.31.dist-info/RECORD +548 -0
- knowlin-0.38.31.dist-info/WHEEL +5 -0
- knowlin-0.38.31.dist-info/entry_points.txt +3 -0
- knowlin-0.38.31.dist-info/licenses/LICENSE +202 -0
- knowlin-0.38.31.dist-info/licenses/NOTICE +77 -0
- knowlin-0.38.31.dist-info/top_level.txt +1 -0
duckpilot/__init__.py
ADDED
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)
|