← Back to Blog
navigationros2tutorialamr

ROS 2 A to Z Part 1: Setup and First Node

Guide to installing ROS 2 Humble on Ubuntu 22.04 and writing first publisher/subscriber node.

Nguyen Anh Tuan9 tháng 1, 20268 min read
ROS 2 A to Z Part 1: Setup and First Node

ROS 2 Humble — Where to Start?

If you want to learn ROS 2 to build robots, this article is your starting point. We will go from installing ROS 2 Humble on Ubuntu 22.04, creating a workspace, writing your first Python publisher/subscriber node, to building and running your own package. I have guided many engineers through ROS 2 beginnings, and experience shows: just follow each step carefully, you will master the fundamentals quickly.

If you don't yet know what ROS 2 is or why you should use ROS 2 instead of ROS 1, read Introduction to ROS 2: Next-Generation Robot Programming Framework first.

Ubuntu terminal with ROS 2 Humble

Install ROS 2 Humble on Ubuntu 22.04

System Requirements

Step 1: Configure Locale

ROS 2 requires a locale with UTF-8 support:

sudo apt update && sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
export LANG=en_US.UTF-8

# Verify
locale

Step 2: Add ROS 2 Repository

# Install dependencies
sudo apt install -y software-properties-common curl
sudo add-apt-repository universe

# Add GPG key
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \
  -o /usr/share/keyrings/ros-archive-keyring.gpg

# Add repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] \
  http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" \
  | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null

Step 3: Install ROS 2 Humble

sudo apt update
sudo apt upgrade -y

# Full desktop installation (includes RViz, Gazebo demos, rqt tools)
sudo apt install -y ros-humble-desktop

# Or minimal installation (libraries + CLI only, no GUI)
# sudo apt install -y ros-humble-ros-base

Installation takes approximately 5-15 minutes depending on your internet speed.

Step 4: Install Build Tools

# colcon — ROS 2's main build tool
sudo apt install -y python3-colcon-common-extensions

# rosdep — dependency manager
sudo apt install -y python3-rosdep2
sudo rosdep init
rosdep update

Step 5: Source Environment

# Add to .bashrc for automatic sourcing on terminal open
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
source ~/.bashrc

# Verify installation
ros2 --version
# Output: ros2 0.xx.x

Quick Test with Turtlesim

Before proceeding, make sure everything works:

# Terminal 1: Run turtlesim
ros2 run turtlesim turtlesim_node

# Terminal 2: Control turtle with keyboard
ros2 run turtlesim turtle_teleop_key

If you see a turtle window and can control it with arrow keys — congratulations, ROS 2 is ready!

Create ROS 2 Workspace

A workspace is where you keep all your source code. ROS 2 uses colcon as the build system (replacing ROS 1's catkin).

# Create workspace structure
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws

# Build empty workspace (creates build, install, log directories)
colcon build
source install/setup.bash

Resulting structure:

~/ros2_ws/
├── build/          # Build artifacts
├── install/        # Installed packages (source this)
├── log/            # Build logs
└── src/            # Your source code

Important Tip: Always source workspace after build:

# Add to .bashrc
echo "source ~/ros2_ws/install/setup.bash" >> ~/.bashrc

Create Your First Package

ROS 2 supports 2 package types: ament_python (Python) and ament_cmake (C++). In this series, we use Python for accessibility.

cd ~/ros2_ws/src

# Create Python package
ros2 pkg create --build-type ament_python my_first_robot \
  --dependencies rclpy std_msgs

# Result:
# my_first_robot/
# ├── my_first_robot/
# │   └── __init__.py
# ├── package.xml          # Metadata + dependencies
# ├── setup.py             # Python package config
# ├── setup.cfg            # Entry points config
# ├── resource/
# │   └── my_first_robot
# └── test/

ROS 2 workspace structure with colcon build

Write Your First Publisher Node

A node is the basic processing unit in ROS 2. Each node is a process that does one specific task. Let's write a node that simulates a temperature sensor, publishing data to the /temperature topic.

Create Publisher File

# ~/ros2_ws/src/my_first_robot/my_first_robot/temperature_publisher.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import Float32
import random


class TemperaturePublisher(Node):
    """Node simulating temperature sensor, publishes every second."""

    def __init__(self):
        super().__init__('temperature_publisher')

        # Create publisher: message type, topic name, queue size
        self.publisher_ = self.create_publisher(Float32, '/temperature', 10)

        # Timer calls callback every 1 second
        self.timer = self.create_timer(1.0, self.timer_callback)

        # Base temperature
        self.base_temp = 25.0

        self.get_logger().info('Temperature publisher node started!')

    def timer_callback(self):
        msg = Float32()
        # Simulate temperature fluctuation around 25°C
        msg.data = self.base_temp + random.uniform(-2.0, 2.0)

        self.publisher_.publish(msg)
        self.get_logger().info(f'Published temperature: {msg.data:.1f}°C')


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

    node = TemperaturePublisher()

    try:
        rclpy.spin(node)  # Keep node running continuously
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()


if __name__ == '__main__':
    main()

Code Explanation

Write a Subscriber Node

Now write a node that receives temperature data and processes it (e.g., warn when too hot):

# ~/ros2_ws/src/my_first_robot/my_first_robot/temperature_subscriber.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import Float32


class TemperatureSubscriber(Node):
    """Node receiving temperature and warning when exceeding threshold."""

    def __init__(self):
        super().__init__('temperature_subscriber')

        # Create subscription
        self.subscription = self.create_subscription(
            Float32,
            '/temperature',
            self.listener_callback,
            10
        )

        # Warning thresholds
        self.warning_threshold = 26.5
        self.critical_threshold = 27.5

        self.get_logger().info('Temperature subscriber node started!')

    def listener_callback(self, msg):
        temp = msg.data

        if temp >= self.critical_threshold:
            self.get_logger().error(
                f'CRITICAL: Temperature {temp:.1f}°C exceeds {self.critical_threshold}°C!'
            )
        elif temp >= self.warning_threshold:
            self.get_logger().warn(
                f'WARNING: Temperature {temp:.1f}°C above normal'
            )
        else:
            self.get_logger().info(f'Temperature OK: {temp:.1f}°C')


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

    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()


if __name__ == '__main__':
    main()

Register Entry Points and Build

Update setup.py

For ROS 2 to know how to run the nodes, register them in setup.py:

# ~/ros2_ws/src/my_first_robot/setup.py
from setuptools import find_packages, setup

package_name = 'my_first_robot'

setup(
    name=package_name,
    version='0.1.0',
    packages=find_packages(exclude=['test']),
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='your_name',
    maintainer_email='[email protected]',
    description='My first ROS 2 robot package',
    license='Apache-2.0',
    entry_points={
        'console_scripts': [
            'temp_pub = my_first_robot.temperature_publisher:main',
            'temp_sub = my_first_robot.temperature_subscriber:main',
        ],
    },
)

Build and Run

# Go to workspace root
cd ~/ros2_ws

# Build only your package (faster than building all)
colcon build --packages-select my_first_robot --symlink-install

# Source workspace again
source install/setup.bash

# Terminal 1: Run publisher
ros2 run my_first_robot temp_pub

# Terminal 2: Run subscriber
ros2 run my_first_robot temp_sub

The --symlink-install flag is very useful: it creates symlinks instead of copying files, so when you edit Python code, you don't need to rebuild (just restart the node).

You will see output like:

[INFO] [temperature_publisher]: Published temperature: 24.3°C
[INFO] [temperature_publisher]: Published temperature: 26.8°C
[INFO] [temperature_subscriber]: Temperature OK: 24.3°C
[WARN] [temperature_subscriber]: WARNING: Temperature 26.8°C above normal

Introspect with CLI Tools

ROS 2 CLI is powerful for debugging:

# List running nodes
ros2 node list
# /temperature_publisher
# /temperature_subscriber

# Get detailed node information
ros2 node info /temperature_publisher

# List all topics
ros2 topic list
# /temperature
# /parameter_events
# /rosout

# View topic data in real-time
ros2 topic echo /temperature

# Check publication rate
ros2 topic hz /temperature
# average rate: 1.000

# Check message type
ros2 topic type /temperature
# std_msgs/msg/Float32

# View message structure
ros2 interface show std_msgs/msg/Float32
# float32 data

Launch Files — Run Multiple Nodes at Once

Instead of opening many terminals, use a launch file:

# ~/ros2_ws/src/my_first_robot/launch/temperature_system.launch.py
from launch import LaunchDescription
from launch_ros.actions import Node


def generate_launch_description():
    return LaunchDescription([
        Node(
            package='my_first_robot',
            executable='temp_pub',
            name='temperature_publisher',
            output='screen',
            parameters=[{
                'publish_rate': 1.0,
            }],
        ),
        Node(
            package='my_first_robot',
            executable='temp_sub',
            name='temperature_subscriber',
            output='screen',
        ),
    ])

For launch files to be found, add to setup.py:

import os
from glob import glob

# Add to data_files:
data_files=[
    ('share/ament_index/resource_index/packages',
        ['resource/' + package_name]),
    ('share/' + package_name, ['package.xml']),
    # Add this for launch files
    (os.path.join('share', package_name, 'launch'),
        glob(os.path.join('launch', '*launch.[pxy][yma]*'))),
],

Create launch directory and rebuild:

mkdir -p ~/ros2_ws/src/my_first_robot/launch
# Copy launch file there

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

# Run entire system
ros2 launch my_first_robot temperature_system.launch.py

ROS 2 rqt_graph showing connections between publisher and subscriber

Visualize with rqt_graph

# Install rqt (if not in desktop installation)
sudo apt install -y ros-humble-rqt ros-humble-rqt-graph

# Open graph viewer
ros2 run rqt_graph rqt_graph

You will see a diagram: temperature_publisher node connected to temperature_subscriber via /temperature topic. This is a very effective debug method when systems become complex.

Summary and Next Steps

In this article, you have:

This is the foundation for everything that follows. In Part 2, we will dive deep into ROS 2's 3 communication primitives: Topics, Services, and Actions — so you know when to use what when designing robot systems.


Related Articles

Related Posts

Deep DiveDigital Twins và ROS 2: Simulation trong sản xuất
simulationros2digital-twinPart 6

Digital Twins và ROS 2: Simulation trong sản xuất

Ứng dụng simulation trong công nghiệp — digital twins, ROS 2 + Gazebo/Isaac integration cho nhà máy thông minh.

3/4/202611 min read
TutorialSim-to-Real Pipeline: Từ training đến robot thật
simulationsim2realtutorialPart 5

Sim-to-Real Pipeline: Từ training đến robot thật

End-to-end guide: train policy trong sim, evaluate, domain randomization, deploy lên robot thật và iterate.

2/4/202615 min read
TutorialBắt đầu với MuJoCo: Cài đặt đến mô phỏng robot đầu tiên
simulationmujocotutorialPart 2

Bắt đầu với MuJoCo: Cài đặt đến mô phỏng robot đầu tiên

Hands-on tutorial MuJoCo — cài đặt, tạo MJCF model, simulate robot arm và visualize kết quả với Python.

30/3/202611 min read