ROS 1 vs ROS 2 Tradeoffs and Advantages

ROS 1 vs ROS 2 Tradeoffs and Advantages

ROS (Robot Operating System) is a popular open-source robotics framework that provides libraries and tools for building robot applications. There are currently two versions of ROS: ROS 1 and ROS 2. In this article, we will explore the tradeoffs and advantages of both versions and discuss the tools available for converting between them. We will also examine the reasons why some researchers continue to publish work in ROS 1 and the challenges of migrating to ROS 2. Finally, we will summarize the key advantages and tradeoffs of ROS 1 and ROS 2 and provide some guidance on when it is worth upgrading to ROS 2.

ROS 1 vs ROS 2

ROS 1 has been around for over a decade and has a large user base, with a wealth of libraries, tools, and resources available. It is based on a publish-subscribe model, where nodes communicate with each other by publishing messages to topics and subscribing to receive messages from topics. This model allows for flexibility and scalability, but it can also lead to some challenges in terms of real-time performance and integration with other systems.

ROS 2 was released in 2017 and was designed to address some of the limitations of ROS 1. It is based on a more efficient data-driven model, where nodes communicate with each other by sending and receiving messages over defined interfaces. This model provides improved real-time performance and better integration with other systems, but it can also be more difficult to set up and use than ROS 1.

Conversion Tools

There are several tools available for converting between ROS 1 and ROS 2. The most commonly used tool is ros1_bridge, which is a package provided by the ROS 2 team that allows ROS 1 and ROS 2 nodes to communicate with each other. This can be useful for migrating existing ROS 1 applications to ROS 2 or for running a ROS 1 application alongside a ROS 2 application.

To use ros1_bridge, you will need to install it on both the ROS 1 and ROS 2 systems. You can then use the ros1_bridge node to translate messages between ROS 1 and ROS 2 topics. For example, the following Python code shows how to use ros1_bridge to subscribe to a ROS 1 topic and publish the received messages to a ROS 2 topic:

#! /usr/bin/env python2

import argparse
import os
import random
import string
import subprocess
import yaml

import rospy

def start():
    ''.join(random.choice(string.ascii_uppercase + string.digits)
            for _ in range(5))
    parser = argparse.ArgumentParser()
    parser.add_argument("--config", default=None, type=str,
                        help="Pass file to select topics/services to be bridged")
    parser.add_argument("--ros2_ws_path", default=None, type=str,
                        help="Pass path to the ROS2 env explicitly")
    parser.add_argument("--ros1_ws_path", default=None, type=str,
                        help="Pass path to the ROS1 env explicitly")
    parser.add_argument("--name", default='ros1_bridge', type=str,
                        help="Name of the bridge node to be created.")
    parser.add_argument("--domain_id", default=0, type=int,
                        help="Domain ID to launch this bridge")
    parser.add_argument("--bridge-all-1to2-topics", action='store_true',
                        help="Bridge all topics from ROS1=>ROS2")
    parser.add_argument("--bridge-all-2to1-topics", action='store_true',
                        help="Bridge all topics from ROS2=>ROS1")
    parser.add_argument("--bridge-all-topics", action='store_true',
                        help="Bidge topics Bi-Directionally (overrules other options)")
    args, _ = parser.parse_known_args()
    raw_data = None
    try:
        with open(args.config, 'r') as f:
            raw_data = f.read()
    except OSError:
        print("failed to read file %s" % args.config)
        exit(1)
    data = None
    try:
        data = yaml.load(raw_data)
    except yaml.YAMLError as e:
        print('Error parsing yaml: \n%s' % e.message)
        exit(1)
    # add queue_size if missing
    if 'topics' not in data and 'services' not in data:
        print("'topics' key missing, 'services' key missing, or invalid yaml. Exiting.")
        exit(1)

    try:
        rosmaster_uri = os.environ["ROS_MASTER_URI"]
    except KeyError:
        print("ROS_MASTER_URI not set")
        exit(1)
    if args.ros2_ws_path != None:
        ros2_ws_path = args.ros2_ws_path
    else:
        try:
            ros2_ws_path = os.environ['ROS2_WS_PATH']
            if not os.path.exists(os.path.join(ros2_ws_path, "install", "setup.bash")):
                print("%s does not appear to be a valid ros2 workspace" %
                      ros2_ws_path)
                exit(1)
        except KeyError:
            print("Please set env var : ROS2_WS_PATH ")
            exit(1)
    if args.ros1_ws_path != None:
        ros1_ws_path = args.ros1_ws_path
    else:
        try:
            ros1_ws_path = os.environ['ROS1_WS_PATH']
            if not os.path.exists(os.path.join(ros1_ws_path, "devel", "setup.bash")):
                print("%s does not appear to be a valid ros1 workspace" %
                      ros1_ws_path)
                exit(1)
        except KeyError:
            print("Please set env var : ROS1_WS_PATH ")
            exit(1)

    randstr = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(5))  # noqa: E501
    paramkey = '_'.join(['/ros12_bridged_', randstr])
    # make sure it's a global key
    progkey = '/' + paramkey if paramkey[0] != '/' else paramkey
    topic_regex_key = progkey + '_topics'
    service_regex_key = progkey + '_services'

    if 'topics' in data:
        rospy.set_param(progkey + '_topics', data['topics'])
    if 'services' in data:
        rospy.set_param(progkey + '_services', data['services'])

    topics_direction = ""
    if args.bridge_all_topics:
        topics_direction += " --bridge-all-topics"
    if args.bridge_all_1to2_topics:
        topics_direction += " --bridge-all-1to2-topics"
    if args.bridge_all_2to1_topics:
        topics_direction += " --bridge-all-2to1-topics"

    cmd_list = ["export ROS_MASTER_URI=%s" % rosmaster_uri]  # set rosmaster_uri
    cmd_list.append("source %s/devel/setup.bash" % ros1_ws_path)  # set ROS1 WS path
    cmd_list.append("source %s/install/setup.bash" % ros2_ws_path)  # set ROS2 WS path
    cmd_list.append("export ROS_DOMAIN_ID=%s" % args.domain_id)  # set ROS2 domain_id
    cmd_list.append(
        "ros2 run ros1_bridge dynamic_whitelist_bridge {topics_direction} --node-suffix {node_suffix} --topic-regex-list {topic_regex_key} --service-regex-list {service_regex_key}")  # noqa: E501
    cmd = " && ".join(cmd_list).format(topics_direction=topics_direction, node_suffix=args.name,
                                       topic_regex_key=topic_regex_key, service_regex_key=service_regex_key)
    res = subprocess.call("/bin/bash -c '%s'" % cmd, shell=True)
    if res != 0:
        print("Error occured while calling command. Something is wrong. ...Exiting.\nCommand Executed: %s" % cmd)
        exit(1)

if __name__ == "__main__":
    try:
        start()
    except KeyboardInterrupt:
        exit(1)

Analysis

Despite the release of ROS 2, many researchers and developers continue to publish work using ROS 1. There are a few reasons for this. One reason is that ROS 1 has a much larger user base and a longer history, so there is a larger pool of resources and tools available. Additionally, ROS 1 has a more mature ecosystem and a larger community of users, which can make it easier to get help and support.

Another reason why researchers may continue to use ROS 1 is that migrating from ROS 1 to ROS 2 can be inconvenient and time-consuming. ROS 2 introduces several major changes from ROS 1, including a new middleware, a new message passing model, and a new build system. This means that existing ROS 1 applications may need to be significantly modified in order to work with ROS 2. In some cases, it may be more practical to continue using ROS 1 rather than investing the time and effort to migrate to ROS 2.

Despite these challenges, there are several reasons why it is still worth upgrading to ROS 2. One reason is that ROS 2 has better real-time performance than ROS 1. This is because ROS 2 uses a data-driven model for message passing, which allows for more efficient communication between nodes. This can be especially important in robotics applications where real-time performance is critical.

Another reason to consider upgrading to ROS 2 is that it is decentralized. This means that nodes can communicate directly with each other without going through a central broker, which can improve scalability and reliability. ROS 2 also supports multiple middlewares, which allows for more flexibility in terms of networking and communication options.

When it comes to the tradeoffs between ROS 1 and ROS 2, it is important to consider the specific needs of your application. If you are building a new application from scratch and do not have any existing dependencies on ROS 1, then it may make sense to use ROS 2. However, if you have an existing ROS 1 application that is working well and you do not need the improved real-time performance or decentralization provided by ROS 2, then it may not be worth the effort to migrate.

Overall, while there are some challenges and tradeoffs to consider when deciding between ROS 1 and ROS 2, we believe that the benefits of ROS 2 make it worth considering for new robotics projects. In particular, the improved real-time performance and decentralization of ROS 2 can be important for applications that require high reliability and scalability. We therefore support the migration to ROS 2 and encourage researchers and developers to consider it for their robotics projects.

Leave a Reply

Your email address will not be published.