ROS 2 Fundamentals
Python Agents with ROS 2 | Humanoid Modeling with URDF
Introductionβ
Robot Operating System 2 (ROS 2) serves as the "Robotic Nervous System" for connecting AI agents to humanoid robot bodies. This chapter introduces the core concepts that form the foundation of ROS 2 architecture.
ROS 2 is not an actual operating system, but rather a flexible framework for writing robot software. It provides services designed specifically for a heterogeneous computer cluster such as hardware abstraction, device drivers, libraries, visualizers, message-passing, package management, and more.
Nodesβ
In ROS 2, a node is a process that performs computation. Nodes are the fundamental building blocks of a ROS 2 system. Each node is designed to perform a specific task and can communicate with other nodes through topics, services, or actions.
Purpose of Nodesβ
- Encapsulate robot functionality in modular units
- Enable distributed computation across multiple machines
- Allow for independent development and testing of robot capabilities
- Facilitate code reuse and sharing
Node Implementationβ
Nodes can be written in various programming languages, with C++ and Python being the most commonly used. Each node initializes a ROS 2 client library, creates publishers/subscribers, and runs a processing loop.
Topicsβ
Topics enable asynchronous communication between nodes using a publish-subscribe pattern. This communication method is ideal for streaming data like sensor readings or motor commands.
Publish-Subscribe Patternβ
- Publishers send messages to a topic without knowing which nodes will receive them
- Subscribers receive messages from a topic without knowing which nodes are publishing
- Multiple publishers and subscribers can exist for the same topic
- Communication is decoupled in time and space
Use Cases for Topicsβ
- Sensor data distribution (camera images, LIDAR scans, IMU data)
- Motor command streaming
- Robot state broadcasting
- Diagnostic information sharing
Servicesβ
Services provide synchronous request-response communication between nodes. This pattern is suitable for operations that have a clear input and output, and typically require a response.
Request-Response Patternβ
- A client sends a request and waits for a response
- The service processes the request and sends back a response
- Communication is synchronous and blocking
- Ideal for operations that must complete before continuing
Use Cases for Servicesβ
- Parameter configuration
- Map loading/unloading
- Path planning requests
- Database queries
- Task execution confirmation
Actionsβ
Actions extend services with the ability to provide feedback during long-running operations and support for canceling operations. They are ideal for goal-oriented tasks.
Action Featuresβ
- Goal: The desired outcome of the action
- Feedback: Periodic updates on the progress toward the goal
- Result: The final outcome of the action
- Cancel: Ability to interrupt a running action
Use Cases for Actionsβ
- Navigation to a specific location
- Object manipulation tasks
- Complex multi-step robot behaviors
- Long-duration operations requiring monitoring
DDS-Based Communicationβ
ROS 2 uses Data Distribution Service (DDS) as its communication middleware. DDS provides:
- Quality of Service (QoS) settings for different communication needs
- Discovery mechanisms for automatic node detection
- Reliable and best-effort delivery options
- Data persistence and lifecycle management
- Security features for protected communication
QoS Settingsβ
Quality of Service settings allow fine-tuning of communication behavior:
- Reliability: Reliable vs best-effort delivery
- Durability: Volatile vs transient-local data persistence
- History: Keep-all vs keep-last policies
- Deadline: Maximum time between consecutive messages
- Lifespan: Maximum time data remains valid
Practical Examplesβ
Simple Node Communication via Topicsβ
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class Talker(Node):
def __init__(self):
super().__init__('talker')
self.publisher = self.create_publisher(String, 'topic', 10)
timer_period = 0.5 # seconds
self.timer = self.create_timer(timer_period, self.timer_callback)
self.i = 0
def timer_callback(self):
msg = String()
msg.data = f'Hello World: {self.i}'
self.publisher.publish(msg)
self.get_logger().info(f'Publishing: "{msg.data}"')
self.i += 1
def main(args=None):
rclpy.init(args=args)
talker = Talker()
rclpy.spin(talker)
talker.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Service Client Exampleβ
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts
class ServiceClient(Node):
def __init__(self):
super().__init__('service_client')
self.cli = self.create_client(AddTwoInts, 'add_two_ints')
while not self.cli.wait_for_service(timeout_sec=1.0):
self.get_logger().info('Service not available, waiting again...')
self.req = AddTwoInts.Request()
def send_request(self, a, b):
self.req.a = a
self.req.b = b
self.future = self.cli.call_async(self.req)
rclpy.spin_until_future_complete(self, self.future)
return self.future.result()
def main():
rclpy.init()
client = ServiceClient()
response = client.send_request(1, 2)
print(f'Result: {response.sum}')
client.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Conclusionβ
ROS 2 fundamentals form the foundation for building complex robotic systems. Understanding nodes, topics, services, and actions is essential for creating distributed robotic applications that can connect AI agents to humanoid robot bodies effectively.
These concepts enable the creation of modular, scalable, and maintainable robotic systems that can adapt to various hardware configurations and application requirements.