{"id":5698,"date":"2024-12-04T07:48:21","date_gmt":"2024-12-04T07:48:21","guid":{"rendered":"https:\/\/algocademy.com\/blog\/dive-into-robotics-programming-mastering-ros-robot-operating-system\/"},"modified":"2026-10-15T12:51:17","modified_gmt":"2026-10-15T12:51:17","slug":"dive-into-robotics-programming-mastering-ros-robot-operating-system","status":"publish","type":"post","link":"https:\/\/algocademy.com\/blog\/dive-into-robotics-programming-mastering-ros-robot-operating-system\/","title":{"rendered":"ROS (Robot Operating System): What It Is and How to Start with ROS 2"},"content":{"rendered":"<p>ROS (Robot Operating System) is not an operating system. It is an open-source set of libraries, tools and conventions for writing robot software as many small programs that talk to each other. You write ROS programs mainly in C++ or Python. If you start today, learn ROS 2: the old ROS 1 reached end of life with Noetic on May 31, 2025, so older tutorials (including the earlier version of this post) that use <code>roscore<\/code>, <code>catkin<\/code> and <code>rospy<\/code> no longer apply.<\/p>\n<h2>What ROS gives you<\/h2>\n<p>A robot needs code for sensors, motors, localization, planning and a user interface. ROS lets you write each as a separate <strong>node<\/strong> and connect them, instead of one giant program. It also supplies ready-made pieces you would not want to write yourself:<\/p>\n<ul>\n<li>message definitions for common data (images, laser scans, poses, velocity commands);<\/li>\n<li>command-line tools to inspect and debug a running system (<code>ros2 topic<\/code>, <code>ros2 node<\/code>, <code>ros2 bag<\/code> for recording data);<\/li>\n<li>visualization (RViz) and the Gazebo simulator, so you can test without hardware;<\/li>\n<li>large packages for navigation (Nav2), arm motion planning (MoveIt) and more, built by the community.<\/li>\n<\/ul>\n<p>The main client libraries are <code>rclcpp<\/code> for C++ and <code>rclpy<\/code> for Python. Python is faster to write and fine for learning and high-level logic. C++ is common where timing and performance matter. So the answer to &#8220;what programming language does ROS use&#8221; is: both, and the nodes can be in different languages in the same robot.<\/p>\n<h2>ROS 1 or ROS 2?<\/h2>\n<p>Use ROS 2. ROS 1 is no longer maintained: its last release, Noetic, ended support on May 31, 2025. ROS 2 replaced the central <code>roscore<\/code> master with a decentralized discovery system on top of DDS, supports Windows and Linux, and is the version all current tutorials and new packages target.<\/p>\n<p>ROS 2 releases come out every May. As of October 2026 (check <a href=\"https:\/\/docs.ros.org\/en\/rolling\/Releases.html\">docs.ros.org\/en\/rolling\/Releases.html<\/a> for the current list):<\/p>\n<table>\n<thead>\n<tr>\n<th scope=\"col\">Distribution<\/th>\n<th scope=\"col\">Type<\/th>\n<th scope=\"col\">Main Ubuntu target<\/th>\n<th scope=\"col\">Supported until<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Lyrical Luth (May 2026)<\/td>\n<td>LTS<\/td>\n<td>26.04<\/td>\n<td>May 2031<\/td>\n<\/tr>\n<tr>\n<td>Kilted Kaiju (May 2025)<\/td>\n<td>Non-LTS<\/td>\n<td>24.04<\/td>\n<td>December 2026<\/td>\n<\/tr>\n<tr>\n<td>Jazzy Jalisco (May 2024)<\/td>\n<td>LTS<\/td>\n<td>24.04<\/td>\n<td>May 2029<\/td>\n<\/tr>\n<tr>\n<td>Humble Hawksbill (May 2022)<\/td>\n<td>LTS<\/td>\n<td>22.04<\/td>\n<td>May 2027<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Pick an LTS release that matches your Ubuntu version. On Ubuntu 24.04, that is Jazzy; on 26.04, Lyrical. The commands below use Jazzy; for another distribution replace <code>jazzy<\/code> with its name (for example <code>lyrical<\/code>) and follow that distribution&#8217;s page on docs.ros.org. Use the installation page for your distribution to confirm the latest steps, because the package source setup has changed over the years.<\/p>\n<h2>Install ROS 2 Jazzy on Ubuntu 24.04<\/h2>\n<p>These are the deb-package steps from the official Jazzy documentation. First make sure the locale is UTF-8 and the <code>universe<\/code> repository is enabled:<\/p>\n<pre><code class=\"language-bash\">sudo apt update &amp;&amp; sudo apt install locales\nsudo locale-gen en_US en_US.UTF-8\nsudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8\nexport LANG=en_US.UTF-8\n\nsudo apt install software-properties-common\nsudo add-apt-repository universe\n<\/code><\/pre>\n<p>Then add the ROS 2 apt source and install the desktop bundle (ROS, RViz, demos and tutorials):<\/p>\n<pre><code class=\"language-bash\">sudo apt update &amp;&amp; sudo apt install curl -y\nexport ROS_APT_SOURCE_VERSION=$(curl -s https:\/\/api.github.com\/repos\/ros-infrastructure\/ros-apt-source\/releases\/latest | grep -F \"tag_name\" | awk -F\\\" '{print $4}')\ncurl -L -o \/tmp\/ros2-apt-source.deb \"https:\/\/github.com\/ros-infrastructure\/ros-apt-source\/releases\/download\/${ROS_APT_SOURCE_VERSION}\/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. \/etc\/os-release &amp;&amp; echo ${UBUNTU_CODENAME:-${VERSION_CODENAME}})_all.deb\"\nsudo dpkg -i \/tmp\/ros2-apt-source.deb\n\nsudo apt update\nsudo apt upgrade\nsudo apt install ros-jazzy-desktop\n<\/code><\/pre>\n<p>In every new terminal, load the environment (or add the line to <code>~\/.bashrc<\/code>):<\/p>\n<pre><code class=\"language-bash\">source \/opt\/ros\/jazzy\/setup.bash\n<\/code><\/pre>\n<p>Check it with two terminals: <code>ros2 run demo_nodes_cpp talker<\/code> in one and <code>ros2 run demo_nodes_py listener<\/code> in the other. The listener should print the messages the C++ talker publishes, which shows C++ and Python nodes working together. No Ubuntu machine? The official docs also describe installing on Windows, and Docker images exist for ROS releases.<\/p>\n<h2>The core concepts<\/h2>\n<ul>\n<li><strong>Node:<\/strong> one process that does one job, such as reading a lidar or driving the wheels.<\/li>\n<li><strong>Topic:<\/strong> a named channel with publish\/subscribe semantics. A node publishes messages to <code>\/chatter<\/code>; any number of nodes can subscribe. Use topics for continuous streams like sensor data.<\/li>\n<li><strong>Message:<\/strong> the typed data on a topic, for example <code>std_msgs\/msg\/String<\/code> or <code>geometry_msgs\/msg\/Twist<\/code>.<\/li>\n<li><strong>Service:<\/strong> request and reply between two nodes, for short one-off questions such as &#8220;reset the map&#8221;.<\/li>\n<li><strong>Action:<\/strong> a long-running goal with feedback and the ability to cancel, such as &#8220;navigate to this point&#8221;.<\/li>\n<li><strong>Parameter:<\/strong> a configurable value of a node, such as a speed limit, which you can change without editing code.<\/li>\n<\/ul>\n<p>There is no master process in ROS 2. Nodes find each other automatically, so you do not run anything like <code>roscore<\/code> first.<\/p>\n<h2>Write a publisher and a subscriber in Python<\/h2>\n<p>This is the classic first program, written for ROS 2 with <code>rclpy<\/code>. Save it as <code>talker.py<\/code>. It publishes a string to the topic <code>chatter<\/code> twice a second:<\/p>\n<pre><code class=\"language-python\">import rclpy\nfrom rclpy.node import Node\nfrom std_msgs.msg import String\n\n\nclass Talker(Node):\n    def __init__(self):\n        super().__init__('talker')\n        self.publisher_ = self.create_publisher(String, 'chatter', 10)\n        self.timer = self.create_timer(0.5, self.on_timer)\n        self.count = 0\n\n    def on_timer(self):\n        msg = String()\n        msg.data = f'hello world {self.count}'\n        self.publisher_.publish(msg)\n        self.get_logger().info(f'Publishing: \"{msg.data}\"')\n        self.count += 1\n\n\ndef main(args=None):\n    rclpy.init(args=args)\n    node = Talker()\n    try:\n        rclpy.spin(node)\n    except KeyboardInterrupt:\n        pass\n    finally:\n        node.destroy_node()\n        rclpy.shutdown()\n\n\nif __name__ == '__main__':\n    main()\n<\/code><\/pre>\n<p>And <code>listener.py<\/code>, which subscribes to the same topic:<\/p>\n<pre><code class=\"language-python\">import rclpy\nfrom rclpy.node import Node\nfrom std_msgs.msg import String\n\n\nclass Listener(Node):\n    def __init__(self):\n        super().__init__('listener')\n        self.subscription = self.create_subscription(\n            String, 'chatter', self.on_message, 10)\n\n    def on_message(self, msg):\n        self.get_logger().info(f'I heard: \"{msg.data}\"')\n\n\ndef main(args=None):\n    rclpy.init(args=args)\n    node = Listener()\n    try:\n        rclpy.spin(node)\n    except KeyboardInterrupt:\n        pass\n    finally:\n        node.destroy_node()\n        rclpy.shutdown()\n\n\nif __name__ == '__main__':\n    main()\n<\/code><\/pre>\n<p>With the ROS environment sourced, run each in its own terminal:<\/p>\n<pre><code class=\"language-bash\">python3 talker.py\npython3 listener.py\n<\/code><\/pre>\n<p>The listener prints each message the talker sends. The number <code>10<\/code> in both calls is the queue size (how many messages are kept if the receiver is slow). <code>rclpy.spin<\/code> keeps the node alive and runs its callbacks. Note that the earlier-style ROS 1 code with <code>rospy.Publisher<\/code> and <code>rospy.init_node<\/code> does not work in ROS 2.<\/p>\n<p>While both run, inspect the system from a third terminal:<\/p>\n<pre><code class=\"language-bash\">ros2 node list\nros2 topic list\nros2 topic echo \/chatter\nros2 topic info \/chatter\n<\/code><\/pre>\n<p>Running scripts directly is fine to learn. For a real project you package the code. Create a workspace with a <code>src<\/code> folder, make a package with <code>ros2 pkg create --build-type ament_python my_package<\/code>, register your functions under <code>entry_points<\/code> in <code>setup.py<\/code>, then build with <code>colcon build<\/code> and load the result with <code>source install\/setup.bash<\/code>. The build tool is <code>colcon<\/code>; ROS 1&#8217;s <code>catkin_make<\/code> is gone. C++ packages use <code>ament_cmake<\/code>.<\/p>\n<h2>Beyond the first program<\/h2>\n<ul>\n<li><strong>Launch files<\/strong> start many nodes with parameters in one command (<code>ros2 launch<\/code>). In ROS 2 they are usually written in Python, with XML and YAML also supported.<\/li>\n<li><strong>tf2<\/strong> tracks coordinate frames, for instance where the camera is relative to the wheels.<\/li>\n<li><strong>URDF<\/strong> describes a robot&#8217;s links and joints so it can be shown in RViz and simulated in Gazebo.<\/li>\n<li><strong>Nav2 and MoveIt 2<\/strong> are the standard stacks for mobile robot navigation and arm motion planning.<\/li>\n<li><strong>ros2 bag<\/strong> records topics so you can replay real sensor data while you debug.<\/li>\n<\/ul>\n<h2>Learning path and common mistakes<\/h2>\n<ol>\n<li>Install ROS 2 and finish the official &#8220;Beginner: CLI tools&#8221; and &#8220;Beginner: Client libraries&#8221; tutorials on docs.ros.org.<\/li>\n<li>Simulate a robot (TurtleSim first, then a Gazebo robot) before buying hardware.<\/li>\n<li>Use a real board or kit later, such as a Raspberry Pi based robot, once you can publish, subscribe and read logs comfortably.<\/li>\n<\/ol>\n<p>Frequent problems: forgetting to <code>source<\/code> the environment in a new terminal (the commands &#8220;are not found&#8221;); mixing distributions in the same shell; using ROS 1 tutorials with ROS 2 commands; and doing slow work (loops, network calls) inside a callback, which blocks all other callbacks in that node.<\/p>\n<p>Robot software leans on ordinary programming skill: data structures, state machines and search. Our post on <a href=\"https:\/\/algocademy.com\/blog\/algorithms-in-robotics-pathfinding-and-navigation\/\">algorithms in robotics: pathfinding and navigation<\/a> shows the kind of code Nav2 runs under the hood, and a solid grounding in Python helps with <code>rclpy<\/code>. You can practice the basics with guided exercises at <a href=\"https:\/\/app.algocademy.com\/\">app.algocademy.com<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>What ROS is, which languages it uses, why you should learn ROS 2 and not ROS 1, and how to install it and write a Python publisher and subscriber.<\/p>\n","protected":false},"author":1,"featured_media":5697,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[23],"tags":[],"class_list":["post-5698","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-problem-solving"],"_links":{"self":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/5698"}],"collection":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/comments?post=5698"}],"version-history":[{"count":1,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/5698\/revisions"}],"predecessor-version":[{"id":8941,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/5698\/revisions\/8941"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/media\/5697"}],"wp:attachment":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/media?parent=5698"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/categories?post=5698"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/tags?post=5698"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}