This tutorial demonstrates how to use roslibjs
to enable communication between a webserver and a ROS stack. By the end of this guide, you’ll be able to view running topics in your ROS system, determine the message type of a topic, create a RosTopic
object in JavaScript, subscribe to a topic and update HTML elements using a callback function.
Introduction and Setup
Overview
First off, know that rosbridge_server provides a WebSocket interface to communicate with ROS 2 nodes. It translates WebSocket-based commands (from roslibjs
) into native ROS 2 commands, such as publish, subscribe, and call service.
On the other hand, roslibjs is A JavaScript library for interacting with ROS nodes from the web browser. It allows clients to communicate with ROS nodes via a WebSocket connection to rosbridge_server
, without requiring native ROS support.
The rosbridge_server
setup has been configured to run automatically as a ROS node, alongside other nodes like the anemometer and sailing algorithm nodes. No additional configuration is required. Front-end developers can focus solely on writing JavaScript code using roslibjs
without worrying about rosbridge_server
.
Running the Current Webserver Code
Run the Docker container with the updated port-forwarding arguments:
cd ..
docker run -it --rm -p 9089:7000 -p 9090:9090 --name ros2_container -v $(pwd)/src:/home/ros2_user/ros2_ws/src ros2_humble_custom
- The
-p 9089:7000 -p 9090:9090
arguments forward ports to make the webserver and ROS server accessible outside of the Docker image.- Port
7000
: Webserver - Port
9090
: ROS server
- Port
Start the webserver and ROS system:
Inside the Docker container, run:
cd /home/ros2_user/ros2_ws/src/webserver/
python3 -m http.server 7000 & ros2 launch sailboat_launch sailboat.launch_sim.py
Access the webserver:
Open your web browser and go to:
http://localhost:9089
You should now see the webserver interface.
Using roslibjs in JavaScript Code
Including roslibjs
To use roslibjs
in your JavaScript code, include the library by adding the following script tag to your HTML file:
<script src="https://cdn.jsdelivr.net/npm/roslib@1/build/roslib.min.js"></script>
This script includes the entire roslibjs
library, making its features available in your code. Think of it as similar to a Python import
statement.
Connecting to ROS
To establish a connection with ROS, use the following function:
let ros;
function connectToROS() {
const rosbridgeAddress = "ws://localhost:9090";
ros = new ROSLIB.Ros({
url: rosbridgeAddress
});
ros.on('connection', function () {
console.log('Connected to rosbridge server.');
subscribeToTopics();
});
ros.on('error', function (error) {
console.error('Error connecting to rosbridge server:', error);
});
ros.on('close', function () {
console.log('Connection to rosbridge server closed.');
});
}
Explanation
Define the connection address: The rosbridgeAddress
specifies the WebSocket URL for rosbridge_server
. In this case, it is ws://localhost:9090
because of the port-forwarding configuration set up earlier (see Introduction and Setup). The ROSLIB.Ros
object is then initialized with the WebSocket URL. Then some event handlers log a success message and call subscribeToTopics
, log any connection errors, and log when the connection is closed.
Automatically Connecting to ROS
To run the connectToROS
function when the web page loads, add the following code:
// Connect to ROS when the page loads
window.onload = function () {
connectToROS();
};
This ensures that the connection to ROS is established as soon as the page is opened in a browser.
Interlude: Identifying and Using ROS 2 Topics
Objective
The primary goal of the webserver is to perform read/write actions on ROS 2 topics. This allows us to:
- View the data being processed by the ROS system.
- Modify system behavior by sending data to specific topics.
Each topic in ROS 2 has a specific, static type structure. To interact with a topic, you need to know: the name of the topic, the type of the topic to properly parse its data.
You can think of the topic type as analogous to an OCaml record type, a C++ struct, or a simple JavaScript object.
Viewing Topics
While your ROS 2 nodes are running (as shown in the previous section), open another terminal and run the following command to list all active topics:
ros2 topic list
You should see a list of topics, such as:
/gps
/sailbot/radio_rudder
/sailbot/control_mode
Some topics, like /gps
, are used for reading data to display in interesting ways. Others, like /sailbot/control_mode
, are used for writing data to modify the system state (e.g., switching between sailing algorithm and radio control).
Checking Topic Details
To view details about a specific topic, including its type, run the following command:
ros2 topic info
</code>
</pre>
For example, to get information about the `/gps` topic:
ros2 topic info /gps
The output will be similar to:
Type: sensor_msgs/msg/NavSatFix
Publisher count: 1
Subscription count: 2
This tells us:
- The type of the `/gps` topic is `sensor_msgs/msg/NavSatFix`.
- There is 1 publisher and 2 subscribers to this topic.
With this information, we can now correctly parse or send data to the topic.
# Reading From a Topic and Displaying Data
## Subscribing to a Topic
To read from a topic and display the data on screen, use the following function:
function subscribeToTopics() {
// Helper function to update DOM element with topic data
function updateValue(elementId, value) {
document.getElementById(elementId).innerText = value;
}
// Subscribe to /sailbot/algo_rudder
const algoRudderTopic = new ROSLIB.Topic({
ros: ros,
name: '/sailbot/algo_rudder',
messageType: 'std_msgs/Int32'
});
algoRudderTopic.subscribe(function (message) {
updateValue('algo-rudder-value', message.data);
});
}
### Explanation
**Helper Function:** The `updateValue` helper function updates the inner text of an HTML element with a specified `elementId` to display the topic data.
**Creating a `RosTopic` Object:** The `ROSLIB.Topic` object is created for the `/sailbot/algo_rudder` topic with the type `std_msgs/Int32`.
**Subscribing to the Topic:** The `subscribe` method attaches a callback function that updates the DOM with the topic's message data.
# Parsing More Complex Datatypes: GPS Example
## Subscribing to the /gps Topic
The `/gps` topic uses the `sensor_msgs/msg/NavSatFix` datatype. This datatype includes important fields like latitude and longitude that represent the sailboat's position. To parse and display this information, follow these steps:
### JavaScript Code to Parse GPS Data
function parseGpsData(message) {
const latitude = message.latitude;
const longitude = message.longitude;
// Format the latitude and longitude to your desired precision
const formattedLatitude = latitude.toFixed(6);
const formattedLongitude = longitude.toFixed(6);
// Update the DOM elements
document.getElementById('latitude-value').innerText = formattedLatitude;
document.getElementById('longitude-value').innerText = formattedLongitude;
}
// Subscribe to the /gps topic
const gpsTopic = new ROSLIB.Topic({
ros: ros,
name: '/gps',
messageType: 'sensor_msgs/NavSatFix'
});
gpsTopic.subscribe(parseGpsData);
### Explanation
**Parsing Message Fields**: The `parseGpsData` function extracts the `latitude` and `longitude` fields from the message using dot notation, which is similar to accessing fields in OCaml or Java objects. These fields are then formatted to six decimal places using the `toFixed(6)` method for better readability.
**Updating the DOM**: The `document.getElementById` function updates the corresponding DOM elements (`latitude-value` and `longitude-value`) with the formatted latitude and longitude values.
**Creating a `RosTopic` Object**: The `ROSLIB.Topic` object is created for the `/gps` topic with the `sensor_msgs/NavSatFix` message type. The `subscribe` method attaches the `parseGpsData` function as a callback, which processes each message received on the `/gps` topic.
---
## Adding HTML Elements for GPS Data
To display the parsed GPS data on the webpage, add the following HTML elements:
Sailboat Longitude:
N/A
Sailboat Latitude:
N/A
### Explanation of HTML
- The `status-row` class organizes the labels and values in rows for better layout.
- The `id` attributes (`longitude-value` and `latitude-value`) correspond to the DOM elements updated by the `parseGpsData` function.
This setup allows the latitude and longitude data from the `/gps` topic to be displayed dynamically in your web application.