robot-runtime
Eine Control-Runtime für entfernte Roboter-Policies
Ein simulierter Franka Panda führt Pick-and-Place aus, gesteuert von einer Policy, die hinter einer HTTP-Grenze lebt – und weiterarbeitet, wenn diese Grenze sich fehlverhält.

Das Interessante ist nicht der Arm. Es ist alles zwischen dem Arm und dem Modell: Action-Chunk-Planung, Staleness-Ablehnung, Retries mit Backoff, ein Circuit Breaker, ein schützender Halt, der sich selbst aufhebt, und eine MCP-Tool-Oberfläche, damit ein Agent die Zelle steuern kann, ohne sie beschädigen zu können.
Läuft vollständig auf einem Laptop. Keine GPU, keine ROS-Installation, keine Hardware.
Warum das Netzwerk der schwierige Teil ist
Manipulations-Policies wollen eine GPU. Roboter wollen eine Echtzeit-Regelschleife. Das sind selten dieselbe Maschine, also sitzt das Modell in der Praxis hinter einem Netzwerk-Hop – weshalb Policies Action-Chunks ausgeben statt einzelner Schritte. Man kann nicht bei 50 Hz zu einem Inferenz-Server hin- und herpendeln, aber man kann 400 ms an Aktionen auf einmal anfordern und weiter ausführen, während der nächste Chunk unterwegs ist.
Jedes schwierige Problem in diesem Repository folgt aus diesem einen Hop:
Ein Chunk beschreibt eine Welt, die existierte, als die Beobachtung aufgenommen wurde. Wenn er ankommt, ist er bereits veraltet. Wie veraltet ist zu veraltet?
Die Regelschleife muss den Arm alle 20 ms befehligen, ob der Server nun geantwortet hat oder nicht. Was tut sie, wenn nichts Gültiges auszuführen ist?
Anfragen schlagen fehl, werden wiederholt und kommen in falscher Reihenfolge an. Was verhindert, dass eine ältere Antwort eine neuere überschreibt?
Ein Modell kann NaNs ausgeben; ein falsch versionierter Server kann Ziele für einen anderen Roboter senden. Was weigert sich, das an die Aktuatoren weiterzugeben?
Related MCP server: omni-kit-mcp
Ergebnisse
25 Seeds pro Bedingung, echtes HTTP, Fehler injiziert aus einem geseedeten RNG.
python experiments/latency_sweep.py --seeds 25 --ablations
Bedingung | Aufgaben-Erfolg | sicher beendet | Median-Zeit | gehalten | Wiederaufnahmen | p50-Latenz | Stale-Ablehnungen | Retries |
| 100% | 100% | 6,4 s | 0,0 s | 0 | 20 ms | 0 | 0 |
| 100% | 100% | 6,7 s | 0,0 s | 0 | 40 ms | 0 | 0 |
| 100% | 100% | 11,1 s | 0,0 s | 0 | 160 ms | 0 | 0 |
| 100% | 100% | 12,6 s | 0,36 s | 68 | 280 ms | 222 | 64 |
| 100% | 100% | 9,8 s | 0,26 s | 50 | 60 ms | 197 | 222 |
| 100% | 100% | 8,1 s | 0,0 s | 0 | 60 ms | 0 | 248 |
| 100% | 100% | 13,9 s | 6,2 s | 25 | 60 ms | 0 | 24 |
Aufgaben-Erfolg bedeutet, dass der Würfel auf dem Ziel liegt. Sicher beendet ist bewusst eine eigene Spalte: Ein Lauf kann die Aufgabe verfehlen und trotzdem korrekt sein, denn Anhalten ist manchmal die richtige Antwort. Beides zusammenzufassen würde den Unterschied zwischen das Netzwerk war schlecht und der Roboter hat etwas getan, was er nicht hätte tun sollen verbergen.
Das Muster über die Tabelle hinweg ist das Designziel: Wenn die Verbindung schlechter wird, wird der Roboter langsamer, nicht falsch. Eine überlastete 250-ms-Verbindung verdoppelt die Zykluszeit und lehnt 222 veraltete Chunks ab; sie lässt den Würfel nicht fallen und greift nicht dorthin, wo sie nicht hingreifen sollte.
Ablationen – jede Absicherung, entfernt
Eine Sicherheitsprüfung, die man noch nie hat fehlschlagen sehen, ist eine Sicherheitsprüfung, von der man nicht behaupten kann, dass sie funktioniert.
entfernt | Aufgaben-Erfolg | Median-Zeit | Anmerkung |
(nichts – Baseline | 100% | 12,6 s | |
Wiederaufnahme aus dem Halt ( | 0 % | 0,5 s | verriegelnder Stopp, nimmt nie wieder auf |
Staleness-Prüfung ( | 88 % | 23,7 s | führt Pläne für eine Welt aus, die sich bewegt hat |
Retries ( | 96 % | 17,5 s | |
Adaptiver Vorlauf ( | 100% | 12,4 s | aber 1,10 s gehalten statt 0,36 s |
Retries ( | 100% | 7,9 s | ohne sie schneller – siehe unten |
Was der Fehler-Sweep tatsächlich gefunden hat
Beides waren echte Defekte. Keiner war gegen einen gesunden Localhost-Server sichtbar; beide zeigten sich beim ersten Lauf des Sweeps.
1. Der Schutzstopp hatte keinen Weg zurück. Beim outage-Profil erkannte die Runtime
korrekt den toten Server, hielt die Position und verriegelte einen E-Stop – und
saß dann da, während der Server drei Sekunden später zurückkam. Korrekt und nutzlos.
Ein Roboter, der nach jedem Netzwerk-Aussetzer einen Menschen braucht, der hinübergeht und ihn wieder scharf schaltet, wird in Woche zwei ausgesteckt.
Die Korrektur teilt ein Konzept in zwei: einen Schutzhalt, der sich in dem Moment
selbst aufhebt, in dem ein gültiger Chunk ankommt, und einen verriegelten E-Stop acht Sekunden später,
falls er nie ankommt. outage ging von 0 % auf 100 %, und dieselbe Änderung korrigierte
congested. Die Abbildung oben ist genau diese Korrektur in Aktion.
2. Die Anforderungs-Vorlaufzeit war kürzer als die Latenz. Die Runtime forderte den nächsten Chunk an, wenn noch 120 ms an Aktionen übrig waren. Auf der überlasteten Verbindung betrug der Roundtrip 280 ms. Jede Anfrage wurde 140 ms zu spät gestellt, um nützlich zu sein, also verhungerte der Arm an fast jeder Chunk-Grenze. Kein noch so häufiges Retrying behebt eine Anfrage, die zu spät gesendet wurde – man muss früher fragen.
Die Runtime misst nun ihre eigene p95-Latenz und skaliert den Vorlauf darauf. Die gehaltene
Zeit bei congested sank von 1,10 s auf 0,36 s.
3. Eine Absicherung, die sich nicht auszahlt. Beim lossy-Profil machte das
Ausschalten von Retries die Sache schneller (7,9 s statt 9,8 s) ohne Verlust an
Erfolgsrate und eliminierte 197 Stale-Ablehnungen. Auf einer Verbindung mit niedriger Latenz
liefert das Chunking bereits die Redundanz: Wenn ein Retry ankommt, wäre eine frische
Anfrage nützlicher gewesen. Retries verdienen ihren Platz bei congested
(96 % → 100 %) und nicht bei lossy. Es steht in der Tabelle, weil man nur über die
Absicherungen zu berichten, die funktioniert haben, genau der Weg ist, auf dem man am Ende diejenigen ausliefert, die es nicht tun.
Wie es funktioniert
robot side │ policy side
│
┌──────────────────────────────┐ │ ┌────────────────────┐
│ runtime.py 50 Hz loop │ │ │ server.py │
│ 1 collect ── poll ─────────┼── HTTP ──┼──────▶│ POST /predict │
│ 2 request ── submit │ │ │ obs → 20 actions │
│ 3 act │◀─────────┼───────│ │
│ 4 check │ │ └────────────────────┘
└──┬────────┬────────┬─────────┘ │ stateless; knows
│ │ │ │ nothing about episodes
▼ ▼ ▼ │ or scheduling
client scheduler safety │
retries staleness NaN/limits/workspace │
backoff ordering rate limit │
breaker discards e-stop │Modul | eine Aufgabe |
jeder Typ, der über die Leitung geht, einmal definiert | |
Zeit, injizierbar – real oder virtuell | |
MuJoCo hinter sechs Methoden; hier gegen Hardware tauschen | |
steht für ein VLA: zustandslos, gechunkt, reaktiv | |
die Policy, hinter HTTP | |
submit/poll, Deadlines, Retries, Backoff, Circuit Breaker | |
welchen Chunks zu trauen ist, welche Aktionen auszuführen sind | |
nimmt an, dass die Policy falsch liegt | |
die 50-Hz-Schleife | |
MCAP-Logging | |
die Zelle als MCP-Tools |
Drei Entscheidungen, die erwähnenswert sind:
Die Schleife blockiert nie auf dem Netzwerk. Schritt 2 sendet ab, Schritt 1 pollt, nichts wartet. Eine Regelschleife, die ein langsamer Server anhalten kann, ist keine Regelschleife.
Staleness wird ab observed_at gemessen, nicht ab Ankunft. Ein Chunk, der
300 ms zur Rückkehr gebraucht hat, ist in dem Moment, in dem er landet, 300 ms veraltet.
Sicherheitsprüfungen laufen mit zwei verschiedenen Raten. Chunk-Validierung ist teuer (Vorwärtskinematik für jede Aktion) und läuft einmal pro Chunk an der Vertrauensgrenze. Ratenbegrenzung ist billig und läuft jeden Takt. Einen schlechten Plan im Ganzen abzulehnen ist besser, als ihn in etwas subtil Falsches zu klemmen.
Der Uhr-Trick
Virtuelle Zeit läuft ~100× schneller als reale Zeit, also vergehen 400 ms Roboterzeit in 4 ms Wanduhrzeit – schneller als ein HTTP-Roundtrip zu localhost. Ohne Sorgfalt wirkt jede Antwort verspätet und das Experiment misst die Testumgebung statt der Runtime.
Deshalb blockiert SimClock.settle() in realen Sekunden, ohne virtuelle
voranzutreiben. Die einzige Verzögerung, die die Runtime je beobachtet, ist die Verzögerung, die das
Fehlerprofil verlangt hat. Derselbe Client-Code, dieselben Retry-Pfade, dieselbe Staleness-Logik – unter
WallClock auf Hardware ist settle() ein No-op. Das ist es, was jede Zahl
oben bis auf das Bit reproduzierbar macht.
Steuerung durch einen Agenten (MCP)
python -m robot_runtime.mcp_serverZwölf Tools. Sechs schreibgeschützt (Zustand, Kameras, Fehlerprofile, Aufzeichnungen, Audit- Log), sechs, die den Roboter bewegen. Die Abschaltung ist serverseitiger Zustand, keine Aufforderung im Prompt:
run_pick_and_place → {"ok": false, "error": "cell is not armed",
"hint": "call arm_cell with a reason before commanding motion"}
arm_cell(" ") → {"ok": false, "error": "a reason is required"}
arm_cell("demo") → {"ok": true, "armed": true, "expires_in_s": 120.0}
emergency_stop() → {"ok": true, "estopped": true}
run_pick_and_place → {"ok": false, "error": "cell is e-stopped"}
clear_estop() → {"ok": false, "error": "confirmation required"}Bewegung ist abgeschaltet; Lesen nicht; der Stopp-Knopf nie. Ein Sicherheits- kontrollelement, das man erst erreichen muss, indem man sich authentifiziert, ist kein Sicherheitskontrollelement.
Scharfschalten braucht einen Grund und läuft ab, und der Grund wird protokolliert.
Fehler sind strukturierte Ergebnisse mit einem
hint, niemals Exceptions. Ein Agent, derhint: start the policy serverliest, kann das Problem beheben. Ein Stacktrace lässt ihn raten.Jeder Aufruf wird an ein Audit-Log angehängt, das über dieselbe Schnittstelle lesbar ist, sodass „was genau hat es aufgerufen?“ immer eine Antwort hat.
Beobachtbarkeit und Wiedergabe
Jede Episode zeichnet in MCAP auf – dem Container, in den ROS 2 loggt – auf vier
Topics: /observation, /action_chunk, /command, /event. Das Event-Topic
ist das, was zählt, denn es zeichnet Entscheidungen auf, nicht nur Daten:
3.28s request_failed: unreachable
3.52s breaker_rejected: circuit open, request not sent
3.80s protective_hold: no valid action for 0.50s
6.66s hold_released: resumed on chunk 136Neun Zeilen, nicht die 128, die die erste Version schrieb – wiederholte Ereignisse kollabieren. Der Breaker lehnt eine Anfrage bei jedem der 50 Ticks pro Sekunde ab, in denen er offen ist, und alle fünfzig aufzuschreiben sagt nichts, was das erste nicht schon gesagt hätte.
Das Abspielen einer Aufzeichnung führt die exakten Befehle in einen frisch geseedeten Simulator aus:
$ python experiments/replay.py recordings/outage-seed0.mcap
commands_replayed: 533
placement_error_m: 0.007850735794278705 # live run: 0.007850735794278705
time drift: 0.000 msBit-exakt. Das war es zunächst nicht: /command wurde auf sechs Dezimalstellen gerundet
geloggt, was einen Mikrometer Drift zwischen einem Lauf und seiner eigenen Wiedergabe brachte. Klein – und es
machte „reproduziert exakt“ falsch, was der ganze Sinn der Aufzeichnung ist.
So wird auch ein Feld-Fehler behoben: die MCAP-Datei vom Standort zurückschicken, abspielen, zusehen, wie der Arm auf dem eigenen Laptop wieder das Falsche tut.
Ausführen
python3 -m venv ~/.venvs/robotarm && ~/.venvs/robotarm/bin/pip install -r requirements.txtDas venv liegt bewusst auf dem internen Laufwerk – dieses Repo sitzt auf einem exFAT-
Volume, wo macOS AppleDouble-._-Dateien verstreut, die MuJoCos Plugin-Loader
per dlopen laden will und daran stirbt, und die git nicht durch einen Pack-Index
aufrechterhalten kann.
python experiments/latency_sweep.py --seeds 25 --ablations # the results table
mjpython demos/run_with_viewer.py --profile outage # watch it hold and recover
python experiments/replay.py recordings/outage-seed0.mcap # replay a recording
python -m robot_runtime.mcp_server # agent-facing tools
mjpython demos/pick_and_place.py # the original scripted demo
pytest -q # 38 tests, ~5 smjpython statt python für alles mit einem Viewer – auf macOS muss das Fenster
den Haupt-Thread besitzen.
Tests
38 Tests, etwa fünf Sekunden, keine Mocks des zu testenden Objekts. Die Client-Tests nutzen einen Fake-Transport, aber den echten Fehler-Injektor; die Runtime-Tests laufen über echtes HTTP zu einem echten Server in einem Thread.
Drei davon sind die obigen Defekte, als Regressionen behalten:
test_server_outage_holds_then_recovers,
test_adaptive_lead_reduces_time_spent_holding und
test_the_stage_machine_does_not_oscillate – eine frühere Policy wechselte
lift→carry→lift→carry, weil sie die Höhe vor der Position prüfte.
Was das hier nicht ist
Um es klar zu sagen: Übertriebene Behauptungen gegenüber Robotik-Ingenieuren scheitern im Interview, nicht beim Screening.
Nur Simulation. Keine Hardware, kein Sim-to-Real-Transfer, keine Kontaktmodell-Kalibrierung gegen einen echten Panda.
Die Policy ist handgeschrieben, nicht gelernt. Sie ist bewusst wie eine VLA geformt — zustandslos, in Chunks aufgeteilt, sprachkonditioniert, reaktiv — damit die Laufzeit so beansprucht wird, wie sie es durch ein echtes Modell würde. Aber hier ist nichts trainiert, und es wird keine Aussage über die Modellqualität gemacht.
Objektposen kommen aus dem Simulator, nicht aus der Wahrnehmung. Die Observation trägt ein Kamerabild und der Contract unterstützt das; die skriptbasierte Policy ignoriert die Pixel. Eine echte Bereitstellung braucht einen Perception-Stack, und diese Lücke ist die größte hier.
Ein Arm, ein einzelnes starres Objekt, eine Aufgabe.
Kein ROS. MCAP wird verwendet, weil es der richtige Container ist und das Ökosystem es liest, aber es gibt keine Nodes, keinen TF-Baum, keine Launch-Dateien.
In etwa einem Tag gebaut, als fokussierte Demonstration der Laufzeitschicht.
Wie es weitergeht
Grob nach Wert geordnet:
Perception in the Loop — Pose aus dem Kamerabild statt aus dem Simulator. Schließt die größte Lücke auf dieser Liste.
Die Policy trainieren. Demonstrationen mit dem skriptbasierten Controller erzeugen, ein Action-Chunking-Behavior-Cloning-Modell fitten, es über denselben
/predict-Contract ausliefern. Die Laufzeit sollte keine einzige geänderte Zeile benötigen — das ist die Behauptung, die dasPolicy-Interface aufstellt, und sie ist derzeit ungetestet.Zwei Arme, was das Chunk-Scheduling zu einem echten Koordinationsproblem macht statt zu einem Buchhaltungsproblem.
Ein echter Panda, wo
settle()zu einem No-op wird und jede Latenzzahl in dieser README wirklich neu gemessen wird.
Danksagungen
Panda-Modell aus MuJoCo Menagerie (Apache 2.0). Physik: MuJoCo. Logging: MCAP.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables natural language control of ElephantRobotics MyCobot series robotic arms (especially ultraArmP340) through MCP protocol, with simulation mode and safety features.36MIT
- AlicenseAqualityBmaintenanceEnables driving Omniverse Kit apps (Isaac Sim, Isaac Lab) over MCP, allowing agents to control simulations, run Python, and call namespace-scoped tools via a single bridge.11MIT
- AlicenseNot gradedqualityAmaintenanceMCP server for AI agents to drive Gazebo / gz-sim simulation, with offline mock mode for CI/demos.4MIT
- FlicenseNot gradedqualityCmaintenanceMCP server for controlling a simulated robot arm with vision-based pick-and-place, driven by LLM or manual control.1
Related MCP Connectors
Hosted MCP endpoint with realistic fake data for prototyping agents. 12 tools, no setup.
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to control Unreal E…
Control plane for autonomous software labor. Agents claim objectives over MCP with audit trail.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Alonbbar6/robot-runtime'
If you have feedback or need assistance with the MCP directory API, please join our Discord server