rclcpp icon indicating copy to clipboard operation
rclcpp copied to clipboard

Programmatically loading parameters from yaml files into nodes dynamically

Open ddengster opened this issue 4 years ago • 11 comments

Feature request

Programmatically loading parameters from yaml files into nodes dynamically

Feature description

A simple c++ function (possibly put into rclcpp::Node?) for loading parameters from yaml files after the node has been started. (ie. not via supplying a yaml file in the ros2 run command)

There were several ros1 repos using this functionality of loading nodes dynamically and supplying yaml file. However, the spec on parameters changed with ros2.

Implementation considerations

  • Not sure if the function should be under rclcpp::Node.

  • Some upcoming repos that could use this functionality: ros2_controls controller_manager loading controllers at runtime ros2 version gazebo_ros_controls (blocked)

  • Reference code doing the same thing as this request that you could use: https://github.com/ros-controls/ros2_control/pull/64/files

  • A good use case would be the following yaml file contains parameters for two nodes:

coffeebot_arm_effort_controller:
  ros__parameters:
    type: joint_trajectory_controller/JointTrajectoryController
    joints:
       - coffeebot_shoulder_pan_joint
       - coffeebot_shoulder_lift_joint
    constraints:
        stopped_velocity_tolerance: 0.05
        coffeebot_shoulder_pan_joint: {trajectory: 0.1, goal: 0.1}
        coffeebot_shoulder_lift_joint: {trajectory: 0.1, goal: 0.1}
    stop_trajectory_duration: 0.5

coffeebot_gripper_effort_controller:
  ros__parameters:
    type: joint_trajectory_controller/JointTrajectoryController
    joints:
      - coffeebot_gripper_left_joint
      - coffeebot_gripper_right_joint
    stop_trajectory_duration: 0.5

As far as I know it follows the spec given on the ROS2 tutorials

ddengster avatar Mar 20 '20 07:03 ddengster

For what it is worth, it should be possible to do this pretty easily right now. You'd just need a small program that opened up a YAML file (maybe using yaml-cpp), formatted that YAML into a https://github.com/ros2/rcl_interfaces/blob/master/rcl_interfaces/srv/SetParameters.srv (one per node), and then called the set_parameters service on the node(s).

I guess we could potentially make this easier with an API on rclcpp, but I'm not sure.

clalancette avatar Mar 20 '20 13:03 clalancette

Actually there is a rcl_parse_yaml_file() under rcl_yaml_param_parser that could be used to parse parameters from yaml config. It returns rcl_params_t though, but the good news, there is also rclcpp::parameter_map_from() that could be used to convert from rcl_params_t to rclcpp::Parameter.

So consider the parsing problem is solved, and it just need to overwrite or create a new parameter from the parsed rclcpp::Parameter.

threeal avatar Jun 08 '20 03:06 threeal

Were the multiple load_parameters functions from pull request #1596 which can take from both a parameter_map and a yaml file enough, or can I work on this?

CursedRock17 avatar Sep 29 '23 13:09 CursedRock17

@CursedRock17 i think we already can do this with, (really easy)

  1. creating node with namespace and node_name.
  2. create rclcpp::AsyncParametersClient with "namespace/node_name"
  3. and then call param_client->load_parameters(parameters_filepath)

we can even do this with remote node.

but there is no such method in NodeParametersInterface, that means we need to create rclcpp::AsyncParametersClient for doing this. i believe that was the original request for this issue, so we would want to add load_parameters to NodeParametersInterface?

CC: @clalancette what do you think?

fujitatomoya avatar Sep 29 '23 21:09 fujitatomoya

@fujitatomoya Thank you very much for the hints, here is how I got it working for reference :

#include <ament_index_cpp/get_package_share_directory.hpp>

// Read yaml parameter file
const std::string yaml_filepath = ament_index_cpp::get_package_share_directory("package_name") +  "/config/params.yaml";
RCLCPP_INFO_STREAM(node_->get_logger(), "Reading yaml parameter files located at" <<   yaml_filename << std::endl);
auto parameters_client =  std::make_shared<rclcpp::AsyncParametersClient>(node_, "package_name");
parameters_client->load_parameters(yaml_filepath);

RobinBaruffa avatar Jan 12 '24 09:01 RobinBaruffa

If this something the rclcpp maintainers fine with adding, we could review a PR @RobinBaruffa.

CursedRock17 avatar Jan 16 '24 01:01 CursedRock17

i believe that having load_parameters in NodeParameterInterface sounds reasonable, so that application can load the parameters with yaml file w/o having AsyncParametersClient.

@clalancette @wjwwood @sloretz @alsora @mjcarroll what do you think?

Note:

  • AsyncParametersClient supports the following methods,

    • get_parameters
    • describe_parameters
    • get_parameter_types
    • set_parameters
    • set_parameters_atomically
    • delete_parameters (precisely it should be more like unset_parameters changed it into PARAMETER_NOT_SET)
    • load_parameters
    • list_parameters
  • NodeParametersInterface supports the followings,

    • declare_parameter
    • undeclare_parameter
    • has_parameter
    • set_parameters
    • set_parameters_atomically
    • get_parameter / get_parameters / get_parameters_by_prefix
    • describe_parameters
    • get_parameter_types
    • list_parameters

fujitatomoya avatar Jan 16 '24 21:01 fujitatomoya

Sorry I haven't time to read everything here or comment before now, but we discussed this a bit today in our triage meeting and I summarized our thoughts here: https://github.com/ros2/rclcpp/pull/2406#pullrequestreview-1844391537

In short, this seems like a good feature, but we would like to avoid expanding the methods on the Node class and/or the node interfaces.

wjwwood avatar Jan 25 '24 18:01 wjwwood

Hm, I don't get the purpose of the MR for multiple reasons

  • Why do you need to load a yaml file in the first place, can the data not be given through the parameter interface ?
  • If you load a yaml file, isn't the configuration state of the node is 'hidden' from external tooling, as it can't be requested via the parameter interface ?

@clalancette wrote in #2406 Looking at this holistically, I think my other problem with this is that our developer experience around parameters is already poor.

I agree to this statement. But I would like to point out the primary reason on why my colleagues and I think so. The current parameter interface does not support tree like parameters. E.g. lists of parameters and therefore you are restricted to an odd subset of yaml.

jmachowinski avatar Feb 11 '24 16:02 jmachowinski

  • Why do you need to load a yaml file in the first place, can the data not be given through the parameter interface ?

When writing a Gazebo plugin that was connected to ROS, I had to load a yaml directly from the c++ code as the gazebo_ros package acts as the node handler and therefore I couldn't pass parameter through it (at least not to my knowledge).

In addition, gazebo plugins are instantiated by Gazebo and are not always ran using a roslaunch file so in this case I believe it makes sense to load a yaml directly from the code.

  • If you load a yaml file, isn't the configuration state of the node is 'hidden' from external tooling, as it can't be requested via the parameter interface ?

I don't think so, if rclcpp::declare_parameter() is used before loading the yaml file, then the configuration state of the node is public and can be read / modified through ROS.

RobinBaruffa avatar Feb 11 '24 18:02 RobinBaruffa

When writing a Gazebo plugin that was connected to ROS, I had to load a yaml directly from the c++ code as the gazebo_ros package acts as the node handler and therefore I couldn't pass parameter through it (at least not to my knowledge).

In addition, gazebo plugins are instantiated by Gazebo and are not always ran using a roslaunch file so in this case I believe it makes sense to load a yaml directly from the code.

You get passed a sdf::ElementPtr _sdf in the Load function of your plugin. Within the _sdf the parameters for your plugin are contained. You are supposed to put them into the SDF together with 'adding' of the plugin of your robot. So no use case here...

I don't think so, if rclcpp::declare_parameter() is used before loading the yaml file, then the configuration state of the node is public and can be read / modified through ROS.

The proposal does not state the export of the loaded parameters. The suggested implementation seems to do this, even though it is not mentioned in the description of the function.

jmachowinski avatar Feb 11 '24 18:02 jmachowinski