RobotForge
Published·~20 min

Your first 20 minutes with ROS 2

Install ROS 2, launch turtlesim, publish a velocity message, and understand the three primitives that underpin every ROS program. No robot required.

by RobotForge
#ros2#beginner#tutorial

ROS is the air that robotics people breathe. It's also the part every beginner bounces off of first. Let's fix that in 20 minutes. No robot required — we'll drive a simulated turtle with arrow keys.

What ROS 2 is, in one paragraph

ROS 2 is a middleware. It lets many small programs (nodes) on one or many computers talk to each other through named channels (topics) with strongly-typed messages. That's it. Every fancy robotics package — Nav2, MoveIt, Gazebo bridges — is built on those three primitives. Learn the primitives first, the packages come for free.

What you'll have at the end

A running ROS 2 install, turtlesim on your screen, and you driving a cartoon turtle with the arrow keys by publishing velocity messages over a topic. Not impressive — but everything after this uses the same three ideas.

Install (pick one)

Linux (Ubuntu 22.04 / 24.04) — native

sudo apt update && sudo apt install -y curl gnupg lsb-release
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \
  -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] \
  http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" \
  | sudo tee /etc/apt/sources.list.d/ros2.list
sudo apt update && sudo apt install -y ros-jazzy-desktop

macOS or Windows — Docker or devcontainer

Running ROS 2 natively on macOS or Windows is painful. In 2026, use a container. The osrf/ros:jazzy-desktop image is the shortest path:

docker run -it --rm \
  -e DISPLAY=$DISPLAY \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
  osrf/ros:jazzy-desktop bash

On macOS you'll also need XQuartz; on Windows 11, WSL 2 with WSLg handles X11 automatically.

The three primitives

  • Node. A process that does one thing. A camera driver is a node. A motor controller is a node. Your code will be a node.
  • Topic. A named pub/sub channel between nodes. /camera/image_raw carries camera frames. /cmd_vel carries velocity commands. Any node can publish or subscribe.
  • Message. The strongly-typed payload on a topic. geometry_msgs/Twist for velocity, sensor_msgs/Image for frames, std_msgs/String when you're being lazy.

Services and actions (remote procedure calls and long-running goals) are variations of the same idea. Master topics first.

Drive the turtle

Terminal 1:

source /opt/ros/jazzy/setup.bash
ros2 run turtlesim turtlesim_node

A window pops up with a cartoon turtle in the middle.

Terminal 2:

source /opt/ros/jazzy/setup.bash
ros2 run turtlesim turtle_teleop_key

Click into this terminal and press the arrow keys. The turtle moves. Congratulations — you've driven your first ROS 2 robot.

See the actual traffic

Terminal 3:

source /opt/ros/jazzy/setup.bash
ros2 topic list
ros2 topic echo /turtle1/cmd_vel

Every arrow press emits one geometry_msgs/Twist message on /turtle1/cmd_vel. The teleop node is the publisher. The simulator node is the subscriber. That's it. That's every ROS program you will ever read.

Write your own publisher (bonus)

In a new file circle.py:

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

class CircleDriver(Node):
    def __init__(self):
        super().__init__('circle_driver')
        self.pub = self.create_publisher(Twist, '/turtle1/cmd_vel', 10)
        self.timer = self.create_timer(0.1, self.tick)

    def tick(self):
        msg = Twist()
        msg.linear.x = 1.0
        msg.angular.z = 1.0
        self.pub.publish(msg)

def main():
    rclpy.init()
    rclpy.spin(CircleDriver())

if __name__ == '__main__':
    main()

Stop the teleop node, run python3 circle.py, and the turtle draws circles. You've now built a node.

Troubleshooting the top three errors

  • "command not found: ros2" — you forgot source /opt/ros/jazzy/setup.bash. Add it to your ~/.bashrc once you're committed.
  • turtlesim window doesn't open in Docker — X11 forwarding isn't set up. On Linux, xhost +local:docker; on macOS, start XQuartz and check "Allow connections from network clients."
  • No topics visible between machines — ROS 2 discovery is on by default but firewalls (and some VPNs) block the multicast it uses. Set ROS_LOCALHOST_ONLY=1 to stay on one machine, or switch to the Zenoh RMW for multi-host setups.

What comes next

The next four lessons in this track are: nodes, topics, messages in depth; writing a Python publisher/subscriber pair; services vs actions vs topics; and launch files for starting 20 nodes at once. If you want a full book, the official ROS 2 Jazzy tutorials are excellent — ours cut straight to what a roboticist needs, theirs cover every corner case.

Comments

    Sign in to post a comment.