How to Create a Subscriber Node in C++ – Jazzy

In this tutorial, we will 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!

Follow along with me click by click, keystroke by keystroke.

Prerequisites

Write the Code

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

cd ~/ros2_ws && code .

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

Type the following code inside cpp_minimal_subscriber.cpp:

/**
 * @file cpp_minimal_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
 *   /cpp_example_topic - std_msgs/String
 * -------
 * Publishing Topics:
 *   None
 * -------
 * @author Addison Sears-Collins
 * @date November 5, 2024
 */

#include "rclcpp/rclcpp.hpp" // ROS 2 C++ client library
#include "std_msgs/msg/string.hpp" // Handle tring messages
using std::placeholders::_1; // Placeholder for callback function argument

/**
 * @class MinimalCppSubscriber
 * @brief Defines a minimal ROS 2 subscriber node.
 *
 * This class inherits from rclcpp::Node and
 * demonstrates creating a subscriber and
 * subscribing to messages.
 */
class MinimalCppSubscriber : public rclcpp::Node
{
public:
    /**
     * @brief Constructs a MinimalCppSubscriber node.
     *
     * Sets up a subscriber for 'std_msgs::msg::String' messages
	 * on the "/cpp_example_topic" topic.
     */
    MinimalCppSubscriber() : Node("minimal_cpp_subscriber")
    {
        // Create a subscriber object for listening to string messages on
        // with a queue size of 10.
        subscriber_ = create_subscription<std_msgs::msg::String>
        (
            "/cpp_example_topic",
            10,
            std::bind(
                &MinimalCppSubscriber::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_;
};

/**
 * @brief Main function.
 *
 * Initializes the ROS 2 system and runs the minimal_cpp_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 MinimalCppSubscriber node and keep it running.
  auto minimal_cpp_subscriber_node = std::make_shared<MinimalCppSubscriber>();
  rclcpp::spin(minimal_cpp_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 code 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(ros2_fundamentals_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 cpp_minimal_publisher
# from the source file src/cpp_minimal_publisher.cpp. Also make sure CMake
# knows about the program's dependencies.
add_executable(cpp_minimal_publisher src/cpp_minimal_publisher.cpp)
ament_target_dependencies(cpp_minimal_publisher ${dependencies})

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

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

install(
  DIRECTORY include/
  DESTINATION include
)

# Install cpp executables
install(
  TARGETS
  cpp_minimal_publisher
  cpp_minimal_subscriber
  DESTINATION lib/${PROJECT_NAME}
)

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

# Add this section to install Python scripts
install(
  PROGRAMS
  ros2_fundamentals_examples/py_minimal_publisher.py
  ros2_fundamentals_examples/py_minimal_subscriber.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

To build the workspace, open a terminal window, and type:

build

If this command doesn’t work, type these commands:

echo "alias build='cd ~/dev_ws/ && colcon build && source ~/.bashrc'" >> ~/.bashrc
build

Run the Nodes

First run your publisher node.

ros2 run ros2_fundamentals_examples cpp_minimal_publisher 

Now run your subscriber node in another terminal window.

ros2 run ros2_fundamentals_examples cpp_minimal_subscriber
1-cpp-minimal-subscriber

An Important Note on Subscribers and Publishers

In the example above, we published a string message to a topic named /cpp_example_topic 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 do the publishing and a C++ node to do the subscribing, and vice versa.

That’s it for now. Keep building!

How to Create a ROS 2 Python Subscriber – Jazzy

In this tutorial, we will create a Python subscriber for ROS 2.

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

Remember, 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.

Follow along with me click by click, keystroke by keystroke.

Let’s get started!

Prerequisites

Write the Code

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

cd ~/ros2_ws && code .

Right-click on src/ros2_fundamentals_examples/ros2_fundamentals_examples, and create a new file called “py_minimal_subscriber.py

1-py-minimal-subscriber

Type the following code inside py_minimal_subscriber.py: 

#! /usr/bin/env python3

"""
Description:
    This ROS 2 node subscribes to "Hello World" messages on a topic.
    It demonstrates basic ROS concepts such as node creation and subscribing.
-------
Publishing Topics:
    None
-------
Subscription Topics:
    The channel containing the "Hello World" messages
    /py_example_topic - std_msgs/String
-------
Author: Addison Sears-Collins
Date: November 5, 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


class MinimalPySubscriber(Node):
    """Create Minimal Subscriber node.

    """

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

        """

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

        # Creates a subscriber
        self.subscriber_1 = self.create_subscription(
            String,
            '/py_example_topic',
            self.listener_callback,
            10)

    def listener_callback(self, msg):
        """Call this function every time a new message is published on
            the topic.

        """
        # Log a message indicating that the message has been published
        self.get_logger().info(f'I heard: "{msg.data}"')


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 MinimalPySubscriber node
    minimal_py_subscriber = MinimalPySubscriber()

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

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

    # Shutdown ROS 2 communication
    rclpy.shutdown()


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

The node we just wrote subscribes to a topic named /py_example_topic that contains String messages.

Configure CMakeLists.txt

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

cmake_minimum_required(VERSION 3.8)
project(ros2_fundamentals_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 cpp_minimal_publisher
# from the source file src/cpp_minimal_publisher.cpp. Also make sure CMake
# knows about the program's dependencies.
add_executable(cpp_minimal_publisher src/cpp_minimal_publisher.cpp)
ament_target_dependencies(cpp_minimal_publisher ${dependencies})

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

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

install(
  DIRECTORY include/
  DESTINATION include
)

# Install cpp executables
install(
  TARGETS
  cpp_minimal_publisher
#  cpp_minimal_subscriber
  DESTINATION lib/${PROJECT_NAME}
)

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

# Add this section to install Python scripts
install(
  PROGRAMS
  ros2_fundamentals_examples/py_minimal_publisher.py
  ros2_fundamentals_examples/py_minimal_subscriber.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

To build the workspace, open a terminal window, and type:

build

If this command doesn’t work, type these commands:

echo "alias build='cd ~/dev_ws/ && colcon build && source ~/.bashrc'" >> ~/.bashrc
build

Run the Node 

Let’s run our subscriber node. 

Open a terminal window, and type the following command to run the publisher node:

ros2 run ros2_fundamentals_examples py_minimal_publisher.py

Open another terminal window, and type the following command to run the subscriber node:

ros2 run ros2_fundamentals_examples py_minimal_subscriber.py

Now, press Enter.

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
2-output

Close the Nodes

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

Creating a Bash Script to Launch Nodes in Sequence

Let’s create a bash script to launch the publisher and subscriber nodes in sequence. Launching in this way gives you more control over the order in which your programs launch.

Right-click on src/ros2_fundamentals_examples/ and create a new folder called scripts.

3-create-scripts-folder

Right-click on src/ros2_fundamentals_examples/scripts, and create a new file called “minimal_pub_sub_launch.sh

Type the following code inside minimal_pub_sub_launch.sh: 

#!/bin/bash
# Launch publisher and subscriber nodes with cleanup handling

cleanup() {
    echo "Restarting ROS 2 daemon to cleanup before shutting down all processes..."
    ros2 daemon stop
    sleep 1
    ros2 daemon start
    echo "Terminating all ROS 2-related processes..."
    kill 0
    exit
}

trap 'cleanup' SIGINT

# Launch the publisher node
ros2 run ros2_fundamentals_examples py_minimal_publisher.py &
sleep 2

# Launch the subscriber node
ros2 run ros2_fundamentals_examples py_minimal_subscriber.py

Save the file and close it.

Go into the terminal, and change the permissions on the file to make it executable:

sudo chmod +x ~/ros2_ws/src/ros2_fundamentals_examples/scripts/minimal_pub_sub_launch.sh

Configure CMakeLists.txt

Now let’s configure the CMakeLists.txt file to add the scripts folder. Change this line:

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

To this:

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

Launch

Build the workspace.

build

Now we launch the nodes.

cd ~/ros2_ws/src/ros2_fundamentals_examples/scripts
./minimal_pub_sub_launch.sh

You could have also written:

bash minimal_pub_sub_launch.sh
4-running-bash-script

Close the Nodes

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, py_minimal_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.

That’s it. Keep building!

How to Create a ROS 2 C++ Publisher – Jazzy

In this tutorial, we will create a C++ publisher for ROS 2. Knowing how to write a publisher node is one of the most important skills in robotics software engineering. 

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.

Follow along with me click by click, keystroke by keystroke.

Let’s get started!

Prerequisites

Why C++?

So why should we write a publisher node in C++ instead of in Python?

Using C++ instead of Python for a ROS 2 node is like choosing a high-performance sports car over a regular sedan for a race. C++ is generally faster and more efficient, which is important for real-time applications where quick responses are necessary.

Also, C++ gives us better control over how the robot’s brain (the node) uses its resources, like memory and processing power. 

And while Python is easier to work with, especially for beginners, C++ provides the speed and precision needed for complex tasks, making it the preferred choice for developing ROS 2 nodes in scenarios where performance is important.

Write the Code

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

cd ~/ros2_ws && code .

Right-click on the src/ros2_fundamentals_examples/src folder, and create a new file called “cpp_minimal_publisher.cpp”.

Type the following code inside cpp_minimal_publisher.cpp:

/**
 * @file cpp_minimal_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
 *   /cpp_example_topic - std_msgs/String
 * -------
 * @author Addison Sears-Collins
 * @date November 5, 2024
 */

#include "rclcpp/rclcpp.hpp" // ROS 2 C++ client library
#include "std_msgs/msg/string.hpp" // Standard message type for strings

using namespace std::chrono_literals; // Handles time duration

/**
 * @class MinimalCppPublisher
 * @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 MinimalCppPublisher : public rclcpp::Node
{
public:
    /**
     * @brief Constructs a MinimalCppPublisher node.
     *
     * Sets up a publisher for 'std_msgs::msg::String' messages on
     * the "/cpp_example_topic" topic and initializes a timer to call
     * the timerCallback method.
     */
    MinimalCppPublisher() : Node("minimal_cpp_publisher"), count_(0)
    {
        // Create a publisher object for sending string messages
        // with a queue size of 10.
        publisher_ = create_publisher<std_msgs::msg::String>(
          "/cpp_example_topic", 10);

        // Set up a timer to call the timerCallback function
        timer_ = create_wall_timer(500ms,
          std::bind(&MinimalCppPublisher::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_;  // Keep track of the number of messages published
  rclcpp::Publisher<std_msgs::msg::String>::SharedPtr
    publisher_;  // The publisher object
  rclcpp::TimerBase::SharedPtr timer_;  // Timer
};

/**
 * @brief Main function.
 *
 * Initializes the ROS 2 system and runs the minimal_cpp_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 MinimalCppPublisher node and keep it running.
  auto minimal_cpp_publisher_node = std::make_shared<MinimalCppPublisher>();
  rclcpp::spin(minimal_cpp_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(ros2_fundamentals_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 cpp_minimal_publisher
# from the source file src/cpp_minimal_publisher.cpp. Also make sure CMake
# knows about the program's dependencies.
add_executable(cpp_minimal_publisher src/cpp_minimal_publisher.cpp)
ament_target_dependencies(cpp_minimal_publisher ${dependencies})

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

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

install(
  DIRECTORY include/
  DESTINATION include
)

# Install cpp executables
install(
  TARGETS
  cpp_minimal_publisher
#  cpp_minimal_subscriber
  DESTINATION lib/${PROJECT_NAME}
)

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

# Add this section to install Python scripts
install(
  PROGRAMS
  ros2_fundamentals_examples/py_minimal_publisher.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>ros2_fundamentals_examples</name>
  <version>0.0.0</version>
  <description>Basic examples demonstrating ROS 2 concepts</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

To build the workspace, open a terminal window, and type:

build

If this command doesn’t work, type these commands:

echo "alias build='cd ~/dev_ws/ && colcon build && source ~/.bashrc'" >> ~/.bashrc
build

Run the Node

Open a terminal window, and type:

ros2 run ros2_fundamentals_examples cpp_minimal_publisher 

Let’s check out the list of topics.

ros2 topic list

Let’s see the data coming over the /cpp_example_topic topic.

ros2 topic echo /cpp_example_topic 

Let’s see how often the topic is publishing a new message.

ros2 topic hz /cpp_example_topic
1-output

That’s it. I will see you in the next tutorial.

Keep building!