Skip to main content
onepocketRobotics

ROS 2 in Depth

Installing ROS 2 and the CLI

Three ways to get a working ROS 2 onto your machine, what sourcing the setup script really does, and the command-line tools you will use every day.

intermediate ยท 13 min read

Lesson content

There are three sensible ways to get ROS 2, and which one is right depends on whether you are learning, developing or shipping.

All three end in the same place: a shell where typing ros2 does something, with the distribution's packages on your path.

That phrase matters more than it looks. ROS 2 is not installed into a machine the way a normal program is. It is switched on inside one shell, by one line, and the rest of this lesson turns on understanding that line.

One source line fills four variables, and only in that shell. Underneath: the five commands that localise most faults, in the order to run them.

Setup effort

apt packages
Low
Docker
Low
From source
High

Needs the matching Ubuntu

apt packages
Yes
Docker
No
From source
No

Isolated from the host

apt packages
No
Docker
Yes
From source
Partly

Reproducible

apt packages
Mostly
Docker
Exactly
From source
If you pin

Can modify ROS itself

apt packages
No
Docker
No
From source
Yes

Best for

apt packages
Learning, day-to-day development
Docker
CI, deployment, other distributions
From source
Core development, unsupported platforms
Bash
# --- Ubuntu: install from packages ---sudo apt update && sudo apt install -y locales curl gnupg lsb-release# add the ROS 2 apt repository and key (see docs.ros.org for the current URL)sudo apt updatesudo apt install -y ros-jazzy-desktop        # desktop = ROS + RViz + demossudo apt install -y ros-dev-tools            # colcon, rosdep, vcstool# --- Make it available in this shell ---source /opt/ros/jazzy/setup.bash# --- Check it works ---ros2 doctor --report | head -20ros2 run demo_nodes_cpp talker      # in one terminalros2 run demo_nodes_py listener     # in another
sudo apt install -y ros-jazzy-desktop        # desktop = ROS + RViz + demos

-desktop includes RViz and the demos; -ros-base is the headless subset for a robot.

source /opt/ros/jazzy/setup.bash

This is the line everything depends on. Nothing ROS works in a shell that has not sourced it.

ros2 run demo_nodes_cpp talker      # in one terminal
ros2 run demo_nodes_py listener     # in another

If talker and listener see each other, your installation and your networking are both fine.

What does sourcing the setup script actually do?

It edits the current shell's environment. Four variables, four jobs.

PATH gains the ROS executables, which is what makes ros2 resolve at all.
PYTHONPATH gains the Python packages, so import rclpy works.
LD_LIBRARY_PATH gains the shared libraries the executables link against.
AMENT_PREFIX_PATH tells ROS where to find package resources.

Nothing outside that shell changed. No service started, no file was written. Close the terminal and the effect is gone.

That is why every new terminal needs it again, and why almost everyone eventually puts the line in .bashrc.

Doing so is fine while you are learning, and a mild trap later. The moment you work with two distributions or two workspaces, an automatic source in .bashrc will quietly hand you the wrong one.

Plenty of developers keep a shell alias per environment instead, so choosing is always deliberate.

Once ROS is on your path, almost all interaction goes through one command. ros2 is a tree of sub-commands, one per concept, and learning it is the fastest route to being productive.

The ros2 sub-commands worth knowing by heart
CommandWhat it doesMost-used form
ros2 nodeInspect running nodesros2 node list, ros2 node info /x
ros2 topicInspect and interact with topicslist, echo, hz, info --verbose, pub
ros2 serviceInspect and call serviceslist, call
ros2 actionInspect and send action goalslist, send_goal --feedback
ros2 paramRead, set and dump parameterslist, get, set, dump
ros2 interfaceShow message, service and action definitionsshow, list
ros2 runRun one executable from a packageros2 run pkg exe
ros2 launchStart a launch fileros2 launch pkg file.launch.py
ros2 bagRecord and replay message trafficrecord -a, play file
ros2 pkgCreate and inspect packagescreate, list, prefix
ros2 doctorCheck the installation and networkros2 doctor --report

Example

The five commands that solve most problems

ros2 node list asks whether the node you expect is running at all.
ros2 topic list asks whether the topic even exists.
ros2 topic hz /x asks whether anything is publishing to it.
ros2 topic echo /x asks whether the values look sensible.
ros2 topic info /x --verbose asks whether both sides agree on QoS.

Each one rules out a whole class of cause, which is why the order matters. There is no point checking values on a topic nobody publishes to.

Run in sequence, these five pin down the great majority of "it isn't working" reports. Reach for them before you open any code.

A handful of environment variables shape how ROS 2 behaves on the network, and setting them deliberately prevents a whole category of confusing problems.

Bash
# Partition the network. Nodes only discover others in the same domain.export ROS_DOMAIN_ID=42# Keep everything on this machine (useful while developing on a shared LAN).export ROS_LOCALHOST_ONLY=1# Choose a DDS vendor. Worth trying both if performance is poor.export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp# Make log output readable.export RCUTILS_COLORIZED_OUTPUT=1export RCUTILS_CONSOLE_OUTPUT_FORMAT='[{severity}] [{name}]: {message}'
export ROS_DOMAIN_ID=42

Set this per robot and per developer. Two people on the default domain will see each other's robots, and can command them.

export ROS_LOCALHOST_ONLY=1

The quickest fix when you do not want your laptop appearing on a colleague's graph.

Two distributions in one shell will not work

Source both Humble and Jazzy in the same terminal and you get a broken environment.

Paths from both. Libraries that do not match each other. And errors that make no sense at all.

The same happens if you source a workspace built against one distribution while another one is active.

The rule is simple: one distribution per shell.

If you need several, use Docker containers, or separate terminals with explicit aliases. Never try to switch in place.

For deployment and CI, Docker is usually the right answer. The official images are ros:jazzy (base) and osrf/ros:jazzy-desktop (with GUI tools), and the one thing to get right is networking.

Bash
# Host networking so DDS discovery works between the container and the hostdocker run -it --rm --network host \  -e ROS_DOMAIN_ID=42 \  -v $(pwd):/ws \  osrf/ros:jazzy-desktop bash# For GUI tools (RViz) from inside a container, on X11:xhost +local:dockerdocker run -it --rm --network host \  -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix \  osrf/ros:jazzy-desktop rviz2
docker run -it --rm --network host \

--network host is essentially required. Docker's default bridge blocks the multicast that DDS discovery relies on.

Forgetting to source the setup script in a new terminal.

ros2: command not found almost always means this. Source the distribution, then your workspace's install/setup.bash.

Running Docker with default bridge networking.

Discovery multicast does not cross it, so nodes never find each other and nothing reports an error. Use --network host.

Leaving ROS_DOMAIN_ID unset on a shared network.

Everyone on the default domain sees everyone else. Assign one per robot. The failure mode here is another machine acting on your commands.

Key takeaway

  • apt for learning and development, Docker for CI and deployment, source only when you must modify ROS itself.
  • Sourcing the setup script edits the current shell's environment, so every new terminal needs it again.
  • The ros2 CLI is a tree of sub-commands; five of them localise most problems.
  • Set ROS_DOMAIN_ID per robot, and use --network host for Docker.
  • Never source two distributions in one shell.

Finished this lesson?

Guest progress is saved on this device.

Installing ROS 2 and the CLI | OnepocketRobotics