Ask Your Question
0

[ROS2][rclpy] how to set parameter hosted by another Node via service [closed]

asked 2020-02-05 01:09:44 -0500

lin404 gravatar image

updated 2020-02-05 18:15:42 -0500

I am looking for a solution to set the Parameter which is host by another node using rclpy.

I found there is a relavent post, but in rclcpp.

While, I tried below. But the error occurred. Any idea how I can fix it? Thank you very much!

import time

import rclpy
from rclpy.node import Node
import rclpy.qos as qos

from rclpy.parameter import Parameter
# from rcl_interfaces.msg import Parameter
from rcl_interfaces.msg import ParameterValue
from rcl_interfaces.srv import SetParameters, GetParameters, ListParameters
from rcl_interfaces.msg import ParameterDescriptor, ParameterValue


class MinimalClientAsync(Node):

    def __init__(self):
        super().__init__('my_node')

        self.cli = self.create_client(SetParameters, '/another_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):
        self.req.parameters = [Parameter(name='parameter_1', value=13)]
        self.future = self.cli.call_async(self.req)


def main(args=None):
    rclpy.init(args=args)

    minimal_client = MinimalClientAsync()
    minimal_client.send_request()

    while rclpy.ok():
        rclpy.spin_once(minimal_client)
        if minimal_client.future.done():
            try:
                response = minimal_client.future.result()
            except Exception as e:
                minimal_client.get_logger().info(
                    'Service call failed %r' % (e,))
            break

    minimal_client.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()

The error is:

  File "/opt/ros/dashing/lib/python3.6/site-packages/rcl_interfaces/srv/_set_parameters.py", line 138, in parameters
    "The 'parameters' field must be a set or sequence and each value of type 'Parameter'"
AssertionError: The 'parameters' field must be a set or sequence and each value of type 'Parameter'

I also tried to use rcl_interfaces.msg.Parameter instead of rclpy.parameter.Parameter so it won't failed at rcl_interfaces/srv/_set_parameters.py. However it failed at rcl_interfaces/msg/_parameter.py this time:

  File "/opt/ros/dashing/lib/python3.6/site-packages/rcl_interfaces/msg/_parameter.py", line 80, in __init__
    self.value = kwargs.get('value', ParameterValue())
  File "/opt/ros/dashing/lib/python3.6/site-packages/rcl_interfaces/msg/_parameter.py", line 149, in value
    "The 'value' field must be a sub message of type 'ParameterValue'"
AssertionError: The 'value' field must be a sub message of type 'ParameterValue'

Then I kept trying, used ParameterValue, like this:

Parameter(name='parameter_1', value=ParameterValue(integer_value=3))

It did not cause any error, but when I check the param, it shows Parameter not set.. I am confused. The parameter parameter_1 seems to be undeclared.

edit retag flag offensive reopen merge delete

Closed for the following reason duplicate question by lin404
close date 2020-02-05 18:18:44.228658

1 Answer

Sort by ยป oldest newest most voted
3

answered 2020-02-05 03:22:14 -0500

marguedas gravatar image

You're very close. As there is currently no helper in rclpy, the "easier" might be to use only rcl_interfaces type for now to avoid confusion

The 'parameters' field must be a set or sequence and each value of type 'Parameter'

This means that you need to pass a rcl_interfaces.msg.Parameter to this function.

Then I kept trying, used ParameterValue, like this:

That's the way to go. On top of defining the value, the type of the parameter needs to be set, as it is not detected automatically

Parameter(name='parameter_1', value=ParameterValue(integer_value=3, type=ParameterType.PARAMETER_INTEGER))

The send_request function will look like this:

def send_request(self):
    new_param_value = ParameterValue(type=ParameterType.PARAMETER_INTEGER, integer_value=13)
    self.req.parameters = [Parameter(name='parameter_1', value=new_param_value)]
    self.future = self.cli.call_async(self.req)

Note that you'll need to import ParameterType from rcl_interfaces.msg at the top of your file.

edit flag offensive delete link more

Comments

@marguedas Thank you very much! I have confirmed it, it works perfect!

lin404 gravatar image lin404  ( 2020-02-05 18:16:32 -0500 )edit

Does it work in foxy? I tried and get an error:

  File "/opt/ros/foxy/lib/python3.8/site-packages/rclpy/parameter.py", line 127, in __init__
type_ = Parameter.Type.from_parameter_value(value)
  File "/opt/ros/foxy/lib/python3.8/site-packages/rclpy/parameter.py", line 70, in from_parameter_value
    raise TypeError('The given value is not one of the allowed types.')
TypeError: The given value is not one of the allowed types.
dgarcialopez gravatar image dgarcialopez  ( 2021-05-23 10:38:59 -0500 )edit

It effectively works on foxy. I encountered the same issue and the problem was that my Parameter class was not imported from rcl_interface.msg. It shouldn't come from rclpy.parameter like the code above.

Lothea gravatar image Lothea  ( 2021-10-19 15:33:25 -0500 )edit

It's worth noting the rclpy Parameter object has a to_parameter_msg() method to it so you do not need to explicitly import rcl_interfaces.msg.Parameter or rcl_interfaces.msg.ParameterValue

Example usage:

from rclpy.parameter import Parameter

...

def send_request(self):
    self.req.parameters = [Parameter(name='parameter_1', value=new_param_value).to_parameter_msg()]
    self.future = self.cli.call_async(self.req)

This is also nice as it will handle the type for you automatically and put the value in the correct message field without you having to manually specify things such as "integer_value" or "string_value" etc.

808brick gravatar image 808brick  ( 2022-05-05 00:57:45 -0500 )edit

Question Tools

2 followers

Stats

Asked: 2020-02-05 01:09:44 -0500

Seen: 1,217 times

Last updated: Feb 05 '20