0% found this document useful (0 votes)
8 views36 pages

ROS Notes Module2

This document provides a comprehensive guide on using COLCON to build and manage packages in ROS 2, including prerequisites, workspace setup, and building steps. It also covers creating a ROS 2 workspace, creating Python-based packages, and implementing a simple publisher-subscriber model in Python. Key steps include installing ROS 2, creating directories, adding packages, building with COLCON, and running nodes.

Uploaded by

Aneez Hassan
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views36 pages

ROS Notes Module2

This document provides a comprehensive guide on using COLCON to build and manage packages in ROS 2, including prerequisites, workspace setup, and building steps. It also covers creating a ROS 2 workspace, creating Python-based packages, and implementing a simple publisher-subscriber model in Python. Key steps include installing ROS 2, creating directories, adding packages, building with COLCON, and running nodes.

Uploaded by

Aneez Hassan
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

MODULE 2

Q. Using COLCON to build packages

COLCON (Collective Construction) is a command-line tool for building and managing multiple software
packages. It's particularly popular in the ROS 2 (Robot Operating System 2) ecosystem, but it's general
enough to be used with other systems too. COLCON automates and optimizes the build process,
making it easier to manage complex projects that consist of many interdependent packages.

Here's a guide on using COLCON to build packages:

Prerequisites:

1. Install COLCON:

On Ubuntu:

sudo apt install python3-colcon-common-extensions

On other systems, you may need to use pip:

pip install colcon-common-extensions

2. Set up your workspace: A typical ROS 2 workspace structure looks like this:

my_ws/

├── src/

├── build/

├── install/

└── log/

3. Source the environment (if using ROS 2):

source /opt/ros/humble/[Link]

This ensures that your workspace can find the ROS 2 packages.

Steps to Build Packages with COLCON:

1. Create a workspace: In an empty directory (e.g., my_ws), create a src folder for the
packages:

mkdir -p my_ws/src

cd my_ws

2. Clone or add your packages to the src/ folder: For example, if you're cloning a package from
GitHub:

cd src

git clone [Link]

3. Build the workspace: After adding your packages to the src/ directory, go back to the root of
the workspace and run COLCON to build:
cd ..

colcon build

COLCON will build all the packages in the workspace.

4. Source the install setup file: Once the build process is complete, you should source the
setup file to make the packages available to your environment:

source install/[Link]

5. Rebuilding after changes: If you modify any package and want to rebuild, you can run:

colcon build --packages-select <package_name>

This will only rebuild the specified package(s), saving time.

6. Build logs and troubleshooting: If there are build issues, COLCON generates detailed logs in
the log/ directory. To access them, you can:

cd log/latest_build

You can also use the --event-handlers argument for more verbose output:

colcon build --event-handlers console_direct+

Additional Options:

• Parallel building: You can tell COLCON to build packages in parallel:

colcon build --executor parallel

• Build specific packages: You can build only a few selected packages:

colcon build --packages-select <package_name1> <package_name2>

• Clean build: If you want to do a clean build (i.e., delete the previous build artifacts):

colcon build --cmake-clean-cache

Package Dependencies:

For COLCON to handle package dependencies, ensure that all dependencies are available in your
workspace or installed system-wide. For ROS 2, most dependencies can be installed using:

rosdep install --from-paths src --ignore-src -r -y

By following these steps, you should be able to use COLCON to build and manage your packages
efficiently.

Q2. Creating a workspace in ROS 2

Creating a workspace in ROS 2 is an essential step to start working with ROS 2 packages. A workspace
is a directory where your ROS 2 packages are built, installed, and stored. It allows you to organize
your code and keep everything necessary to run your ROS 2 nodes, including custom and external
packages.

Steps to Create a ROS 2 Workspace


1. Install ROS 2 (if not already installed)

Before creating a workspace, make sure ROS 2 is installed on your system. You can follow the
installation instructions specific to your operating system from the official ROS 2 documentation.

After installation, source the ROS 2 environment:

source /opt/ros/<ros_distro>/[Link]

Replace <ros_distro> with your ROS 2 distribution, such as humble.

2. Create the Workspace Directory

A ROS 2 workspace typically contains the following folders:

• src/ - Source code for ROS 2 packages.

• build/ - Where the workspace is built.

• install/ - Where the compiled code is installed.

• log/ - Logs generated during the build process.

To create the workspace:

mkdir -p ~/ros2_ws/src

cd ~/ros2_ws

Here, ros2_ws is the name of your workspace, but you can name it whatever you like.

3. Add Packages to the Workspace

Inside the src/ directory, you can clone or create ROS 2 packages. For example, if you want to clone a
ROS 2 package from GitHub:

cd src

git clone [Link]

Alternatively, you can create your own package by using ros2 pkg create command:

ros2 pkg create my_package --build-type ament_python dependencies rclpy

4. Build the Workspace Using COLCON

Once you have added packages to the src/ directory, go back to the root of the workspace and use
COLCON to build the packages:

cd ~/ros2_ws

colcon build

This will build all the packages in the src/ directory and place the compiled output in the install/ and
build/ directories.

5. Source the Workspace

After building the workspace, you need to source the environment setup file to use the packages
that were built. This step makes sure ROS 2 knows about your custom packages:
source install/[Link]

You need to do this every time you open a new terminal session, or you can add it to your ~/.bashrc
file for convenience:

echo "source ~/ros2_ws/install/[Link]" >> ~/.bashrc

Then reload your terminal:

source ~/.bashrc

6. Check the Workspace and Packages

You can verify that your workspace and packages are set up correctly by using ROS 2 commands:

ros2 pkg list

This will list all the ROS 2 packages, including the ones you added in your workspace.

Example Workflow Summary

1. Create a workspace:

mkdir -p ~/ros2_ws/src

cd ~/ros2_ws

2. Add a package:

cd src

git clone [Link]

3. Build the workspace:

cd ~/ros2_ws

colcon build

4. Source the workspace:

source install/[Link]

5. Check available packages:

ros2 pkg list

Now you're ready to develop and run ROS 2 nodes in your workspace

Q Creating a package in ROS 2

Below is a guide for creating a Python-based package in ROS 2.

1. Set Up Your ROS 2 Workspace

1. Create a directory for your workspace (if you don’t already have one):

mkdir -p ~/ros2_ws/src

cd ~/ros2_ws
2. Source your ROS 2 environment:

source /opt/ros/<ros-distro>/[Link]

3. Build the workspace (for initialization):

colcon build

4. Source the workspace:

source install/[Link]

2. Create a Python ROS 2 Package

1. Navigate to the src directory of your workspace:

cd ~/ros2_ws/src

2. Use the ros2 pkg create command to create a package with a Python build type:

ros2 pkg create <package_name> --build-type ament_python --dependencies rclpy std_msgs

Replace <package_name> with the name of the package. The --build-type ament_python specifies
that the package will use Python, and --dependencies lists the dependencies (such as rclpy for
Python ROS 2 nodes and std_msgs for standard message types).

3. Understand the Generated Package Structure

The package generation command will create the following structure:

<package_name>

├── [Link]

├── [Link]

├── [Link]

├── resource/

│ └── <package_name>

├── <package_name>/

│ └── __init__.py

├── tests/

└── ...

• [Link]: Defines the package’s metadata, such as its name, version, and dependencies.

• [Link]: Python setup script that specifies how to install the package.

• <package_name>/: This directory contains Python code, including the nodes you’ll write.

• resource/: Contains a file named after your package, used for declaring it as a ROS 2
resource.

• tests/: Optional folder for testing scripts.


4. Edit [Link]

Ensure the [Link] includes all necessary dependencies:

<package format="3">

<name>your_package_name</name>

<version>0.0.0</version>

<description>My Python ROS 2 package</description>

<maintainer email="your-email@[Link]">Your Name</maintainer>

<license>Apache License 2.0</license>

<buildtool_depend>ament_python</buildtool_depend>

<exec_depend>rclpy</exec_depend>

<exec_depend>std_msgs</exec_depend>

<export>

<build_type>ament_python</build_type>

</export>

</package>

5. Modify [Link]

The [Link] file is a Python script for packaging and installing the module. Update it to include your
package:

from setuptools import setup

package_name = 'your_package_name'

setup(

name=package_name,

version='0.0.0',

packages=[package_name],

data_files=[

('share/ament_index/resource_index/packages',

['resource/' + package_name]),

('share/' + package_name, ['[Link]']),

],

install_requires=['setuptools'],

zip_safe=True,
maintainer='your_name',

maintainer_email='your_email@[Link]',

description='Python package for ROS 2',

license='Apache License 2.0',

tests_require=['pytest'],

entry_points={

'console_scripts': [

'your_node_name = your_package_name.your_node:main'

],

},

)
7. Edit [Link] (Optional)

You can add the following lines to [Link] to specify the entry point:

[develop]

script_dir=$base/lib/<package_name>

[install]

install_scripts=$base/lib/<package_name>

8. Build the Package

Return to your workspace and build the package:

cd ~/ros2_ws

colcon build
After building, source the workspace:

source install/[Link]

9. Run the Node

You can now run the node using the ros2 run command:

ros2 run <package_name> your_node_name

For example:

ros2 run your_package_name your_node_name

10. Test the Node

To check the messages being published on the topic, use:

ros2 topic echo /topic

This will output the messages published by your Python node.

This is the process for creating a Python-based ROS 2 package.

Q. Writing a simple publisher and subscriber in python with ROS 2

In ROS 2 (Robot Operating System 2), communication between nodes is facilitated through a
publisher-subscriber mechanism. A publisher sends messages to a specific topic, and a subscriber
listens to that topic to receive those messages. Let's implement a simple publisher and subscriber in
Python using rclpy, the ROS 2 Python client library.

1. Create the Publisher Node

The publisher will continuously publish messages to a topic named /chatter.

import rclpy

from [Link] import Node

from std_msgs.msg import String

class SimplePublisher(Node):

def __init__(self):

super().__init__('simple_publisher')

self.publisher_ = self.create_publisher(String, 'chatter', 10)

timer_period = 0.5 # seconds

[Link] = self.create_timer(timer_period, self.timer_callback)


self.i = 0

def timer_callback(self):

msg = String()

[Link] = f'Hello World: {self.i}'

self.publisher_.publish(msg)

self.get_logger().info(f'Publishing: "{[Link]}"')

self.i += 1

def main(args=None):

[Link](args=args)

publisher = SimplePublisher()

try:

[Link](publisher)

except KeyboardInterrupt:

pass

publisher.destroy_node()

[Link]()

if __name__ == '__main__':

main()

This publisher:

• Publishes a message every 0.5 seconds to the topic chatter.


• Logs the message to the console.
2. Create the Subscriber Node

The subscriber will listen to the /chatter topic and print the received messages.

# [Link]

import rclpy

from [Link] import Node

from std_msgs.msg import String

class SimpleSubscriber(Node):

def __init__(self):

super().__init__('simple_subscriber')

[Link] = self.create_subscription(

String,

'chatter',

self.listener_callback,

10)

[Link] # prevent unused variable warning

def listener_callback(self, msg):

self.get_logger().info(f'I heard: "{[Link]}"')

def main(args=None):

[Link](args=args)

subscriber = SimpleSubscriber()

try:
[Link](subscriber)

except KeyboardInterrupt:

pass

subscriber.destroy_node()

[Link]()

if __name__ == '__main__':

main()

This subscriber:

• Listens to the /chatter topic.


• Logs the received message to the console when it gets a message from the publisher.

Step 3: Running the Nodes

To run these nodes, you will need two separate terminals:

1. In the first terminal, run the publisher node:

python3 [Link]

In the second terminal, run the subscriber node:

python3 [Link]

Now, the publisher will send messages to the /chatter topic, and the subscriber will receive
those messages and print them to the console.

Explanation:

• Publisher: The SimplePublisher node creates a String message, publishes it to the


/chatter topic, and logs the message to the console.
• Subscriber: The SimpleSubscriber node listens to the /chatter topic, receives
messages, and prints them.

Explanation for the above program

1. Importing Libraries
import rclpy
from [Link] import Node
from std_msgs.msg import String

• rclpy: This is the ROS 2 client library for Python, which is used to interact with the
ROS system.
• Node: The Node class is a fundamental concept in ROS that allows communication
between different nodes in the network.
• String: The String class is a message type defined in std_msgs, representing
simple string data that will be published on a topic

2. Creating the Publisher Node


class SimplePublisher(Node):

• This class SimplePublisher is a subclass of Node. It represents a single node in the


ROS 2 system that will publish messages on a certain topic.

3. Initializing the Node


def __init__(self):
super().__init__('simple_publisher')
self.publisher_ = self.create_publisher(String, 'chatter', 10)
timer_period = 0.5 # seconds
[Link] = self.create_timer(timer_period, self.timer_callback)
self.i = 0

• Node Name: The node is initialized with the name simple_publisher using the
super().__init__('simple_publisher') call.
• Publisher:
o self.publisher_ = self.create_publisher(String, 'chatter', 10):
This line creates a publisher that will publish String messages on the topic
'chatter'.
o The 10 in the create_publisher call represents the queue size for storing
messages before they are sent, which allows for some buffering.
• Timer: A timer is created with a period of 0.5 seconds ([Link] =
self.create_timer(timer_period, self.timer_callback)), meaning the
timer_callback function will be called every 0.5 seconds.
• Counter: self.i = 0 initializes a counter that will increment each time a message is
published.

4. Timer Callback
def timer_callback(self):
msg = String()
[Link] = f'Hello World: {self.i}'
self.publisher_.publish(msg)
self.get_logger().info(f'Publishing: "{[Link]}"')
self.i += 1
• This is the function that gets called every time the timer triggers (every 0.5 seconds).
• It creates a String message (msg = String()).
• The content of the message is set to "Hello World: <i>", where <i> is the current
value of self.i.
• The message is published on the 'chatter' topic using
self.publisher_.publish(msg).
• The log message self.get_logger().info(f'Publishing: "{[Link]}"')
prints the data being published for debugging purposes.
• Finally, self.i += 1 increments the counter i after each message is sent.

5. Main Function
def main(args=None):
[Link](args=args)
publisher = SimplePublisher()

try:
[Link](publisher)
except KeyboardInterrupt:
pass

publisher.destroy_node()
[Link]()

• Initialization: [Link]() initializes the ROS 2 communication infrastructure.


• Create Publisher: A SimplePublisher object is created.
• Spin: The [Link](publisher) function keeps the node alive, processing the
timer callback and allowing it to continue publishing messages.
• Shutdown: The program catches a KeyboardInterrupt (usually when the user
presses Ctrl+C), which triggers the shutdown process:
o The node is destroyed (publisher.destroy_node()).
o ROS is shut down gracefully using [Link]().

6. Entry Point
if __name__ == '__main__':
main()

• This block ensures that the main() function is called when the script is run directly.

Summary

This script is a simple ROS 2 publisher node that publishes a string message ("Hello
World: <i>") to the topic 'chatter' every 0.5 seconds. The node logs each message it
publishes.
2. Create the Subscriber Node

The subscriber will listen to the /chatter topic and print the received messages.

# [Link]

import rclpy

from [Link] import Node

from std_msgs.msg import String

class SimpleSubscriber(Node):

def __init__(self):

super().__init__('simple_subscriber')

[Link] = self.create_subscription(

String,

'chatter',

self.listener_callback,

10)

[Link] # prevent unused variable warning

def listener_callback(self, msg):

self.get_logger().info(f'I heard: "{[Link]}"')

def main(args=None):

[Link](args=args)

subscriber = SimpleSubscriber()
try:

[Link](subscriber)

except KeyboardInterrupt:

pass

subscriber.destroy_node()

[Link]()

if __name__ == '__main__':

main()

This subscriber:

• Listens to the /chatter topic.


• Logs the received message to the console when it gets a message from the publisher.

Explanation for the program

This program is a ROS 2 subscriber node written in Python that listens to a topic called
chatter and logs the messages it receives. The messages are of type String. Here's a
detailed explanation of each part of the code:

1. Imports:
import rclpy
from [Link] import Node
from std_msgs.msg import String

• rclpy: This is the ROS 2 client library in Python, which provides functionality to create ROS
nodes, publish, subscribe, and handle communication.
• Node: The Node class is the basic building block of ROS communication, allowing you to
create and manage a ROS node.
• String: This is a simple string message type defined in the std_msgs package, which will
be used to receive messages from the chatter topic.

2. Creating the Subscriber Node:


class SimpleSubscriber(Node):

• The class SimpleSubscriber is created, inheriting from Node. This class defines the
behavior of a ROS 2 node that will subscribe to a topic and react to the received messages.
3. Initializing the Subscriber Node:
def __init__(self):
super().__init__('simple_subscriber')
[Link] = self.create_subscription(
String,'chatter',
self.listener_callback,10)
[Link] # prevent unused variable warning

• super().init('simple_subscriber'): This line calls the Node class constructor with the name
'simple_subscriber'. The node is now named simple_subscriber within the ROS 2
network.
• self.create_subscription: This method creates a subscription to a ROS 2 topic. Here’s what
each parameter means:
o String: The message type that will be received on the topic (in this case, a string).
o 'chatter': The name of the topic to subscribe to, which is 'chatter' in this case.
o self.listener_callback: The callback function that will be called whenever a
message is received. This function processes the received messages.
o 10: The queue size, which defines how many incoming messages can be buffered if
they are arriving faster than the node can process them.
• [Link]: This stores the subscription object to avoid a warning about an unused
variable.

4. Listener Callback (Processing Incoming Messages):


def listener_callback(self, msg):
self.get_logger().info(f'I heard: "{[Link]}"')

• This is the callback function that is triggered whenever the node receives a message from
the chatter topic.
• [Link]: This contains the data from the received String message. The message object
msg has a data field that contains the actual string.
• self.get_logger().info(...): This logs the received message using the node's logger, displaying
the string message in the terminal (e.g., I heard: "Hello World: 0").

5. Main Function:
def main(args=None):
[Link](args=args)
subscriber = SimpleSubscriber()

try:
[Link](subscriber)
except KeyboardInterrupt:
pass

subscriber.destroy_node()
[Link]()

• [Link](): This initializes the ROS 2 system, getting everything ready for the node to start
working.
• subscriber = SimpleSubscriber(): This creates an instance of the SimpleSubscriber class,
initializing the node.
• [Link](subscriber): This function keeps the node alive and actively processing incoming
messages. It ensures the node continues to receive messages and triggers the callback
whenever a message arrives.
• try/except KeyboardInterrupt: This block handles a graceful shutdown when the user stops
the program by pressing Ctrl+C.
• subscriber.destroy_node(): Destroys the node cleanly when shutting down.
• [Link](): Shuts down the ROS 2 system after the node is destroyed, ensuring a
graceful exit.

6. Entry Point:
if __name__ == '__main__':
main()

• This block ensures that the main() function is called when the script is executed directly. If
the script is imported as a module, this block will not run.

Summary:

This program creates a simple ROS 2 subscriber node that listens to a topic called chatter
and logs each message it receives. The messages are of type String. It runs in a loop and
processes incoming messages until the user manually interrupts it (with Ctrl+C). The main
purpose of this node is to listen for messages and print them to the console for debugging or
observation.

Step 3: Running the Nodes

To run these nodes, you will need two separate terminals:

2. In the first terminal, run the publisher node:

python3 [Link]

In the second terminal, run the subscriber node:

python3 [Link]

Now, the publisher will send messages to the /chatter topic, and the subscriber will receive
those messages and print them to the console.

Explanation:

• Publisher: The SimplePublisher node creates a String message, publishes it to the


/chatter topic, and logs the message to the console.
• Subscriber: The SimpleSubscriber node listens to the /chatter topic, receives
messages, and prints them.
Q. Writing a simple service in ROS 2 with python

Creating a simple service in ROS 2 using Python involves setting up a node that provides a
service and another node (or the same one) to request it. Here's a step-by-step guide using
ROS 2 rclpy (the Python client library).

1. Prerequisites

Ensure ROS 2 is installed on your machine. This example assumes you have a ROS 2
workspace set up.

• Create a ROS 2 workspace

mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
colcon build
source install/[Link]

2. Create a Package

Use the ros2 pkg command to create a new package:

cd ~/ros2_ws/src
ros2 pkg create my_service_pkg --build-type ament_python --dependencies
rclpy example_interfaces

This creates a basic package. We’ll use the example_interfaces package, which provides a
simple AddTwoInts service.

3. Writing the Service Node

Create a service node that provides the AddTwoInts service.

File: my_service_pkg/add_two_ints_server.py

import rclpy
from [Link] import Node
from example_interfaces.srv import AddTwoInts

class AddTwoIntsService(Node):
def __init__(self):
super().__init__('add_two_ints_server')
[Link] = self.create_service(AddTwoInts, 'add_two_ints',
self.add_two_ints_callback)
self.get_logger().info('Service ready to add two integers.')

def add_two_ints_callback(self, request, response):


[Link] = request.a + request.b
self.get_logger().info(f'Received request: {request.a} +
{request.b} = {[Link]}')
return response

def main(args=None):
[Link](args=args)
node = AddTwoIntsService()
[Link](node)
[Link]()

if __name__ == '__main__':
main()

Q. Writing a simple client in ROS 2 with python

Create a client node that requests the AddTwoInts service.

File: my_service_pkg/add_two_ints_client.py

import sys
import rclpy
from [Link] import Node
from example_interfaces.srv import AddTwoInts

class AddTwoIntsClient(Node):
def __init__(self):
super().__init__('add_two_ints_client')
[Link] = self.create_client(AddTwoInts, 'add_two_ints')
while not [Link].wait_for_service(timeout_sec=1.0):
self.get_logger().info('Waiting for service to become
available...')
self.get_logger().info('Service is now available!')

def send_request(self, a, b):


request = [Link]()
request.a = a
request.b = b
future = [Link].call_async(request)
rclpy.spin_until_future_complete(self, future)
return [Link]()

def main(args=None):
[Link](args=args)
if len([Link]) < 3:
print('Usage: ros2 run my_service_pkg add_two_ints_client <int_a>
<int_b>')
return

client = AddTwoIntsClient()
response = client.send_request(int([Link][1]), int([Link][2]))
print(f'Result: {[Link]}')

[Link]()

if __name__ == '__main__':
main()

5. Update [Link]
Modify the [Link] to make the scripts executable.

File: my_service_pkg/[Link]
from setuptools import setup

package_name = 'my_service_pkg'

setup(
name=package_name,
version='0.0.0',
packages=[package_name],
install_requires=['setuptools'],
zip_safe=True,
maintainer='your_name',
maintainer_email='your_email@[Link]',
description='Simple service and client example in ROS 2',
license='Apache License 2.0',
entry_points={
'console_scripts': [
'add_two_ints_server =my_service_pkg.add_two_ints_server:main',
'add_two_ints_client =my_service_pkg.add_two_ints_client:main',
],
},
)

6. Build the Package

Make sure to build the package.

cd ~/ros2_ws
colcon build
source install/[Link]

7. Run the Service and Client

1. Start the service node in one terminal:

ros2 run my_service_pkg add_two_ints_server

2. Run the client node from another terminal:

ros2 run my_service_pkg add_two_ints_client 5 10

You should see output like:

Result: 15

8. Explanation

• Service Node: Provides the AddTwoInts service that sums two integers.
• Client Node: Sends two integers as a request to the service and prints the result.
Q Creating custom msg and srv files in ROS 2

Creating custom message (msg) and service (srv) files in ROS 2 involves defining your own
message and service types, building them into your package, and then using them in nodes.
Below is a step-by-step guide for this.

1. Create a ROS 2 Package

Create a new package that will hold the custom messages and services.

cd ~/ros2_ws/src
ros2 pkg create my_custom_interfaces --build-type ament_cmake

2. Create msg and srv Directories

Inside the new package, create the directories for message and service definitions.

cd ~/ros2_ws/src/my_custom_interfaces
mkdir msg srv

3. Define a Custom Message File

Create a message file (e.g., [Link]) inside the msg directory.

File: my_custom_interfaces/msg/[Link]
int64 num

This message contains a single integer field named num.

4. Define a Custom Service File

Create a service file (e.g., [Link]) inside the srv directory.

File: my_custom_interfaces/srv/[Link]
# Request
int64 a
int64 b
int64 c
---
# Response
int64 sum

This service takes three integers as input and returns their sum.
5. Modify [Link]

Update [Link] to build the message and service files.

File: my_custom_interfaces/[Link]

Add these modifications:

find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
"msg/[Link]"
"srv/[Link]"
)

ament_package()

6. Modify [Link]

Ensure your [Link] includes dependencies for building messages and services.

File: my_custom_interfaces/[Link]

Add these dependencies:

<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>

7. Build the Package

Go to your workspace root and build the package.

cd ~/ros2_ws
colcon build --packages-select my_custom_interfaces
source install/[Link]

8. Use Custom Messages and Services in Nodes

You can now use the custom message and service in your Python nodes.

Example Service Node: add_three_ints_server.py

import rclpy
from [Link] import Node
from my_custom_interfaces.srv import AddThreeInts

class AddThreeIntsService(Node):
def __init__(self):
super().__init__('add_three_ints_server')
[Link] = self.create_service(AddThreeInts, 'add_three_ints',
self.handle_add_three_ints)
self.get_logger().info('Service ready to add three integers.')

def handle_add_three_ints(self, request, response):


[Link] = request.a + request.b + request.c
self.get_logger().info(f'Request: {request.a} + {request.b} +
{request.c} = {[Link]}')
return response

def main():
[Link]()
node = AddThreeIntsService()
[Link](node)
[Link]()

if __name__ == '__main__':
main()
Example Client Node: add_three_ints_client.py

import rclpy
from [Link] import Node
from my_custom_interfaces.srv import AddThreeInts

class AddThreeIntsClient(Node):
def __init__(self):
super().__init__('add_three_ints_client')
[Link] = self.create_client(AddThreeInts, 'add_three_ints')

while not [Link].wait_for_service(timeout_sec=1.0):


self.get_logger().info('Waiting for the service...')

def send_request(self, a, b, c):


request = [Link]()
request.a = a
request.b = b
request.c = c

future = [Link].call_async(request)
rclpy.spin_until_future_complete(self, future)
return [Link]()

def main():
[Link]()
client = AddThreeIntsClient()
response = client.send_request(1, 2, 3)
print(f'Sum: {[Link]}')
[Link]()

if __name__ == '__main__':
main()

9. Update [Link] (if needed)

If you are working with an ament Python package, you’ll need to make sure your [Link]
is configured to include your new nodes.
10. Build and Run the Nodes

Make sure the package is built, and then run the service and client:

1. Run the service:

ros2 run my_custom_interfaces add_three_ints_server

2. Run the client:

ros2 run my_custom_interfaces add_three_ints_client

You should see output like:

Sum: 6

11. Verify Custom Messages and Services

You can verify your custom interfaces with:

ros2 interface show my_custom_interfaces/msg/Num


ros2 interface show my_custom_interfaces/srv/AddThreeInts

Q Implementing custom interfaces in ROS 2

Implementing custom interfaces (both messages and services) in ROS 2 involves creating
your own data structures that can be exchanged between nodes. Below is a detailed guide to
create, build, and use custom message and service interfaces in ROS 2.

1. Create a ROS 2 Package for Custom Interfaces

Let’s start by creating a new package where the custom messages and services will reside.

cd ~/ros2_ws/src
ros2 pkg create my_custom_interfaces --build-type ament_cmake

This command creates the package named my_custom_interfaces with ament_cmake as the
build tool.

2. Create msg and srv Directories

Navigate to the package directory and create the necessary subdirectories for messages and
services.
cd ~/ros2_ws/src/my_custom_interfaces
mkdir msg srv

3. Define a Custom Message and Service

Custom Message: [Link]

Create a new message file to send an integer value.

File: msg/[Link]

int64 num

Custom Service: [Link]

Create a service file that accepts three integers and returns their sum.

File: srv/[Link]

# Request
int64 a
int64 b
int64 c
---
# Response
int64 sum

4. Modify [Link] to Build Custom Interfaces

Update [Link] to make sure ROS 2 builds the custom message and service.

5. Update [Link]

Ensure that the necessary dependencies are included in [Link].

File: [Link]

Add the following dependencies:

<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>

6. Build the Custom Interfaces

Go to the workspace root and build the package.

cd ~/ros2_ws
colcon build --packages-select my_custom_interfaces
source install/[Link]
7. Implement Nodes Using Custom Interfaces

We’ll now create a service node and a client node that use the custom AddThreeInts
service.

Service Node: add_three_ints_server.py

Create a service node that provides the AddThreeInts service.

File: add_three_ints_server.py
import rclpy
from [Link] import Node
from my_custom_interfaces.srv import AddThreeInts

class AddThreeIntsService(Node):
def __init__(self):
super().__init__('add_three_ints_server')
[Link] = self.create_service(AddThreeInts, 'add_three_ints',
self.handle_add_three_ints)
self.get_logger().info('AddThreeInts service is ready.')

def handle_add_three_ints(self, request, response):


[Link] = request.a + request.b + request.c
self.get_logger().info(f'Request: {request.a} + {request.b} +
{request.c} = {[Link]}')
return response

def main(args=None):
[Link](args=args)
node = AddThreeIntsService()
[Link](node)
[Link]()

if __name__ == '__main__':
main()

Client Node: add_three_ints_client.py

Create a client node that sends a request to the AddThreeInts service.

File: add_three_ints_client.py
import rclpy
from [Link] import Node
from my_custom_interfaces.srv import AddThreeInts

class AddThreeIntsClient(Node):
def __init__(self):
super().__init__('add_three_ints_client')
[Link] = self.create_client(AddThreeInts, 'add_three_ints')

while not [Link].wait_for_service(timeout_sec=1.0):


self.get_logger().info('Waiting for the service to become
available...')

def send_request(self, a, b, c):


request = [Link]()
request.a = a
request.b = b
request.c = c

future = [Link].call_async(request)
rclpy.spin_until_future_complete(self, future)
return [Link]()

def main(args=None):
[Link](args=args)
client = AddThreeIntsClient()
response = client.send_request(3, 4, 5)
print(f'Result: {[Link]}')
[Link]()

if __name__ == '__main__':
main()

8. Update [Link] to Register Scripts (Optional for Python)

If you want to run your nodes as executables, modify the [Link] file.

File: [Link]
from setuptools import setup

package_name = 'my_custom_interfaces'

setup(
name=package_name,
version='0.0.0',
packages=[],
install_requires=['setuptools'],
zip_safe=True,
maintainer='your_name',
maintainer_email='your_email@[Link]',
description='Custom interfaces example in ROS 2',
license='Apache License 2.0',
entry_points={
'console_scripts': [
'add_three_ints_server =
my_custom_interfaces.add_three_ints_server:main',
'add_three_ints_client =
my_custom_interfaces.add_three_ints_client:main',
],
},
)

9. Build the Package Again

After adding the new nodes and making changes, build the package again.

cd ~/ros2_ws
colcon build
source install/[Link]
10. Run the Service and Client Nodes

Open two terminals and run the service and client nodes.

Terminal 1: Start the Service


ros2 run my_custom_interfaces add_three_ints_server

Terminal 2: Run the Client


ros2 run my_custom_interfaces add_three_ints_client

You should see output similar to:

Client Output:

Result: 12

Service Output:

[INFO] [add_three_ints_server]: Request: 3 + 4 + 5 = 12

11. Verify the Custom Interfaces

To confirm that the custom interfaces were generated successfully, you can use the following
commands:

ros2 interface show my_custom_interfaces/msg/Num


ros2 interface show my_custom_interfaces/srv/AddThreeInts

Q. Using parameters in a class

Using parameters in a ROS 2 class allows you to set and manage configuration values that
can be adjusted without changing the code. Below is an example of how to use parameters
inside a ROS 2 node implemented as a Python class.

Step-by-Step Guide: Using Parameters in a ROS 2 Node

1. Create a ROS 2 Package

If you don’t have a package yet, create one:

cd ~/ros2_ws/src
ros2 pkg create my_param_pkg --build-type ament_python --dependencies rclpy

2. Write the ROS 2 Node with Parameters

Create a Python script that demonstrates how to declare, set, and get parameters within a
class.

File: my_param_pkg/param_node.py
import rclpy
from [Link] import Node

class ParameterNode(Node):
def __init__(self):
super().__init__('parameter_node')

# Declare parameters with default values


self.declare_parameter('my_integer', 42)
self.declare_parameter('my_string', 'hello')

# Get parameters
self.my_integer = self.get_parameter('my_integer').value
self.my_string = self.get_parameter('my_string').value

# Log parameter values


self.get_logger().info(f'My integer: {self.my_integer}')
self.get_logger().info(f'My string: {self.my_string}')

# Periodically update parameters (for demonstration)


[Link] = self.create_timer(5.0, self.timer_callback)

def timer_callback(self):
# Update parameters dynamically (if needed)
self.my_integer = self.get_parameter('my_integer').value
self.my_string = self.get_parameter('my_string').value

# Log the updated parameters


self.get_logger().info(f'Updated integer: {self.my_integer}')
self.get_logger().info(f'Updated string: {self.my_string}')

def main(args=None):
[Link](args=args)
node = ParameterNode()

try:
[Link](node)
except KeyboardInterrupt:
pass

node.destroy_node()
[Link]()

if __name__ == '__main__':
main()

3. Update [Link]
Make sure the script is properly registered in [Link].

File: [Link]
from setuptools import setup

package_name = 'my_param_pkg'

setup(
name=package_name,
version='0.0.0',
packages=[package_name],
install_requires=['setuptools'],
zip_safe=True,
maintainer='your_name',
maintainer_email='your_email@[Link]',
description='A ROS 2 node using parameters',
license='Apache License 2.0',
entry_points={
'console_scripts': [
'param_node = my_param_pkg.param_node:main',
],
},
)

4. Build the Package

Go to the workspace root and build the package.

cd ~/ros2_ws
colcon build --packages-select my_param_pkg
source install/[Link]

5. Run the Node with Default Parameters

Launch the node from the terminal:

ros2 run my_param_pkg param_node

You should see output similar to:

[INFO] [parameter_node]: My integer: 42


[INFO] [parameter_node]: My string: hello

6. Override Parameters from the Command Line

You can set parameters dynamically when launching the node:

ros2 run my_param_pkg param_node --ros-args -p my_integer:=100 -p


my_string:="world"

This will override the default values of my_integer and my_string.


Output:

[INFO] [parameter_node]: My integer: 100


[INFO] [parameter_node]: My string: world

7. Verify Parameters at Runtime

You can use ros2 param commands to get or set parameters dynamically.

1. List all parameters:

ros2 param list /parameter_node

2. Get a parameter value:

ros2 param get /parameter_node my_integer

3. Set a parameter value dynamically:

ros2 param set /parameter_node my_integer 200

4. Observe the updated value: The updated value will be logged after the next timer
callback.

8. Explanation

• Parameter Declaration:
o declare_parameter() is used to declare parameters with default values.
o Parameters must be declared before they can be accessed or set.
• Accessing Parameters:
o get_parameter('param_name').value retrieves the value of a declared
parameter.
• Dynamic Updates:
o Parameters can be updated dynamically using the ros2 param command or within
the node using a callback.

Q. Using ros2doctor to identify issues in ROS 2

ros2doctor is a useful tool for diagnosing and identifying issues in a ROS 2 environment.
It checks your system configuration and identifies potential problems that could affect your
ROS 2 setup.

Here's how you can use it effectively.


1. Install ros2doctor

ros2doctor is included in the ros2cli package, but if it's not installed, you can install it
with the following command:

sudo apt update


sudo apt install python3-ros2doctor

2. Running ros2doctor

You can run ros2doctor from the terminal to scan for common issues.

ros2 doctor

Sample Output:
yaml
=== ROS 2 SYSTEM STATUS ===
ROS 2 distro: humble
System OS: Ubuntu 22.04
Python version: 3.10.6
RMW implementation: rmw_fastrtps_cpp

Checking environment variables... OK


Checking RMW implementation... OK
Checking network configuration... OK
Checking for duplicate nodes... OK

3. Common Checks Performed by ros2doctor

ros2doctor performs several types of checks, including:

• ROS 2 distribution: Checks if the correct ROS 2 version is installed and used.
• Environment variables: Ensures that critical ROS 2 environment variables are set correctly
(e.g., ROS_DOMAIN_ID, RMW_IMPLEMENTATION).
• Network configuration: Verifies if the system's network configuration is appropriate for ROS
2 communication.
• Node duplication: Ensures there are no duplicate nodes running.
• Python and dependencies: Confirms that compatible Python versions and dependencies are
installed.

4. Using Options with ros2doctor

View Available Checks

You can see what checks are available by running:

ros2 doctor --list-checks


Example Output:
Available checks:
- environment_variable_check
- network_check
- rmw_check
- python_check

Run a Specific Check

To run a particular check, use the --include option:

ros2 doctor --include network_check

5. Troubleshooting with ros2doctor

If ros2doctor detects issues, it will print a summary along with suggestions for resolving
them. Here are some common issues it may report:

1. Environment Variables Not Set

• Problem: Missing or incorrect environment variables (like ROS_DOMAIN_ID or


RMW_IMPLEMENTATION).
• Solution:

source /opt/ros/humble/[Link]

2. Network Issues Detected

• Problem: Hostname not set properly, or multicast is disabled, which may cause nodes to fail
to communicate.
• Solution: Ensure that the hostname is correctly configured and that multicast is enabled on
your network.

3. Unsupported Python Version

• Problem: Python version mismatch.


• Solution: Use a compatible Python version (e.g., Python 3.10 for ROS 2 Humble).

6. Example Use Case: Checking RMW Implementation

If you encounter issues with communication, you can use ros2doctor to check the RMW
implementation.

ros2 doctor --include rmw_check

If ros2doctor reports a mismatch, set the correct RMW implementation:

export RMW_IMPLEMENTATION=rmw_fastrtps_cpp

You might also like