Isaac Sim robot workbench

io.github.Milokuciav0.1.2更新於 Oct 3, 2026

Drive a live Isaac Sim session: any robot, poses, gain tuning, range tests, Isaac Lab training.

概覽

AI 產生的概覽

讓助理驅動一個常駐的無頭 Isaac Sim 工作階段,對機器人進行擺位、調參、量測與錄影,並啟動 Isaac Lab 訓練。

功能
此伺服器在 Docker 守護程序中保持一個 Isaac Sim(Kit)工作階段常駐,因此每次工具呼叫只花一個影格的時間,不必重新啟動。工具涵蓋工作階段控制(sim_up、sim_down、sim_reload、sim_status)、檢查(關節狀態、統計、截圖)、驅動(正規化關節目標、具名姿態、步進、行程測試)、場景道具、無頭截圖與帶字幕 GIF 錄影、即時 PD 增益與耦合調參及參數掃描,以及 Isaac Lab 訓練控制(train_start、train_status、train_metrics、train_checkpoints、train_stop)。任何關節式機器人只需一個 USD 檔案和一份小型 JSON 設定即可描述。
適用情境
適合讓代理在無人看管或遠端 GPU 機器上調參、除錯、量測或訓練某一台特定機器人,包括無頭截圖與錄影。它不是通用的場景建置工具;若要在圖形介面中互動式建置場景並使用豐富的資產庫,內建 GUI 的 Isaac Sim 伺服器更合適。
執行需求
Linux、受支援的 NVIDIA GPU 和 NVIDIA Container Toolkit,含 Compose 外掛的 Docker,以及用於拉取 Isaac Lab 映像的 NGC 登入(docker login nvcr.io)。主機上需要 Python 3.10 或更新版本以執行 MCP 伺服器。守護程序從倉庫複製目錄執行,因此 PyPI 安裝時須以 ISAAC_MCP_HOME 指向該目錄;ISAAC_MCP_ROBOT 可選地設定預設機器人設定。首次 sim_up 需要數分鐘。
安裝前請注意
此伺服器會啟動與停止 Docker 容器,並可啟動分離的訓練任務,這些任務在用戶端中斷後仍會繼續執行,需以 train_stop 結束。它會把錄影寫入 .cache/recordings,把日誌寫入設定的日誌目錄;sim_up 結束時會刪除容器,因此除非在前景執行守護程序,啟動錯誤會遺失。NGC 憑證透過 docker login 使用,而非透過本伺服器的環境變數。守護程序執行期間會佔用 GPU 與容器資源。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Isaac Sim robot workbench,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

dex-isaac-mcp

[An agent driving the Allegro hand through dex-isaac-mcp]

Recorded headless with the server's own tools (sim_frame_robot, sim_set_backdrop, sim_record_start), driving the Allegro hand example. Each caption is the request and the tool call it became.

An MCP server that lets an AI agent (Claude Code, or any MCP client) drive a live, persistent Isaac Sim session and launch Isaac Lab training runs.

Kit takes tens of seconds to boot. If every experiment is a fresh launch, most of your time goes to waiting. Here Kit starts once inside a daemon and stays up. Each tool call lands in that running session, so changing a gain, stepping physics or taking a screenshot costs a frame, not a relaunch.

 MCP client ──stdio──▶ dex_isaac_mcp (host, plain python)                          │  newline-delimited JSON over a Unix socket                          ▼                       scripts/simd.py (Isaac Lab container, Kit stays up)                          └─ one articulation, described by a robot JSON
  • Any articulated robot. Point it at a USD and a small JSON config. Franka and Allegro examples are included.
  • Normalized joint control. Targets are 0..1, where 0 is a joint's lower limit and 1 its upper limit, so agents don't need to know radians or meters.
  • Named poses per robot (home, fist, …), which can be blended part-way.
  • Headless capture and GIF recording from an auto-framed camera, with captions. The clip above was made this way.
  • Props: spawn a table, a ball or a USD into the running scene and read back where they settle.
  • Measurements: joint state, per-joint travel and tracking error, a range test that finds blocked joints, and parameter sweeps inside one session.
  • Training control: each run is a detached docker compose run. You can poll its status, TensorBoard scalars, checkpoints and logs.

How it differs from other Isaac Sim MCP servers

NVIDIA's official Isaac Sim MCP is a documentation search for coding assistants, and works well alongside this one. Servers like isaacsim-mcp-server and omni-mcp/isaac-sim-mcp run inside the Isaac Sim GUI and cover scene building broadly. This one runs headless, as a daemon in Docker, and focuses on tuning, measuring and training one robot.

NVIDIA Isaac Sim MCPIn-GUI servers (isaacsim-mcp-server, omni-mcp)dex-isaac-mcp
What it isDocs and code searchKit extension inside a running Isaac SimExternal daemon; Kit stays up in a container
Controls the simulationNoYesYes
Headless / remote GPU boxn/aNeeds the GUIYes: headless capture, Docker, Unix socket
Scene building (lights, materials, sensors, asset library)n/aBroadMinimal: primitive and USD props
Robot setupn/aBuilt-in robot libraryAny USD plus a small JSON config (joints, poses, couplings, gains)
Measurementn/aJoint and prim stateRange tests that find blocked joints, tracking error, sweeps that restore the original value
Live tuningn/aNot documentedPD gains, effort, software mimic couplings
Isaac Lab trainingn/aNot documentedLaunch, poll, TensorBoard metrics, checkpoints, stop
Recordingn/aCamera capturesCaptioned GIFs from an auto-framed camera, headless
PlatformAnyWherever Isaac Sim runsLinux, NVIDIA GPU, Docker, NGC

Pick an in-GUI server to build a scene by talking to it, interactively, with a wide robot and asset library.

Pick this one to have an agent tune, debug and train a specific robot (your own, from a USD), unattended or on a remote box, with numbers it can act on instead of only screenshots.

Requirements

  • Linux with an NVIDIA GPU that Isaac Sim supports, plus the NVIDIA Container Toolkit
  • Docker with the Compose plugin
  • An NGC login to pull the Isaac Lab image: docker login nvcr.io (user $oauthtoken, password: your NGC API key)
  • Python ≥ 3.10 on the host, for the MCP server only

Quick start

bash
git clone <this repo> dex-isaac-mcp && cd dex-isaac-mcp
# 1. Build the image (Isaac Lab 2.3.2 base, pinned by digest)cd docker && docker compose build && cd ..
# 2. Install the host-side server, editable so it finds this clonepip install -e .            # add [metrics] for train_metrics: pip install -e '.[metrics]'
# 3. Register it with Claude Codeclaude mcp add isaac -- python -m dex_isaac_mcp
# 4. Allow the container to open windows (once per login; needed for screenshots)xhost +local:docker

Then ask the agent something like "start the sim with the Franka, move it to ready and show me a screenshot." It will call sim_up, sim_set_pose, sim_step and sim_screenshot.

From PyPI instead: pip install dex-isaac-mcp. The daemon still runs from a clone (it needs docker/ and scripts/simd.py), so point the server at it:

bash
claude mcp add isaac -e ISAAC_MCP_HOME=/abs/path/dex-isaac-mcp -- dex-isaac-mcp

Other MCP clients can launch python -m dex_isaac_mcp (or the dex-isaac-mcp script) over stdio.

The first sim_up takes several minutes: Kit builds its shader cache and downloads Nucleus assets. Later starts are much faster.

Running the daemon by hand

sim_up deletes its container (--rm) when the daemon exits, so a crash on startup takes its traceback with it. To see the error, run the daemon in the foreground:

bash
cd dockerdocker compose run --rm simd scripts/simd.py --gui --robot examples/robots/franka.jsondocker compose run --rm simd scripts/simd.py --headless --usd /path/in/container/robot.usddocker compose run --rm simd scripts/simd.py --sliders --robot examples/robots/allegro_hand.json

--sliders opens an omni.ui panel with one slider per driven joint. The socket stays live alongside it.

Tools

Session

ToolWhat it does
sim_statusWhether the daemon is up, plus its robot, driven joints, poses, couplings and gains
sim_upStart the daemon container (robot, usd, gui, ground, extra_args) and wait until it answers. Does nothing if it is already up
sim_downStop the daemon
sim_reloadRestart with a different spawn property: robot, USD, pos_iters, self-collision

Inspect

ToolWhat it does
sim_inspect_jointsA USD's articulation DOFs, its loop-closure joints (excluded from the articulation) and its articulation roots. Reads the file, so it shows edits saved from the GUI
sim_get_joint_statePositions and velocities, plus driven joints' normalized positions and limits
sim_get_statsPer-joint travel and mean tracking error since the last reset
sim_screenshotViewport capture, returned as an image. Needs gui=True
sim_set_cameraPoint the viewport camera

Drive

ToolWhat it does
sim_set_targetsNormalized targets, as a full vector or {joint: value}
sim_list_poses / sim_set_poseNamed poses from the robot config. amount blends toward a pose, starting from the lower limits or from the current targets
sim_stepAdvance N physics steps (default dt 1/120 s)
sim_playRun continuously, or pause
sim_waveSweep every driven joint through its range and return travel stats
sim_range_testDrive every joint from its lower limit toward a target and report the fraction of travel reached. Below 0.9 counts as blocked (self-collision, a binding linkage, too little effort)

Scene

ToolWhat it does
sim_spawn_objectAdd a prop to the live scene: cuboid, sphere, cylinder, capsule, cone or a USD file, with collision. static for a fixed table or wall, kinematic for a body contact cannot move
sim_list_objectsEvery prop's current world pose
sim_remove_objectDelete a prop

Capture and record

These work headless, with no GUI or viewport. They use a dedicated camera that is independent of the GUI view.

ToolWhat it does
sim_frame_robotAim the capture camera so the whole robot fills the frame, from a given direction. raise_frac leaves room for captions
sim_set_capture_cameraPlace the capture camera by hand
sim_set_backdropA plain colored panel behind the robot. Use it with ground=False for clean footage
sim_captureOne frame, returned as an image
sim_record_start / sim_record_caption / sim_record_stopRecord every Nth physics step, with a caption drawn on each frame, to an animated GIF under .cache/recordings/. Anything that steps the sim is recorded

Tune

ToolWhat it does
sim_set_paramsLive stiffness / damping / effort on the driven joints
sim_set_couplingLive software-mimic ratios, keyed by follower joint
sim_sweepTry several values of one live parameter (stiffness, damping, effort, coupling:<follower>), running wave or range for each. Restores the original value afterwards. A diverging solver is recorded as a result rather than raised

Train

ToolWhat it does
train_startLaunch a headless training run in its own container and return at once. extra_args go to the script verbatim (e.g. Hydra overrides). device pins a GPU
train_listRunning and recent runs, and log directories holding checkpoints
train_statusContainer state, checkpoints and latest scalars
train_logsTail a running container's output
train_metricsList TensorBoard tags, or get a downsampled series for one tag
train_checkpointsCheckpoints with step and size
train_stopStop a run

Training runs do not depend on the daemon or on the MCP session. They keep going after the client disconnects.

Robot config

A robot is one JSON file. Only usd is required. Unknown keys are rejected, so a typo fails loudly instead of quietly falling back to a default.

json
{  "name": "franka",  "usd": "{ISAACLAB_NUCLEUS_DIR}/Robots/FrankaEmika/panda_instanceable.usd",  "fix_root_link": true,  "spawn_pos": [0, 0, 0],  "init_joint_pos": {"panda_joint4": -2.81, "panda_joint6": 3.04, ".*": 0.0},  "driven_joints": ["panda_joint[1-7]", "panda_finger_joint.*"],  "passive_joints": [],  "couplings": [{"leader": "joint_a", "follower": "joint_b", "ratio": 1.0}],  "actuator": {"stiffness": 400, "damping": 40, "effort": 87, "velocity": null},  "solver": {"pos_iters": 32, "vel_iters": 4, "self_collisions": true},  "camera": {"eye": [1.8, 1.8, 1.4], "target": [0, 0, 0.4]},  "poses": {"ready": {"panda_joint[1357]": 0.5, "panda_finger_joint.*": 1.0}}}
KeyMeaning
usdLocal path (relative paths resolve from the JSON's own directory), a URL, or a path using {ISAAC_NUCLEUS_DIR} / {ISAACLAB_NUCLEUS_DIR}
init_joint_posSpawn pose in the joints' own units (rad / m), keyed by regex. It must lie inside every joint's limits or spawning fails. The default is all zeros, which is out of range for e.g. Franka's joint 4
driven_jointsRegexes (full match) for joints that take commands. Default .*
passive_jointsJoints whose angle is owned by a constraint, such as a closed-chain linkage. They get a zero-stiffness drive, because a live PD drive fights the constraint and the mechanism jitters
couplingsSoftware mimic joints: follower target = ratio × leader target. They are applied as drive targets rather than PhysX mimic constraints, so a large ratio cannot blow up the solver
actuatorImplicit PD gains and effort/velocity limits for the driven joints. Live-tunable
solverSpawn properties. Changing them needs sim_reload
poses{name: {joint_regex: 0..1}}. Later patterns win, so {".*": 0, "thumb.*": 1} works. A pattern that matches no driven joint is an error

Using your own robot without forking

Keep the robot config and assets in your own repo, and add them to the container with a compose override that you list in COMPOSE_FILE. Set it in the MCP server's environment, using absolute paths:

yaml
# my-robot/mcp-compose.yamlservices:  simd:    volumes:      - /abs/path/my-robot:/workspace/my-robot
bash
claude mcp add isaac \  -e COMPOSE_FILE=/abs/path/dex-isaac-mcp/docker/docker-compose.yaml:/abs/path/my-robot/mcp-compose.yaml \  -e ISAAC_MCP_ROBOT=/workspace/my-robot/robot.json \  -- python -m dex_isaac_mcp

Training defaults

Out of the box, train_start runs Isaac Lab's stock skrl script inside the isaac-lab service. It tags the run name onto the log directory (logs/skrl/<experiment>/<timestamp>_ppo_torch_<run_name>/), which the other train_* tools use to find the run. To use your own launcher, set these in the environment the MCP server starts in:

VariableDefault
ISAAC_MCP_HOMEthe clone this package was installed from (editable install); required for a PyPI install
ISAAC_MCP_COMPOSE_DIR<repo>/docker
ISAAC_MCP_TRAIN_SERVICEisaac-lab
ISAAC_MCP_TRAIN_WORKDIR/workspace/isaaclab
ISAAC_MCP_TRAIN_SCRIPTscripts/reinforcement_learning/skrl/train.py
ISAAC_MCP_LOGS_DIR<repo>/logs (mounted at /workspace/isaaclab/logs)
ISAAC_MCP_RUN_NAME_ARGagent.agent.experiment.experiment_name={run_name} (empty = don't pass one)
ISAAC_MCP_TRAIN_ARGShydra.run.dir=/tmp/hydra hydra.output_subdir=null: appended to every run. The stock script otherwise writes Hydra's outputs/ into the root-owned /workspace/isaaclab and dies. Set it empty for a non-Hydra script
ISAAC_MCP_SIMD_SERVICEsimd
ISAAC_MCP_ROBOTrobot config sim_up loads when none is given (unset: Franka example)
ISAAC_MCP_SOCKET<repo>/.cache/simd.sock

The script must accept --task, --headless and, when given, --num_envs, --seed, --max_iterations and --checkpoint. Tasks from your own extension need to be importable inside the container, either installed into the image or mounted.

Design notes

These are the constraints the code is built around. Most were learned by breaking them.

  • Every Kit call happens on the main thread. Kit, PhysX and USD are not thread-safe. Socket threads only parse JSON and queue requests, and the main loop executes them between physics steps. Answering from a reader thread appears to work, then corrupts the stage under load.
  • Spawn properties are frozen. Replacing a spawned articulation needs SimulationContext.stop(), which blocks on a timeline event that only advances while the Kit loop pumps. A command runs on that loop, so the call never returns. omni.usd new_stage() has the same trap. So the USD, solver iterations and self-collision need a restart (sim_reload), and gains stay live.
  • A Unix socket, not TCP. The repo is bind-mounted and the container runs as the host uid, so the host sees the socket file directly, with no port mapping. Paths are capped at 107 bytes (AF_UNIX). If your checkout is deep, set ISAAC_MCP_SOCKET.
  • The host side imports no Isaac code. protocol.py, robot.py and training.py are stdlib-only. The MCP server adds only mcp. Nothing on the host needs isaaclab, torch or a GPU.
  • Cache directories are committed with .gitkeep. If Docker auto-creates a bind-mount source, it is root-owned, and Kit then dies with registry cache path is not set before any script runs.
  • The base image is pinned by digest. A re-pulled tag once shipped /isaac-sim as mode 750, and every non-root container lost its Python.
  • Recorded GIFs are stabilized. The renderer's denoiser shimmers: between two frames of a motionless scene, about 9% of background pixels change slightly, and a GIF re-encodes every one of them. Holding sub-threshold changes and using one shared palette took a 9-second clip from 15 MB to 1.2 MB.
  • The daemon always renders, even headless (enable_cameras). Without rendering, PhysX never registers a prop spawned at runtime. Prop poses are read from fabric, because the USD transform and the PhysX CPU query both stay at the spawn pose, and creating a PhysX tensor view mid-simulation crashes CUDA.
  • No floor for range tests (ground=False) on anything whose links can reach the ground. Otherwise the test measures the floor, not the robot.

Development

bash
python -m unittest discover tests   # host-side tests: no Isaac, no GPUruff check .

dex_isaac_mcp/protocol.Client is a handy debugging client:

python
from dex_isaac_mcp.protocol import Clientwith Client() as c:    print(c.call("status"))    c.call("set_pose", name="ready"); c.call("step", n=240)

Status

Tested against Isaac Lab 2.3.2 (Isaac Sim 5.x) and mcp 2.3, over the stdio protocol, with the GUI on:

  • Franka and Allegro examples, plus a custom closed-linkage hand through a compose override: sim_up, poses, screenshots, sim_range_test, sim_sweep (restores the original value), sim_down.

  • Headless daemon: gains, frozen-parameter rejection, wave.

  • Headless capture and recording, Allegro and Franka: auto-framing, backdrop, captions, GIF output (the clip at the top).

  • Props, GUI and headless: a sphere and a cylinder dropped onto a static table settle at exactly table height plus their radius and half-height.

  • Training, against Isaac Lab's stock skrl script: Isaac-Cartpole-v0 launched, polled, logged, checkpointed and read back through every train_* tool, plus a run stopped mid-training. skrl's write_interval: auto writes no TensorBoard scalars on a very short run (5 iterations), so train_metrics comes back empty there; 50 iterations gives 18 tags.

Issues and PRs are welcome.

License

MIT, see LICENSE.

來源:README.md,提交 00b4531

工具

0
工具後設資料尚未被收錄。

版本歷史

1
  1. v0.1.2最新Oct 3, 2026