--- ---

Communicating with ROS using roslibjs

13 Nov 2024 - Nikil Shyamsunder

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

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:

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:

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.