[ROS2 Mastery Part 4] ROS2 Topic Programming (1) – Controlling the Turtle with a Publisher

Hello, fellow students and researchers exploring the world of autonomous driving and robotics!

In Part 3, we created an empty ROS2 package and built a very basic node that outputs logs over time using Python. Starting with this fourth installment, we will dive into the core of robot control: ‘Communication.’ In this first session, we will implement a Publisher node to control the Turtlesim turtle, making it move autonomously in a circle through Topic programming.


Analyzing the Control Target: Identifying Topics and Messages

Before writing code to control a robot, the very first step is to identify: “To which topic and in what data format should I send commands to make the robot move?” We will investigate this using ROS2’s powerful CLI (Command Line Interface) tools.

First, open your terminal and run the Turtlesim simulator:

Bash
ros2 run turtlesim turtlesim_node

Now, open a new terminal to check the information of the topic used to move the turtle. The name of the topic for controlling the turtle is /turtle1/cmd_vel. Use the ros2 topic info command to look up the data type of this topic.

Bash
ros2 topic info /turtle1/cmd_vel

Based on the output, you can see that the /turtle1/cmd_vel topic uses the geometry_msgs/msg/Twist data type. So, what specific variables are contained within this Twist message? To find out, we use the ros2 interface show command.

Bash
ros2 interface show geometry_msgs/msg/Twist

Running the command will display the following structure in your terminal:

Bash
Vector3  linear
        float64 x
        float64 y
        float64 z
Vector3  angular
        float64 x
        float64 y
        float64 z

Upon analysis, the Twist message consists of two internal structures representing 3D vectors: linear (translational velocity) and angular (rotational velocity).

  • linear.x (Forward): The velocity at which the robot moves forward.
  • angular.z (Yaw): The rotational speed (angular velocity) at which the robot turns in place.

Therefore, the Python node we are about to write simply needs to create an object of type geometry_msgs/msg/Twist, assign appropriate values to linear.x and angular.z, and periodically publish it to the /turtle1/cmd_vel topic!


Writing the Publisher Node Code: draw_circle.py

Now, let’s get into the actual coding. Navigate to the source code directory of the my_robot_controller package we created in Part 3. Create a new Python file named draw_circle.py and grant it execution permissions (chmod +x) so the system can run the script.

Python
# Navigate to the node directory
cd ~/ros2_ws/src/my_robot_controller/my_robot_controller

# Create the file and grant execution permissions
touch draw_circle.py
chmod +x draw_circle.py

Open draw_circle.py in an editor like VS Code and write the following code. As you type, think about the specific role each part plays.

Python
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist  # Import the Twist message type analyzed earlier

class DrawCircleNode(Node):
    def __init__(self):
        super().__init__('draw_circle') # Set the node name to 'draw_circle'
        
        # 1. Create Publisher
        # Type: Twist, Topic Name: '/turtle1/cmd_vel', Queue Size: 10
        self.cmd_vel_pub_ = self.create_publisher(Twist, '/turtle1/cmd_vel', 10)
        
        # 2. Create Timer
        # Executes the 'send_velocity_command' callback function every 0.5 seconds
        self.timer_ = self.create_timer(0.5, self.send_velocity_command)
        
        self.get_logger().info("The Turtle Draw Circle node has started!")

    def send_velocity_command(self):
        # 3. Create Message Object and Assign Data
        msg = Twist()
        msg.linear.x = 2.0   # Move forward at a speed of 2.0 m/s
        msg.angular.z = 1.0  # Rotate at an angular velocity of 1.0 rad/s
        
        # 4. Publish the Topic
        self.cmd_vel_pub_.publish(msg)

def main(args=None):
    rclpy.init(args=args)
    node = DrawCircleNode()
    
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()

if __name__ == '__main__':
    main()

[In-depth Code Analysis]

  1. Module Import: We import rclpy, the core Python client library for ROS2, along with the Twist message type from geometry_msgs.msg, which we previously identified via the CLI.
  2. Creating the Publisher: We use the self.create_publisher method to create the publisher object. The first argument is the data type (Twist), the second is the topic name (/turtle1/cmd_vel), and the third is the Queue size (10), which represents the buffer size for storing messages in case of communication delays.
  3. Setting the Timer: In ROS2 programming, it is recommended to use timer callbacks rather than infinite loops (while True:). By using create_timer(0.5, ...), we set the registered callback function to execute every 0.5 seconds.
  4. Callback Function and Data Publishing: Inside the send_velocity_command function—which is called every 0.5 seconds—we create an instance of the Twist message. We assign 2.0 to linear.x and 1.0 to angular.z to command the robot to move forward while rotating. Finally, we broadcast the data to the ROS2 network using the publish() method.


Modifying Configuration Files: package.xml and setup.py

Just writing the Python code isn’t the end of the process! You must modify two configuration files so that the ROS2 build system (Colcon) can correctly build and execute this package.

1) Adding Dependencies to package.xml

In our Python code, we imported and used an external message package called geometry_msgs. You must specify that this package is required during the system build to avoid errors. Open the package.xml file located in the package root directory (~/ros2_ws/src/my_robot_controller) and add the following lines.

Additionally, since we are controlling the Turtlesim simulator, it is good practice to add a dependency for the turtlesim package as well.

XML
<!-- Add below other existing dependency tags -->
  <depend>geometry_msgs</depend>
  <depend>turtlesim</depend>

2) Adding an Entry Point to setup.py

You need to map the code so that when you enter the command ros2 run my_robot_controller draw_circle in the terminal, the system knows to find and execute the main function inside your Python file. Open the setup.py file in the package root directory and add the following line to the console_scripts array:

Python
entry_points={
        'console_scripts': [
            'test_node = my_robot_controller.my_first_node:main',
            # Add the entry point for the new node here
            'draw_circle = my_robot_controller.draw_circle:main'
        ],
    },
  • draw_circle: The name of the node to be executed from the terminal.
  • my_robot_controller.draw_circle:main: Points to the main function inside the draw_circle.py script within the my_robot_controller directory.


Build and Execution Test

All coding and configuration are now complete! Now, navigate back to the root directory of your workspace to build the newly updated package.

Bash
# Navigate to the workspace root
cd ~/ros2_ws

# Build all packages
colcon build --symlink-install

If the build completes successfully without errors, you are ready to run the node.

[Execution Steps]

  1. First Terminal: Launch the Turtlesim simulator.
  2. Second Terminal: Open a new terminal, source your newly created overlay environment, and execute the draw_circle node.

Success! Even without manual keyboard input, you will see the turtle rotating infinitely in a beautiful circle because our Publisher node is sending velocity commands (Topics) every 0.5 seconds.

💡 Researcher Tip: Visualizing Communication with RQT Graph

As robotic systems grow in complexity, visually tracking where data flows is crucial for effective debugging. While the turtle is moving, open a completely new terminal and run the rqt_graph command.

Bash
rqt_graph

On the screen, you will be able to clearly visualize the communication structure: the /draw_circle node (the publisher we created) sending /turtle1/cmd_vel topic data in real-time toward the /turtlesim node (the subscriber).


Closing

이In this 4th installment, we have explored the entire process of ‘Topic Publishing,’ which can be considered the heart of ROS2 programming, through hands-on coding.

To summarize what we’ve achieved:

  • We analyzed the data format we intended to control using CLI commands such as ros2 topic info and ros2 interface show.
  • We inherited from rclpy.node.Node to create a Publisher object and a periodic Timer.
  • We established a structure to continuously transmit data by assigning the correct values to the Twist message.

You now possess the fundamental ability to issue autonomous driving commands using your own algorithms for any robot platform, as long as you can identify the topic name and data structure the robot is Subscribing to!

In the next [ROS2 Mastery Part 5], we will explore the counterpart to this session: “Creating a Subscriber Node.” We will learn how to receive the turtle’s current position (x, y coordinates) sensor data in real-time and print it to the terminal.

We support your successful research in robotics and look forward to seeing you in the next post. Thank you!

Author: Aiden, Marketing Team at QUAD Drone Research Lab

Date: May 10, 2026

Similar Posts

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다