Robot-side ROS 2 packages for the ROSClaw stack.
This repository contains the runtime half of ROSClaw: discovery, safety, optional Gemini perception, optional remote bridges, and the launch/config bundles that expose a robot to the OpenClaw plugin.
| Repo | Responsibility |
|---|---|
rosclaw |
Workspace bootstrap, Docker Compose, demos, top-level docs, integration tests |
rosclaw-ros2 |
Robot-side ROS 2 packages and launch files |
rosclaw-plugin |
OpenClaw plugin that consumes the manifest, scene, and safety surfaces exported by this repo |
If you want the full stack up quickly, start with the bootstrap repo:
rosclaw. If you are changing
robot-side behavior, start here.
| Package | Purpose |
|---|---|
rosclaw_msgs |
ROS interfaces shared by the rest of the stack |
rosclaw_discovery |
Publishes a capability manifest and serves on-demand manifest requests |
rosclaw_estop |
Handles robot-side emergency stop and publishes safety status |
rosclaw_perception |
Publishes structured scene descriptions and typed perception snapshots |
rosclaw_webrtc_bridge |
Robot-side relay and WebRTC bridge nodes for remote sessions |
rosclaw_bringup |
Launch files and platform config bundles |
Repository layout:
rosclaw_msgs/ Interface definitions (.msg / .srv)
rosclaw_discovery/ Discovery node and standalone launch file
rosclaw_estop/ Safety / emergency-stop node and launch file
rosclaw_perception/ Gemini perception node and package-specific docs
rosclaw_webrtc_bridge/ Relay and WebRTC transport bridge nodes
rosclaw_bringup/ Cross-package launch files and platform config bundles
tests/ Cross-package unit tests
Package-level READMEs exist where behavior is non-trivial or commonly used on its own.
- ROS 2 Humble or newer
- Python 3.10+
rosdepinitialized on the machine
Optional:
- Gemini perception:
pip install 'google-genai>=1.0.0' - Relay/WebRTC transports: install declared system dependencies through
rosdep
The simplest path is the workspace bootstrap repo:
git clone https://github.com/ROSClaw/rosclaw.git
cd rosclaw
./scripts/setup.shThat imports this repo under src/rosclaw-ros2 and sets up the companion
repos.
cd ~/ros2_ws/src
ln -s /path/to/rosclaw-ros2 rosclaw-ros2
cd ~/ros2_ws
source /opt/ros/$ROS_DISTRO/setup.bash
rosdep update
rosdep install --from-paths src --ignore-src -r -y
colcon build --packages-select rosclaw_msgs rosclaw_discovery rosclaw_estop rosclaw_bringup
source install/setup.bashBuild optional packages only if you use them:
colcon build --packages-select rosclaw_perception rosclaw_webrtc_bridgeThis launches discovery, estop, and rosbridge for a single robot:
ros2 launch rosclaw_bringup rosclaw.launch.py platform:=turtlebot3Equivalent platform choices:
ros2 launch rosclaw_bringup rosclaw.launch.py platform:=go2
ros2 launch rosclaw_bringup rosclaw.launch.py platform:=g1
ros2 launch rosclaw_bringup rosclaw.launch.py platform:=genericWhat you get by default:
rosbridge_websocketlistening onws://0.0.0.0:9090/rosclaw/manifestand/rosclaw/get_manifest/rosclaw/estop,/rosclaw/safety_status, and/rosclaw/set_safety_policy
If something is already listening on 127.0.0.1:9090, bringup fails by
default. Set reuse_existing_rosbridge:=true only when you intentionally want
to reuse that websocket server.
The companion plugin repo is
rosclaw-plugin. In the default
path, configure the plugin with transport mode rosbridge and point it at:
ws://<robot-ip>:9090
If you launch with robot_namespace:=/robot1, set the plugin's
robot.namespace to /robot1 as well.
Enable Gemini scene analysis:
export GEMINI_API_KEY=your-api-key
ros2 launch rosclaw_bringup rosclaw.launch.py platform:=turtlebot3 perception:=trueThis adds:
/rosclaw/scenefor JSON scene summaries/rosclaw/perceptionfor typed snapshots with image bytes and detections/rosclaw/perception_statusfor health reporting
Perception details and scene schema live in rosclaw_perception/README.md.
Relay transport:
ros2 launch rosclaw_bringup rosclaw.launch.py \
platform:=turtlebot3 \
relay:=true \
signaling_url:=ws://your-signaling-server.com \
robot_token:=your-auth-token \
robot_key:=your-secret-key \
robot_id:=robot_1WebRTC transport:
ros2 launch rosclaw_bringup rosclaw.launch.py \
platform:=turtlebot3 \
webrtc:=true \
signaling_url:=wss://your-signaling-server.com \
robot_token:=your-auth-token \
robot_key:=your-secret-key \
robot_id:=robot_1You can run rosbridge and relay/WebRTC together or disable rosbridge entirely:
ros2 launch rosclaw_bringup rosclaw.launch.py \
platform:=go2 \
rosbridge:=false \
webrtc:=trueAcross the full ROSClaw stack:
- The plugin repo exposes client transport modes
rosbridgeandlocal. - This repo launches robot-side
relayandwebrtcbridges for remote deployments. relayandwebrtcare coordinated withrosclaw-signaling; they are not plugin config values.
That distinction matters when reading the docs:
- If you are configuring the OpenClaw plugin, look for
transport.mode,rosbridge.url, andlocal.domainId. - If you are configuring the robot-side deployment, look for bringup launch
arguments such as
rosbridge,relay,webrtc,signaling_url, androbot_namespace.
ros2 launch rosclaw_bringup sim_turtlebot3.launch.pyOr directly:
ros2 launch rosclaw_bringup rosclaw.launch.py \
platform:=sim_turtlebot3 \
use_sim_time:=trueWith perception:
export GEMINI_API_KEY=your-api-key
ros2 launch rosclaw_bringup rosclaw.launch.py \
platform:=sim_turtlebot3 \
use_sim_time:=true \
perception:=trueSet robot_namespace to scope discovery, estop, perception, relay, and WebRTC
to one robot:
ros2 launch rosclaw_bringup rosclaw.launch.py \
platform:=turtlebot3 \
robot_namespace:=/robot1 \
perception:=true \
relay:=true \
robot_id:=robot_1With robot_namespace:=/robot1:
/rosclaw/manifestbecomes/robot1/rosclaw/manifest/rosclaw/get_manifestbecomes/robot1/rosclaw/get_manifest/rosclaw/estopbecomes/robot1/rosclaw/estop/rosclaw/safety_statusbecomes/robot1/rosclaw/safety_status/rosclaw/scenebecomes/robot1/rosclaw/scene- relative config defaults such as
cmd_velandcamera/image_raw/compressedbecome/robot1/cmd_veland/robot1/camera/image_raw/compressed
Important transport behavior:
- Relay/WebRTC sessions are namespace-enforced. Requests outside the configured robot namespace are rejected.
- Rosbridge is namespace-safe by convention, not hard-isolated. It still exposes the full ROS graph, so clients must use the intended namespaced endpoints.
Primary launch file:
ros2 launch rosclaw_bringup rosclaw.launch.py ...Arguments:
| Argument | Default | Description |
|---|---|---|
platform |
turtlebot3 |
Config bundle to load: turtlebot3, go2, g1, generic, sim_turtlebot3 |
use_sim_time |
false |
Use simulation clock |
rosbridge |
true |
Launch rosbridge_websocket on port 9090 |
reuse_existing_rosbridge |
false |
Reuse an already-running rosbridge listener on 127.0.0.1:9090 |
relay |
false |
Launch the relay bridge |
webrtc |
false |
Launch the WebRTC bridge |
perception |
false |
Launch Gemini perception |
robot_namespace |
"" |
Namespace all ROSClaw endpoints for fleet deployments |
safety_config |
"" |
Override safety_policy.yaml |
discovery_config |
"" |
Override discovery.yaml |
perception_config |
"" |
Override perception.yaml |
signaling_url |
ws://localhost:8443 |
Signaling server URL for relay/WebRTC |
robot_token |
"" |
Signaling-server auth token |
robot_key |
"" |
Secret key checked on session invitation |
robot_id |
robot_1 |
Robot identifier presented to the signaling layer |
Convenience launch files are also provided:
turtlebot3.launch.pygo2.launch.pyg1.launch.pygeneric.launch.pysim_turtlebot3.launch.py
Each platform in rosclaw_bringup/config/<platform>/ ships three files:
safety_policy.yamldiscovery.yamlperception.yaml
The important convention is that namespace-sensitive defaults are relative:
cmd_velcamera/image_raw/compressedrosclaw/manifestrosclaw/get_manifest
That means robot_namespace:=/robot1 automatically scopes them under
/robot1. Use absolute overrides only if your underlying robot stack is
intentionally global.
Example safety_policy.yaml:
/**:
ros__parameters:
linear_max: 1.0
angular_max: 2.84
cmd_vel_topic: "cmd_vel"
estop_rate: 10.0
allowlisted_topics:
- "cmd_vel"
- "odom"
- "scan"
- "camera/image_raw/compressed"Example discovery.yaml:
/**:
ros__parameters:
robot_name: "TurtleBot3"
robot_namespace: ""
publish_interval: 2.0
exclude_prefixes:
- "/rosout"
- "/parameter_events"
- "rosclaw/manifest"
- "rosclaw/get_manifest"Example perception.yaml:
/**:
ros__parameters:
camera_topic: "camera/image_raw/compressed"
frame_rate: 1.0
gemini_model: "gemini-2.5-flash"
api_key_env: "GEMINI_API_KEY"
inference_timeout: 20.0
max_retries: 2
empty_cycles_before_refresh: 4The names below are the single-robot graph names. In fleet mode, prefix the
rosclaw/... endpoints and relative platform topics with the robot namespace.
| Topic | Type | Source |
|---|---|---|
/rosclaw/manifest |
rosclaw_msgs/CapabilityManifest |
discovery |
/rosclaw/safety_status |
rosclaw_msgs/SafetyStatus |
estop |
/rosclaw/scene |
std_msgs/String |
perception |
/rosclaw/perception |
rosclaw_msgs/PerceptionSnapshot |
perception |
/rosclaw/perception_status |
std_msgs/String |
perception |
/cmd_vel |
geometry_msgs/Twist |
estop zero-velocity output |
| Topic | Type | Consumer |
|---|---|---|
/rosclaw/estop |
std_msgs/Bool |
estop |
<camera_topic> |
sensor_msgs/CompressedImage |
perception |
| Service | Type | Provider |
|---|---|---|
/rosclaw/get_manifest |
rosclaw_msgs/GetManifest |
discovery |
/rosclaw/set_safety_policy |
rosclaw_msgs/SetSafetyPolicy |
estop |
CapabilityManifestincludesrobot_name,robot_namespace, and lists of topics, services, and actions.SafetyStatusincludes estop state, velocity limits, and resolved allowlisted topics.GetManifestaccepts arobot_namespacequery and returns a manifest.SetSafetyPolicyupdates velocity limits and allowlisted topics at runtime.
See rosclaw_msgs/msg/ and rosclaw_msgs/srv/ for the exact definitions.
rosclaw_estop is the robot-side safety authority:
- A
truemessage on/rosclaw/estopimmediately starts zero-velocity publishing on the configuredcmd_veltopic. /rosclaw/safety_statusis latched so late subscribers receive the current safety state./rosclaw/set_safety_policyallows runtime updates to velocity limits and allowlisted topics.- Plugin-side validation is still expected as defense in depth.
allowlisted_topics are informational. They are published for clients and
operators; the estop node does not use them as an execution gate.
Single-robot examples:
Trigger estop:
ros2 topic pub --once /rosclaw/estop std_msgs/Bool "data: true"Release estop:
ros2 topic pub --once /rosclaw/estop std_msgs/Bool "data: false"Read safety status:
ros2 topic echo /rosclaw/safety_status --onceQuery discovery:
ros2 service call /rosclaw/get_manifest rosclaw_msgs/srv/GetManifest "{robot_namespace: ''}"In fleet mode, use the namespaced equivalents such as
/robot1/rosclaw/estop and /robot1/rosclaw/get_manifest.
| Platform | Linear Max | Angular Max | Camera Topic | Robot Name |
|---|---|---|---|---|
| TurtleBot3 | 1.0 m/s | 2.84 rad/s | /camera/image_raw/compressed |
TurtleBot3 |
| Unitree Go2 | 2.0 m/s | 3.0 rad/s | /utlidar/depth_image/compressed |
Go2 |
| Unitree G1 | 1.5 m/s | 2.0 rad/s | /camera/color/image_raw/compressed |
G1 |
| Generic | 0.5 m/s | 1.0 rad/s | /camera/compressed |
Robot |
| Sim TurtleBot3 | 1.0 m/s | 2.84 rad/s | /camera/image_raw/compressed |
TurtleBot3 (Sim) |
Those are the resolved graph names for a single robot. The shipped config files store the namespace-sensitive topic defaults as relative names.
- Create
rosclaw_bringup/config/<platform>/. - Add
safety_policy.yaml,discovery.yaml, andperception.yaml. - Use
config/generic/as the starting point. - Optionally add
launch/<platform>.launch.pyas a convenience wrapper.
No packaging change is required for a new config directory; setup.py
discovers platform config directories automatically.
Apache-2.0