ROS2 @Home Guide
Main commands
Build
From the root of the workspace (home2) with ROS2 installed.
Build all packages (this should be run after updating a script, dependencies, packages, etc):
Build specific package/s:
Source the workspace (this is needed to run the nodes):
Run
Run a node:
Run a launch file (this is a file that can run multiple nodes):
Debug
Check current topics:
For build issues, you can clean the workspace and rebuild:
If your node is not appearing when running ros2 run ... It is possible that it does not have the correct permissions. You can change the permissions of the file:
General Node Structure (Python)
In general, a node is a script that will run and interact with the ROS2 system through topics, services, actions, etc.
A topic
is a channel where nodes can send and receive messages.
#!/usr/bin/env python3
import time
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
from rclpy.action import ActionServer
# Topics
SUBSCRIBER_TOPIC = "/example/subscriber_topic"
PUBLISHER_TOPIC = "/example/publisher_topic"
# Node
class ExampleNode(Node):
def __init__(self):
"""Initial setup for the node"""
# Initialize Node with name
super().__init__("example_node")
# Add publishers, subscribers, services, action services, etc.
self.example_publisher = self.create_publisher(String, PUBLISHER_TOPIC, 10)
self.timer = self.create_timer(1.0, self.timer_callback)
# Use logger to print messages (this can also be .warn() or .error())
self.get_logger().info("Example node ready.")
# Define callbacks
def timer_callback(self):
pass
# Main function
def main(args=None):
# Initialize node
rclpy.init(args=args)
node = ExampleNode()
try:
# Spin the node (keep it running)
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy_node()
rclpy.shutdown()
# Run main function
if __name__ == "__main__":
main()
Subscribers
A subscriber will listen to a topic and receive messages from it.
It should have a callback function that will be called every time a message is received. In this case the type of the message is String
, which sould be imported from std_msgs.msg
.
def __init__(self):
super().__init__("example_node")
# Create Subscriber
self.example_subscriber = self.create_subscription(String, "/example/subscriber_topic", self.subscriber_callback, 10)
def subscriber_callback(self, msg):
# Print message data each time a message is received
self.get_logger().info("I heard: " + msg.data)
To test a subscriber, you can run the node in one terminal and publish messages in another terminal:
Publishers and Timers
A publisher is a node that sends messages to a topic. After creating the publisher, you can create a message and publish it.
If you want to send messages periodically, you can use a timer that calls a function every x seconds.
This example also publishes a message of type String
.
def __init__(self):
super().__init__("example_node")
# Create Publisher
self.example_publisher = self.create_publisher(String, "/example/publisher_topic", 10)
# Create Timer: this will call a function called timer_callback every 1.0 seconds
self.timer = self.create_timer(1.0, self.timer_callback)
def timer_callback(self):
# Create message and set data
msg = String()
msg.data = "Hello World"
# Publish message
self.example_publisher.publish(msg)
To test a publisher, you can run the node in one terminal and listen to the topic in another terminal:
Services
A service is a node that provides a service to other nodes. This means that it receives a request and should return a response. For this, it should have a callback function that will be called when the service is requested.
In this example, we are using a srv file called SetBool
that has the following structure:
---
. This standard service should be imported from std_srvs.srv
.
def __init__(self):
super().__init__("example_node")
# Create Service
self.example_service = self.create_service(SetBool, "/example/service_topic", self.service_callback)
def service_callback(self, request, response):
# Get the request data
req = request.data
# Print
if req:
self.get_logger().info("Service called with request: True")
else:
self.get_logger().info("Service called with request: False")
# Set the response data
response.message = "Result msg"
response.success = True
return response
To test a service, you can run the node in one terminal and call the service in another terminal.
Example:Action services
An action service is a more complex service that allows for feedback and goal handling.
This also receives a request and should return a response, but it can also send feedback during the process. The ActionServer should be imported from rclpy.action
.
In this example we are using an action file called DetectPerson
that should be imported from frida_interfaces
and has the following structure:
def __init__(self):
super().__init__("example_node")
# Create Action Service
self.example_action_server = ActionServer(self, DetectPerson, "/example/action_service_topic", self.action_callback)
def action_callback(self, goal_handle):
# Get the request data
self.goal_handle = goal_handle
request = goal_handle.request
self.get_logger().info(f"Goal received: {request}")
# Simulate a process and send feedback (It just counts from 1-5)
for i in range(1,6):
# Check if the request was canceled
if goal_handle.is_cancel_requested:
goal_handle.canceled()
return DetectPerson.Result()
# Create feedback message
feedback_msg = DetectPerson.Feedback()
feedback_msg.feedback = str(i)
# Send feedback
goal_handle.publish_feedback(feedback_msg)
self.get_logger().info(f"Feedback: {i}")
time.sleep(1)
# After finishing a process, send the result
result = DetectPerson.Result()
result.success = True
goal_handle.succeed()
return result
To test an action service, you can run the node in one terminal and call the action service in another terminal.
Example:ros2 action send_goal /example/action_service_topic frida_interfaces/action/DetectPerson "request: true" --feedback
Interfaces
Interfaces are files that define the structure of messages, services, and actions. They are defined in .msg
, .srv
, and .action
files respectively.
For this repo, the interfaces are located in the frida_interfaces
package according to each area.
Message
A message is a structure that can be sent through a topic. It can have multiple fields of different types.
Example:
In frida_interfaces/vision/msg/Person.msg
:
A message can also include an array of other messages like in frida_interfaces/vision/msg/PersonList.msg
:
Service
A service is a structure that defines a request and a response. It can have multiple fields of different types.
Example:
In frida_interfaces/vision/srv/FindSeat.srv
:
Action
An action is a structure that defines a goal, feedback, and result. It can have multiple fields of different types.
Example:
ExampleDetectObject.action
: