PickNikRobotics / generate_parameter_library

Declarative ROS 2 Parameters
BSD 3-Clause "New" or "Revised" License
240 stars 45 forks source link

Set parameters from YAML dictionary. #208

Closed MarqRazz closed 2 months ago

MarqRazz commented 3 months ago

I'm in the need of using generate_parameter_library to declare, set and update my parameters when launching/running my node but I also need the ability to control which values are loaded when testing.

When I test I create a ROS2 node, declare the ParamListener and then get my parameters but the problem is that nothing has set them so everything defaults. I would like the ability to get my parameters and then override them from a YAML dictionary

self.node = rclpy.create_node('my_test_node')
self.param_listener = my_params.ParamListener(self.node)
self.params = self.param_listener.get_params()

path_to_test_config = os.path.dirname(__file__) + '/config/test_parameters_from_file.yaml'                   
# Load the parameters specific to the test Node
with open(path_to_test_config, 'r') as file:
     my_manually_loaded_yaml_parameters = yaml.safe_load(file)['my_test_node']['ros__parameters'] 
     print(my_manually_loaded_yaml_parameters['load_pose_list_from_file']) # print a parameter from the dictionary

If I could update the parameters from a YAML file it would allow me to write test configs that I can use to check expected behavior.

self.node = rclpy.create_node('my_test_node')
self.param_listener = my_params.ParamListener(self.node)
path_to_test_config = os.path.dirname(__file__) + '/config/test_parameters_from_file.yaml'                   
# Update parameters from test config YAML file before fetching a copy.
with open(path_to_test_config, 'r') as file:
     my_manually_loaded_yaml_parameters = yaml.safe_load(file)['my_test_node']['ros__parameters'] 
     self.param_listener.update_internal_params(my_manually_loaded_yaml_parameters)
self.params = self.param_listener.get_params()

@pac48 does this sound hard to implement?

pac48 commented 3 months ago

I think it is possible. Here is an issue where we discussed doing the opposite (going from the struct to a dictionary) https://github.com/PickNikRobotics/generate_parameter_library/issues/155 I think something similar could be done for the use case you presented. Ideally, this can be implemented without modifying the code generator.

MarqRazz commented 3 months ago

I got things to set from a YAML config but it does not collapse the mapped dictionaries correctly so it fails to get the item as an object.

path_to_test_config = os.path.dirname(__file__) + '/config/test_parameters_from_file.yaml'                   
# Load the parameters specific to the test Node
with open(path_to_test_config, 'r') as file:
    configParams = yaml.safe_load(file)['my_test_node']['ros__parameters'] 

my_loaded_params = self.param_listener.get_params()
for p in configParams:      
    vars(my_loaded_params).update({p: configParams[p]})
    print(vars(my_loaded_params).get(p))
self.param_listener.update_internal_params(my_loaded_params)
self.params = self.param_listener.get_params()

The generate_parameter_library smash the parameter name down to "some_namespace.my_map_name.frame_id", value="base_link"

and the Yaml dictionary is still a dictionary when loaded like I suggest. {some_namespace: {'my_map_name' : {'frame_id': 'link_of_interest'}}}

If you have suggestions on where to put this I'm all ears!

pac48 commented 3 months ago

@MarqRazz Can we schedule the meeting tomorrow at 11:30 instead of 11:00?