Rosbridge Integration Guide¶
See also: network_architecture.md for the full communication topology and security model, web_control_api.md for the Web Components that use this connection, and ros2_interface_reference.md for the complete topic/service definitions.
What is Rosbridge?¶
rosbridge_suite provides a JSON-based WebSocket interface to ROS2. It allows browser-based applications (and any WebSocket client) to subscribe to topics, publish messages, and call services without a native ROS2 installation.
The robot platform uses rosbridge as the bridge between the web control UI and the ROS2 topic bus running on the companion computer.
Architecture¶
Browser (Web Components)
↕ WebSocket (JSON)
rosbridge_server (on RPi5)
↕ ROS2 DDS
micro-ROS agent
↕ UART serial
ESP32-S3 firmware
Installation¶
On the companion computer (Raspberry Pi 5):
# Install rosbridge_suite
sudo apt install ros-humble-rosbridge-suite
# Or from source (if you need the latest)
cd ~/ros2_ws/src
git clone https://github.com/RobotWebTools/rosbridge_suite.git -b ros2
cd ~/ros2_ws
colcon build --packages-select rosbridge_suite rosbridge_server rosbridge_library
Launch¶
Standalone¶
Default port: 9090 (WebSocket)
With Docker Deployment¶
The Docker image starts rosbridge automatically alongside the ROS2 stack:
Rosbridge is launched on port 9090 inside the container (see Dockerfile entrypoint).
Note: The
robot_bringuplaunch files do NOT include rosbridge. In non-Docker setups, start rosbridge separately:
Custom Port¶
Protocol Overview¶
Rosbridge uses a JSON protocol over WebSocket. Each message has an op field indicating the operation.
Subscribe to a Topic¶
Incoming messages arrive as:
{
"op": "publish",
"topic": "/battery_state",
"msg": {
"voltage": 7.8,
"percentage": 0.85,
"present": true,
"power_supply_status": 2
}
}
Publish a Message¶
{
"op": "publish",
"topic": "/cmd_vel",
"type": "geometry_msgs/msg/Twist",
"msg": {
"linear": { "x": 0.2, "y": 0.0, "z": 0.0 },
"angular": { "x": 0.0, "y": 0.0, "z": 0.5 }
}
}
Call a Service¶
{
"op": "call_service",
"service": "/emergency_stop",
"type": "robot_interfaces/srv/EmergencyStop",
"args": { "reason": "User triggered from web UI" },
"id": "svc_1_1715000000"
}
Response:
{
"op": "service_response",
"service": "/emergency_stop",
"values": {
"success": true,
"current_state": 1,
"message": "E-stop activated"
},
"result": true,
"id": "svc_1_1715000000"
}
Unsubscribe¶
Web Control Connection¶
The sdk/web_control/ components use the RosbridgeConnection class which handles:
- WebSocket lifecycle management
- Auto-reconnect with exponential backoff (1s → 30s max)
- Automatic re-subscription on reconnect
- Zod schema validation of incoming messages
- Service call timeout (10 seconds)
import { connection } from './connection';
connection.connect('ws://192.168.1.100:9090');
// Subscribe
const unsub = connection.subscribe<BatteryState>(
'/battery_state',
'sensor_msgs/msg/BatteryState',
(msg) => console.log(msg.voltage)
);
// Publish
connection.publish('/cmd_vel', 'geometry_msgs/msg/Twist', {
linear: { x: 0.1, y: 0, z: 0 },
angular: { x: 0, y: 0, z: 0 }
});
// Call service
const result = await connection.callService(
'/emergency_stop',
'robot_interfaces/srv/EmergencyStop',
{ reason: 'Test' }
);
Web Basic Connection¶
The sdk/web_basic/ minimal interface uses raw WebSocket without the connection wrapper:
const ws = new WebSocket('ws://192.168.1.100:9090');
ws.onopen = () => {
ws.send(JSON.stringify({
op: 'subscribe',
topic: '/battery_state',
type: 'sensor_msgs/msg/BatteryState'
}));
};
Security Considerations¶
Default: No Authentication¶
Rosbridge does not authenticate connections by default. Anyone with network access can: - Read all sensor data - Publish motor commands - Call services (including E-stop)
Mitigations¶
- Network isolation — Run rosbridge only on the robot's local Wi-Fi network
- Firewall — Restrict port 9090 to known client IPs
- wss:// with TLS — Use a reverse proxy (nginx) for encrypted WebSocket
- Topic allowlist — Configure rosbridge to expose only specific topics:
<!-- In launch file -->
<param name="topics_glob" value="['/battery_state', '/odom', '/ultrasonic/*']"/>
<param name="services_glob" value="['/emergency_stop']"/>
Safety Net¶
Even if an unauthorized client publishes to /cmd_vel, the firmware enforces:
- Speed clamping (configurable, default 1.0 m/s, hard cap 1.2 m/s)
- Watchdog timeout (500ms)
- E-stop override (hardware)
Troubleshooting¶
WebSocket Connection Refused¶
- Verify rosbridge is running:
ros2 node list | grep rosbridge - Check port:
ss -tlnp | grep 9090 - Check firewall:
sudo ufw status - Verify IP address:
hostname -I
No Topics Visible¶
The browser connects but no data arrives:
- Check micro-ROS agent is running:
ros2 topic list - Verify firmware is publishing:
ros2 topic hz /odom - Check topic type matches exactly (case-sensitive)
High Latency¶
Messages arrive with noticeable delay:
- Wi-Fi congestion — move closer to AP or use 5GHz band
- rosbridge overload — reduce subscription count or publish rate
- micro-ROS buffer full — check serial connection quality
Connection Drops Frequently¶
- Wi-Fi signal strength:
iwconfig wlan0 - rosbridge memory: large message backlog can crash the server
- Check system load:
topon the companion computer
Service Call Timeout¶
The callService() promise rejects after 10 seconds:
- Verify the service exists:
ros2 service list - Check service type matches:
ros2 service type /emergency_stop - Ensure the firmware node is responsive (not stuck in E-stop recovery)