Using the rosbridge Gateway

Connect non-ROS clients (Python or browser) to the fleet over WebSocket — where the bridge runs and how to talk to it.

Using the rosbridge Gateway

rosbridge lets you read and write the fleet’s ROS 2 topics from code that has no ROS install at all — plain JSON over a WebSocket. Use it from a laptop, a browser, a script, or any agent that can open a WebSocket. This page covers everything specific to our setup; for the generic protocol and client libraries it links out.

Where it is (our environment)

Thing Value Notes
Gateway host agony The only host running the bridge by default.
URL ws://agony:9090 rosbridge v2 JSON protocol over WebSocket.
Port 9090 rosbridge_port default.
DDS domain 42 The bridge only sees topics on domain 42 (the fleet domain).
systemd service rosbridge Auto-starts on boot, restarts on failure.
Auth none Any client that connects can pub/sub any topic and call any service. Lab-only — see Security.
Env var overrides ROSBRIDGE_HOST, ROSBRIDGE_PORT Honored by the demo clients in test/; default to agony / 9090.

Confirm it’s up before debugging a client:

ssh agony 'systemctl status rosbridge'

Talk to it from Python

roslibpy is pure Python/WebSocket — no ROS needed (pip install roslibpy):

import roslibpy
ros = roslibpy.Ros(host='agony', port=9090); ros.run()

# read: ROS → client
roslibpy.Topic(ros, '/agony/joy/0', 'sensor_msgs/Joy').subscribe(
    lambda m: print(m['axes']))

# write: client → ROS
cmd = roslibpy.Topic(ros, '/cmd_vel', 'geometry_msgs/Twist')
cmd.publish(roslibpy.Message({'linear': {'x': 0.5}, 'angular': {'z': 0.2}}))

You must supply the message type string (e.g. sensor_msgs/Joy) — discover it with rosapi introspection below.

Talk to it from a browser

Same idea with roslib.js against ws://agony:9090:

const ros = new ROSLIB.Ros({ url: 'ws://agony:9090' });
new ROSLIB.Topic({ ros, name: '/heartbeat', messageType: 'std_msgs/String' })
  .subscribe(m => console.log(m.data));

Discovering what’s available (rosapi)

The bridge ships rosapi, so clients can introspect without ROS. Service calls in roslibpy block and return the result:

topics = roslibpy.Service(ros, '/rosapi/topics', 'rosapi/Topics').call(
    roslibpy.ServiceRequest())            # -> {'topics': [...], 'types': [...]}
t = roslibpy.Service(ros, '/rosapi/topic_type', 'rosapi/TopicType').call(
    roslibpy.ServiceRequest({'topic': '/agony/joy/0'}))   # -> {'type': 'sensor_msgs/Joy'}

Useful rosapi services: /rosapi/topics, /rosapi/topics_for_type, /rosapi/topic_type.

Ready-made clients (fastest start)

The repo’s test/ directory has working roslibpy clients you can copy or run directly. They already encode the connection conventions (_conn.py handles host/port/env-var defaults):

cd test && uv sync                     # installs roslibpy + rich
uv run list_topics.py                  # every visible topic + type
uv run echo.py /heartbeat              # dump a topic (type auto-detected)
uv run joydump.py                      # live joystick viewer (pick a topic)

Override the target with --host/--port or ROSBRIDGE_HOST/ROSBRIDGE_PORT.

Topic naming conventions

  • Per-host sources are namespaced by hostname, e.g. joystick axes/buttons publish on /agony/joy/0 (host / joy / controller index).
  • Fleet-wide topics aren’t namespaced, e.g. /heartbeat (liveness, host names) and the /chatter demo talker.
  • When in doubt, list topics with list_topics.py rather than guessing.

Security

The bridge binds 0.0.0.0 with no authentication — anyone who can reach agony:9090 has full pub/sub/service access. That’s acceptable inside the lab LAN. If untrusted clients could reach the host, set rosbridge_address to a specific LAN interface in host_vars and/or front it with a curated gateway. The bridge can only surface topics it sees over DDS — nodes must be interoperating on domain 42.

Enabling the bridge on another host

It runs on agony today. To add it elsewhere, set in inventory/host_vars/<host>.yml:

install_rosbridge: true
# rosbridge_port: 9090        # default
# rosbridge_address: 0.0.0.0  # default — set a LAN IP to restrict reach

Then deploy:

ansible-playbook playbooks/rosbridge.yml --limit <host>

Reference