Panda-TAMP MCP Server

Panda-TAMP MCP Server

Exposes the Panda-TAMP planning stack as tools via the Model Context Protocol, enabling natural language-driven goal grounding and closed-loop task and motion planning for a simulated Franka Panda robot.

Category
访问服务器

README

Panda-TAMP — Closed-Loop Task & Motion Planning

PDDL symbolic planning, bridged to real geometry, executed on a simulated Franka Panda — with a planner that knows when it's wrong and replans.

core-tests License: MIT Python ROS 2 Gazebo

I built this project to answer a question that most robot-planning demos dodge: what happens when the symbolic plan meets geometry that disagrees with it? Classical task planners happily emit pick(cup); place(cup, tray) with no idea whether the cup is reachable, graspable, or whether the tray is 2.5 metres away. Most demos hide that gap. This project makes the gap the whole point: the planner and the geometry engine argue with each other in a closed loop until they agree on a plan that is both logically correct and physically executable — or until the system honestly reports that no such plan exists.

flowchart LR
    subgraph Perception
        CAM[RGB-D camera] --> YOLO[YOLOv8 detector] --> BP[depth back-projection] --> WS[/world_state/]
    end
    subgraph Planning["Closed TAMP loop"]
        WS --> PG[PDDL problem generator]
        PG --> FD[Fast Downward<br/>symbolic plan]
        FD --> GR[Geometric refiner<br/>IK · grasp sampling · swept-path collision]
        GR -- "infeasible: falsify bridge predicate" --> PG
        GR -- feasible --> PLAN[/task_plan/]
    end
    subgraph Execution
        PLAN --> EXE[MoveIt 2 / OMPL executor]
        EXE --> GZ[Gazebo Harmonic<br/>physics grasping]
        GZ -- "execution failure -> falsify + replan" --> PG
    end
    subgraph Language["Language layer (optional)"]
        NL["natural-language command"] --> GG[goal grounder<br/>LLM or grammar] --> PG
        MCP[MCP server] --> PG
    end

The core idea: bridge predicates

PDDL plans purely symbolically (pick / place / stack / unstack). Geometry — kinematics, grasps, collisions — lives in a separate engine (pykin FK + a damped-least-squares IK I wrote myself, plus trimesh/FCL collision checking). The two layers meet through four geometric bridge predicates:

reachable(o) · graspable(o) · placeable(o, s) · stackable(o, o')

The symbolic planner treats them as ordinary facts, but it never gets to decide them — the geometric layer does. The loop:

  1. Seed optimistically. Every bridge predicate starts true.
  2. Plan symbolically. Fast Downward produces a candidate action sequence.
  3. Refine geometrically. For each action: sample grasps, solve IK, sweep the gripper along the actual joint-space path, and check the arm links against the scene — not just the endpoint poses.
  4. On failure, falsify and loop. The offending predicate is set false in the PDDL initial state and the planner runs again — now knowing that fact.
  5. Execution failures feed the same loop. A grasp that slips or a placement that misses at runtime falsifies the same predicates and triggers a replan from the observed world state, not the assumed one.

The part I like most: the symbolic planner needs no geometric knowledge and the geometric engine needs no search. Each does the thing it is good at, and the falsification protocol is the entire interface between them.

<p align="center"> <img src="docs/images/plan_stack3.png" width="46%" alt="Refined TAMP plan for the 3-block stacking scene">   <img src="docs/images/plan_pnp.png" width="46%" alt="Refined TAMP plan for the cup/tray pick-and-place scene"> <br> <em>Refined plans rendered straight from the solver (<code>tamp-offline --viz</code>): numbered end-effector waypoints over the perceived scene — every pose has passed IK, grasp sampling, and swept-path collision checking.</em> </p>

Honesty is a design constraint

A recurring failure mode of planning demos is quietly reporting success. I built the opposite bias into every layer:

  • The replanning benchmark asserts the property that matters (the block can only ever land on the reachable tray) instead of a scripted replan count — and it includes a counterfactual test proving the solver reports failure when no feasible surface remains, rather than emitting a plan onto one it can't reach.
  • The Gazebo executor verifies arrival by TF (a plan is not an execution), gates every grasp on measured finger closure (a weld with open fingers is not a grasp), and runs a drop detector against simulator ground truth.
  • The reliability harness (scripts/reliability_pnp.sh) scores a run as a pass only if the plan completed and Gazebo's ground-truth poses show every object physically on the target surface. Log-parsing alone doesn't count.

What's inside

Layer What I built
Symbolic A tabletop PDDL domain (canonical .pddl + a runtime unified-planning model kept in lockstep), a problem generator that derives on/stacked-on/clear from perceived poses via support heuristics, and a Fast Downward wrapper.
Geometric Grasp sampling (top/side approaches), my own DLS IK with joint-limit clamping, random restarts, and a retry ladder for near-workspace-envelope targets; swept-path gripper collision and a per-link arm check; a shadow scene that simulates plan effects forward so later actions refine against the world the earlier actions will have created.
The loop TampSolver: plan ⇄ refine ⇄ falsify ⇄ replan, with execution-time falsification (pre_falsified) closing the loop across plan → execute → observe → replan.
Execution ROS 2 Jazzy + MoveIt 2 (MoveItPy/OMPL): action-interface trajectory execution with TF-verified arrival, a populated planning scene (surfaces, perceived objects, attached payloads), allowed-collision handling for grasps, and physics-based grasping in Gazebo Harmonic (DetachableJoint + finger-closure gating + contact-band verification).
Perception YOLOv8 (COCO) on a simulated RGB-D camera with depth back-projection to metric object poses (~4 cm), publishing the same /world_state the mock path uses — the planner cannot tell them apart.
Language A neuro-symbolic goal grounder: an LLM (Anthropic API / NVIDIA NIM / local Ollama) or a deterministic grammar proposes a goal, never actions; a validating DSL grounds it against perceived objects and the planner verifies feasibility. Fails closed on questions, negations, and hallucinated ids. Plus an MCP server exposing the whole planning stack as tools.

<p align="center"> <img src="docs/images/yolo_detections.png" width="46%" alt="YOLOv8 detections on the simulated camera">   <img src="docs/images/camera_frame.png" width="46%" alt="Simulated RGB-D camera frame"> <br> <em>The perception front-end: YOLOv8 detections on the simulated overhead camera, back-projected through depth into metric object poses.</em> </p>

Quickstart

The core is deliberately ROS-free — the full plan⇄refine⇄replan loop runs in plain Python in seconds:

python3 -m virtualenv -p python3.12 .venv && . .venv/bin/activate
pip install -e ".[dev]"

tamp-offline --scene stack3     # symbolic + geometric plan for a 3-block stack
tamp-offline --scene replan     # watch the loop veto an unreachable tray
tamp-command "put the cups on the tray" --scene pnp   # language -> goal -> plan
python -m pytest tests/unit -q  # 70+ tests, no ROS required

Example output (replan scene — the goal offers two trays, one physically unreachable):

=== TAMP [replan] ===
success=True  replans=0  reason=ok
  1. PICK(block, ...)                -> EEF [0.45  0.    0.435]
  2. PLACE(block, s=tray_near, ...)  -> EEF [0.55  0.2   0.453]
[replan] counterfactual (execution later falsifies tray_near too):
[replan]   success=False ... falsified=[('placeable','block','tray_near'), ('placeable','block','tray_far')]
[replan]   -> honest failure: no feasible tray remains.

Full stack (ROS 2 Jazzy + MoveIt 2 + Gazebo Harmonic)

cd ros2_ws && colcon build --symlink-install && cd ..
bash scripts/run_ros_full.sh stack3      # plan + MoveIt execution
bash scripts/run_ros_full.sh replan      # closed-loop recovery over ROS topics
bash run.sh pnp                          # Gazebo physics pick-and-place (GUI)
bash scripts/reliability_pnp.sh 6        # ground-truth-verified reliability batch

The ROS layer runs two Python interpreters (the planner node needs the pinned geometry stack; MoveItPy needs the system ROS python) — scripts/ros_env.sh and the launch files handle the split. Setup details and the non-obvious MoveItPy / Gazebo integration notes are in docs/RUNNING.md and docs/ros_integration.md.

Language-grounded goals & the MCP server

The part I find most fun: you can drive the planner conversationally, and it still can't be talked into doing something infeasible.

$ tamp-command --scene pnp "put all the cups on the tray"
  goal [rule]: {'type': 'sort_by_class', 'assignment': {'cup_1': 'tray', 'cup_2': 'tray'}}
  ✓ plan (4 steps, 0 replan):
     1. PICK cup_1
     2. PLACE cup_1
     3. PICK cup_2
     4. PLACE cup_2

$ tamp-command --scene pnp "do not put cup_1 on the tray"
  ✗ refused: the command is a prohibition, not a goal

The grounder chain is LLM-optional: Anthropic API → OpenAI-compatible (NVIDIA NIM) → local Ollama → a deterministic grammar that always works offline. Every backend's output — LLM or not — passes through the same validating DSL and the same planner. The model proposes; the planner verifies. There is also an MCP server exposing get_world_state / plan_goal / plan_command as tools, so any MCP-capable client or agent can read the robot's world and submit goals under exactly the same verification contract.

Details: docs/nl_grounding.md.

Repository layout

tamp/                 ROS-free core
├── pddl/             domain model + problem generator (bridge-predicate seeding)
├── planning/         Fast Downward wrapper
├── geometry/         robot model (DLS IK), grasp sampling, collision, refiner
├── reasoning/        pose -> symbolic facts (on / stacked-on / clear)
├── execution/        execution-failure -> falsified-predicate mapping
├── nl/               goal DSL, grammar + LLM grounders
└── mcp_server.py     the planning stack as MCP tools
pddl/domains/         canonical tabletop.pddl
benchmarks/           scene fixtures (stack3, replan, pnp) — also test fixtures
goals/                example goal specs (YAML)
ros2_ws/src/          ROS 2 overlay: planner node, MoveIt executor, grasp manager,
                      YOLOv8 + mock perception, launches, worlds, custom msgs
scripts/              runners: offline demo, full ROS bringup, reliability harness
tests/                unit + integration (the core runs anywhere Python runs)
docs/                 architecture, running guide, ROS notes, language layer

Testing

python -m pytest tests/unit tests/integration -q

70+ tests cover the domain encoding, the reasoner, the refiner (including swept-path and per-link collision regressions), the closed loop (recovery and honest failure), the NL grammar's safety refusals, DSL validation, and the MCP tool surface. CI runs the ROS-free suite on every push — deliberately only that: I don't fake Gazebo coverage in a container that can't run it; the physics-dependent layer is exercised by the ground-truth reliability harness on a real machine instead.

Engineering notes & honest limitations

Things I learned the hard way, and the edges I have not sanded off:

  • Simulated grasping is its own research problem. The gripper uses Gazebo's DetachableJoint with finger-closure gating and contact-band verification — a documented compromise, not force-closure physics. Under Gazebo Harmonic's bullet-featherstone backend the fixed-joint constraint is violable (the upstream fix lands in a later Gazebo generation), which bounds multi-object pick-and-place reliability; the reliability harness exists precisely to measure that honestly rather than cherry-pick green runs.
  • Physics backends disagree. DART silently drops URDF mimic joints; bullet enforces articulated dynamics faithfully enough to expose bad inertials and contact-margin effects that DART masked. The gripper stack (independent finger joints, effort limits, collision geometry, joint limits) is tuned against measurements, and the measurement scripts ship in scripts/.
  • The refiner's arm model is deliberately coarse — a swept gripper proxy plus per-link spheres. It catches gross pass-throughs cheaply; MoveIt's full-mesh planning scene remains authoritative at execution time.
  • Simulation only. No physical robot yet: the execution layer speaks standard MoveIt 2 / ros2_control interfaces, so a real Panda is the natural next step, but I make no hardware claims until it runs on one.

Roadmap

  • Force-aware grasping (effort-interface gripper control) to retire the detachable-joint compromise entirely
  • The upstream fixed-constraint enforcement once it ships in binaries (or a local backport) for higher multi-object reliability
  • Real-hardware bring-up on a physical Panda
  • Richer domains: obstructions, regrasping, multi-step manipulation puzzles

License

MIT — see LICENSE.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选