robot-runtime
Управляющая среда выполнения для удалённых политик робота
Симулированный Franka Panda выполняет операцию «взять и поставить», управляемый политикой, которая живёт за HTTP-границей — и продолжает работать, когда эта граница начинает вести себя некорректно.

Интересна не рука. Интересно всё, что между рукой и моделью: планирование пакетов действий, отбраковка устаревших данных, повторные попытки с экспоненциальной задержкой, автоматический выключатель, защитная остановка, которая сбрасывается сама, и поверхность MCP-инструментов, позволяющая агенту управлять ячейкой, не имея возможности ей навредить.
Работает целиком на ноутбуке. Без GPU, без установки ROS, без железа.
Почему сеть — это самое сложное
Политики манипуляций хотят GPU. Роботы хотят контур управления в реальном времени. Это редко одна и та же машина, поэтому на практике модель находится за сетевым переходом — именно поэтому политики выдают пакеты действий, а не отдельные шаги. Нельзя сделать round-trip к серверу инференса на 50 Гц, но можно запросить 400 мс действий за раз и продолжать выполнять, пока следующий пакет в полёте.
Каждая сложная проблема в этом репозитории вытекает из этого одного перехода:
Пакет описывает мир, каким он был в момент снятия наблюдения. К моменту получения он уже устарел. Насколько устаревшее — это слишком?
Контур управления должен отдавать команды руке каждые 20 мс, независимо от того, ответил ли сервер. Что он делает, когда нет ничего валидного для выполнения?
Запросы падают, повторяются и приходят не по порядку. Что мешает более старому ответу перезаписать более новый?
Модель может выдать NaN; сервер с неправильной версией может прислать целевые точки для другого робота. Что отказывается передавать это на приводы?
Related MCP server: omni-kit-mcp
Результаты
25 сидов на условие, реальный HTTP, сбои, внедряемые из сидированного ГПСЧ.
python experiments/latency_sweep.py --seeds 25 --ablations
условие | успех задачи | безопасное завершение | медианное время | удержание | восстановления | p50 задержка | отклонено устаревших | повторные попытки |
| 100% | 100% | 6.4 с | 0.0 с | 0 | 20 мс | 0 | 0 |
| 100% | 100% | 6.7 с | 0.0 с | 0 | 40 мс | 0 | 0 |
| 100% | 100% | 11.1 с | 0.0 с | 0 | 160 мс | 0 | 0 |
| 100% | 100% | 12.6 с | 0.36 с | 68 | 280 мс | 222 | 64 |
| 100% | 100% | 9.8 с | 0.26 с | 50 | 60 мс | 197 | 222 |
| 100% | 100% | 8.1 с | 0.0 с | 0 | 60 мс | 0 | 248 |
| 100% | 100% | 13.9 с | 6.2 с | 25 | 60 мс | 0 | 24 |
Успех задачи — это куб на цели. Безопасное завершение — отдельная колонка не случайно: запуск может провалить задачу и при этом быть корректным, потому что остановка иногда — правильный ответ. Сведение этих двух метрик в одну скрыло бы разницу между сеть была плохой и робот сделал то, чего не должен был.
Закономерность по всей таблице — это и есть цель дизайна: при деградации канала робот становится медленнее, но не ошибочнее. Перегруженный канал с задержкой 250 мс удваивает время цикла и отклоняет 222 устаревших пакета; он не роняет куб и не тянется туда, куда не следует.
Абляции — каждая мера защиты, удалённая
Проверка безопасности, которую вы ни разу не видели в сбое, — это проверка, о которой вы не можете утверждать, что она работает.
удалено | успех задачи | медианное время | примечание |
(ничего — базовое | 100% | 12.6 с | |
восстановление из удержания ( | 0% | 0.5 с | защёлкивающаяся остановка, не возобновляется |
проверка устаревания ( | 88% | 23.7 с | выполняет планы для мира, который сдвинулся |
повторные попытки ( | 96% | 17.5 с | |
адаптивное упреждение ( | 100% | 12.4 с | но 1.10 с удержания против 0.36 с |
повторные попытки ( | 100% | 7.9 с | быстрее без них — см. ниже |
Что на самом деле обнаружил прогон сбоев
Оба пункта были реальными дефектами. Ни один не был виден при работе со здоровым localhost-сервером; оба проявились при первом же прогоне.
1. У защитной остановки не было пути назад. На профиле outage среда выполнения корректно обнаружила мёртвый сервер, удержала позицию и защёлкнула аварийную остановку — а затем сидела, пока сервер не вернулся через три секунды. Корректно и бесполезно. Робот, которому нужен человек, чтобы подойти и перевзвести его после каждого сетевого сбоя, будет отключён на второй неделе.
Исправление разделяет одно понятие на два: защитное удержание, которое сбрасывается само в момент получения валидного пакета, и защёлкивающаяся аварийная остановка через восемь секунд, если пакет так и не пришёл. outage вырос с 0% до 100%, и то же изменение исправило congested. Рисунок вверху — это работающее исправление.
2. Время упреждения запроса было меньше задержки. Среда выполнения запрашивала следующий пакет, когда оставалось 120 мс действий. На перегруженном канале round-trip составлял 280 мс. Каждый запрос отправлялся на 140 мс позже, чем нужно, чтобы быть полезным, поэтому рука голодала почти на каждой границе пакета. Никакое количество повторных попыток не исправит запрос, отправленный слишком поздно — нужно спрашивать раньше.
Теперь среда выполнения измеряет собственную p95-задержку и масштабирует упреждение под неё. Время удержания на congested упало с 1.10 с до 0.36 с.
3. Мера защиты, которая себя не окупает. На профиле lossy отключение повторных попыток сделало всё быстрее (7.9 с против 9.8 с) без потери доли успеха и устранило 197 отклонений устаревших пакетов. На канале с низкой задержкой пакетирование уже обеспечивает избыточность: к моменту, когда приземляется повторная попытка, свежий запрос был бы полезнее. Повторные попытки оправдывают себя на congested (96% → 100%) и не оправдывают на lossy. Это в таблице потому, что сообщать только о сработавших мерах — это путь к тому, чтобы выпустить в продакшн те, что не работают.
Как это устроено
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 │модуль | одна задача |
каждый тип, пересекающий границу, определён один раз | |
время, инъектируемое — реальное или виртуальное | |
MuJoCo за шестью методами; здесь заменяется на железо | |
заменяет VLA: без состояния, пакетно, реактивно | |
политика за HTTP | |
submit/poll, дедлайны, повторные попытки, backoff, выключатель | |
каким пакетам доверять, какие действия выполнять | |
исходит из того, что политика ошибается | |
цикл на 50 Гц | |
запись в MCAP | |
ячейка как MCP-инструменты |
Три решения, которые стоит выделить:
Цикл никогда не блокируется на сети. Шаг 2 отправляет, шаг 1 опрашивает, ничего не ждёт. Контур управления, который медленный сервер может остановить, — это не контур управления.
Устаревание измеряется от observed_at, а не от момента прибытия. Пакет, который шёл 300 мс, устарел на 300 мс в момент приземления.
Проверки безопасности выполняются с двумя разными частотами. Валидация пакета дорогая (прямая кинематика на каждое действие) и выполняется один раз на пакет на границе доверия. Ограничение частоты дёшево и выполняется каждый тик. Отклонить плохой план целиком лучше, чем зажимать его до чего-то едва заметно неправильного.
Трюк с часами
Виртуальное время идёт в ~100 раз быстрее реального, поэтому 400 мс роботного времени проходят за 4 мс настенных часов — быстрее, чем HTTP round-trip к localhost. Без осторожности каждый ответ выглядит запоздавшим, и эксперимент измеряет стенд, а не среду выполнения.
Поэтому SimClock.settle() блокируется в реальных секундах, не продвигая виртуальные. Единственная задержка, которую когда-либо наблюдает среда выполнения, — это задержка, запрошенная профилем сбоя. Тот же клиентский код, те же пути повторных попыток, та же логика устаревания — под WallClock на железе settle() — это no-op. Именно это делает каждое число выше воспроизводимым до бита.
Управление из агента (MCP)
python -m robot_runtime.mcp_serverДвенадцать инструментов. Шесть read-only (состояние, камеры, профили сбоев, записи, журнал аудита), шесть двигают робота. Ограничение — это серверное состояние, а не просьба в промпте:
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"}Движение ограничено; чтение — нет; кнопка остановки — никогда. Контроль безопасности, до которого нужно аутентифицироваться, — это не контроль безопасности.
Взятие под управление требует причины и истекает, и причина логируется.
Ошибки — это структурированные результаты с
hint, никогда не исключения. Агент, читающийhint: start the policy server, может исправить проблему. Stack trace заставляет его гадать.Каждый вызов добавляется в журнал аудита, читаемый через тот же интерфейс, так что на вопрос «что именно он вызывал?» всегда есть ответ.
Наблюдаемость и воспроизведение
Каждый эпизод записывается в MCAP — контейнер, в который логирует ROS 2 — по четырём топикам: /observation, /action_chunk, /command, /event. Топик событий — самый важный, потому что он записывает решения, а не только данные:
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 136Девять строк, а не 128, которые писала первая версия — повторяющиеся события схлопываются. Выключатель отклоняет запрос на каждом из 50 тиков в секунду, пока он открыт, и запись всех пятидесяти не говорит ничего, чего не сказала бы первая.
Воспроизведение записи заново прогоняет те же команды в свежесидированный симулятор:
$ python experiments/replay.py recordings/outage-seed0.mcap
commands_replayed: 533
placement_error_m: 0.007850735794278705 # live run: 0.007850735794278705
time drift: 0.000 msБитово точно. Сначала это было не так: /command логировался с округлением до шести знаков, что вносило микрон дрейфа между запуском и его собственным воспроизведением. Мелочь — и она делала «воспроизводится точно» ложным, а это весь смысл записи.
Так же исправляется полевой сбой: пришлите MCAP с площадки, воспроизведите его, посмотрите, как рука снова делает неправильное на вашем ноутбуке.
Запуск
python3 -m venv ~/.venvs/robotarm && ~/.venvs/robotarm/bin/pip install -r requirements.txtВиртуальное окружение намеренно ставится на внутренний диск — этот репозиторий лежит на томе exFAT, где macOS раскидывает AppleDouble-файлы ._, которые загрузчик плагинов MuJoCo пытается dlopen и умирает, и через которые git не может поддерживать pack index.
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, а не python для всего, что с вьюером — на macOS окно должно владеть главным потоком.
Тесты
38 тестов, около пяти секунд, без моков тестируемого объекта. Клиентские тесты используют фейковый транспорт, но настоящий инжектор сбоев; тесты среды выполнения идут через реальный HTTP к реальному серверу в потоке.
Три из них — это описанные выше дефекты, сохранённые как регрессии:
test_server_outage_holds_then_recovers,
test_adaptive_lead_reduces_time_spent_holding и
test_the_stage_machine_does_not_oscillate — более ранняя политика переключалась lift→carry→lift→carry, потому что проверяла высоту до позиции.
Чем это не является
Говоря прямо, потому что преувеличение перед инженерами-робототехниками проваливает собеседование, а не скрининг.
Только симуляция. Никакого железа, никакого переноса sim-to-real, никакой калибровки модели контакта на реальном Panda.
Политика написана вручную, а не обучена. Она намеренно сформирована как VLA — без сохранения состояния, с чанкингом, управляемая языком, реактивная — чтобы среда выполнения использовалась так же, как её использовала бы настоящая модель. Но здесь ничего не обучается, и никаких утверждений о качестве модели не делается.
Позы объектов берутся из симулятора, а не из восприятия. Наблюдение содержит изображение с камеры, и контракт это поддерживает; скриптовая политика игнорирует пиксели. Для реального развёртывания нужен стек восприятия, и этот пробел — самый большой здесь.
Одна рука, один жёсткий объект, одна задача.
Не ROS. MCAP используется, потому что это правильный контейнер и экосистема его читает, но здесь нет узлов, нет дерева TF, нет launch-файлов.
Создано примерно за день как целенаправленная демонстрация слоя выполнения.
Что дальше
В порядке убывания ценности:
Восприятие в цикле — поза из изображения с камеры вместо симулятора. Закрывает самый большой пробел в этом списке.
Обучить политику. Сгенерировать демонстрации с помощью скриптового контроллера, обучить модель поведенческого клонирования с чанкингом действий, обслуживать её через тот же контракт
/predict. Среда выполнения не должна измениться ни на строку — это утверждение, которое делает интерфейсPolicy, и оно пока не проверено.Две руки, что превращает планирование чанков в настоящую проблему координации, а не учётную.
Настоящий Panda, где
settle()становится no-op, и каждое значение задержки в этом README измеряется заново по-настоящему.
Благодарности
Модель Panda из MuJoCo Menagerie (Apache 2.0). Физика: MuJoCo. Логирование: 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