How to Create a ROS 2 C++ Subscriber – Iron

In this tutorial, we will go over how to create a C++ subscriber for ROS 2.

In ROS 2 (Robot Operating System 2), a C++ subscriber is a program (written in C++) that listens for messages being published on a specific topic.

Topics in ROS 2 are channels of communication named according to the type of information they carry, such as “/robot/speed” for speed information or “/camera/image” for vision information. Each subscriber in ROS 2 declares its interest in a particular topic and is programmed to react or process the messages received on that topic.

The official instructions for creating a subscriber are here, but I will walk you through the entire process, step by step.

We will be following the ROS 2 C++ Style Guide.

Let’s get started!

Prerequisites

Directions

Open a terminal, and type these commands to open VS Code.

cd ~/ros2_ws
code .

Write the Code

Go back to the Explorer (Ctrl + Shift + E).

Right-click on the src folder to create a new file called “minimal_cpp_subscriber.cpp”.

Type the following code inside minimal_cpp_subscriber.cpp:

/**
 * @file minimal_cpp_subscriber.cpp
 * @brief Demonstrates subscribing to string messages on a ROS 2 topic.
 *
 * Description: Demonstrates the basics of subscribing to messages within the ROS 2 framework. 
 * The core functionality of this subscriber is to display output to the terminal window
 * when a message is received over a topic.
 * 
 * -------
 * Subscription Topics:
 *   String message
 *   /topic_cpp - std_msgs/String
 * -------
 * Publishing Topics:
 *   None
 * -------
 * @author Addison Sears-Collins
 * @date 2024-02-15
 */

#include "rclcpp/rclcpp.hpp" // ROS 2 C++ client library for node creation and management
#include "std_msgs/msg/string.hpp" // Standard message type for string messages
using std::placeholders::_1; // Create a placeholder for the first argument of the function

/**
 * @class MinimalSubscriber
 * @brief Defines a minimal ROS 2 subscriber node.
 *
 * This class inherits from rclcpp::Node and demonstrates creating a subscriber and
 * subscribing to messages.
 */
class MinimalSubscriber : public rclcpp::Node
{
public:
    /**
     * @brief Constructs a MinimalSubscriber node.
     *
     * Sets up a subscriber for 'std_msgs::msg::String' messages on the "topic_cpp" topic.     * 
     */
    MinimalSubscriber() : Node("minimal_subscriber")
    {
        // Create a subscriber object for listening to string messages on 
        // the "topic_cpp" topic with a queue size of 10.
        subscriber_ = create_subscription<std_msgs::msg::String>
        (
            "/topic_cpp", 
            10, 
            std::bind(
                &MinimalSubscriber::topicCallback, 
                this, 
                _1
            )
        );	
    }

    /**
     * @brief This function runs every time a message is received on the topic.
     *
     * This is the callback function of the subscriber. It publishes a string message
     * every time a message is received on the topic.
     * 
     * @param msg The string message received on the topic
     * @return Void.
     */
    void topicCallback(const std_msgs::msg::String &msg) const
    {
        // Write a message every time a new message is received on the topic.
        RCLCPP_INFO_STREAM(get_logger(), "I heard: " << msg.data.c_str());

    }
	
private:
    // Member variables.
    rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscriber_; // The subscriber object.
};

/**
 * @brief Main function.
 *
 * Initializes the ROS 2 system and runs the minimal_subscriber node. It keeps the node
 * alive until it is manually terminated.
 */
int main(int argc, char * argv[])
{

  // Initialize ROS 2.
  rclcpp::init(argc, argv); 
  
  // Create an instance of the MinimalPublisher node and keep it running.
  auto minimal_subscriber_node = std::make_shared<MinimalSubscriber>();
  rclcpp::spin(minimal_subscriber_node);

  // Shutdown ROS 2 upon node termination.
  rclcpp::shutdown(); 

  // End of program.
  return 0; 
}

Configure CMakeLists.txt

Now we need to modify the CMakeLists.txt file inside the package so that the ROS 2 system will be able to find the cost we just wrote.

Open up the CMakeLists.txt file that is inside the package.

Make it look like this:

cmake_minimum_required(VERSION 3.8)
project(cobot_arm_examples)

# Check if the compiler being used is GNU's C++ compiler (g++) or Clang.
# Add compiler flags for all targets that will be defined later in the 
# CMakeLists file. These flags enable extra warnings to help catch
# potential issues in the code.
# Add options to the compilation process
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# Locate and configure packages required by the project.
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_python REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclpy REQUIRED)
find_package(std_msgs REQUIRED)

# Define a CMake variable named dependencies that lists all
# ROS 2 packages and other dependencies the project requires.
set(dependencies
  rclcpp
  std_msgs
)

# Add the specified directories to the list of paths that the compiler
# uses to search for header files. This is important for C++
# projects where you have custom header files that are not located
# in the standard system include paths.
include_directories(
  include
)

# Tells CMake to create an executable target named minimal_cpp_publisher
# from the source file src/minimal_cpp_publisher.cpp. Also make sure CMake
# knows about the program's dependencies.
add_executable(minimal_cpp_publisher src/minimal_cpp_publisher.cpp)
ament_target_dependencies(minimal_cpp_publisher ${dependencies})

add_executable(minimal_cpp_subscriber src/minimal_cpp_subscriber.cpp)
ament_target_dependencies(minimal_cpp_subscriber ${dependencies})

# Copy necessary files to designated locations in the project
install (
  DIRECTORY cobot_arm_examples scripts
  DESTINATION share/${PROJECT_NAME}
)

install(
  DIRECTORY include/
  DESTINATION include
)

# Install cpp executables
install(
  TARGETS
  minimal_cpp_publisher
  minimal_cpp_subscriber
  DESTINATION lib/${PROJECT_NAME}
)

# Install Python modules for import
ament_python_install_package(${PROJECT_NAME})

# Install Python executables
install(
  PROGRAMS
  scripts/minimal_py_publisher.py
  scripts/minimal_py_subscriber.py
  #scripts/example3.py
  #scripts/example4.py
  #scripts/example5.py
  #scripts/example6.py
  #scripts/example7.py
  DESTINATION lib/${PROJECT_NAME}
)

# Automates the process of setting up linting for the package, which
# is the process of running tools that analyze the code for potential
# errors, style issues, and other discrepancies that do not adhere to
# specified coding standards or best practices.
if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # comment the line when a copyright and license is added to all source files
  set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # comment the line when this package is in a git repo and when
  # a copyright and license is added to all source files
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

# Used to export include directories of a package so that they can be easily
# included by other packages that depend on this package.
ament_export_include_directories(include)

# Generate and install all the necessary CMake and environment hooks that 
# allow other packages to find and use this package.
ament_package()

Configure package.xml

Now we need to configure the package.xml file.

Open the package.xml file, and make sure it looks like this:

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>cobot_arm_examples</name>
  <version>0.0.0</version>
  <description>Basic examples demonstrating ROS 2</description>
  <maintainer email="automaticaddison@example.com">Addison Sears-Collins</maintainer>
  <license>Apache-2.0</license>

  <!--Specify build tools that are needed to compile the package-->
  <buildtool_depend>ament_cmake</buildtool_depend>
  <buildtool_depend>ament_cmake_python</buildtool_depend>

  <!--Declares package dependencies that are required for building the package-->
  <depend>rclcpp</depend>
  <depend>rclpy</depend>
  <depend>std_msgs</depend>

  <!--Specifies dependencies that are only needed for testing the package-->
  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

Build the Workspace

cd ~/ros2_ws
colcon build
source ~/.bashrc

Run the Nodes

First run your publisher node.

ros2 run cobot_arm_examples minimal_cpp_publisher 
23-run-cpp-publisher-1
24-topic-echo-cpp-publisher-1

Now run your subscriber node.

ros2 run cobot_arm_examples minimal_cpp_subscriber
27_minimal_cpp_subscriber

An Important Notes on Subscribers and Publishers

In the example above, we published a string message to a topic named /topic_cpp using a C++ node, and we subscribed to that topic using a C++node. 

ROS 2 is language agnostic, so we could have also used a Python node to the publishing and a C++ node to do the subscribing, and vice versa.

That’s it for now. Keep building!

How to Create a ROS2 C++ Publisher – Iron

In this tutorial, we will go over how to create a C++ publisher for ROS 2.

In ROS 2 (Robot Operating System 2), a C++ publisher is a program (written in C++) that sends messages across the ROS network to other parts of the system.

The official instructions for creating a publisher are here, but I will walk you through the entire process, step by step.

We will be following the ROS 2 C++ Style Guide.

Let’s get started!

Prerequisites

Directions

Open a terminal, and type these commands to open VS Code.

cd ~/ros2_ws
code .

Go to View -> Extensions

Search for the “Doxygen Documentation Generator” extension.

22-doxygen-documentation-generator

Install it.

Write the Code

Go back to the Explorer (Ctrl + Shift + E).

Right-click on the src folder to create a new file called “minimal_cpp_publisher.cpp”.

Type the following code inside minimal_cpp_publisher.cpp:

/**
 * @file minimal_cpp_publisher.cpp
 * @brief Demonstrates publishing string messages to a ROS 2 topic.
 *
 * Description: Demonstrates the basics of publishing messages within the ROS 2 framework. 
 * The core functionality of this publisher is to repeatedly send out string messages
 * at a fixed frequency.
 * 
 * -------
 * Subscription Topics:
 *   None
 * -------
 * Publishing Topics:
 *   String message
 *   /topic_cpp - std_msgs/String
 * -------
 * @author Addison Sears-Collins
 * @date 2024-02-12
 */

#include "rclcpp/rclcpp.hpp" // ROS 2 C++ client library for node creation and management
#include "std_msgs/msg/string.hpp" // Standard message type for string messages

using namespace std::chrono_literals; // Enables the specification of a time duration

/**
 * @class MinimalPublisher
 * @brief Defines a minimal ROS 2 publisher node.
 *
 * This class inherits from rclcpp::Node and demonstrates creating a publisher,
 * publishing messages, and using a timer callback in ROS 2.
 */
class MinimalPublisher : public rclcpp::Node
{
public:
    /**
     * @brief Constructs a MinimalPublisher node.
     *
     * Sets up a publisher for 'std_msgs::msg::String' messages on the "topic_cpp" topic
     * and initializes a timer to call the timerCallback method.
     */
    MinimalPublisher() : Node("minimal_publisher"), count_(0) 
    {
        // Create a publisher object for sending string messages on the "topic_cpp" topic 
        // with a queue size of 10.
        publisher_ = create_publisher<std_msgs::msg::String>("topic_cpp", 10);
	
	    // Set up a timer to call the timerCallback function 
	    timer_ = create_wall_timer(500ms, std::bind(&MinimalPublisher::timerCallback, this));
        RCLCPP_INFO(get_logger(), "Publishing at 2 Hz");
    }

    /**
     * @brief Timer callback function.
     *
     * This method is called at a fixed interval. It publishes a string message
     * containing "Hello World" followed by a sequence number.
     * 
     *  @return Void.
     */
    void timerCallback() 
    {
        // Create a new String message object.
        auto message = std_msgs::msg::String();
        message.data = "Hello World! " +  std::to_string(count_++);
        
		// Publish the message
        publisher_->publish(message);
    }
	
private:
    // Member variables.
    size_t count_; // A counter to keep track of the number of messages published.
    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_; // The publisher object.
    rclcpp::TimerBase::SharedPtr timer_; // Timer for scheduling the publishing task.
};

/**
 * @brief Main function.
 *
 * Initializes the ROS 2 system and runs the minimal_publisher node. It keeps the node
 * alive until it is manually terminated.
 */
int main(int argc, char * argv[])
{

  // Initialize ROS 2.
  rclcpp::init(argc, argv); 
  
  // Create an instance of the MinimalPublisher node and keep it running.
  auto minimal_publisher_node = std::make_shared<MinimalPublisher>();
  rclcpp::spin(minimal_publisher_node);

  // Shutdown ROS 2 upon node termination.
  rclcpp::shutdown(); 

  // End of program.
  return 0; 
}

Configure CMakeLists.txt

Now we need to modify the CMakeLists.txt file inside the package so that the ROS 2 system will be able to find the cost we just wrote.

Open up the CMakeLists.txt file that is inside the package.

Make it look like this:

cmake_minimum_required(VERSION 3.8)
project(cobot_arm_examples)

# Check if the compiler being used is GNU's C++ compiler (g++) or Clang.
# Add compiler flags for all targets that will be defined later in the 
# CMakeLists file. These flags enable extra warnings to help catch
# potential issues in the code.
# Add options to the compilation process
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# Locate and configure packages required by the project.
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_python REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclpy REQUIRED)
find_package(std_msgs REQUIRED)

# Define a CMake variable named dependencies that lists all
# ROS 2 packages and other dependencies the project requires.
set(dependencies
  rclcpp
  std_msgs
)

# Add the specified directories to the list of paths that the compiler
# uses to search for header files. This is important for C++
# projects where you have custom header files that are not located
# in the standard system include paths.
include_directories(
  include
)

# Tells CMake to create an executable target named minimal_cpp_publisher
# from the source file src/minimal_cpp_publisher.cpp. Also make sure CMake
# knows about the program's dependencies.
add_executable(minimal_cpp_publisher src/minimal_cpp_publisher.cpp)
ament_target_dependencies(minimal_cpp_publisher ${dependencies})

add_executable(minimal_cpp_subscriber src/minimal_cpp_subscriber.cpp)
ament_target_dependencies(minimal_cpp_subscriber ${dependencies})

# Copy necessary files to designated locations in the project
install (
  DIRECTORY cobot_arm_examples scripts
  DESTINATION share/${PROJECT_NAME}
)

install(
  DIRECTORY include/
  DESTINATION include
)

# Install cpp executables
install(
  TARGETS
  minimal_cpp_publisher
  minimal_cpp_subscriber
  DESTINATION lib/${PROJECT_NAME}
)

# Install Python modules for import
ament_python_install_package(${PROJECT_NAME})

# Install Python executables
install(
  PROGRAMS
  scripts/minimal_py_publisher.py
  scripts/minimal_py_subscriber.py
  #scripts/example3.py
  #scripts/example4.py
  #scripts/example5.py
  #scripts/example6.py
  #scripts/example7.py
  DESTINATION lib/${PROJECT_NAME}
)

# Automates the process of setting up linting for the package, which
# is the process of running tools that analyze the code for potential
# errors, style issues, and other discrepancies that do not adhere to
# specified coding standards or best practices.
if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # comment the line when a copyright and license is added to all source files
  set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # comment the line when this package is in a git repo and when
  # a copyright and license is added to all source files
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

# Used to export include directories of a package so that they can be easily
# included by other packages that depend on this package.
ament_export_include_directories(include)

# Generate and install all the necessary CMake and environment hooks that 
# allow other packages to find and use this package.
ament_package()

Configure package.xml

Now we need to configure the package.xml file.

Open the package.xml file, and make sure it looks like this:

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>cobot_arm_examples</name>
  <version>0.0.0</version>
  <description>Basic examples demonstrating ROS 2</description>
  <maintainer email="automaticaddison@example.com">Addison Sears-Collins</maintainer>
  <license>Apache-2.0</license>

  <!--Specify build tools that are needed to compile the package-->
  <buildtool_depend>ament_cmake</buildtool_depend>
  <buildtool_depend>ament_cmake_python</buildtool_depend>

  <!--Declares package dependencies that are required for building the package-->
  <depend>rclcpp</depend>
  <depend>rclpy</depend>
  <depend>std_msgs</depend>

  <!--Specifies dependencies that are only needed for testing the package-->
  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

Build the Workspace

cd ~/ros2_ws
colcon build
source ~/.bashrc

Run the Node

ros2 run cobot_arm_examples minimal_cpp_publisher 
23-run-cpp-publisher

Let’s check out the list of topics.

ros2 topic list

Let’s check out the data coming over the topic_cpp topic.

ros2 topic echo /topic_cpp 
24-topic-echo-cpp-publisher
ros2 topic hz /topic_cpp
ros2 topic info /topic_cpp --verbose

How to Create a ROS 2 Python Subscriber – Iron

In this tutorial, we will go over how to create a Python subscriber for ROS 2.

In ROS 2 (Robot Operating System 2), a Python subscriber is a program or script (written in Python) that listens for messages being published on a specific topic.

Topics in ROS 2 are channels of communication named according to the type of information they carry, such as “/robot/speed” for speed information or “/camera/image” for vision information. Each subscriber in ROS 2 declares its interest in a particular topic and is programmed to react or process the messages received on that topic.

The official instructions for creating a subscriber are here, but I will walk you through the entire process, step by step.

We will be following the ROS 2 Python Style Guide.

Let’s get started!

Prerequisites

Directions

Open a terminal, and type these commands to open VS Code.

cd ~/ros2_ws
code .

Write the Code

Right-click on src/cobot_arm_examples/scripts, and create a new file called “minimal_py_subscriber.py

25-minimal-subscriber

Type the following code inside minimal_py_subscriber.py:

#! /usr/bin/env python3

"""
Description:
    This ROS 2 node periodically publishes "Hello World" messages on a topic.
    It demonstrates basic ROS concepts such as node creation, publishing, and
    timer usage.
-------
Publishing Topics:
    The channel containing the "Hello World" messages
    /topic - std_msgs/String
-------
Subscription Topics:
    None    
-------
Author: Addison Sears-Collins
Date: January 31, 2024
"""

import rclpy # Import the ROS 2 client library for Python
from rclpy.node import Node # Import the Node class for creating ROS 2 nodes

from std_msgs.msg import String # Import the String message type for publishing


class MinimalPublisher(Node):
    """Create MinimalPublisher node.

    """
    def __init__(self):
        """ Create a custom node class for publishing messages

        """

        # Initialize the node with a name
        super().__init__('minimal_publisher')

        # Creates a publisher on the topic "topic" with a queue size of 10 messages
        self.publisher_1 = self.create_publisher(String, '/topic', 10)

        # Create a timer with a period of 0.5 seconds to trigger the callback function
        timer_period = 0.5  # seconds
        self.timer = self.create_timer(timer_period, self.timer_callback)   

        # Initialize a counter variable for message content
        self.i = 0

    def timer_callback(self):
        """Callback function executed periodically by the timer.

        """
        # Create a new String message object
        msg = String()

        # Set the message data with a counter
        msg.data = 'Hello World: %d' % self.i

        # Publish the message on the topic
        self.publisher_1.publish(msg)

        # Log a message indicating that the message has been published
        self.get_logger().info('Publishing: "%s"' % msg.data)

        # Increment the counter for the next message
        self.i = self.i + 1


def main(args=None):
    """Main function to start the ROS 2 node.

    Args:
        args (List, optional): Command-line arguments. Defaults to None.
    """

    # Initialize ROS 2 communication
    rclpy.init(args=args)

    # Create an instance of the MinimalPublisher node
    minimal_publisher = MinimalPublisher()

    # Keep the node running and processing events.
    rclpy.spin(minimal_publisher)

    # Destroy the node explicitly
    # (optional - otherwise it will be done automatically
    # when the garbage collector destroys the node object)
    minimal_publisher.destroy_node()

    # Shutdown ROS 2 communication
    rclpy.shutdown()

if __name__ == '__main__':
    # Execute the main function if the script is run directly
    main()

To generate the comments for each class and function, you follow these steps for the autoDocstring package.

What we are going to do in this node is subscribe to a topic named /topic that contains String messages. 

Configure the Package

Modify the CMakeLists.txt File

Now let’s configure the CMakeLists.txt file. Here is what it should look like:

cmake_minimum_required(VERSION 3.8)
project(cobot_arm_examples)

# Check if the compiler being used is GNU's C++ compiler (g++) or Clang.
# Add compiler flags for all targets that will be defined later in the 
# CMakeLists file. These flags enable extra warnings to help catch
# potential issues in the code.
# Add options to the compilation process
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# Locate and configure packages required by the project.
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_python REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclpy REQUIRED)
find_package(std_msgs REQUIRED)

# Define a CMake variable named dependencies that lists all
# ROS 2 packages and other dependencies the project requires.
set(dependencies
  rclcpp
  std_msgs
)

# Add the specified directories to the list of paths that the compiler
# uses to search for header files. This is important for C++
# projects where you have custom header files that are not located
# in the standard system include paths.
include_directories(
  include
)

# Tells CMake to create an executable target named minimal_cpp_publisher
# from the source file src/minimal_cpp_publisher.cpp. Also make sure CMake
# knows about the program's dependencies.
add_executable(minimal_cpp_publisher src/minimal_cpp_publisher.cpp)
ament_target_dependencies(minimal_cpp_publisher ${dependencies})

add_executable(minimal_cpp_subscriber src/minimal_cpp_subscriber.cpp)
ament_target_dependencies(minimal_cpp_subscriber ${dependencies})

# Copy necessary files to designated locations in the project
install (
  DIRECTORY cobot_arm_examples scripts
  DESTINATION share/${PROJECT_NAME}
)

install(
  DIRECTORY include/
  DESTINATION include
)

# Install cpp executables
install(
  TARGETS
  minimal_cpp_publisher
  minimal_cpp_subscriber
  DESTINATION lib/${PROJECT_NAME}
)

# Install Python modules for import
ament_python_install_package(${PROJECT_NAME})

# Install Python executables
install(
  PROGRAMS
  scripts/minimal_py_publisher.py
  scripts/minimal_py_subscriber.py
  #scripts/example3.py
  #scripts/example4.py
  #scripts/example5.py
  #scripts/example6.py
  #scripts/example7.py
  DESTINATION lib/${PROJECT_NAME}
)

# Automates the process of setting up linting for the package, which
# is the process of running tools that analyze the code for potential
# errors, style issues, and other discrepancies that do not adhere to
# specified coding standards or best practices.
if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # comment the line when a copyright and license is added to all source files
  set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # comment the line when this package is in a git repo and when
  # a copyright and license is added to all source files
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

# Used to export include directories of a package so that they can be easily
# included by other packages that depend on this package.
ament_export_include_directories(include)

# Generate and install all the necessary CMake and environment hooks that 
# allow other packages to find and use this package.
ament_package()

Build the Workspace

Open a new terminal window, and type the following commands:

cd ~/ros2_ws/
colcon build
source ~/.bashrc

Run the Node 

In this section, we will finally run our node. Open a terminal window, and type:

ros2 run cobot_arm_examples minimal_py_publisher.py

Open another terminal window, and type:

ros2 run cobot_arm_examples minimal_py_subscriber.py

Now, press Enter.

Here is what the output looks like:

26-minimal-py-subscriber

Open a new terminal window.

Let’s see a list of all currently active topics.

ros2 topic list

What are the currently active nodes?

ros2 node list

Close the Node

Now go back to the terminals where your scripts are running and press CTRL + C to stop the execution.

To clear the terminal window, type:

clear

Congratulations! You have written your first subscriber in ROS 2.

In this example, you have written a subscriber to listen to a basic string message. On a real robot, you will write many different subscribers that subscribe to data that gets published by the different components of a robot: strings, LIDAR scan readings, ultrasonic sensor readings, camera frames, 3D point cloud data, integers, float values, battery voltage readings, odometry data, and much more. 

The code you wrote serves as a template for creating these more complex subscribers. All subscribers in ROS 2 are based on the basic framework as the subscriber you just wrote, minimal_py_subscriber.py.

In fact, even after over a decade of working with ROS, I still refer back to this template when I am building new subscriber nodes for my robots.