CREATING PUBLISHER AND SUBSCRIBER NODE USING C++
Introduction.
In ROS2(Robot Operating System 2), a node is the smallest executable unit that performs computation. It is essentially a process that communicates with other nodes to exchange data (via topics, services, or actions) and perform tasks in a robotic system. ROS 2 publishers and subscribers are the basic communication mechanism between nodes using topics. They can be created by either using python or C++ language. In this blog we will be creating using C++.
Prerequisites
ROS installed. installing ros2
ROS2 workspace set up. setting up ros2 workspace
Package setup.
Creating a package — ROS 2 Documentation: Jazzy documentationBasic Understanding of ROS concepts(nodes, topic, messages).
Beginner: CLI tools — ROS 2 Documentation: Jazzy documentation
Publisher-Subscriber Node in C++
1. Create a ROS 2 Package
cd ~ros2_ws/src
ros2 pkg create --build-type ament_cmake cpp_pubsub --dependencies rclcpp std_msgsThis creates a new C++ package named cpp_pubsub.
2. Add Publisher and Subscriber code
Navigate into the package:
cd ~/ros2_ws/src/cpp_pubsub/srcCreate the following files in the src/ directory:
src/talker.cpp
Key in the code below in the opened file above:
#include <chrono> // For time intervals like 500ms
#include <memory> // For shared_ptr
#include "rclcpp/rclcpp.hpp" // Core ROS 2 C++ client library
#include "std_msgs/msg/string.hpp" // Message type used in the publisher
using namespace std::chrono_literals; // Enables use of 500ms directly
class Talker : public rclcpp::Node {
public:
Talker() : Node("talker_node"), count_(0) {
publisher_ = this->create_publisher<std_msgs::msg::String>("chatter", 10);
timer_ = this->create_wall_timer(500ms, std::bind(&Talker::publish_message, this));
}
private:
void publish_message() {
auto message = std_msgs::msg::String();
message.data = "Hello ROS2! Count: " + std::to_string(count_++);
RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
publisher_->publish(message);
}
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
rclcpp::TimerBase::SharedPtr timer_;
int count_;
};
int main(int argc, char * argv[]) {
rclcpp::init(argc, argv); // Initialize ROS
rclcpp::spin(std::make_shared<Talker>()); // Run node
rclcpp::shutdown(); // Clean up
return 0;
}
src/listener.cpp
Key in the code below in the opened file above:
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
class Listener : public rclcpp::Node {
public:
Listener() : Node("listener_node") {
subscription_ = this->create_subscription<std_msgs::msg::String>(
"chatter", 10,
[this](const std_msgs::msg::String::SharedPtr msg) {
RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());
});
}
private:
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};
int main(int argc, char * argv[]) {
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<Listener>());
rclcpp::shutdown();
return 0;
}
3. Update CMakeLists.txt
Update CMakeLists.txt and add to make:
cmake_minimum_required(VERSION 3.8)
project(cpp_pubsub)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
add_executable(talker src/talker.cpp)
ament_target_dependencies(talker rclcpp std_msgs)
add_executable(listener src/listener.cpp)
ament_target_dependencies(listener rclcpp std_msgs)
install(TARGETS
talker
listener
DESTINATION lib/${PROJECT_NAME})
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
set(ament_cmake_copyright_FOUND TRUE)
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()4. Update package.xml
Update the code to have:
<?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>cpp_pubsub</name>
<version>0.1.0</version>
<description>TODO: Package description</description>
<maintainer email="wambuacaleb253@gmail.com">caleb253</maintainer>
<license>MIT</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>std_msgs</depend>
//Test dependencies
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
Ensure to include the dependencies if you did not include when creating the package.
<depend>rclcpp</depend>
<depend>std_msgs</depend>5. Build the Package.
From your workspace root:
cd ~/ros2_ws
colcon build --packages-select cpp_pubsubThen source your setup file:
source install/setup.bash6. Run the Publisher and Subscriber.
In two separate terminals:
Terminal 1 (Publisher):
source ~/ros2_ws/install/setup.bash
ros2 run cpp_pubsub talkerTerminal 2 (Subscriber):
source ~/ros2_ws/install/setup.bash
ros2 run cpp_pubsub listenerYou should be able to generate the results as shown below:
To stop the runs you should press Ctrl+c
Common Errors in ROS 2 C++ Publisher/Subscriber Nodes
1. Build Fails: Missing Dependency
Error
fatal error: rclcpp/rclcpp.hpp: No such file or directoryCause
You didn’t declare the dependency on rclcpp in CMakeLists.txt or package.xml.
Fix
In CMakeLists.txt:
find_package(rclcpp REQUIRED)
target_link_libraries(your_node_name rclcpp::rclcpp)In package.xml:
<build_depend>rclcpp</build_depend>
<exec_depend>rclcpp</exec_depend>2. Build Succeeds, but Node Crashes at Runtime
Symptom
Segmentation fault when running the node.
Common Causes
You forgot to initialize ROS before using the node.
You used a raw pointer without initializing it.
Missing
rclcpp::spin(…)inmain().
Fix
Always structure main() like this:
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<MyNode>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
3. Topic Mismatch: No Messages Received
Causes
Topic names differ (
"chatter"vs"chat").Mismatched message types.
Publisher or subscriber not active when tested.
Fix
Double-check both:
// Publisher
this->create_publisher<std_msgs::msg::String>("chatter", 10);
// Subscriber
this->create_subscription<std_msgs::msg::String>("chatter", 10, callback);
Use ros2 topic list and ros2 topic info to inspect topic state.
4. CMake Errors: Target Not Found
Error
CMake Error: Target "talker" not foundCause
You forgot to define the executable or link libraries.
Fix in CMakeLists.txt:
add_executable(talker src/talker.cpp)
ament_target_dependencies(talker rclcpp std_msgs)
install(TARGETS talker DESTINATION lib/${PROJECT_NAME})5. "Could not find package configuration file"
Error
Could not find a package configuration file provided by "rclcpp"Cause
Your environment isn't sourced properly.
Fix
source /opt/ros/humble/setup.bash
source ~/ros2_ws/install/setup.bash6. Nothing Is Published or Logged
Cause
rclcpp::TimerBase::SharedPtrnot retained — it gets garbage-collected.You forgot to create the timer or use
rclcpp::spin().
Fix
In your class:
rclcpp::TimerBase::SharedPtr timer_; // define in class
timer_ = this->create_wall_timer(
std::chrono::seconds(1),
std::bind(&Talker::timer_callback, this));7. Missing #include Statements
Error
‘cout’ was not declared in this scopeCause
You’re using standard C++ things (like std::cout) but forgot the includes.
Fix
#include <iostream>Similarly, always include:
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"8. Publisher Not Publishing
Cause
You forgot to store the publisher as a class member.
Fix
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
publisher_ = this->create_publisher<std_msgs::msg::String>("chatter", 10);If you declare the publisher inside a function (local scope), it will go out of scope and stop working.
9. Wrong ament_target_dependencies Syntax
Error
Unknown CMake command: ament_target_dependenciesCause
You didn’t call
find_package()before usingament_target_dependencies.Typos in dependency names.
Fix
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
ament_target_dependencies(your_node rclcpp std_msgs)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
ament_target_dependencies(your_node rclcpp std_msgs)

Comments
Post a Comment