# ROS2 Topics
## 0. Post Details
- Reference: [Section 5, ROS 2 Topics - Make Your Nodes Communicate Between Each Other, ROS 2 For Beginners (ROS Jazzy - 2025), Edouard Renard, Udemy.](https://www.udemy.com/course/ros2-for-beginners)
- Post by: Jedd Yang
- Date:
- 2025-09-19 (Started)
- [x] 2025-09-30 (Deadline)
- Keywords: Robotics, ROS2
## 1. ROS 2 Topics - Make Your Nodes Communicate Between Each Other
### 1.1 Write a Python Publisher
```bash=
cd ~/ros2_ws/src/my_py_pkg/my_py_pkg/
touch robot_news_station.py
chmod +x robot_news_station.py
cd ../..
code .
```
in the new file, `robot_news_station.py`, write
```py=
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from example_interfaces.msg import String
class RobotNewsStationNode(Node):
def __init__(self):
super().__init__("robot_news_station")
self.robot_name = "C3PO"
self.publisher_ = self.create_publisher(String, "robot_news", 10)
self.timer_ = self.create_timer(0.5, self.publish_news)
self.get_logger().info("Robot News Station has been started.")
def publish_news(self):
msg = String()
msg.data = f"Hi, this is {self.robot_name} from the robot news station"
self.publisher_.publish(msg)
def main(args=None):
rclpy.init(args=args)
node = RobotNewsStationNode()
rclpy.spin(node)
rclpy.shutdown()
if __name__ == "__main__":
main()
```
Remember to add this in the `entry_points` in `setup.py`,
```py=
"robot_news_station = my_py_pkg.robot_news_station:main"
```
and the dependencies in the `package.xml`,
```xml=
<depend>example_interfaces</depend>
```
Open two terminals in terminator, in the first one, build, source and run.
```bash=
cd ~/ros2_ws
colcon build --packages-select my_py_pkg
source ~/.bashrc
ros2 run my_py_pkg robot_news_station
```
in the second one,
```bash=
ros2 topic list
ros2 topic echo /robot_news
# You'll see the output
# data: Hi, this is C3PO from the robot news station
# ---
# data: Hi, this is C3PO from the robot news station
# ---
# data: Hi, this is C3PO from the robot news station
# ---
```
That means your publisher is working successfully.
### 1.2 Write a Python Subscriber
```bash=
cd ~/ros2_ws/src/my_py_pkg/my_py_pkg/
touch smartphone.py
chmod +x smartphone.py
cd ../..
code .
```
in the new file, `smartphone.py`, write
```py=
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from example_interfaces.msg import String
class SmartphoneNode(Node):
def __init__(self):
super().__init__("smartphone")
self.subscriber_ = self.create_subscription(
String, "robot_news", self.callback_robot_news, 10)
self.get_logger().info("Smartphone has been started.")
def callback_robot_news(self, msg: String):
self.get_logger().info(msg.data)
def main(args=None):
rclpy.init(args=args)
node = SmartphoneNode()
rclpy.spin(node) # to keep the node alive
rclpy.shutdown()
if __name__ == "__main__":
main()
```
Remember to add this in the `entry_points` in `setup.py`,
```py=
"smartphone = my_py_pkg.smartphone:main"
```
Open two terminals in terminator, in the first one, build, source and run.
```bash=
cd ~/ros2_ws
colcon build --packages-select my_py_pkg
source ~/.bashrc
ros2 run my_py_pkg smartphone # run the subscriber
```
in the second one,
```bash=
ros2 run my_py_pkg robot_news_station # run the publisher
# You'll see the output on the subscriber terminal
# [INFO] [1758088340.978817111] [smartphone]: Smartphone has been started.
# [INFO] [1758088366.559186492] [smartphone]: Hello, this is C3PO from the robot news station
# [INFO] [1758088367.061466364] [smartphone]: Hello, this is C3PO from the robot news station
# [INFO] [1758088367.560473038] [smartphone]: Hello, this is C3PO from the robot news station
# [INFO] [1758088368.059429232] [smartphone]: Hello, this is C3PO from the robot news station
```
That means your subscriber has subscibed your publisher successfully.
### 1.3 Write a C++ Publisher
```bash=
cd ~/ros2_ws/src/my_cpp_pkg/src/
touch robot_news_station.cpp
cd ../..
code .
```
In the VS Code,
```cpp=
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/msg/string.hpp"
using namespace std::chrono_literals;
class RobotNewsStationNode:
public rclcpp::Node{
public:
RobotNewsStationNode() : Node("robot_news_station"), robot_name_("R2D2"){
publisher_ = this->create_publisher<example_interfaces::msg::String>("robot_news", 10);
timer_ = this->create_wall_timer(0.5s, std::bind(&RobotNewsStationNode::publishNews, this));
RCLCPP_INFO(this->get_logger(), "Robot news station has been started.");
}
private:
void publishNews(){
auto msg = example_interfaces::msg::String();
msg.data = std::string("Hi, this is ") + robot_name_ + std::string(" from the robot news station.");
publisher_ -> publish(msg);
}
std::string robot_name_;
rclcpp::Publisher<example_interfaces::msg::String>::SharedPtr publisher_;
rclcpp::TimerBase::SharedPtr timer_;
int counter_;
};
int main(int argc, char **argv){
rclcpp::init(argc, argv);
auto node = std::make_shared<RobotNewsStationNode>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
```
And added this to `package.xml`,
```txt=
<depend>example_interfaces</depend>
```
and these to CMakeLists.txt
```cpp=
find_package(example_interfaces REQUIRED)
add_executable(robot_news_station src/robot_news_station.cpp)
ament_target_dependencies(robot_news_station rclcpp example_interfaces)
install(TARGETS
//...
robot_news_station
//...
)
```
Now back to the bash,
```bash=
cd ~/ros2_ws/
colcon build --packages-select my_cpp_pkg
source install/setup.bash
```
Open terminator and open two terminals, in the first one,
```bash=
ros2 run my_cpp_pkg robot_news_station
# You will see [INFO] [1758249996.341116903] [robot_news_station]: Robot news station has been started.
```
in the second one,
```bash=
ros2 topic echo /robot_news
# You will see
# data: Hi, this is R2D2 from the robot news station.
# ---
# data: Hi, this is R2D2 from the robot news station.
# ---
# data: Hi, this is R2D2 from the robot news station.
# ---
```
Now we use the Python subscriber to subscribe the C++ publisher,
in the second terminal, `Ctrl + C` cancel it and run
```bash=
ros2 run my_py_pkg smartphone
# You will see
# [INFO] [1758250078.882947170] [smartphone]: Smartphone has been started.
# [INFO] [1758250079.345520218] [smartphone]: Hi, this is R2D2 from the robot news station.
# [INFO] [1758250079.843194320] [smartphone]: Hi, this is R2D2 from the robot news station.
# [INFO] [1758250080.343514942] [smartphone]: Hi, this is R2D2 from the robot news station.
```
### 1.4 Write a C++ Subscriber
```bash=
cd
cd ros2_ws/src/
touch smartphone.cpp
code .
```
Write the following code in the `smartphone.cpp`,
```cpp=
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/msg/string.hpp"
using namespace std::placeholders;
class SmartphoneNode:
public rclcpp::Node{
public:
SmartphoneNode() : Node("smartphone"){
subscriber_ = this->create_subscription<example_interfaces::msg::String>(
"robot_news", 10, // 10 is the queue size for the topic
std::bind(&SmartphoneNode::callbackRobotNews, this, _1)
);
RCLCPP_INFO(this->get_logger(), "Smartphone has been started.");
// timer_ = this->create_wall_timer(std::chrono::seconds(1), std::bind(&MyNode::timerCallback, this));
}
private:
void callbackRobotNews(const example_interfaces::msg::String::SharedPtr msg){
RCLCPP_INFO(this->get_logger(), "%s", msg->data.c_str());
// counter_++;
}
rclcpp::Subscription<example_interfaces::msg::String>::SharedPtr subscriber_;
};
int main(int argc, char **argv){
rclcpp::init(argc, argv);
auto node = std::make_shared<SmartphoneNode>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
```
Remember to add this to the `CMakeLists.txt`,
```cmake=
# ...
add_executable(smartphone src/smartphone.cpp)
ament_target_dependencies(smartphone rclcpp example_interfaces)
install(TARGETS
# ...
smartphone
)
```
Now open up a terminator in two terminals, in the first one, run
```bash
ros2 run my_cpp_pkg smartphone
# You'll see
# [INFO] [1758540224.354945632] [smartphone]: Smartphone has been started.
```
in the second one, run the C++ publisher
```bash
ros2 run my_cpp_pkg robot_news_station
# You'll see this in the subscriber terminal
# [INFO] [1758540280.956643721] [smartphone]: Hi, this is R2D2 from the robot news station.
# [INFO] [1758540281.452004473] [smartphone]: Hi, this is R2D2 from the robot news station.
# [INFO] [1758540281.953071703] [smartphone]: Hi, this is R2D2 from the robot news station.
# ...
```
Or you can run the Python publisher
```bash
ros2 run my_py_pkg robot_news_station
# You'll see this in the subscriber terminal
# [INFO] [1758540301.478050457] [smartphone]: Hello, this is C3PO from the robot news station
# [INFO] [1758540301.981253411] [smartphone]: Hello, this is C3PO from the robot news station
# [INFO] [1758540302.482983316] [smartphone]: Hello, this is C3PO from the robot news station
# ...
```
### 1.5 Useful ROS2 Topics Commands
1. Introspect ROS2 Topics
```bash=
# list all topics
ros2 topic list
# /parameters_events
# /robot_news
# /rosout
# show publisher and subscriber counts
ros2 topic info /robot_news
# show bandwidth
ros2 topic bw /robot_news
```
2. Remap a Topic at Runtime
```bash=
# remap publisher
ros2 run my_py_pkg robot_news_station --ros-args -r __node:=my_station -r robot_news:=abc
# remap subscriber
ros2 run my_py_pkg smartphone --ros-args -r robot_news:=abc
```