ROS Guide: Robot Operating System Fundamentals
The Robot Operating System (ROS) is a framework for building robot software. It provides a structured communication layer between sensors, actuators, and algorithms through nodes, topics, services, and actions. ROS 2 is the current standard, built on DDS middleware for real-time and production use.
ROS 2 Architecture
ROS 2 improves on ROS 1 with real-time support, security, and multi-platform compatibility. Nodes communicate over DDS (Data Distribution Service), a real-time publish-subscribe middleware. The composition model allows multiple nodes to run in a single process, reducing overhead. ROS 2 supports multiple DDS implementations including Fast DDS, Cyclone DDS, and GurumDDS. Launch files in ROS 2 use Python, providing more flexibility than the XML format in ROS 1.
Core Concepts
| Concept | Description | Analogy |
|---|---|---|
| Node | A process that performs computation | A program |
| Topic | Named bus for pub/sub communication | A message board |
| Service | Request/reply communication | A function call |
| Action | Long-running task with feedback | A background job with progress |
| Bag | Recording and playback of messages | A black box recorder |
Nodes and Topics
A node publishes messages to topics and subscribes to topics from other nodes. This decouples producers from consumers — a camera node publishes images, and any number of processing nodes can subscribe without the camera knowing about them.
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
from sensor_msgs.msg import LaserScan
class ObstacleAvoider(Node):
def __init__(self):
super().__init__('obstacle_avoider')
self.publisher = self.create_publisher(Twist, 'cmd_vel', 10)
self.subscription = self.create_subscription(
LaserScan, 'scan', self.scan_callback, 10)
def scan_callback(self, msg):
# Simple obstacle avoidance
cmd = Twist()
min_distance = min(msg.ranges)
if min_distance < 0.5:
cmd.angular.z = 0.5 # turn
else:
cmd.linear.x = 0.2 # move forward
self.publisher.publish(cmd)Checking Topics
# List all topics
ros2 topic list
# See topic info
ros2 topic info /scan
# Echo messages on a topic
ros2 topic echo /odom
# Measure publishing rate
ros2 topic hz /scanServices
Services provide synchronous request-reply communication. Use them for one-time operations like setting a parameter or triggering a calibration:
from custom_interfaces.srv import SetTarget
class TargetServer(Node):
def __init__(self):
super().__init__('target_server')
self.srv = self.create_service(
SetTarget, 'set_target', self.set_target_callback)
def set_target_callback(self, request, response):
self.get_logger().info(f'Setting target to ({request.x}, {request.y})')
response.success = True
response.message = 'Target updated'
return response# Call a service
ros2 service call /set_target custom_interfaces/srv/SetTarget "{x: 1.0, y: 2.0}"Actions
Actions handle long-running tasks with feedback and cancelation. Use actions for navigation goals, arm movements, or complex sequences:
from custom_interfaces.action import NavigateToPose
class NavigationServer(Node):
def __init__(self):
super().__init__('navigation_server')
self.action_server = ActionServer(
self, NavigateToPose, 'navigate', self.execute_callback)
async def execute_callback(self, goal_handle):
feedback = NavigateToPose.Feedback()
goal = goal_handle.request
while not at_target(goal):
feedback.distance_remaining = compute_distance(goal)
goal_handle.publish_feedback(feedback)
await asyncio.sleep(0.1)
goal_handle.succeed()
return NavigateToPose.Result(success=True)ROS 2 CLI Tools
Essential command-line tools include ros2 node list to see running nodes, ros2 topic echo /topic_name to inspect messages, ros2 service call /service_name to invoke services, ros2 action send_goal /action_name to execute actions, and ros2 bag record -a to record all topics for later analysis and replay.
Launch Files
ROS 2 launch files are Python scripts that start multiple nodes with configuration:
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='my_robot',
executable='camera_node',
name='camera',
parameters=[{'camera_id': 0}]
),
Node(
package='my_robot',
executable='navigation_node',
name='navigation',
),
])ros2 launch my_robot robot.launch.pyROS 2 Communication Patterns
Beyond basic pub-sub, ROS 2 supports several communication patterns for different needs. Services provide synchronous request-response — use for occasional operations like setting a parameter or triggering a calibration routine. Actions handle long-running tasks with feedback and preemption — ideal for navigation goals, arm movement sequences, and complex behaviors. Parameters store configurable values that nodes read at startup and can be dynamically updated. Lifecycle nodes manage deterministic startup states (unconfigured, inactive, active, finalized) for coordinating complex robot initialization sequences. Choose the pattern that matches your communication semantics for clean, maintainable code.
Summary
ROS provides a structured communication framework for robotics. Nodes publish/subscribe to topics for streaming data, call services for request/reply, and use actions for long-running tasks. ROS 2 is the current standard with DDS middleware, real-time support, and security. Use colcon for building, Python launch files for configuration, and ros2 CLI for debugging.
ROS 2 Quality of Service (QoS)
QoS settings control message delivery guarantees. Reliability can be RELIABLE (retransmit lost messages) or BEST_EFFORT (no retransmission — use for sensor data where fresh data is preferred over guaranteed delivery). Durability can be TRANSIENT_LOCAL (late-joining subscribers receive the last message) or VOLATILE (no history for late joiners). Depth sets the message queue size — deeper queues buffer more messages at the cost of latency. Publisher and subscriber QoS must be compatible: a reliable publisher with a best-effort subscriber degrades to best-effort. Choose QoS based on data criticality: use reliable for commands, best-effort for camera feeds, and transient-local for static maps.
FAQ
What is the difference between a topic and a service?
Topics are for continuous data streams (sensor data, odometry) where multiple subscribers receive data simultaneously. Services are for one-time operations (set parameter, trigger action) where you need a response. Topics are asynchronous; services are synchronous.
How do I create a custom message type?
Create a .msg file in a msg/ directory within a package. Define fields with types: float64 x, float64 y, string name. Build with colcon. Import in Python: from my_package.msg import MyMessage. In C++: #include "my_package/msg/my_message.hpp".
What is ROS 2 composition?
Composition runs multiple nodes in a single operating system process using component containers. This reduces overhead compared to running each node as a separate process. Use rclcpp_components and component_container to load shared libraries at runtime. Composition is especially beneficial for resource-constrained systems.
ROS 2 Launch Files and Parameters
ROS 2 launch system replaces ROS 1’s XML-only launch files with Python scripts that give full programmatic control. Launch files define nodes, parameters, composable node containers, and lifecycle transitions. Use LaunchConfiguration for command-line overrides: robot_name = LaunchConfiguration('robot_name'). Group actions organize nodes by namespace. Parameters can be set per-node, loaded from YAML files, or computed dynamically. Lifecycle nodes (unconfigured, inactive, active, finalized) enable deterministic startup and shutdown sequences. The launch system supports condition-based inclusion, event handlers, and remapping rules for flexible deployment across different robot configurations.
ROS 2 CLI Tools Reference
The ros2 command line tool provides comprehensive system introspection. ros2 node list shows active nodes. ros2 topic list shows available topics with types. ros2 topic echo /topic prints live messages. ros2 service list shows available services. ros2 param list shows node parameters. ros2 action list shows active action servers. ros2 interface show sensor_msgs/msg/Image displays message field definitions. ros2 run my_package my_node launches a node. ros2 launch my_package my_launch.py starts a launch file. Master these commands for efficient ROS 2 development and debugging.
Building with Colcon
Colcon is the ROS 2 build tool that compiles packages while handling dependencies. Create a workspace: mkdir -p ~/ros2_ws/src && cd ~/ros2_ws && colcon build. Build a specific package: colcon build --packages-select my_package. Build in release mode: colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release. Source the setup files: source install/setup.bash. Use colcon test to run package tests. For large workspaces, use --symlink-install to avoid recompiling when Python files change. Colcon replaces ROS 1’s catkin build system with faster, more flexible builds.
ROS 2 Ecosystem Growth
The ROS 2 ecosystem has grown to include thousands of packages spanning navigation (Nav2), manipulation (MoveIt 2), perception (PCL, OpenCV), SLAM (Cartographer, SLAM Toolbox), and robot models (Universal Robots, Fanuc, Kinova). Major hardware manufacturers provide ROS 2 drivers. Cloud integration through AWS RoboMaker, Azure Robotics, and Google Cloud provides simulation, data logging, and remote monitoring. The ecosystem maturity makes ROS 2 suitable for production deployment in warehouse logistics, manufacturing, agriculture, and service robotics.
ROS 2 vs ROS 1: Key Differences
ROS 2 differs from ROS 1 in several fundamental ways: DDS replaces the custom ROS 1 transport layer, providing built-in QoS, security, and multi-platform support. Build system changed from catkin to colcon. Launch files changed from XML to Python. ROS 2 supports real-time execution, multiple nodes per process (composition), and deterministic callback execution. ROS 1 topics were typed at connection time; ROS 2 validates types at compile time through IDL interfaces. ROS 1 nodes used a single master for discovery; ROS 2 uses DDS discovery with no single point of failure. All new development should target ROS 2 exclusively — ROS 1 support ended with Noetic in May 2025.
Troubleshooting Common ROS 2 Issues
Common ROS 2 problems and solutions: nodes not discovering each other — check firewall settings and DDS domain ID (default 0, must match across all nodes). QoS mismatch — publishers and subscribers must have compatible reliability, durability, and history settings. Clock synchronization — use chrony or ptpd for multi-machine setups. Workspace sourcing — source install/setup.bash in every terminal. Colcon build failures — clean build: rm -rf build/ install/ && colcon build. Mapping localhost to 127.0.0.1 in /etc/hosts resolves discovery issues on systems with IPv6 enabled.
How do I debug ROS 2 communication issues?
Use ros2 topic echo /topic_name to inspect messages. Use ros2 topic info to see publisher/subscriber counts. Check QoS compatibility — publishers and subscribers must have compatible QoS settings. Use RCLCPP_DEBUG or rclpy logging at DEBUG level. For network issues, check DDS discovery with fastdds discovery -i 0.
For a comprehensive overview, read our article on Ai Robotics.
For a comprehensive overview, read our article on Autonomous Mobile Robots.