Section 1: Nodes and the Graph
Estimated Reading Time: 7 minutes Bloom's Level: Understand, Apply Key Learning: What are ROS 2 nodes and how they form a computation graph
What is a ROS 2 Node?ā
A node is an independent ROS 2 program. It's a single, focused process that does one thing well.
Think of it like this:
- Traditional robot code: One big program does everything (sensing, planning, control)
- ROS 2 code: Many small programs, each doing one thing (sensor driver, planner, controller)
Real-World Analogyā
Imagine a restaurant:
- Traditional approach: One chef does everything (prep, cooking, plating, cleaning)
- ROS 2 approach: Specialized stations
- Prep station (sensor input)
- Cooking station (processing)
- Plating station (control)
- Each station communicates with the next
Why Nodes?ā
ā Modularity: Easy to understand, test, and reuse ā Reliability: One failing node doesn't crash everything ā Scalability: Run nodes on different computers ā Flexibility: Mix and match nodes for different robots
The ROS 2 Graphā
A running ROS 2 system is a graph of communicating nodes:
āāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Sensor Driver Node ā
ā (camera_node) ā
āāāāāāāāāāāāāāāā¬āāāāāāāāāāā
ā
[Topic: /image]
ā
āāāāāāāāāāāāāāāvāāāāāāāāāāā
ā Processing Node ā
ā (detector_node) ā
āāāāāāāāāāāāāāāā¬āāāāāāāāāāā
ā
[Topic: /detections]
ā
āāāāāāāāāāāāāāāvāāāāāāāāāāā
ā Control Node ā
ā (controller_node) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāā
Key components:
- Nodes (boxes): Independent programs
- Topics (arrows): Communication channels
- Messages (flow): Data traveling through topics
Each node:
- Runs independently
- Doesn't know about other nodes
- Only cares about topics it uses
Your First ROS 2 Node: Hello, ROS 2!ā
Let's create the simplest possible ROS 2 node:
Code Example 1: hello_node.pyā
Location: code-examples/ros2_packages/hello_world_py/
Generated by: ros2-code-generator skill
# File: hello_world_py/hello_world_py/hello_node.py
import rclpy
from rclpy.node import Node
class HelloNode(Node):
"""Simple node that prints a greeting."""
def __init__(self):
# Initialize the node with name 'hello_node'
super().__init__('hello_node')
# Create a timer that calls a callback every 1 second
self.timer = self.create_timer(1.0, self.timer_callback)
# Counter for demonstration
self.i = 0
def timer_callback(self):
"""This function is called every 1 second."""
self.get_logger().info(f'Hello, ROS 2! Message #{self.i}')
self.i += 1
def main(args=None):
# Initialize ROS 2
rclpy.init(args=args)
# Create and run the node
node = HelloNode()
rclpy.spin(node) # Keep the node running (blocking call)
# Cleanup
rclpy.shutdown()
if __name__ == '__main__':
main()
Understanding the Codeā
Line by line (annotated by code-explainer subagent):
import rclpy
- Import ROS 2 Python client library
from rclpy.node import Node
- Import the Node base class (all ROS 2 nodes inherit from this)
class HelloNode(Node):
- Create our custom node class that inherits from Node
super().__init__('hello_node')
- Initialize the Node parent class
'hello_node'is the node's unique name in the ROS 2 graph
self.create_timer(1.0, self.timer_callback)
- Create a timer that calls
timer_callbackevery 1.0 second - Timers are how nodes do periodic work
self.get_logger().info(...)
- Log a message visible in the console
get_logger()is inherited from Node class
rclpy.init(args=args)
- Initialize ROS 2 (must be called before creating nodes)
rclpy.spin(node)
- Start the event loop for this node
- The node will keep running until you press Ctrl+C
Running Your First Nodeā
Step 1: Build the Packageā
cd physical-ai-textbook/code-examples
colcon build --packages-select hello_world_py
Step 2: Source the Environmentā
source ~/physical-ai-textbook/install/setup.bash
Step 3: Run the Nodeā
ros2 run hello_world_py hello_node
Expected Outputā
[INFO] [hello_node]: Hello, ROS 2! Message #0
[INFO] [hello_node]: Hello, ROS 2! Message #1
[INFO] [hello_node]: Hello, ROS 2! Message #2
...
Press Ctrl+C to stop the node.
Inspecting the Nodeā
While the node is running (in another terminal), try these commands:
See All Nodesā
ros2 node list
Output:
/hello_node
Get Node Informationā
ros2 node info /hello_node
Output:
/hello_node
Subscribers:
Publishers:
Service Servers:
Service Clients:
Action Servers:
Action Clients:
(This node doesn't publish or subscribe to anything yet - that's next!)
Anatomy of a ROS 2 Nodeā
Every ROS 2 node has these parts:
| Part | Purpose | Example |
|---|---|---|
| Class | Inherits from Node | class HelloNode(Node) |
__init__ | Create publishers, subscribers, timers | self.create_timer() |
| Callback | Code that runs when event happens | timer_callback() |
| Main | Entry point, init ROS, spin | rclpy.init(), rclpy.spin() |
Node Lifecycleā
Every ROS 2 node goes through these phases:
1. Initialize ROS 2
rclpy.init()
ā
2. Create Node
HelloNode()
ā
3. Create Resources (publishers, subscribers, services)
self.create_timer(), self.create_publisher(), etc.
ā
4. Spin (keep running, process events)
rclpy.spin(node)
ā
5. Shutdown
rclpy.shutdown()
Key Takeawaysā
ā
A node is an independent ROS 2 program
ā
Every node inherits from the Node class
ā
Nodes are connected by topics and services
ā
Nodes run independently and don't know about each other
ā
Use timers to make nodes do work periodically
Try It Yourselfā
Modify hello_node.py to:
- Change the message text
- Change the timer interval from 1.0 second to 2.0 seconds
- Remove the counter (don't increment
self.i) - Log messages at different levels:
.info(),.warning(),.error()
Next Stepsā
Now that you understand nodes, let's look at how they communicate:
š Next: Section 2: Topics - Publish and Subscribe
Or jump to:
Resourcesā
Section Status: Complete ā
Skills Used: ros2-code-generator, code-explainer