Tutorial Create a C Publisher Node - wjwwood/ros_cmake_auto_examples GitHub Wiki

Create a C++ Publisher Node

Create Package Folder

For this tutorial we'll assume the package name is node_with_publisher, but you can use any name you like.

First create a folder for the package you are creating, as well as a folder to keep the C++ source files within:

$ mkdir -p node_with_publisher/src

Create C++ Source File

In that folder you just created, make a C++ source file called node_with_publisher.cpp:

#include <chrono>
#include <cstdio>

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

using namespace std::chrono_literals;

class NodeWithPublisher : public rclcpp::Node
{
  size_t count_;
  rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
  rclcpp::TimerBase::SharedPtr timer_;
public:
  NodeWithPublisher() : rclcpp::Node("node_with_publisher"), count_(1) {
    printf("In NodeWithPublisher()!\n");
    publisher_ = this->create_publisher<std_msgs::msg::String>("chatter");
    timer_ = this->create_wall_timer(1s, [this]() {
      std_msgs::msg::String msg;
      msg.data = "Hello World: " + std::to_string(count_++);
      this->publisher_->publish(msg);
      printf("Publishing: '%s'\n", msg.data.c_str());
    });
  }
};

#include "rclcpp_components/register_node_macro.hpp"

RCLCPP_REGISTER_NODE(NodeWithPublisher)

Create Package and Build System Files

Before you can build this node you need to declare it and its dependencies with a package manifest (a package.xml file) and create a CMakeLists.txt file with instructions on how to build it.

First create the package.xml in the package folder, e.g. node_with_publisher:

<?xml version="1.0"?>
<?xml-model
  href="http://download.ros.org/schema/package_format2.xsd"
  schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
  <name>node_with_publisher</name>
  <version>0.0.0</version>
  <description>
    Example package with a Node that contains a publisher called node_with_publisher.
  </description>
  <maintainer email="[email protected]">Your Name</maintainer>

  <license>Apache License 2.0</license>

  <buildtool_depend>ros_cmake_auto</buildtool_depend>

  <depend>rclcpp</depend>
  <depend>rclcpp_components</depend>
  <depend>std_msgs</depend>

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

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

And then create the CMakeLists.txt in the same folder as the package.xml:

cmake_minimum_required(VERSION 3.5)
project(node_with_publisher)

find_package(ros_cmake_auto REQUIRED)
ros_cmake_auto_alias_to_rca()

rca_start(LANGUAGES c++ CPP_STD c++14)

rca_add_node(node_with_publisher CLASS_NAME NodeWithPublisher src/node_with_publisher.cpp)

rca_end()

The rca_add_node() CMake macro takes a node name, in this case node_with_publisher, the class name of your node, and the source files that make up your node. It is also doing several things that you should be aware of, it:

  • creates a shared library for your node that can be run in its own process or loaded into a process with other nodes,
    • the library is created with the given source files and its name is _nodeplugin appended to the given node name,
    • the library also uses all of the include directories, directives, and linker flags for the dependencies of the package,
  • creates a convenience executable with the given node name,
    • this executable can run your node in a process or
    • load your node as a plugin in another process
  • registers your node in a registry so that other packages and tools can find it

Build the Package

Before trying to build your package, make sure you sourced the setup file that comes with ROS 2 so your package can find all of its dependencies:

$ source path/to/ROS2/install/setup.bash

Since, at its core, this is just a CMake package you can go to the package directory and follow the normal CMake build process:

$ cd path/to/node_with_publisher
$ mkdir build
$ cd build
$ cmake .. -DCMAKE_INSTALL_PREFIX=./install
$ make install

Notice that we've prescribed a install folder in the build folder but only to avoid the need for sudo. You can adjust the CMake settings to suit your needs.

Run the Node

Before "using" your package, you need to source the setup file produced while building:

$ source path/to/node_with_publisher/build/install/share/node_with_publisher/setup.bash

You will now have an executable on your PATH called node_with_publisher, which you can be used to run your node:

$ node_with_publisher
Load library path/to/node_with_publisher/build/install/lib/libnode_with_publisher_nodeplugin.dylib
Instantiate class NodeWithPublisher
In NodeWithPublisher()!
Publishing: 'Hello World: 1'
Publishing: 'Hello World: 2'
Publishing: 'Hello World: 3'
Publishing: 'Hello World: 4'

You can ctrl-c that process to stop the node.

Run the Node in a Separate Process

You can also use this executable to run your node in a separate process with other nodes. For this you will need a "container" for your node. A generic container is provided by rclcpp, which you can run like this:

$ rclcpp_component_container --name my_container

You have to give a name, but it can be whatever you want.

In a new terminal (don't forget to source your setup file again) you can use the executable your package created to have that container load and run your node:

$ node_with_publisher --remote-container my_container
Launching node component 'node_with_publisher/NodeWithPublisher' in remote container 'my_container'...
Loading was successful

That invocation should have exited directly after loading.

In the other terminal where the container is running you should see some output from your node:

Load library path/to/node_with_publisher/build/install/lib/libnode_with_publisher_nodeplugin.dylib
Instantiate class NodeWithPublisher
In NodeWithPublisher()!
Publishing: 'Hello World: 1'
Publishing: 'Hello World: 2'
Publishing: 'Hello World: 3'
Publishing: 'Hello World: 4'

You can ctrl-c it as well when you are done.

⚠️ **GitHub.com Fallback** ⚠️