Lesson 4.3: Introduction to Robot Operating System—ROS

Introduction

Robot Operating System, commonly called ROS, is a software framework used to develop robotic and autonomous systems. Despite its name, ROS is not a complete operating system in the same way as Windows, Linux, or macOS. It operates on top of an existing operating system and provides communication tools, libraries, development conventions, visualization utilities, simulation support, and reusable software packages.

An autonomous robot contains many interconnected software components. One component may read a camera, another may process LiDAR measurements, another may estimate the robot’s position, and another may generate wheel commands. Developing all these functions inside one large program would make the system difficult to test, maintain, and expand.

ROS allows the system to be divided into smaller independent programs called nodes. Each node performs a defined task and communicates with other nodes using standard ROS communication methods.

A camera node may publish images. An object-detection node may receive those images and publish detected objects. A localization node may publish the robot’s estimated position. A navigation node may calculate a path and publish velocity commands.

The general ROS communication process can be represented as:

Sensors → ROS Nodes → Messages → Perception → Localization → Planning → Control → Actuators

ROS supports autonomous vehicles, drones, mobile robots, robotic arms, warehouse robots, agricultural machines, underwater robots, service robots, and industrial research platforms.

Its main value is not one particular algorithm. Its value lies in helping developers connect different hardware devices and software components through a modular and reusable architecture.

Why ROS Is Used in Autonomous-System Development

Autonomous systems contain several functions that must operate at the same time. Sensors collect data continuously, perception algorithms interpret the environment, localization estimates the robot’s pose, planners select safe routes, and controllers generate actuator commands.

ROS provides a structured method for organizing these functions.

A sensor driver can be developed independently from a path-planning algorithm. A new camera can be connected without rewriting the complete navigation system, provided that it publishes the expected ROS message format.

ROS also provides standard message types for common robotic information such as images, point clouds, laser scans, positions, velocities, joint states, maps, and coordinate transformations.

This standardization allows software developed by different teams to communicate more easily.

ROS provides command-line tools for inspecting the system, recording data, replaying experiments, visualizing sensor outputs, monitoring coordinate frames, and diagnosing communication problems.

These capabilities reduce development time and make robotic systems easier to analyse.

ROS 1 and ROS 2

ROS exists in two major generations known as ROS 1 and ROS 2.

ROS 1 introduced the core concepts of nodes, topics, messages, services, actions, packages, and launch files. It became widely used in academic robotics and research environments.

ROS 2 was developed to address additional requirements such as improved real-time support, distributed communication, security, reliability settings, multi-robot systems, and production-oriented deployment.

ROS 2 uses Data Distribution Service, commonly called DDS, for communication between nodes.

The basic concepts of ROS 1 and ROS 2 are similar, but their commands, software interfaces, and development structures differ.

This lesson primarily uses ROS 2-style Python examples because ROS 2 is designed for modern robotic and autonomous-system development. However, understanding the general concepts also helps when working with ROS 1 systems.

ROS Architecture

A ROS-based system is divided into nodes.

Each node normally performs one main function.

A camera node captures images. A LiDAR node produces range measurements. A localization node estimates pose. A planner node generates a route. A control node generates velocity commands.

Nodes communicate through topics, services, and actions.

Topics are used for continuous data streams.

Services are used for short request-and-response interactions.

Actions are used for longer tasks that require progress feedback and may need cancellation.

Parameters store configuration values.

Launch files start several nodes with defined settings.

Packages organize source code, configuration files, message definitions, launch files, and related resources.

Coordinate transformations describe how robot components and environmental frames are positioned relative to each other.

Nodes

A node is an executable software component in a ROS system.

A node should normally perform one clearly defined responsibility.

For example, a mobile robot may contain a wheel-encoder node, camera node, LiDAR node, localization node, map server, path planner, obstacle-avoidance node, velocity controller, motor-driver node, and health-monitoring node.

This modularity provides several benefits.

A failed camera-processing node does not necessarily require every other component to stop. A new planner may be tested without changing the motor driver. A sensor node may be replaced while preserving the rest of the architecture.

Each node should have clearly defined inputs, outputs, parameters, and failure behaviour.

A node name identifies the component within the ROS communication graph.

ROS Communication Graph

The ROS graph represents active nodes and their communication relationships.

Nodes appear as processing components. Topics appear as communication channels. Services and actions appear as request-based connections.

The graph helps developers understand how data moves through the system.

For example, a camera node may publish to an image topic. An object-detection node subscribes to that topic. It then publishes detections to another topic. A navigation node subscribes to the detection topic and changes the planned motion.

The flow may be represented as:

Camera Node → Image Topic → Detection Node → Detection Topic → Navigation Node

Visualizing this graph helps identify missing publishers, incorrect topic names, duplicate nodes, and disconnected software components.

Creating a ROS 2 Workspace

A ROS workspace is a directory containing one or more ROS packages.

A common workspace contains a src directory for package source files.

The following commands create a workspace.

mkdir -p autonomous_ws/src cd autonomous_ws

The workspace can be built using the ROS 2 build tool called colcon.

colcon build

After building, the workspace environment must be sourced.

On Linux, the following command loads the workspace.

source install/setup.bash

The workspace must normally be sourced in every new terminal before its packages can be used.

A developer may add the sourcing command to the shell configuration, but this should be done carefully when several ROS workspaces are used.

ROS Packages

A ROS package groups related code and resources.

A package may contain Python nodes, C++ nodes, launch files, configuration files, message definitions, models, maps, and tests.

A package should have a clear purpose.

A perception package may contain camera-processing nodes. A navigation package may contain planners and controllers. A robot-description package may contain physical models and coordinate-frame definitions.

A Python ROS 2 package can be created using the following command.

cd autonomous_ws/src 
ros2 pkg create autonomous_control \ --build-type ament_python \ --dependencies rclpy geometry_msgs

This command creates a package called autonomous_control.

The ament_python build type indicates that the package contains Python code.

The dependencies indicate that the package uses the ROS 2 Python client library and geometry message definitions.

A simplified Python package structure may appear as follows.

autonomous_control/ autonomous_control/ __init__.py velocity_publisher.py obstacle_subscriber.py resource/ autonomous_control test/ package.xml setup.cfg setup.py

The inner package directory contains Python source files.

The package.xml file describes package metadata and dependencies.

The setup.py file defines installation and executable information.

The ROS 2 Python Client Library

Python ROS 2 nodes are developed using the rclpy library.

The library provides functions for node creation, publishing, subscribing, services, actions, parameters, timers, logging, and shutdown.

A Python ROS node normally initializes ROS, creates a node object, keeps the node running, and shuts ROS down when execution ends.

A minimal ROS 2 Python node is shown below.

import rclpy from rclpy.node import Node 

class BasicNode(Node): def __init__(self) -> None: super().__init__("basic_node")
self.get_logger().info( "The basic ROS node has started." )

def main(args=None) -> None: rclpy.init(args=args)
node = BasicNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()

if __name__ == "__main__": main()

The rclpy.init() function initializes ROS communication.

The node class inherits from Node.

The rclpy.spin() function keeps the node active so that it can process messages, timers, services, and callbacks.

The node is destroyed and ROS is shut down when execution finishes.

Registering a Python Node as an Executable

A Python file does not automatically become a ROS executable.

The setup.py file must include an entry point.

A simplified setup.py configuration is shown below.

from setuptools import find_packages, setup 

package_name = "autonomous_control"

setup( name=package_name, version="0.0.1", 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="Autonomous Systems Team", maintainer_email="[email protected]", description=( "ROS 2 package for autonomous-system examples." ), license="Apache-2.0", tests_require=[ "pytest" ], entry_points={ "console_scripts": [ ( "basic_node = " "autonomous_control.basic_node:main" ) ] } )

After changing package files, the workspace should be rebuilt.

cd autonomous_ws 
colcon build

The workspace should then be sourced again.

source install/setup.bash

The node can be started using:

ros2 run autonomous_control basic_node

Topics

Topics provide asynchronous communication for continuous data streams.

A node publishes messages to a topic.

Other nodes subscribe to that topic.

The publisher does not need to know which subscribers are receiving the data.

The subscriber does not need direct knowledge of the publisher’s internal implementation.

This creates loose coupling between components.

Topics are suitable for camera images, LiDAR scans, odometry, joint states, sensor values, detected objects, velocity commands, and system status.

A topic has a name and a message type.

A publisher and subscriber must use compatible message types.

Publishers

A publisher sends messages to a topic.

The following node publishes a text message every second.

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

class StatusPublisher(Node): def __init__(self) -> None: super().__init__("status_publisher")
self.publisher = self.create_publisher( String, "robot_status", 10 )
self.timer = self.create_timer( 1.0, self.publish_status )
self.message_count = 0
def publish_status(self) -> None: message = String()
message.data = ( f"Robot operating normally: " f"message {self.message_count}" )
self.publisher.publish( message )
self.get_logger().info( f"Published: {message.data}" )
self.message_count += 1

def main(args=None) -> None: rclpy.init(args=args)
node = StatusPublisher()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()

if __name__ == "__main__": main()

The publisher uses the String message type.

The topic name is robot_status.

The timer calls the publishing function once every second.

The number 10 represents a basic communication queue-depth setting.

Subscribers

A subscriber receives messages from a topic.

The following node subscribes to the robot_status topic.

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

class StatusSubscriber(Node): def __init__(self) -> None: super().__init__("status_subscriber")
self.subscription = self.create_subscription( String, "robot_status", self.status_callback, 10 )
def status_callback( self, message: String ) -> None: self.get_logger().info( f"Received: {message.data}" )

def main(args=None) -> None: rclpy.init(args=args)
node = StatusSubscriber()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()

if __name__ == "__main__": main()

The callback function runs whenever a new message arrives.

Subscriber callbacks should remain efficient.

A long-running callback may delay other messages, timers, or service requests.

Computationally expensive processing may need to be placed in another thread, process, or dedicated node.

Inspecting Topics

Active topics can be listed using the following command.

ros2 topic list

The message type associated with each topic can also be displayed.

ros2 topic list -t

Information about a specific topic can be inspected.

ros2 topic info /robot_status

Messages can be viewed directly from the command line.

ros2 topic echo /robot_status

The publication frequency can be measured.

ros2 topic hz /robot_status

These commands are useful for diagnosing missing messages, incorrect topic names, low update frequency, and communication problems.

Publishing Messages from the Command Line

ROS messages can be published directly from the terminal for testing.

The following command publishes a text message.

ros2 topic pub --once \ /robot_status \ std_msgs/msg/String \ "{data: 'System test message'}"

Command-line publishing is useful when testing a subscriber without running the complete hardware or publisher node.

For safety-related topics such as velocity commands, command-line testing should be performed only in simulation or a controlled environment.

Message Types

ROS provides standard message definitions.

Common message packages include std_msgs, sensor_msgs, geometry_msgs, nav_msgs, and trajectory_msgs.

A text message may use std_msgs/msg/String.

A laser scanner may use sensor_msgs/msg/LaserScan.

A camera may use sensor_msgs/msg/Image.

An inertial sensor may use sensor_msgs/msg/Imu.

A robot pose may use geometry_msgs/msg/Pose.

A velocity command may use geometry_msgs/msg/Twist.

A navigation path may use nav_msgs/msg/Path.

An occupancy map may use nav_msgs/msg/OccupancyGrid.

Using standard message types improves compatibility among packages.

Velocity Commands with Twist

Mobile robots commonly receive linear and angular velocity commands through a Twist message.

The message contains linear and angular velocity vectors.

For a two-dimensional mobile robot, linear motion is often represented using the x component, while rotation is represented using the z angular component.

The following node publishes a forward velocity command.

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

class VelocityPublisher(Node): def __init__(self) -> None: super().__init__("velocity_publisher")
self.publisher = self.create_publisher( Twist, "cmd_vel", 10 )
self.timer = self.create_timer( 0.1, self.publish_velocity )
def publish_velocity(self) -> None: command = Twist()
command.linear.x = 0.5 command.linear.y = 0.0 command.linear.z = 0.0
command.angular.x = 0.0 command.angular.y = 0.0 command.angular.z = 0.0
self.publisher.publish( command )

def main(args=None) -> None: rclpy.init(args=args)
node = VelocityPublisher()
rclpy.spin(node)
stop_command = Twist()
node.publisher.publish( stop_command )
node.destroy_node()
rclpy.shutdown()

if __name__ == "__main__": main()

A real robot should include command validation, speed limits, communication timeouts, emergency stopping, and actuator-health checks.

Publishing a constant velocity without sensor feedback or obstacle monitoring is unsafe on a physical platform.

Services

Services provide request-and-response communication.

A client sends a request.

A service server processes the request and returns a response.

Services are suitable for short operations such as resetting a sensor, requesting a map, changing a mode, checking a system condition, or performing a calculation.

Services are not ideal for continuous data streams because topics are more suitable for repeated updates.

They are also not ideal for long-running tasks because actions provide feedback and cancellation support.

Creating a Service Server

The following example uses a standard service that adds two integers.

import rclpy from example_interfaces.srv import AddTwoInts from rclpy.node import Node 

class AdditionService(Node): def __init__(self) -> None: super().__init__("addition_service")
self.service = self.create_service( AddTwoInts, "add_two_numbers", self.handle_request )
def handle_request( self, request: AddTwoInts.Request, response: AddTwoInts.Response ) -> AddTwoInts.Response: response.sum = ( request.a + request.b )
self.get_logger().info( f"Received {request.a} and {request.b}" )
return response

def main(args=None) -> None: rclpy.init(args=args)
node = AdditionService()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()

if __name__ == "__main__": main()

The server waits for requests, calculates the result, and returns the response.

Calling a Service from the Command Line

Available services can be listed.

ros2 service list

The service type can be inspected.

ros2 service type /add_two_numbers

The service can be called directly.

ros2 service call \ /add_two_numbers \ example_interfaces/srv/AddTwoInts \ "{a: 5, b: 7}"

This command sends two numbers and displays the returned result.

Creating a Service Client

A Python client can request the service.

import rclpy from example_interfaces.srv import AddTwoInts from rclpy.node import Node 

class AdditionClient(Node): def __init__(self) -> None: super().__init__("addition_client")
self.client = self.create_client( AddTwoInts, "add_two_numbers" )
while not self.client.wait_for_service( timeout_sec=1.0 ): self.get_logger().info( "Waiting for the addition service." )
def send_request( self, value_a: int, value_b: int ): request = AddTwoInts.Request()
request.a = value_a request.b = value_b
return self.client.call_async( request )

def main(args=None) -> None: rclpy.init(args=args)
node = AdditionClient()
future = node.send_request( 5, 7 )
rclpy.spin_until_future_complete( node, future )
if future.result() is not None: node.get_logger().info( f"Result: {future.result().sum}" ) else: node.get_logger().error( "The service request failed." )
node.destroy_node()
rclpy.shutdown()

if __name__ == "__main__": main()

The client waits for the service to become available before sending the request.

A real autonomous system should also use timeouts and define safe behaviour when a required service is unavailable.

Actions

Actions are used for tasks that take time and require progress feedback.

An action client sends a goal.

An action server accepts or rejects the goal, publishes feedback during execution, and eventually returns a result.

The client may also cancel the task.

Actions are suitable for navigation goals, robotic-arm movements, docking, inspection missions, object manipulation, and long-duration autonomous operations.

A robot navigating across a building should not use a simple service because the task may take several minutes and may need cancellation.

The action can report progress such as distance remaining or current mission stage.

Action Communication Process

The action process can be represented as:

Client Sends Goal → Server Accepts Goal → Server Publishes Feedback → Client May Cancel → Server Returns Result

A navigation action may receive a target pose, report current progress, and return whether the destination was reached.

Actions provide a clearer structure for long-running autonomous tasks than topics or services alone.

Parameters

Parameters store configurable values associated with a node.

A node may use parameters for maximum speed, sensor topic name, obstacle threshold, controller gain, map frame, update frequency, or confidence threshold.

Parameters allow behaviour to be adjusted without changing the source code.

The following node declares and reads a parameter.

import rclpy from rclpy.node import Node 

class SpeedController(Node): def __init__(self) -> None: super().__init__("speed_controller")
self.declare_parameter( "maximum_speed_mps", 1.0 )
maximum_speed_mps = ( self.get_parameter( "maximum_speed_mps" ) .get_parameter_value() .double_value )
self.get_logger().info( f"Maximum speed: " f"{maximum_speed_mps:.2f} m/s" )

def main(args=None) -> None: rclpy.init(args=args)
node = SpeedController()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()

if __name__ == "__main__": main()

A parameter can be supplied when the node is started.

ros2 run autonomous_control speed_controller \ --ros-args \ -p maximum_speed_mps:=0.6

Parameter values should be validated.

A negative speed or unsafe maximum value should not be accepted without checking.

Inspecting Parameters

Active node parameters can be listed.

ros2 param list

A parameter value can be read.

ros2 param get \ /speed_controller \ maximum_speed_mps

A parameter may be changed while the node is running when the node permits dynamic updates.

ros2 param set \ /speed_controller \ maximum_speed_mps \ 0.8

Safety-critical parameters should not be changed without authorization, validation, and controlled system behaviour.

YAML Parameter Files

Several parameters can be stored in a YAML configuration file.

speed_controller: ros__parameters: maximum_speed_mps: 1.0 minimum_speed_mps: 0.0 control_frequency_hz: 20.0 emergency_stop_distance_m: 0.5

The parameter file can be supplied when starting a node.

ros2 run autonomous_control speed_controller \ --ros-args \ --params-file config/controller_parameters.yaml

Configuration files should include documented units and version control.

Critical files should be protected from unauthorized modification.

Launch Files

A launch file starts several nodes together.

It may also define parameters, namespaces, remappings, environment settings, and conditions.

Without launch files, a developer would need to start every node manually in a separate terminal.

A Python launch file may start a status publisher and subscriber.

from launch import LaunchDescription from launch_ros.actions import Node 

def generate_launch_description(): return LaunchDescription([ Node( package="autonomous_control", executable="status_publisher", name="status_publisher" ),
Node( package="autonomous_control", executable="status_subscriber", name="status_subscriber" ) ])

The launch file may be saved inside a launch directory.

autonomous_control/ launch/ status_system.launch.py

The package setup must install the launch file so ROS can locate it.

A launch file can be started using:

ros2 launch \ autonomous_control \ status_system.launch.py

Topic Remapping

Topic remapping changes a topic name without modifying node source code.

A node may publish to cmd_vel, but the robot may expect robot_velocity_command.

The topic can be remapped when the node starts.

ros2 run autonomous_control velocity_publisher \ --ros-args \ -r cmd_vel:=robot_velocity_command

Remapping supports reusable nodes and integration across systems with different naming conventions.

Topic names should still be planned carefully because excessive remapping can make the architecture difficult to understand.

Namespaces

Namespaces organize nodes and topics into groups.

They are especially useful in multi-robot systems.

Two robots may each use a topic called cmd_vel, but their complete topic names can be separated through namespaces.

The topics may become:

/robot_1/cmd_vel /robot_2/cmd_vel

A node can be started within a namespace.

ros2 run autonomous_control velocity_publisher \ --ros-args \ -r __ns:=/robot_1

Namespaces reduce naming conflicts and support repeated deployment of the same software architecture.

Quality of Service

ROS 2 allows developers to control communication behaviour through Quality of Service, commonly called QoS.

QoS settings determine how messages are delivered, stored, and handled under different network conditions.

Important QoS concepts include reliability, durability, history, and queue depth.

Reliable communication attempts to deliver every message.

Best-effort communication prioritizes speed and accepts that some messages may be lost.

A camera stream may use best-effort delivery because receiving the newest frame is more important than recovering every previous frame.

A critical state command may require reliable delivery.

Durability determines whether newly connected subscribers can receive previously published information.

QoS settings must match between publishers and subscribers sufficiently for communication to occur.

Creating a Publisher with QoS

The following example creates a reliable publisher with a defined queue depth.

import rclpy from rclpy.node import Node from rclpy.qos import ( HistoryPolicy, QoSProfile, ReliabilityPolicy )
from std_msgs.msg import String

class ReliableStatusPublisher(Node): def __init__(self) -> None: super().__init__( "reliable_status_publisher" )
qos_profile = QoSProfile( reliability=ReliabilityPolicy.RELIABLE, history=HistoryPolicy.KEEP_LAST, depth=10 )
self.publisher = self.create_publisher( String, "reliable_robot_status", qos_profile )

QoS should be selected according to the meaning, frequency, and safety importance of the data.

Using reliable communication for every high-rate sensor stream may increase delay and resource use.

Coordinate Frames

Autonomous systems use several coordinate frames.

A map frame represents the global environment.

An odometry frame represents continuous local motion.

A base frame is attached to the robot body.

Sensor frames are attached to cameras, LiDAR units, radar devices, and inertial sensors.

A navigation system must know how these frames relate to each other.

A point detected in the camera frame must be transformed into the robot or map frame before it can be used for path planning.

ROS provides the TF2 system for managing coordinate transformations.

Common Coordinate Frames

A mobile robot may use the following conceptual frame hierarchy:

map └── odom └── base_link ├── camera_link ├── lidar_link └── imu_link

The map frame provides a global reference.

The odom frame provides locally continuous motion.

The base_link frame represents the robot body.

Sensor frames describe the physical location and orientation of individual sensors.

Incorrect transformations can place obstacles, goals, or sensor measurements in the wrong location.

Publishing a Static Transform

A static transform represents a fixed physical relationship.

For example, a camera may be mounted twenty centimetres in front of the robot centre.

A static transform can be published from the command line.

ros2 run tf2_ros static_transform_publisher \ --x 0.20 \ --y 0.00 \ --z 0.30 \ --roll 0.00 \ --pitch 0.00 \ --yaw 0.00 \ --frame-id base_link \ --child-frame-id camera_link

The values represent translation and rotation between the frames.

Units and axis conventions must be verified carefully.

Inspecting Coordinate Transformations

The relationship between two frames can be inspected.

ros2 run tf2_ros tf2_echo \ base_link \ camera_link

The complete frame tree can be generated using TF2 tools.

ros2 run tf2_tools view_frames

A frame diagram helps identify missing transforms, disconnected frame trees, incorrect parent-child relationships, and unexpected coordinate orientations.

Robot Description

A robot description represents the robot’s physical structure.

It may include links, joints, sensors, dimensions, collision geometry, visual geometry, mass, and inertial properties.

ROS commonly uses Unified Robot Description Format, known as URDF.

A robot arm may contain several connected links and joints.

A mobile robot may contain a base, wheels, camera, LiDAR, and sensors.

A simplified URDF structure is shown below.

<?xml version="1.0"?> 
<robot name="simple_mobile_robot">
<link name="base_link"> <visual> <geometry> <box size="0.6 0.4 0.2"/> </geometry> </visual> </link>
<link name="camera_link"> <visual> <geometry> <box size="0.1 0.1 0.1"/> </geometry> </visual> </link>
<joint name="camera_joint" type="fixed">
<parent link="base_link"/> <child link="camera_link"/>
<origin xyz="0.20 0.00 0.20" rpy="0.00 0.00 0.00"/>
</joint>
</robot>

This model defines a robot base and a camera connected by a fixed joint.

A production model should also include collision and inertial information.

Xacro

Large robot descriptions can become repetitive.

Xacro is a macro language used to generate URDF files.

It supports variables, mathematical expressions, reusable components, and conditional sections.

A parameterized wheel or sensor component can be defined once and reused several times.

This reduces duplication and improves maintainability.

However, generated robot descriptions should still be inspected because an error in one macro may affect many components.

RViz

RViz is a ROS visualization tool.

It can display robot models, coordinate frames, camera images, laser scans, point clouds, paths, maps, poses, markers, and navigation goals.

RViz does not normally simulate physical movement. It visualizes data published by ROS nodes.

A developer can use RViz to verify whether a LiDAR scan aligns with the map, whether the robot pose is correct, whether coordinate frames are connected, and whether a planned path avoids obstacles.

RViz can be started using:

rviz2

Displays can be added through the interface according to active topics and message types.

A saved RViz configuration can preserve preferred visualization settings.

Simulation with Gazebo

Gazebo is commonly used with ROS to simulate robots, sensors, physics, obstacles, and environments.

A simulation may include robot mass, friction, collisions, motor behaviour, camera images, LiDAR scans, and environmental objects.

ROS nodes can receive simulated sensor data and send commands as though they were communicating with a physical robot.

This supports simulation-first development.

The same navigation node may be tested in Gazebo and later connected to a real robot through a hardware driver.

Simulation reduces hardware risk but does not perfectly represent reality.

Real sensors contain noise, calibration errors, delay, vibration, wear, lighting variation, and communication problems that may not be reproduced accurately.

Simulation-to-Reality Transfer

Software that works in simulation may fail on physical hardware.

A simulated wheel may have perfect traction, while a real wheel slips.

A simulated camera may provide clear images, while a real camera experiences motion blur and glare.

A simulated motor may respond immediately, while a real actuator has delay and saturation.

Developers should introduce realistic noise, delay, friction, and sensor failure into simulation.

Physical testing should begin at low speed in a controlled environment.

Simulation is a development tool, not final proof of safe performance.

Recording Data with rosbag

ROS can record topic messages using rosbag.

Recorded data can later be replayed for debugging, algorithm development, and testing.

The following command records selected topics.

ros2 bag record \ /camera/image_raw \ /scan \ /odom \ /cmd_vel

All active topics can also be recorded.

ros2 bag record -a

Recording every topic may consume substantial storage and include unnecessary or sensitive data.

Developers should select topics according to the purpose of the experiment.

Inspecting and Replaying a Bag

Recorded bag information can be inspected.

ros2 bag info rosbag2_2026_07_14-10_00_00

The recording can be replayed.

ros2 bag play rosbag2_2026_07_14-10_00_00

Replay allows perception, localization, and planning algorithms to be tested repeatedly using the same sensor sequence.

Commands sent during replay should not control a physical robot unless the test architecture has been made safe.

ROS Logging

ROS nodes provide structured logging.

The following code records informational, warning, and error messages.

self.get_logger().info( "Navigation node started." )
self.get_logger().warning( "Localization confidence is low." )
self.get_logger().error( "Motor communication failed." )

Logs should describe meaningful system conditions.

Excessive logging can reduce performance and make important messages difficult to identify.

Critical events should include timestamps, operating mode, relevant values, and component state.

Timers

Timers execute a callback at a defined interval.

They are useful for periodic sensor polling, command publication, status checks, and monitoring.

The following node runs a callback ten times per second.

import rclpy from rclpy.node import Node 

class MonitoringNode(Node): def __init__(self) -> None: super().__init__("monitoring_node")
self.timer = self.create_timer( 0.1, self.monitor_system )
def monitor_system(self) -> None: self.get_logger().info( "System health check executed." )

A timer period does not guarantee that the callback will execute at an exact hard real-time interval.

Long callbacks, processor load, communication, and executor behaviour may introduce delay.

Callbacks

A callback is a function executed in response to an event.

Subscriber callbacks run when messages arrive.

Timer callbacks run at scheduled intervals.

Service callbacks run when requests arrive.

Action callbacks manage goals, feedback, cancellation, and results.

Callbacks should be designed carefully.

A callback that performs heavy image processing may block other events.

The developer may use separate nodes, multiple threads, asynchronous execution, or optimized libraries for computationally intensive tasks.

Executors

An executor manages callback execution.

A single-threaded executor processes one callback at a time.

A multi-threaded executor can process compatible callbacks concurrently.

The following example uses a multi-threaded executor.

import rclpy from rclpy.executors import MultiThreadedExecutor 

def main(args=None) -> None: rclpy.init(args=args)
sensor_node = SensorNode() monitoring_node = MonitoringNode()
executor = MultiThreadedExecutor()
executor.add_node( sensor_node )
executor.add_node( monitoring_node )
try: executor.spin()
finally: executor.shutdown()
sensor_node.destroy_node() monitoring_node.destroy_node()
rclpy.shutdown()

Concurrent callbacks may access shared data.

Locks, callback groups, queues, or message-based separation may be required to avoid race conditions.

Lifecycle Nodes

Lifecycle nodes provide controlled operating states.

A lifecycle node may move through states such as unconfigured, inactive, active, and finalized.

This is useful when a component should not begin normal operation until configuration and hardware checks are complete.

A sensor node may first load configuration, then connect to hardware, then begin publishing only after successful activation.

Lifecycle management supports controlled startup, shutdown, fault recovery, and system supervision.

It is especially valuable in complex or safety-sensitive autonomous systems.

Navigation Stack

ROS provides navigation frameworks for mobile robots.

A navigation system commonly includes localization, mapping, global planning, local planning, obstacle layers, recovery behaviour, and velocity control.

The system receives a navigation goal and attempts to move the robot to the target.

It uses map information and live sensor data to avoid obstacles.

A typical navigation flow is:

Goal Pose → Global Planner → Global Path → Local Planner → Velocity Command → Robot Motion

The robot continuously updates its position and local obstacle information.

If the path becomes blocked, the system may replan or activate recovery behaviour.

Nav2

Nav2 is the ROS 2 navigation framework commonly used for mobile-robot navigation.

It supports planning, controlling, obstacle avoidance, recovery, behaviour trees, maps, localization, and navigation actions.

Nav2 uses modular servers for planning, control, recovery, and behaviour execution.

A developer can configure global and local cost maps, planners, controllers, robot footprint, speed limits, obstacle sources, and recovery behaviour.

Nav2 should be tuned according to robot dimensions, movement model, sensors, environment, and control performance.

Using default settings without physical validation may produce unsafe or ineffective behaviour.

Cost Maps

Navigation cost maps represent free space, obstacles, unknown regions, and areas near obstacles.

A global cost map supports large-scale path planning.

A local cost map represents nearby obstacles and dynamic changes.

Cost-map layers may include static map information, live obstacles, inflation zones, and restricted areas.

The robot footprint must be represented accurately.

If the footprint is too small, the planner may allow collision.

If it is too large, the robot may be unable to navigate through valid passages.

SLAM in ROS

Simultaneous Localization and Mapping allows a robot to build a map while estimating its pose.

ROS supports several SLAM packages and sensor configurations.

A mobile robot may combine LiDAR, wheel odometry, and inertial measurements.

A visual robot may use monocular, stereo, or RGB-D cameras.

The SLAM node publishes a map and coordinate transformation.

The map may later be saved and used for localization.

SLAM performance depends on sensor quality, odometry, environmental features, movement speed, loop closure, and correct coordinate transformations.

Mapping and Map Saving

An occupancy map can represent free, occupied, and unknown cells.

After completing a mapping session, the map may be saved for later navigation.

A map commonly includes an image and metadata describing resolution, origin, and occupancy thresholds.

The saved map should be associated with the physical environment and version.

If shelves, walls, or corridors change, the map may become inaccurate.

Autonomous systems should not assume that a stored map is always current.

Hardware Drivers

A ROS hardware driver connects ROS messages with a physical device.

A camera driver converts device-specific camera output into ROS image messages.

A LiDAR driver converts scanner data into laser-scan or point-cloud messages.

A motor driver converts velocity commands into electrical or communication commands understood by the actuator controller.

Hardware drivers should handle connection, configuration, timestamps, calibration, invalid data, timeouts, and shutdown.

A hardware driver should not silently continue publishing old data after communication is lost.

Hardware Abstraction

Hardware abstraction allows high-level software to remain independent of device-specific details.

A navigation node should not need to understand the electrical protocol used by the motor driver.

It should publish a standard velocity message.

The motor node converts the message into device-specific commands.

This allows simulation and physical hardware to use the same high-level software.

A simulated robot and a real robot may both subscribe to cmd_vel, even though their internal implementations are different.

Watchdogs and Command Timeouts

A motor controller should not continue moving indefinitely when command messages stop arriving.

A watchdog checks the age of the latest command.

If the command becomes too old, the system stops the robot.

A simplified watchdog concept is shown below.

import time 

last_command_time_s = time.monotonic()
command_timeout_s = 0.5

def command_callback( message ) -> None: global last_command_time_s
last_command_time_s = time.monotonic()
send_command_to_motors( message )

def watchdog_check() -> None: command_age_s = ( time.monotonic() - last_command_time_s )
if command_age_s > command_timeout_s: stop_motors()

Critical watchdogs should preferably operate in lower-level hardware or control software so that they remain active if a ROS node crashes.

Safe Velocity Filtering

A velocity command should be checked before it reaches the actuator.

The following function limits linear and angular speed.

import math 

def validate_velocity( linear_speed_mps: float, angular_speed_radps: float, maximum_linear_speed_mps: float, maximum_angular_speed_radps: float ) -> tuple[float, float]: if not math.isfinite( linear_speed_mps ): raise ValueError( "Linear speed must be finite." )
if not math.isfinite( angular_speed_radps ): raise ValueError( "Angular speed must be finite." )
safe_linear_speed_mps = max( -maximum_linear_speed_mps, min( linear_speed_mps, maximum_linear_speed_mps ) )
safe_angular_speed_radps = max( -maximum_angular_speed_radps, min( angular_speed_radps, maximum_angular_speed_radps ) )
return ( safe_linear_speed_mps, safe_angular_speed_radps )

This validation should operate together with obstacle checks, localization confidence, emergency stopping, and actuator constraints.

System Health Monitoring

A ROS health-monitoring node may observe sensor frequency, message age, battery status, localization confidence, processor load, actuator state, and communication health.

The monitor may publish a system-health message.

A supervisor node can use this information to change operating modes.

A failed optional sensor may reduce capability.

A failed critical sensor may require stopping.

The response to each failure should be defined through system safety analysis.

Diagnosing Nodes

Active nodes can be listed.

ros2 node list

Information about a particular node can be inspected.

ros2 node info /status_publisher

This information may include publishers, subscribers, services, and actions associated with the node.

These commands help diagnose unexpected node names, missing interfaces, and incorrect communication relationships.

Visualizing the ROS Graph

The ROS graph can be visualized using rqt_graph.

rqt_graph

The graph displays nodes and topics.

It helps developers understand system structure and identify missing or unintended communication paths.

Large systems may contain many nodes, so clear naming and namespaces are important.

Custom Messages

Standard messages are suitable for many tasks, but some applications require custom data structures.

A custom message may contain a detected object class, confidence, position, velocity, and timestamp.

A simplified message definition may appear as follows.

string class_name float32 confidence float32 position_x_m float32 position_y_m float32 velocity_x_mps float32 velocity_y_mps builtin_interfaces/Time timestamp

Custom interfaces should be created only when standard messages are insufficient.

Field names should include clear meaning and units where appropriate.

Changing a custom message may affect every node that uses it, so interface versions should be managed carefully.

Custom Services

A custom service definition contains a request section and a response section separated by three hyphens.

A service for changing robot mode may appear as follows.

string requested_mode --- bool accepted string message

The client sends the requested mode.

The server responds with acceptance status and a descriptive message.

The service server should validate whether the requested transition is permitted.

For example, a robot should not enter normal navigation mode while an emergency stop remains active.

Custom Actions

A custom action definition contains goal, result, and feedback sections.

A simplified navigation action may appear as follows.

float32 target_x_m float32 target_y_m --- bool success string final_status --- float32 distance_remaining_m string current_state

The goal contains the destination.

The result indicates whether navigation succeeded.

The feedback reports remaining distance and current state.

Custom actions should define cancellation and failure behaviour clearly.

Security

Autonomous ROS systems may operate across networks and communicate with sensors, computers, cloud services, and operators.

Unauthorized nodes should not be allowed to publish control commands.

Configuration files, maps, launch files, and software packages should be protected.

ROS 2 supports security mechanisms for authentication, encryption, and access control through its underlying communication system.

Security should be planned during architecture design.

A secure communication channel does not prevent unsafe logic, so software validation and physical safety controls remain necessary.

Network Communication

ROS nodes can operate on different computers.

A robot may contain an embedded computer for sensors, another computer for perception, and a remote workstation for monitoring.

Distributed communication introduces delay, packet loss, bandwidth limits, and network failure.

High-resolution camera and point-cloud streams consume substantial bandwidth.

Critical control should not depend entirely on an unreliable remote network.

The robot should maintain local emergency stopping and minimum-risk behaviour when communication is lost.

Time Synchronization

Messages from cameras, LiDAR, IMU, GPS, and wheel encoders must be associated with accurate timestamps.

Sensor fusion depends on knowing when each measurement was captured.

If the system combines a new camera image with an old LiDAR scan, the environmental interpretation may be incorrect.

Computers in a distributed system should have synchronized clocks.

Message timestamps should represent data-acquisition time rather than only transmission time whenever possible.

Sensor Synchronization

Some algorithms require several sensor messages from approximately the same moment.

A camera and depth sensor may need synchronized frames.

A LiDAR scan may need to be matched with the closest odometry estimate.

ROS provides utilities for exact and approximate message synchronization.

Approximate synchronization is useful when sensors operate at different rates or have small timing differences.

The acceptable timing difference depends on robot speed and application risk.

Error Handling

ROS nodes should detect and report errors.

A camera node should identify failed frame capture.

A localization node should report low confidence.

A motor node should report communication loss.

A planner should report when no path can be found.

A node should not continue publishing apparently valid information when its internal state is invalid.

Failure information should be communicated to supervisory and safety components.

Graceful Shutdown

ROS nodes should stop hardware and release resources safely.

A motor node should send a stop command before shutdown when possible.

A camera node should close the device.

A recording node should complete file writing.

A node should also handle unexpected shutdown, but software cleanup alone is not sufficient for critical safety.

Independent hardware emergency systems and command timeouts should protect the robot if the node crashes.

Testing ROS Nodes

Individual ROS nodes should be tested before complete-system integration.

A publisher can be tested using command-line subscribers.

A subscriber can be tested using command-line publication.

Services can be called manually.

Parameters can be changed and validated.

Topics can be recorded and replayed.

Mock nodes may simulate sensors and actuators.

Unit tests should verify internal functions.

Integration tests should verify message types, topic names, timestamps, coordinate frames, and failure behaviour.

Simulation Testing

ROS simulation allows nodes to be tested without physical hardware.

A simulated LiDAR node may publish scans.

A simulated localization system may publish pose.

A navigation node may publish velocity commands.

The simulated robot responds to the commands.

Testing should include obstacle appearance, blocked paths, sensor noise, localization drift, message delay, and communication loss.

Simulation should be followed by controlled physical testing.

Common ROS Development Errors

Common errors include forgetting to source the workspace, using incorrect topic names, mismatching message types, failing to rebuild after code changes, and omitting executable entry points.

Other errors include incorrect coordinate frames, missing transforms, inconsistent QoS settings, stale messages, incorrect parameter types, and callbacks that block communication.

A node may appear to run correctly while publishing to a topic no other component uses.

A sensor may publish data with an incorrect timestamp.

A planner may operate in the map frame while an obstacle is interpreted in the sensor frame.

These errors can create physically unsafe behaviour.

ROS for Autonomous Vehicles

Autonomous vehicles can use ROS for sensor drivers, point-cloud processing, camera perception, localization, mapping, route planning, trajectory generation, control, visualization, and recording.

Vehicle platforms often require additional real-time, safety, and automotive communication systems.

ROS may support research and prototyping while dedicated controllers manage braking, steering, and drive-by-wire functions.

The complete vehicle architecture should include independent safety monitoring and emergency control.

ROS for Drones

Drones can use ROS for camera processing, visual odometry, mapping, mission planning, object tracking, and high-level navigation.

Flight-controller firmware normally manages rapid attitude stabilization and motor control.

ROS sends higher-level commands such as position, velocity, waypoint, or mission goals.

This separation allows ROS to perform intelligent planning while the flight controller maintains fast and stable flight control.

Communication loss and low battery should activate safe flight behaviour.

ROS for Mobile Robots

Mobile robots commonly use ROS for wheel odometry, LiDAR, mapping, localization, navigation, obstacle avoidance, docking, fleet coordination, and human interaction.

A service robot may use ROS nodes for person detection, speech processing, map navigation, and task management.

A warehouse robot may use navigation actions, fleet scheduling, traffic management, and charging control.

The robot should maintain safe behaviour around people and other machines.

ROS for Robotic Arms

Robotic arms can use ROS for joint-state monitoring, motion planning, collision checking, trajectory execution, gripper control, and visual perception.

A motion-planning framework may generate a collision-free joint trajectory.

A controller then executes the trajectory through hardware interfaces.

Force sensing and safety systems may operate through additional controllers.

Industrial deployments require careful validation of speed, force, workspace, and human-access restrictions.

ROS for Multi-Robot Systems

Namespaces, distributed communication, and reusable packages make ROS suitable for multi-robot systems.

Each robot can run a similar node structure under its own namespace.

A central system may assign tasks, coordinate routes, and monitor health.

Multi-robot systems must manage network load, robot identity, time synchronization, shared maps, collision avoidance, and task conflicts.

One robot’s topic or command should not accidentally control another robot.

Human–Robot Interaction

ROS can connect interfaces such as graphical dashboards, voice systems, remote controls, buttons, and monitoring tools.

Human commands should be validated.

An operator should not be able to activate movement when a safety interlock is active.

The interface should clearly show operating mode, sensor health, localization status, current goal, and emergency conditions.

Control transfer between autonomy and human operation should be predictable and recorded.

Advantages of ROS

ROS supports modularity, reuse, hardware abstraction, distributed computing, visualization, simulation, testing, and standard communication.

Developers can combine sensor drivers, mapping packages, planners, controllers, and user interfaces without developing every component from the beginning.

The ROS ecosystem supports research, education, prototyping, and many production-oriented applications.

Its tools make complex robotic systems easier to inspect and debug.

Limitations of ROS

ROS does not automatically make a robot autonomous or safe.

It does not replace perception algorithms, planning methods, control theory, hardware design, or safety engineering.

Incorrect topic connections, parameters, transforms, or message timing can create serious failures.

Distributed communication introduces delay and network dependence.

Python ROS nodes may not provide hard real-time execution.

Third-party packages may contain defects, outdated dependencies, or cybersecurity risks.

A ROS system requires controlled package versions, testing, monitoring, and fault handling.

Safety-critical functions may require certified controllers, real-time systems, and independent hardware protections outside the main ROS software.

Conclusion

Robot Operating System provides a structured framework for developing complex robotic and autonomous systems.

It divides software into nodes that communicate using topics, services, and actions.

Topics support continuous data streams. Services support short request-and-response interactions. Actions support long-running tasks with feedback and cancellation.

Parameters store configurable values, while launch files start and configure groups of nodes.

Packages organize code, interfaces, launch files, configuration, and related resources.

ROS provides standard message types for images, laser scans, point clouds, poses, maps, paths, joint states, and velocity commands.

TF2 manages relationships among map, odometry, robot, and sensor coordinate frames.

URDF and Xacro describe robot structure, joints, geometry, and sensor placement.

RViz visualizes robot state and environmental information, while simulation platforms allow software to be tested before physical deployment.

Rosbag supports recording and replaying sensor and system data.

ROS 2 adds communication controls, security support, distributed operation, and improved suitability for modern multi-robot and production environments.

Reliable ROS development requires correct topic names, compatible message types, suitable QoS settings, accurate timestamps, valid coordinate transforms, safe parameters, command timeouts, hardware watchdogs, and structured error handling.

ROS does not replace autonomous intelligence. It connects perception, localization, planning, control, sensors, actuators, and monitoring into an organized software architecture.

Its greatest strength is its ability to transform a complex autonomous machine into a collection of understandable, testable, reusable, and replaceable components.

Comments are closed.

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
FREEDOM TO LEARN • 10% OFF All Courses & Workshops • Use Code: NANOINDIA10 • ⏳ Offer Ends In: Loading... • Learn Today. Lead Tomorrow. • Explore Programs →
FREEDOM TO LEARN • 10% OFF All Courses & Workshops • Use Code: NANOINDIA10 • ⏳ Offer Ends In: Loading... • Learn Today. Lead Tomorrow. • Explore Programs →
Support