What you will learn:
Parameters in ROS 2 are associated with individual nodes, it is not possible to work with another node’s parameters simply by using the Node API. For example, a node calling get_parameter() can only get its own parameters.
In this tutorial, you’ll learn how to get and set another node’s parameters by using a service call in code. All other parameter services should be straightforward as they are similar.
If you want to learn more advanced ROS 2 topics, including parameters and more, in a practical, hands-on way, check out the course Intermediate ROS 2: https://app.theconstruct.ai/courses/113
Opening the rosject
In order to follow this tutorial, we need to have ROS 2 installed in our system, and ideally a ros2_ws (ROS2 Workspace). To make your life easier, we have already prepared a rosject for that: https://app.theconstruct.ai/l/639b9a55/
Just by copying the rosject (clicking the link above), you will have a setup already prepared for you.
After the rosject has been successfully copied to your own area, you should see a Run button. Just click that button to launch the rosject.
After pressing the Run button, you should have the rosject loaded. Now, let’s head to the next section to get some real practice.
In order to interact with ROS2, we need a terminal.
Let’s open a terminal by clicking the Open a new terminal button.
Create a ROS2 python package with a simple node that has two parameters
In a terminal, create a python package called python_parameters
:
cd ~/ros2_ws/src/ ros2 pkg create --build-type ament_python python_parameters --dependencies rclpy
In the same terminal, create a python node called python_parameters_node.py
:
touch python_parameters/python_parameters/python_parameters_node.py
Here is a simple node with two parameters of type string (to paste in python_parameters_node.py
):
- The parameters are:
my_parameter
with valueme
and descriptionThis parameter is mine!
your_parameter
with valueyou
and descriptionThis parameter is yours!
You can also have other types as listed in the documentation.
- The node has a timer callback with a period of 1 second just to log what the current parameters are.
import rclpy import rclpy.node from rcl_interfaces.msg import ParameterDescriptor class MinimalParam(rclpy.node.Node): def __init__(self): super().__init__('minimal_param_node') my_parameter_descriptor = ParameterDescriptor(description='This parameter is mine!') self.declare_parameter('my_parameter', 'me', my_parameter_descriptor) your_parameter_descriptor = ParameterDescriptor(description='This parameter is yours!') self.declare_parameter('your_parameter', 'you', your_parameter_descriptor) self.timer = self.create_timer(1, self.timer_callback) def timer_callback(self): my_param = self.get_parameter('my_parameter').get_parameter_value().string_value self.get_logger().info('I am %s!' % my_param) your_param = self.get_parameter('your_parameter').get_parameter_value().string_value self.get_logger().info('You are %s!' % your_param) def main(): rclpy.init() node = MinimalParam() rclpy.spin(node) if __name__ == '__main__': main()
Add an entry point to the setup.py
file so that it looks like this:
entry_points={ 'console_scripts': [ 'minimal_param_node = python_parameters.python_parameters_node:main', ], },
In a terminal, compile the package:
cd ~/ros2_ws/ colcon build --packages-select python_parameters
Then start the node:
source ~/ros2_ws/install/setup.bash ros2 run python_parameters minimal_param_node
You should get something like this:
[INFO] [1713695285.349594469] [minimal_param_node]: I am me! [INFO] [1713695285.349875641] [minimal_param_node]: You are you! [INFO] [1713695286.337758113] [minimal_param_node]: I am me! [INFO] [1713695286.338776447] [minimal_param_node]: You are you! [INFO] [1713695287.337323765] [minimal_param_node]: I am me! [INFO] [1713695287.338010397] [minimal_param_node]: You are you!
Create a simple client node that retrieves another node’s parameters
The goal is to get the parameter values of the node that we just created in the previous section (python_parameters_node.py
) from within a new client node.
Create a new Python file for the client node called get_parameters_client_node.py
:
touch ~/ros2_ws/src/python_parameters/python_parameters/get_parameters_client_node.py
Here is a simple asynchronous client (to paste in get_parameters_client_node.py
):
- It creates a client with service type
GetParameters
and service name/minimal_param_node/get_parameters
- The request message needs to be a list of names of parameters whose values we want to get. In this case, we chose to retrieve the values of both
my_parameter
andyour_parameter
- Remember that we can use the terminal command
ros2 interface show rcl_interfaces/srv/GetParameters
to determine the request (and response) fields
- Remember that we can use the terminal command
- The
send_request
method is passed the request message and handles the service call
import rclpy from rclpy.node import Node from rcl_interfaces.srv import GetParameters class MinimalGetParamClientAsync(Node): def __init__(self): super().__init__('minimal_get_param_client_async') self.cli = self.create_client(GetParameters, '/minimal_param_node/get_parameters') while not self.cli.wait_for_service(timeout_sec=1.0): self.get_logger().info('service not available, waiting again...') self.req = GetParameters.Request() def send_request(self, params_name_list): self.req.names = params_name_list self.future = self.cli.call_async(self.req) rclpy.spin_until_future_complete(self, self.future) return self.future.result() def main(): rclpy.init() minimal_get_param_client = MinimalGetParamClientAsync() list_of_params_to_get = ['my_parameter', 'your_parameter'] response = minimal_get_param_client.send_request(list_of_params_to_get) minimal_get_param_client.get_logger().info('First value: %s, Second value: %s' % (response.values[0].string_value, response.values[1].string_value)) minimal_get_param_client.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
Add the entry point to the setup.py
file so that it looks like this:
... entry_points={ 'console_scripts': [ 'minimal_param_node = python_parameters.python_parameters_node:main', 'minimal_get_param_client_async = python_parameters.get_parameters_client_node:main', ], }, )
Compile the package:
cd ~/ros2_ws/ colcon build --packages-select python_parameters
Run the client node in one terminal:
source ~/ros2_ws/install/setup.bash ros2 run python_parameters minimal_get_param_client_async
And the service node in a different terminal:
source ~/ros2_ws/install/setup.bash ros2 run python_parameters minimal_param_node
You should get something like this in the terminal of the client:
[INFO] [1719039475.331557220] [minimal_get_param_client_async]: service not available, waiting again... [INFO] [1719039476.333952497] [minimal_get_param_client_async]: service not available, waiting again... [INFO] [1719039477.336702012] [minimal_get_param_client_async]: service not available, waiting again... [INFO] [1719039478.338849982] [minimal_get_param_client_async]: service not available, waiting again... [INFO] [1719039478.591154332] [minimal_get_param_client_async]: First value: me, Second value: you ...
Since the client was started first, it was initially waiting for the GetParameters
service to be available. After the service started, the client successfully retrieved the parameter values before shutting down.
End all terminal processes before continuing.
Create a simple client node that modifies another node’s parameters
The process here is similar to the previous section, only this time we are setting the parameter values.
Create a new Python file for the client node called set_parameters_client_node.py
:
touch ~/ros2_ws/src/python_parameters/python_parameters/set_parameters_client_node.py
Here is a simple asynchronous client (to paste in set_parameters_client_node.py
):
- It creates a client with service type
SetParameters
and service name/minimal_param_node/set_parameters
- The request message needs to be a list of the
Parameter
msg type which has sub-fields to store the parameter name, data type and value- Remember that we can use the terminal command
ros2 interface show rcl_interfaces/srv/SetParameters
to determine the request (and response) fields
- Remember that we can use the terminal command
- The
send_request
method is passed the new parameter values to set and handles the service call
import rclpy from rclpy.node import Node from rcl_interfaces.srv import SetParameters from rcl_interfaces.msg import Parameter, ParameterType class MinimalSetParamClientAsync(Node): def __init__(self): super().__init__('minimal_set_param_client_async') self.cli = self.create_client(SetParameters, '/minimal_param_node/set_parameters') while not self.cli.wait_for_service(timeout_sec=1.0): self.get_logger().info('service not available, waiting again...') self.req = SetParameters.Request() def send_request(self, my_parameter_value, your_parameter_value): param = Parameter() param.name = "my_parameter" param.value.type = ParameterType.PARAMETER_STRING param.value.string_value = my_parameter_value self.req.parameters.append(param) param = Parameter() param.name = "your_parameter" param.value.type = ParameterType.PARAMETER_STRING param.value.string_value = your_parameter_value self.req.parameters.append(param) self.future = self.cli.call_async(self.req) rclpy.spin_until_future_complete(self, self.future) return self.future.result() def main(): rclpy.init() minimal_set_param_client = MinimalSetParamClientAsync() my_parameter_value = 'a teacher' your_parameter_value = 'a student' response = minimal_set_param_client.send_request(my_parameter_value, your_parameter_value) minimal_set_param_client.get_logger().info('Results: %s' % (response)) minimal_set_param_client.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
Add the entry point to the setup.py
file so that it looks like this:
... entry_points={ 'console_scripts': [ 'minimal_param_node = python_parameters.python_parameters_node:main', 'minimal_get_param_client_async = python_parameters.get_parameters_client_node:main', 'minimal_set_param_client_async = python_parameters.set_parameters_client_node:main', ], }, )
Compile the package:
cd ~/ros2_ws/ colcon build --packages-select python_parameters
Run the service node first:
source ~/ros2_ws/install/setup.bash ros2 run python_parameters minimal_param_node
Then the client node in another terminal:
source ~/ros2_ws/install/setup.bash ros2 run python_parameters minimal_set_param_client_async
You should get something like this in the terminal of the client:
[INFO] [1719045088.071267960] [minimal_set_param_client_async]: Results: rcl_interfaces.srv.SetParameters_Response(results=[rcl_interfaces.msg.SetParametersResult(successful=True, reason=''), rcl_interfaces.msg.SetParametersResult(successful=True, reason='')])
And this in the terminal of the service node:
[INFO] [1719045085.623969364] [minimal_param_node]: You are you! [INFO] [1719045086.613542065] [minimal_param_node]: I am me! [INFO] [1719045086.614106714] [minimal_param_node]: You are you! [INFO] [1719045087.613402451] [minimal_param_node]: I am me! [INFO] [1719045087.613898409] [minimal_param_node]: You are you! [INFO] [1719045088.613406942] [minimal_param_node]: I am a teacher! [INFO] [1719045088.613964772] [minimal_param_node]: You are a student! [INFO] [1719045088.613406942] [minimal_param_node]: I am a teacher! [INFO] [1719045088.613964772] [minimal_param_node]: You are a student! ...
Just like in my previous tutorial (How to manipulate multiple parameters at once from the command line using parameter services), you managed to set multiple parameters at once- only this time it was from within another node! This opens up the possibility of scripted interaction with external parameters rather than being restricted to just the CLI!
Now you know how to interact with parameter services in code.
Congratulations. You now have a basic understanding of parameters and are familiar with creating and using services.
To learn more advanced topics about ROS 2, have a look at the course below:
- Intermediate ROS 2: https://app.theconstruct.ai/courses/113
We hope this post was really helpful to you.
This tutorial is created by Robotics Ambassador Ernest.
Video Tutorial
0 Comments