Skip to main content
Version: 9

ROS Bridge Integration for Custom View Panes

note

This guide assumes basic knowledge of HTML and JavaScript, and that you have already created a custom view pane.

For preliminary reading, please refer to:

The ROS Bridge integration allows your custom view panes to interact with ROS topics through a secure post message API. This enables you to create interactive dashboards that subscribe to sensor data, publish commands, and monitor your robot's state in real-time.

Add ROS Bridge Integration

To enable ROS Bridge integration, you need to include the iframe-ros-client.js library in your HTML file and initialize the ROS client.

The iframe-ros-client.js file is automatically generated and served by MoveIt Pro. You don't need to download or manually include this file in your project—it's available at /iframe-ros-client.js when your custom view pane iframe is loaded.

Add the following script tag to your HTML file's <head> or before the closing </body> tag:

<script src="/iframe-ros-client.js"></script>

Initialize the ROS Client

Create an instance of the IframeROSClient:

<script>
// Initialize the ROS client
const ros = new IframeROSClient();

// Optional: Use direct rosbridge connection instead of postMessage
// const ros = new IframeROSClient({ useDirectConnection: true });
</script>

The client will automatically receive the rosbridge URL from the parent window via postMessage. If you enable useDirectConnection, the client will attempt to connect directly to rosbridge using roslibjs (if available).

Subscribe to ROS Topics

To receive messages from ROS topics, use the subscribe method:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Joint States Monitor</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
background-color: #1a1a1a;
color: #ffffff;
}
#joint-states {
background-color: #2a2a2a;
padding: 15px;
border-radius: 5px;
margin-top: 20px;
font-family: monospace;
white-space: pre-wrap;
}
</style>
</head>
<body>
<h1>Joint States Monitor</h1>
<div id="joint-states">Waiting for joint states...</div>

<script src="/iframe-ros-client.js"></script>
<script>
const ros = new IframeROSClient();
const displayElement = document.getElementById('joint-states');

// Subscribe to joint states topic
ros.subscribe(
'/joint_states',
'sensor_msgs/msg/JointState',
(message) => {
// Update the display with received joint states
const jointNames = message.name || [];
const positions = message.position || [];

let displayText = 'Joint States:\n\n';
for (let i = 0; i < jointNames.length; i++) {
displayText += `${jointNames[i]}: ${positions[i]?.toFixed(4) || 'N/A'}\n`;
}

displayElement.textContent = displayText;
}
);

// Clean up on page unload
window.addEventListener('beforeunload', () => {
ros.unsubscribe('/joint_states', 'sensor_msgs/msg/JointState');
ros.disconnect();
});
</script>
</body>
</html>

This example subscribes to the /joint_states topic and displays the joint names and positions in real-time.

Publish to ROS Topics

To send messages to ROS topics, use the publish method:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Velocity Controller</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
background-color: #1a1a1a;
color: #ffffff;
}
button {
background-color: #4a9eff;
color: white;
border: none;
padding: 10px 20px;
margin: 5px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #3a8eef;
}
button:active {
background-color: #2a7edf;
}
</style>
</head>
<body>
<h1>Velocity Controller</h1>
<p>Click buttons to send velocity commands:</p>
<button id="forward">Forward</button>
<button id="backward">Backward</button>
<button id="left">Left</button>
<button id="right">Right</button>
<button id="stop">Stop</button>

<script src="/iframe-ros-client.js"></script>
<script>
const ros = new IframeROSClient();

function publishVelocity(linearX, angularZ) {
const message = {
linear: { x: linearX, y: 0.0, z: 0.0 },
angular: { x: 0.0, y: 0.0, z: angularZ }
};

ros.publish('/cmd_vel', 'geometry_msgs/msg/Twist', message);
}

document.getElementById('forward').addEventListener('click', () => {
publishVelocity(0.5, 0.0);
});

document.getElementById('backward').addEventListener('click', () => {
publishVelocity(-0.5, 0.0);
});

document.getElementById('left').addEventListener('click', () => {
publishVelocity(0.0, 0.5);
});

document.getElementById('right').addEventListener('click', () => {
publishVelocity(0.0, -0.5);
});

document.getElementById('stop').addEventListener('click', () => {
publishVelocity(0.0, 0.0);
});

// Clean up on page unload
window.addEventListener('beforeunload', () => {
ros.disconnect();
});
</script>
</body>
</html>

This example creates a simple velocity controller that publishes geometry_msgs/msg/Twist messages to the /cmd_vel topic.

Complete Interactive Example

Here's a complete example that combines subscription and publishing, with error handling and resource cleanup:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interactive Robot Dashboard</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
background-color: #1a1a1a;
color: #ffffff;
}
.container {
display: flex;
gap: 20px;
}
.panel {
flex: 1;
background-color: #2a2a2a;
padding: 15px;
border-radius: 5px;
}
button {
background-color: #4a9eff;
color: white;
border: none;
padding: 10px 20px;
margin: 5px;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #3a8eef;
}
#status {
margin-top: 10px;
padding: 10px;
border-radius: 5px;
background-color: #3a3a3a;
}
.error {
background-color: #8b0000;
}
.success {
background-color: #006400;
}
</style>
</head>
<body>
<h1>Interactive Robot Dashboard</h1>

<div class="container">
<div class="panel">
<h2>Joint States</h2>
<div id="joint-states">Waiting for data...</div>
</div>

<div class="panel">
<h2>Control</h2>
<button id="start">Start</button>
<button id="stop">Stop</button>
<div id="status"></div>
</div>
</div>

<script src="/iframe-ros-client.js"></script>
<script>
const ros = new IframeROSClient();
const jointStatesElement = document.getElementById('joint-states');
const statusElement = document.getElementById('status');
let isSubscribed = false;

function updateStatus(message, isError = false) {
statusElement.textContent = message;
statusElement.className = isError ? 'error' : 'success';
}

function subscribeToJointStates() {
if (isSubscribed) {
return;
}

try {
ros.subscribe(
'/joint_states',
'sensor_msgs/msg/JointState',
(message) => {
const jointNames = message.name || [];
const positions = message.position || [];

let displayText = '';
for (let i = 0; i < jointNames.length; i++) {
displayText += `${jointNames[i]}: ${positions[i]?.toFixed(4) || 'N/A'}\n`;
}

jointStatesElement.textContent = displayText || 'No joint data';
}
);

isSubscribed = true;
updateStatus('Subscribed to /joint_states');
} catch (error) {
updateStatus(`Error subscribing: ${error.message}`, true);
}
}

function unsubscribeFromJointStates() {
if (!isSubscribed) {
return;
}

try {
ros.unsubscribe('/joint_states', 'sensor_msgs/msg/JointState');
isSubscribed = false;
jointStatesElement.textContent = 'Unsubscribed';
updateStatus('Unsubscribed from /joint_states');
} catch (error) {
updateStatus(`Error unsubscribing: ${error.message}`, true);
}
}

document.getElementById('start').addEventListener('click', subscribeToJointStates);
document.getElementById('stop').addEventListener('click', unsubscribeFromJointStates);

// Clean up on page unload
window.addEventListener('beforeunload', () => {
if (isSubscribed) {
ros.unsubscribe('/joint_states', 'sensor_msgs/msg/JointState');
}
ros.disconnect();
});

// Initial subscription
subscribeToJointStates();
</script>
</body>
</html>

This example demonstrates:

  • Subscribing to ROS topics with error handling
  • Displaying received data in real-time
  • Proper cleanup when the page unloads
  • User interface controls for managing subscriptions

API Reference

The IframeROSClient class provides the following methods:

subscribe(topic, messageType, callback)

Subscribe to a ROS topic and receive messages via a callback function.

  • topic (string): The ROS topic name (e.g., /joint_states)
  • messageType (string): The ROS message type (e.g., sensor_msgs/msg/JointState)
  • callback (function): Function called with each received message
ros.subscribe('/joint_states', 'sensor_msgs/msg/JointState', (message) => {
console.log('Received:', message);
});

unsubscribe(topic, messageType)

Unsubscribe from a ROS topic.

  • topic (string): The ROS topic name
  • messageType (string): The ROS message type
ros.unsubscribe('/joint_states', 'sensor_msgs/msg/JointState');

publish(topic, messageType, message)

Publish a message to a ROS topic.

  • topic (string): The ROS topic name (e.g., /cmd_vel)
  • messageType (string): The ROS message type (e.g., geometry_msgs/msg/Twist)
  • message (object): The message data (must match the message type structure)
ros.publish('/cmd_vel', 'geometry_msgs/msg/Twist', {
linear: { x: 0.5, y: 0.0, z: 0.0 },
angular: { x: 0.0, y: 0.0, z: 0.0 }
});

disconnect()

Disconnect from ROS and clean up all subscriptions.

ros.disconnect();

Security Considerations

The ROS Bridge postMessage API includes the following security features:

  • Origin Validation: The API validates message origins to prevent spoofing
  • Sandbox Permissions: Iframes run with restricted permissions
  • Secure Communication: All communication between iframe and parent uses postMessage with origin validation

When using ROS Bridge integration:

  • Be cautious when enabling ROS Bridge integration, as it allows your iframe to interact with the ROS system
  • Validate and sanitize any data received from ROS topics before displaying it
  • Only subscribe to topics you trust
  • Be careful when publishing commands that could affect robot behavior

Best Practices

Error Handling

Always include error handling for ROS operations:

try {
ros.subscribe('/topic', 'message_type', (msg) => {
// Handle message
});
} catch (error) {
console.error('Subscription failed:', error);
// Show user-friendly error message
}

Resource Cleanup

Always clean up subscriptions and disconnect when your page unloads:

window.addEventListener('beforeunload', () => {
ros.unsubscribe('/topic', 'message_type');
ros.disconnect();
});

Performance

  • Unsubscribe from topics when you no longer need them
  • Avoid subscribing to high-frequency topics unnecessarily
  • Use throttling or debouncing for UI updates based on ROS messages
  • Consider using useDirectConnection: true for better performance if roslibjs is available

Conclusion

This guide has covered how to integrate custom view panes with ROS topics:

  • Including and initializing the ROS Bridge client library
  • Subscribing to ROS topics to receive data
  • Publishing messages to ROS topics
  • Error handling and resource cleanup
  • Best practices for performance and security

You should now be able to create interactive custom view panes that integrate seamlessly with your ROS system.

For more advanced ROS integration patterns, see the Websocket Client Library API documentation.