Open 808brick opened 2 years ago
For the time being I've added two methods to my Node
class which will allow me to easily set/retrieve parameter data of "remote nodes" with the following code:
import rclpy
import rclpy.node
from rclpy.parameter import Parameter
from rcl_interfaces.srv import SetParameters, GetParameters
class Node(rclpy.node.Node):
def __init__(self, node_name):
super().__init__(node_name)
self.type_arr = ["not_set", "bool_value", "integer_value", "double_value", "string_value",
"byte_array_value", "bool_array_value", "integer_array_value",
"double_array_value", "string_array_value"]
def get_remote_parameter(self, remote_node_name, param_name):
cli = self.create_client(GetParameters, remote_node_name + '/get_parameters')
while not cli.wait_for_service(timeout_sec=1.0):
self.get_logger().info('service not available, waiting again...')
req = GetParameters.Request()
req.names = [param_name]
future = cli.call_async(req)
while rclpy.ok():
rclpy.spin_once(self)
if future.done():
try:
res = future.result()
return getattr(res.values[0], self.type_arr[res.values[0].type])
except Exception as e:
self.get_logger().warn('Service call failed %r' % (e,))
break
def set_remote_parameter(self, remote_node_name, parameter_name, new_parameter_value):
cli = self.create_client(SetParameters, remote_node_name + '/set_parameters')
while not cli.wait_for_service(timeout_sec=1.0):
self.get_logger().info('service not available, waiting again...')
req = SetParameters.Request()
req.parameters = [Parameter(parameter_name, value=new_parameter_value).to_parameter_msg()]
future = cli.call_async(req)
while rclpy.ok():
rclpy.spin_once(self)
if future.done():
try:
res = future.result()
return res.results[0].successful
except Exception as e:
self.get_logger().warn('Service call failed %r' % (e,))
break
In this case I created a new Node
instance using the code above in a file called ros2_helper.py
so that I can use this functionality across all my nodes without having to copy the code. Then in future files I just call the following:
import rclpy
from ros2_helper another_import Node
rclpy.init()
a = Node("node_a")
## Prints the value of the remote node parameter
print(a.get_remote_parameter("node_b", "test_param"))
## Prints True/False depending on if the value of the remote node parameter was successfully changed to the new value
print(a.set_remote_parameter("node_b", "test_param", "new_value"))
a.destory_node()
rclpy.shutdown()
Assuming node_b
was already running prior and declared the parameter test_param
.
I'm sure this is not efficient / clean code in the slightest, but it does make my life easier when creating new nodes which need parameter information from other nodes. So maybe this can be used as an inspiration for the feature request, or maybe it can just be used as a template by others who are looking for the same type of functionality in the meantime.
-Raymond
Thanks for the report. I agree that it would be a good idea to have an AsyncParameterClient
in rclpy, as that would get rid of some boilerplate.
I'm less convinced about SyncParameterClient
. Because it uses a service under the hood, and can take an arbitrary amount of time to complete, you basically would never want to call SyncParameterClient
from inside a ROS 2 callback or a timer. If you did, you would deadlock. So the places it is useful is limited, and the use of it is actually dangerous in some cases.
Anyway, this is useful. We don't have any current plans to work on it, but please do consider opening a pull request with the feature implemented and we can consider it.
Feature Request
Feature description
In ROS1, rospy allowed the retrieval of parameters (from the parameter server) with the one-liner:
In ROS2, parameters are associated by Node, and retrieving information about another Node's parameters proves to be a pretty big hassle currently in rclpy since you need to declare a server client and do some message processing to do so. An example of this can be seen in this ROS Answers post.
rclpy also lacks an
AsynParametersClient
andSyncParametersClient
which is available in rclcpp (rclcpp GitHub paramet_client.hpp). So if it was possible to implement a similar functionality in rclpy, I think it would greatly help streamline sharing parameter values between Nodes in Python.Implementation considerations
For
SyncParametersClient
the return I'd assume is just a list of Parameter objects. ForAsynParametersClient
I'd assume it would return a Future object (or similar).Getting parameter values should just return a list of Parameter objects. To set parameter values, it can just take in a list of Parameter objects.
Example usage could look something like this:
Or alternatively it could be a feature added directly into the
Node
class as a method similar to that of the currentget_parameter
method like so:You could then have
get_remote_parameter_sync
,get_remote_parameter_async
,set_remote_parameter_sync
,set_remote_parameter_async
. This could also be constructed to instead be plural and take arrays instead of single entries.Edit 1: Added second possible implementation Edit 2: Fixed typos