# 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 ```