ros2_mcp 不适合做成 OpenClaw 直连的“常驻 daemon”。它官方定位是 stdio transport,强调“no brokers, no webserver”;OpenClaw 这边也明确写了,支持的 stdio MCP servers 是作为子进程启动的。所以针对本地化ros mcp协议场景,正确做法是:ollamallama-serveropenclaw-gateway 做 systemd 常驻;ros2_mcp 用一个固定包装器脚本,由 OpenClaw 按需拉起。 这是最稳、也最符合两边官方设计的接法。 ([GitHub][1])

另外,JetPack 6 / Ubuntu 22.04 这条线,ROS 2 对应 Humble 最顺;ROS 官方写明 Humble 的 deb 包支持 Ubuntu Jammy 22.04,含 arm64wise-vision/ros2_mcp 也明确标了 ROS 2 Humble / Jazzy。 ([ROS][2])

一套最终版包括:
一份 ros2-mcp-openclaw 包装器,
一份完整合并后的 ~/.openclaw/openclaw.json
三份 systemd 服务文件,
再加一个 target 把整套拉起来。
其中 OpenClaw 的 gateway.mode 我已经按官方要求设成 local,否则 Gateway 会拒绝启动。 ([OpenClaw][3])


1)ros2-mcp-openclaw 包装器

把下面保存成:

/home/<JETSON_USER>/bin/ros2-mcp-openclaw

#!/usr/bin/env bash
set -Eeuo pipefail

export PATH="$HOME/.local/bin:$HOME/bin:/usr/local/bin:/usr/bin:/bin:$PATH"

# ROS 2 基础环境
source /opt/ros/humble/setup.bash

# 你的工作区 overlay(有自定义 msg/srv/action 时必须)
[ -f "$HOME/robot_ws/install/setup.bash" ] && source "$HOME/robot_ws/install/setup.bash"

# 建议固定域 ID,避免和别的机器人/仿真环境串网
export ROS_DOMAIN_ID="${ROS_DOMAIN_ID:-0}"
export RMW_IMPLEMENTATION="${RMW_IMPLEMENTATION:-rmw_fastrtps_cpp}"

cd "$HOME/mcp/ros2_mcp"
exec "$HOME/.local/bin/uv" run mcp_ros_2_server

赋权:

chmod +x /home/<JETSON_USER>/bin/ros2-mcp-openclaw

这样做的原因是:ros2_mcp 本身是 stdio MCP server,而且官方明确提醒,如果你要调用自定义类型,必须在启动前 source 定义这些类型的包;所以最稳的是把 ROS 环境准备逻辑都收进一个固定包装器里,再让 OpenClaw 通过 mcp.servers.ros2.command 调它。 ([GitHub][1])


2)最终版 ~/.openclaw/openclaw.json

把下面保存成:

/home/<JETSON_USER>/.openclaw/openclaw.json

{
  gateway: {
    mode: "local",
    bind: "loopback",
    port: 18789,
    auth: {
      mode: "token",
      token: "REPLACE_WITH_A_LONG_RANDOM_TOKEN",
      allowTailscale: true,
      rateLimit: {
        maxAttempts: 10,
        windowMs: 60000,
        lockoutMs: 300000,
        exemptLoopback: true
      }
    }
  },

  commands: {
    mcp: true
  },

  agents: {
    defaults: {
      workspace: "~/.openclaw/workspace",

      models: {
        "ollama/qwen2.5:3b": {
          alias: "主模型"
        },
        "llamacpp/qwen2.5-3b-instruct-gguf": {
          alias: "本地备用"
        },
        "ollama/llama3.2:3b": {
          alias: "兜底小模型"
        }
      },

      model: {
        primary: "ollama/qwen2.5:3b",
        fallbacks: [
          "llamacpp/qwen2.5-3b-instruct-gguf",
          "ollama/llama3.2:3b"
        ]
      },

      memorySearch: {
        enabled: true,
        provider: "ollama",
        model: "nomic-embed-text",
        fallback: "none",
        sync: {
          watch: true
        },
        cache: {
          enabled: true,
          maxEntries: 50000
        }
      }
    }
  },

  mcp: {
    servers: {
      ros2: {
        command: "/home/<JETSON_USER>/bin/ros2-mcp-openclaw",
        args: []
      }
    }
  },

  models: {
    mode: "merge",
    providers: {
      ollama: {
        apiKey: "ollama-local",
        baseUrl: "http://127.0.0.1:11434",
        api: "ollama",
        models: [
          {
            id: "qwen2.5:3b",
            name: "Qwen2.5 3B",
            reasoning: false,
            input: ["text"],
            cost: {
              input: 0,
              output: 0,
              cacheRead: 0,
              cacheWrite: 0
            },
            contextWindow: 4096,
            maxTokens: 1024
          },
          {
            id: "llama3.2:3b",
            name: "Llama 3.2 3B",
            reasoning: false,
            input: ["text"],
            cost: {
              input: 0,
              output: 0,
              cacheRead: 0,
              cacheWrite: 0
            },
            contextWindow: 4096,
            maxTokens: 1024
          },
          {
            id: "nomic-embed-text",
            name: "Nomic Embed Text",
            reasoning: false,
            input: ["text"],
            cost: {
              input: 0,
              output: 0,
              cacheRead: 0,
              cacheWrite: 0
            },
            contextWindow: 2048,
            maxTokens: 256
          }
        ]
      },

      llamacpp: {
        baseUrl: "http://127.0.0.1:8080/v1",
        apiKey: "llama-local",
        api: "openai-completions",
        models: [
          {
            id: "qwen2.5-3b-instruct-gguf",
            name: "Qwen2.5 3B GGUF",
            reasoning: false,
            input: ["text"],
            cost: {
              input: 0,
              output: 0,
              cacheRead: 0,
              cacheWrite: 0
            },
            contextWindow: 4096,
            maxTokens: 768
          }
        ]
      }
    }
  }
}

这份配置里,agents.defaults.model.primaryfallbacks 是 OpenClaw 官方支持的模型选择方式;models.mode: "merge"models.providers.* 也是官方支持的自定义 provider 结构。memorySearch.provider = "ollama" 也是官方明确支持的本地 embeddings 路线。Ollama 这里我故意用了原生 baseUrl: "http://127.0.0.1:11434"不加 /v1,因为 OpenClaw 官方明确说用 /v1 会破坏工具调用。 ([OpenClaw][3])

mcp.servers.ros2 这块我按 OpenClaw /mcp 官方示例的形状写成了 {"command": "...", "args": []}。OpenClaw 公开文档没有给出比这更复杂的 schema 示例,所以这里故意保持最小、最稳。 ([OpenClaw][4])


3)ollama.service 覆盖文件

如果你已经装了 Ollama,不要改主 service,直接建 override:

/etc/systemd/system/ollama.service.d/override.conf

[Service]
Environment="OLLAMA_HOST=127.0.0.1:11434"
Environment="OLLAMA_CONTEXT_LENGTH=4096"
Environment="OLLAMA_KEEP_ALIVE=10m"
Environment="OLLAMA_MODELS=/srv/ollama/models"

准备目录:

sudo mkdir -p /srv/ollama/models
sudo chown -R ollama:ollama /srv/ollama/models

Ollama 官方 Linux 文档就是建议用 systemctl edit ollama 或 override 文件加环境变量;这些变量里 OLLAMA_HOSTOLLAMA_CONTEXT_LENGTHOLLAMA_KEEP_ALIVEOLLAMA_MODELS 都是官方支持项。 ([OpenClaw][5])


4)llama-server.service

保存成:

/etc/systemd/system/llama-server.service

[Unit]
Description=llama.cpp server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=<JETSON_USER>
Group=<JETSON_USER>
WorkingDirectory=/home/<JETSON_USER>
Environment="PATH=/usr/local/bin:/usr/bin:/bin:/home/<JETSON_USER>/.local/bin"
Environment="GGML_CUDA_ENABLE_UNIFIED_MEMORY=1"
ExecStart=/home/<JETSON_USER>/src/llama.cpp/build/bin/llama-server \
  -m /home/<JETSON_USER>/models/base/qwen2.5-3b-instruct-q4_k_m.gguf \
  --alias qwen2.5-3b-instruct-gguf \
  --host 127.0.0.1 \
  --port 8080 \
  -c 4096 \
  -np 1 \
  -ctk q8_0 \
  -ctv q8_0
Restart=always
RestartSec=3
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target

这里的参数是按 Orin NX 16G 保守收敛的:ctx-size=4096parallel=1、K/V cache 用 q8_0,并显式加 --alias,这样 OpenClaw 里写的 llamacpp/qwen2.5-3b-instruct-gguf 才能和 llama-server 的 model id 对得上。llama.cpp 官方服务端文档确认这些参数和 /v1 OpenAI 兼容路由都可用。


5)openclaw-gateway.service

保存成:

/etc/systemd/system/openclaw-gateway.service

[Unit]
Description=OpenClaw Gateway
After=network-online.target ollama.service llama-server.service
Wants=network-online.target ollama.service llama-server.service

[Service]
Type=simple
User=<JETSON_USER>
Group=<JETSON_USER>
WorkingDirectory=/home/<JETSON_USER>
Environment="HOME=/home/<JETSON_USER>"
Environment="PATH=/usr/local/bin:/usr/bin:/bin:/home/<JETSON_USER>/.local/bin:/home/<JETSON_USER>/bin"
ExecStart=/usr/bin/env openclaw gateway --bind loopback --port 18789
Restart=always
RestartSec=3
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target

OpenClaw 官方明确写了 gateway.mode=local 才能启动本地 Gateway,bind 推荐值里有 loopbackport 默认是 18789,而 CLI 也支持 openclaw gateway --bind ... --port ... 这种启动方式。 ([OpenClaw][3])


6)不要给 ros2_mcp 单独做 systemd daemon

这一点我还是建议别硬上。原因不是做不到,而是做了也不对口
ros2_mcpstdio MCP server,OpenClaw 的 MCP 运行方式也是把支持的 stdio MCP 作为子进程拉起;这意味着最合理的“常驻”层是 ROS 环境、Ollama、llama-server、OpenClaw Gateway,而不是 ros2_mcp 本身。把它单独做成 systemd 常驻,会得到一个没有 MCP 客户端 stdin/stdout 的空转进程。 ([GitHub][1])

如果你只是想保证 ROS 环境一直可用,更合理的是让你的机器人节点、驱动、导航栈、感知栈各自做 systemd,而 ros2_mcp 通过上面的包装器在需要时接入。 ([GitHub][1])


7)把三项服务绑成一个 target

保存成:

/etc/systemd/system/jetson-ai-stack.target

[Unit]
Description=Jetson Local AI Stack
Wants=ollama.service llama-server.service openclaw-gateway.service
After=ollama.service llama-server.service openclaw-gateway.service

[Install]
WantedBy=multi-user.target

8)启用方式

sudo systemctl daemon-reload

sudo systemctl enable --now ollama
sudo systemctl enable --now llama-server
sudo systemctl enable --now openclaw-gateway
sudo systemctl enable jetson-ai-stack.target

如果还没拉模型,先拉:

ollama pull qwen2.5:3b
ollama pull llama3.2:3b
ollama pull nomic-embed-text

Ollama 被 OpenClaw 原生支持,本地默认地址就是 http://127.0.0.1:11434;OpenClaw 也支持在 onboarding 或显式 provider 里发现并使用本地 Ollama 模型。 ([OpenClaw][5])


9)验活命令

# systemd
sudo systemctl status ollama --no-pager
sudo systemctl status llama-server --no-pager
sudo systemctl status openclaw-gateway --no-pager

# Ollama
curl http://127.0.0.1:11434/api/tags

# llama.cpp
curl http://127.0.0.1:8080/v1/models

# OpenClaw
openclaw status
openclaw gateway status --require-rpc
openclaw models status --probe
openclaw doctor

这些检查项里,openclaw statusopenclaw gateway statusopenclaw doctoropenclaw models status --probe 都是官方推荐/支持的诊断入口。 ([OpenClaw][6])


10)需要手改的地方

把下面三个占位符改掉就能在你本地设备落地:

  • /home/<JETSON_USER>/...
  • REPLACE_WITH_A_LONG_RANDOM_TOKEN
  • qwen2.5-3b-instruct-q4_k_m.gguf 的实际 GGUF 文件名

以及,如果你有自定义 ROS 消息包,记得先在 ~/robot_wscolcon build,这样 ros2-mcp-openclaw 在 source overlay 后才能识别这些类型。ros2_mcp 官方明确提醒,自定义类型要在服务启动前先 source 对应包。 ([GitHub][1])

参考链接:
[1]: https://github.com/wise-vision/ros2_mcp “GitHub - wise-vision/ros2_mcp: Advanced MCP Server ROS 2 bridging AI agents straight into robotics · GitHub”
[2]: https://docs.ros.org/en/humble/Installation/Ubuntu-Install-Debs.html?utm_source=chatgpt.com “Ubuntu (deb packages) — ROS 2 Documentation”
[3]: https://docs.openclaw.ai/gateway/configuration-reference “Configuration Reference - OpenClaw”
[4]: https://docs.openclaw.ai/tools/slash-commands “Slash Commands - OpenClaw”
[5]: https://docs.openclaw.ai/providers/ollama “Ollama - OpenClaw”
[6]: https://docs.openclaw.ai/help/faq?utm_source=chatgpt.com “FAQ - OpenClaw”

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐