Skip to main content

Command Palette

Search for a command to run...

My First ROS2 Robot: Making Turtlesim Move in a Circle

Updated
5 min read
My First ROS2 Robot: Making Turtlesim Move in a Circle
S
I am an AI Research Engineer with a combined motivation of building AI models as well as developing AI integrated apps. I am currently exploring Robot Learning and groundbreaking DL, RL and Robotics papers and trying to understand how this is shaping the future.

Demo Video: https://drive.google.com/file/d/154eNwBe6CacOM7m-WoXqhVTfeJqjVc1J/view?usp=drive_link

Introduction

Today I built my first ROS2 robot controller.

The goal was simple: create a custom ROS2 node that publishes velocity commands to a robot and makes it move autonomously. Instead of controlling the robot manually with keyboard inputs, I wrote my own ROS2 node that continuously publishes movement commands.

To keep things simple, I used Turtlesim, the standard ROS2 learning simulator.

By the end of this exercise, I learned:

  • ROS2 Nodes

  • Topics

  • Publishers and Subscribers

  • ROS2 Python Packages

  • Message Types

  • The /cmd_vel interface

  • How robot movement is controlled in ROS2


Environment Setup

Operating System

Ubuntu Linux

ROS2 Workspace

mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
colcon build
source install/setup.bash

Verify ROS2 Installation

ros2 topic list
ros2 node list
ros2 action list

Initially only system topics were available:

/parameter_events
/rosout

Launching Turtlesim

Start the simulator:

ros2 run turtlesim turtlesim_node

Control it manually:

ros2 run turtlesim turtle_teleop_key

This launches a turtle that can be moved using the keyboard.


Exploring ROS2 Topics

List available topics:

ros2 topic list

Output:

/parameter_events
/rosout
/turtle1/cmd_vel
/turtle1/color_sensor
/turtle1/pose

Important topics:

Topic Purpose
/turtle1/cmd_vel Movement commands
/turtle1/pose Current position and orientation
/turtle1/color_sensor Color beneath the turtle

Inspecting Robot State

View live position updates:

ros2 topic echo /turtle1/pose

Example output:

x: 9.40
y: 0.0
theta: -3.13
linear_velocity: 0.0
angular_velocity: 0.0

This provides:

  • X position

  • Y position

  • Orientation (theta)

  • Current velocity


Understanding Topic Information

Check publisher and subscriber counts:

ros2 topic info /turtle1/pose

Output:

Type: turtlesim/msg/Pose
Publisher count: 1
Subscription count: 0

Meaning:

  • Turtlesim publishes pose data.

  • No node is currently subscribing.


Measuring Topic Frequency

Check publishing frequency:

ros2 topic hz /turtle1/pose

Output:

average rate: 62.5 Hz

Interpretation:

The turtle publishes its state approximately 62.5 times per second.

Frequency calculations:

1 / 62.5 = 0.016 seconds

The pose is updated every 16 milliseconds.

This introduced an important robotics concept:

Sensor Frequency (Hz)

Examples:

Sensor Typical Frequency
Camera 30 Hz
LiDAR 10 Hz
IMU 200+ Hz
Turtlesim Pose 62.5 Hz

Creating My First ROS2 Package

Navigate to the workspace source directory:

cd ~/ros2_ws/src

Create a Python package:

ros2 pkg create --build-type ament_python my_first_package

Package structure:

my_first_package/
├── package.xml
├── setup.py
├── setup.cfg
├── resource/
├── test/
└── my_first_package/

Creating My First ROS2 Node

File:

my_first_package/my_first_node.py

Code:

import rclpy
from rclpy.node import Node


class MyFirstNode(Node):
    def __init__(self):
        super().__init__("my_first_node")
        self.get_logger().info("Hello ROS2 developer")


def main(args=None):
    rclpy.init(args=args)

    node = MyFirstNode()

    rclpy.spin(node)

    rclpy.shutdown()


if __name__ == "__main__":
    main()

Register in setup.py:

entry_points={
    'console_scripts': [
        'my_first_node = my_first_package.my_first_node:main'
    ],
}

Build:

cd ~/ros2_ws
colcon build
source install/setup.bash

Run:

ros2 run my_first_package my_first_node

Verify:

ros2 node list

Output:

/my_first_node

Learning Publisher and Subscriber Communication

Publisher Node

Created:

number_publisher.py

Purpose:

Publish messages to:

/robot_news

Output:

Hello robot 0
Hello robot 1
Hello robot 2
...

Run:

ros2 run my_first_package number_publisher

Subscriber Node

Created:

number_subscriber.py

Purpose:

Listen to:

/robot_news

Output:

I heard: Hello robot 0
I heard: Hello robot 1
I heard: Hello robot 2
...

This demonstrated ROS2 communication between nodes.

Architecture:

number_publisher
       ↓
   /robot_news
       ↓
number_subscriber

Controlling a Robot Using ROS2

The final step was controlling the turtle directly.

Created:

turtle_controller.py

Code:

import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist


class TurtleController(Node):
    def __init__(self):
        super().__init__("turtle_controller")

        self.publisher_ = self.create_publisher(
            Twist,
            "/turtle1/cmd_vel",
            10
        )

        self.timer = self.create_timer(
            0.5,
            self.move_turtle
        )

    def move_turtle(self):
        msg = Twist()

        msg.linear.x = 2.0
        msg.angular.z = 1.0

        self.publisher_.publish(msg)


def main(args=None):
    rclpy.init(args=args)

    node = TurtleController()

    rclpy.spin(node)

    rclpy.shutdown()


if __name__ == "__main__":
    main()

Register in setup.py:

'turtle_controller = my_first_package.turtle_controller:main',

Build:

cd ~/ros2_ws
colcon build
source install/setup.bash

Run:

ros2 run my_first_package turtle_controller

Why Did the Turtle Move in a Circle?

The node continuously publishes:

msg.linear.x = 2.0
msg.angular.z = 1.0

Interpretation:

linear.x
→ move forward

angular.z
→ rotate left

The turtle receives both commands simultaneously:

forward motion
+
continuous rotation
=
circular path

Result:

The turtle draws a nearly perfect circle.


What I Learned

By completing this exercise I learned:

✅ ROS2 Workspace Structure

✅ ROS2 Packages

✅ ROS2 Nodes

✅ Topics

✅ Publishers

✅ Subscribers

✅ Message Types

✅ Topic Inspection Tools

✅ Topic Frequencies

/cmd_vel Robot Control

✅ Using Twist Messages

✅ Writing Autonomous Robot Controllers


Connection to Real Robotics

The exact same architecture is used in real robots.

Future project architecture:

Camera Node
      ↓
YOLO Detection Node
      ↓
Follower Node
      ↓
/cmd_vel
      ↓
Mobile Robot

Today's turtlesim exercise is therefore not just a toy example, it is the foundation of autonomous robot control in ROS2.


Next Steps

  • Learn ROS2 Services

  • Learn ROS2 Actions

  • Learn TF2

  • Learn URDF Robot Models

  • Learn Gazebo Simulation

  • Learn SLAM Toolbox

  • Learn Nav2 Navigation Stack

  • Integrate YOLO Object Detection

  • Build an Autonomous Object-Following Robot

This marks the completion of my first ROS2 robot control project.