Tutorial Create a C Publisher Node - wjwwood/ros_cmake_auto_examples GitHub Wiki
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
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)
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,
- the library is created with the given source files and its name is
- 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
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.
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.
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.