By ihsumlee , 9 June 2026
content

Position in the full course

This is the first step before learning TF2.

The full learning order is:

1. ROS 2 node/topic/launch review
2. TF2 frame concept
3. Static sensor frames
4. Dynamic odometry frame
5. URDF + robot_state_publisher
6. SLAM mapping
7. AMCL localization
8. Nav2 path planning
9. RViz goal navigation
10. TF2 debugging in Nav2

This module prepares you for TF2 because TF2 also uses ROS 2 nodes, topics, messages, and launch files.


1. Learning objectives

After this module, you should be able to:

  1. Explain what a ROS 2 node is.
  2. Explain what a ROS 2 topic is.
  3. Run multiple ROS 2 nodes from the terminal.
  4. Use command-line tools to inspect nodes and topics.
  5. Create a simple Python publisher node.
  6. Create a simple Python subscriber node.
  7. Build and run a Python ROS 2 package.
  8. Create a simple Python launch file.
  9. Understand why these skills are needed before learning TF2.

2. Teaching duration

Suggested duration: 2 hours

| Time | Activity | | ----------- | ------------------------------------ | | 0–15 min | Concept review: node, topic, message | | 15–35 min | Run turtlesim and inspect ROS graph | | 35–70 min | Create Python publisher/subscriber | | 70–95 min | Create launch file | | 95–110 min | Practice | | 110–120 min | Summary and assignment explanation |


3. Big picture explanation

In ROS 2, a robot system is usually divided into many small programs.

Each small program is called a node.

For example, an AMR may contain:

camera_node
lidar_node
imu_node
wheel_odometry_node
navigation_node
controller_node

These nodes need to exchange information.

For example:

lidar_node publishes laser scan data
navigation_node subscribes to laser scan data
controller_node publishes velocity command
motor_driver_node subscribes to velocity command

The communication channel is called a topic.

A simple ROS 2 communication structure is:

Publisher Node  --->  Topic  --->  Subscriber Node

Example:

/lidar_node  --->  /scan  --->  /navigation_node

For mobile robot navigation, this idea is very important.

Later in TF2, you will see that TF information is also published and received by ROS 2 nodes.


4. Basic ROS 2 commands

4.1 Source ROS 2 Jazzy

Open a terminal:

source /opt/ros/jazzy/setup.bash

If you use your own workspace later:

source ~/ros2_ws/install/setup.bash

4.2 Check ROS 2 environment

printenv | grep ROS

Expected result may include:

ROS_VERSION=2
ROS_DISTRO=jazzy

5. Practice 1: Run turtlesim nodes

Terminal 1: Start turtlesim

ros2 run turtlesim turtlesim_node

Terminal 2: Start keyboard control

ros2 run turtlesim turtle_teleop_key

Now you can control the turtle using keyboard arrow keys.


6. Practice 2: Inspect running nodes

Open a new terminal:

source /opt/ros/jazzy/setup.bash

List all running nodes:

ros2 node list

Expected result:

/teleop_turtle
/turtlesim

Check information about the turtlesim node:

ros2 node info /turtlesim

You should observe that a node may have:

Publishers
Subscribers
Services
Action servers
Action clients

Teaching point:

A ROS 2 node is not isolated. It communicates with other nodes through topics, services, actions, and parameters.


7. Practice 3: Inspect topics

List all topics:

ros2 topic list

Expected topics may include:

/turtle1/cmd_vel
/turtle1/pose

Check topic type:

ros2 topic type /turtle1/cmd_vel

Expected result:

geometry_msgs/msg/Twist

Check live topic data:

ros2 topic echo /turtle1/pose

Move the turtle and observe the output.

Teaching point:

A topic has a message type. Publisher and subscriber must use the same message type.


8. Practice 4: Publish velocity command manually

Stop the teleop node or leave it unused.

Publish a velocity command manually:

ros2 topic pub /turtle1/cmd_vel geometry_msgs/msg/Twist \
"{linear: {x: 1.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.5}}"

The turtle should move forward and rotate.

Publish continuously at 1 Hz:

ros2 topic pub --rate 1 /turtle1/cmd_vel geometry_msgs/msg/Twist \
"{linear: {x: 1.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.5}}"

Teaching point:

cmd_vel is also commonly used in mobile robot navigation. Nav2 finally sends velocity commands to make the robot move.


9. Create a Python package

9.1 Create workspace

mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src

9.2 Create package

ros2 pkg create --build-type ament_python --license Apache-2.0 basic_ros2_review

Go into the package:

cd ~/ros2_ws/src/basic_ros2_review

The package structure should look like:

basic_ros2_review/
β”œβ”€β”€ basic_ros2_review/
β”‚   └── __init__.py
β”œβ”€β”€ package.xml
β”œβ”€β”€ setup.py
β”œβ”€β”€ setup.cfg
└── resource/

10. Create a simple Python publisher

Create a file:

cd ~/ros2_ws/src/basic_ros2_review/basic_ros2_review
nano simple_publisher.py

Paste the following code:

import rclpy
from rclpy.node import Node
from std_msgs.msg import String


class SimplePublisher(Node):

    def __init__(self):
        super().__init__('simple_publisher')
        self.publisher_ = self.create_publisher(String, 'robot_status', 10)
        self.timer = self.create_timer(1.0, self.timer_callback)
        self.count = 0

    def timer_callback(self):
        msg = String()
        msg.data = 'Robot is alive: %d' % self.count
        self.publisher_.publish(msg)
        self.get_logger().info('Publishing: "%s"' % msg.data)
        self.count += 1


def main(args=None):
    rclpy.init(args=args)
    node = SimplePublisher()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()

Explanation:

simple_publisher is the node name.
robot_status is the topic name.
String is the message type.
The node publishes one message every second.

11. Create a simple Python subscriber

Create another file:

nano simple_subscriber.py

Paste the following code:

import rclpy
from rclpy.node import Node
from std_msgs.msg import String


class SimpleSubscriber(Node):

    def __init__(self):
        super().__init__('simple_subscriber')
        self.subscription = self.create_subscription(
            String,
            'robot_status',
            self.listener_callback,
            10
        )

    def listener_callback(self, msg):
        self.get_logger().info('I heard: "%s"' % msg.data)


def main(args=None):
    rclpy.init(args=args)
    node = SimpleSubscriber()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()

Explanation:

simple_subscriber is the node name.
It subscribes to robot_status.
When a message arrives, listener_callback() is executed.

12. Modify setup.py

Open setup.py:

cd ~/ros2_ws/src/basic_ros2_review
nano setup.py

Find entry_points and modify it:

entry_points={
    'console_scripts': [
        'simple_publisher = basic_ros2_review.simple_publisher:main',
        'simple_subscriber = basic_ros2_review.simple_subscriber:main',
    ],
},

13. Build the package

cd ~/ros2_ws
colcon build --packages-select basic_ros2_review

Source the workspace:

source install/setup.bash

14. Run publisher and subscriber

Terminal 1

source ~/ros2_ws/install/setup.bash
ros2 run basic_ros2_review simple_publisher

Terminal 2

source ~/ros2_ws/install/setup.bash
ros2 run basic_ros2_review simple_subscriber

Expected result:

Publisher terminal:

Publishing: "Robot is alive: 0"
Publishing: "Robot is alive: 1"
Publishing: "Robot is alive: 2"

Subscriber terminal:

I heard: "Robot is alive: 0"
I heard: "Robot is alive: 1"
I heard: "Robot is alive: 2"

15. Inspect the custom topic

List topics:

ros2 topic list

You should see:

/robot_status

Check topic type:

ros2 topic type /robot_status

Expected result:

std_msgs/msg/String

Echo the topic:

ros2 topic echo /robot_status

Check publisher/subscriber information:

ros2 topic info /robot_status

Expected result:

Publisher count: 1
Subscription count: 1

16. Create a launch file

16.1 Create launch folder

cd ~/ros2_ws/src/basic_ros2_review
mkdir launch

Create a launch file:

nano launch/basic_review_launch.py

Paste the following code:

from launch import LaunchDescription
from launch_ros.actions import Node


def generate_launch_description():
    publisher_node = Node(
        package='basic_ros2_review',
        executable='simple_publisher',
        name='simple_publisher'
    )

    subscriber_node = Node(
        package='basic_ros2_review',
        executable='simple_subscriber',
        name='simple_subscriber'
    )

    return LaunchDescription([
        publisher_node,
        subscriber_node
    ])

17. Modify setup.py to install launch file

Open setup.py:

nano setup.py

Add these imports at the top:

import os
from glob import glob

Find data_files and modify it like this:

data_files=[
    ('share/ament_index/resource_index/packages',
        ['resource/' + package_name]),
    ('share/' + package_name, ['package.xml']),
    (os.path.join('share', package_name, 'launch'), glob('launch/*.py')),
],

18. Rebuild and run launch file

cd ~/ros2_ws
colcon build --packages-select basic_ros2_review
source install/setup.bash

Run the launch file:

ros2 launch basic_ros2_review basic_review_launch.py

Expected result:

simple_publisher publishes messages.
simple_subscriber receives messages.
Both nodes start from one launch command.

Teaching point:

Launch files are useful because real robot systems usually need many nodes running together.

For example, a Nav2 system may start:

robot_state_publisher
lidar_driver
camera_driver
slam_toolbox or amcl
controller_server
planner_server
bt_navigator
rviz2

Running each one manually is not convenient, so launch files are necessary.


19. Connection to TF2

This review prepares you for TF2.

In the next step, you will learn that TF2 also uses ROS 2 nodes.

For example:

TF broadcaster node  --->  /tf topic
Static TF broadcaster --->  /tf_static topic
TF listener node      --->  receives transform information

The idea is similar to today’s publisher/subscriber example.

Today:

simple_publisher  --->  /robot_status  --->  simple_subscriber

Next TF2 lesson:

tf_broadcaster  --->  /tf  --->  tf_listener

20. Practice

Practice A

Run turtlesim and answer:

  1. What nodes are running?
  2. What topics are available?
  3. What is the message type of /turtle1/cmd_vel?
  4. What is the message type of /turtle1/pose?

Commands:

ros2 node list
ros2 topic list
ros2 topic type /turtle1/cmd_vel
ros2 topic type /turtle1/pose

Practice B

Modify the publisher message.

Original:

msg.data = 'Robot is alive: %d' % self.count

Change it to:

msg.data = 'AMR navigation system is ready: %d' % self.count

Then rebuild and run again:

cd ~/ros2_ws
colcon build --packages-select basic_ros2_review
source install/setup.bash
ros2 launch basic_ros2_review basic_review_launch.py

Practice C

Change the topic name from:

robot_status

to:

amr_status

You must modify both:

simple_publisher.py
simple_subscriber.py

Then verify:

ros2 topic list
ros2 topic echo /amr_status

21. Common problems and solutions

Problem 1: package not found

Error:

Package 'basic_ros2_review' not found

Possible reason:

The workspace has not been sourced.

Solution:

source ~/ros2_ws/install/setup.bash

Problem 2: executable not found

Error:

No executable found

Possible reasons:

The entry point in setup.py is wrong.
The package was not rebuilt.

Solution:

cd ~/ros2_ws
colcon build --packages-select basic_ros2_review
source install/setup.bash

Problem 3: subscriber receives nothing

Possible reasons:

The publisher is not running.
The topic names are different.
The message types are different.

Check:

ros2 topic list
ros2 topic info /robot_status

22. Homework

Create a new publisher/subscriber pair for AMR battery status.

Requirements

Publisher node name:

battery_publisher

Subscriber node name:

battery_monitor

Topic name:

/battery_status

Message type:

std_msgs/msg/String

Example published message:

Battery level: 90%
Battery level: 80%
Battery level: 70%

You should submit:

  1. Screenshot of publisher output.
  2. Screenshot of subscriber output.
  3. Screenshot of ros2 topic list.
  4. Screenshot of ros2 topic echo /battery_status.
  5. Short explanation of publisher, topic, and subscriber.

23. Mini quiz

  1. What is a ROS 2 node?
  2. What is a ROS 2 topic?
  3. What command lists all running nodes?
  4. What command lists all active topics?
  5. What command shows the message type of a topic?
  6. Why do we need launch files?
  7. In mobile robot navigation, what is /cmd_vel usually used for?
  8. Why is this lesson important before learning TF2?

24. Key summary

In this module, you reviewed the basic ROS 2 communication structure:

Node  --->  Topic  --->  Node

A publisher sends messages to a topic.

A subscriber receives messages from a topic.

A launch file starts multiple nodes together.

This foundation is necessary before learning TF2 because TF2 also works through ROS 2 nodes and topics.

In the next module, you will learn:

TF2 frame concept
map, odom, base_link, sensor frames
TF tree
parent frame and child frame

Tags