First time here? Check out the FAQ!


ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
3

declare nested parameter

asked Jun 17 '19

Christian Rauch gravatar image

updated Jun 17 '19

How do I declare nested variables or lists of key value pairs in ROS2 dashing?

E.g for a yaml structure like

tag_lists:
    base: 9
    object: 14

tag_lists is the known parameter name which contains a list of mappings from strings ("base", "object") to integers (9, 14).

I used to read these nested structures by:

static const std::string tag_list_prefix = "tag_lists";
auto parameters_and_prefixes = list_parameters({tag_list_prefix}, 10);
for (const std::string &name : parameters_and_prefixes.names) {
    const int id = get_parameter(name).get_value<int>();
    tracked_tags[id] = name.substr(tag_list_prefix.size()+1, name.size());
}

I.e. the parameter tag_lists is a namespace with optional sub-parameters that are determined at runtime.

And how would I declare a parameter like:

tag_lists:
    base:
        id: 9
        size: 0.1
    object:
        id: 14
        size: 0.2

that maps from strings ("base", "object") to a list of known parameters ("id", "size")?

Preview: (hide)

3 Answers

Sort by » oldest newest most voted
1

answered Jun 17 '19

William gravatar image

updated Jun 17 '19

You can either declare them one at a time, e.g.:

int base_id = node->declare_parameter("tag_lists.base.id").as_int();
double base_size = node->declare_parameter("tag_lists.base.size").as_double();
int object_id = node->declare_parameter("tag_lists.object.id").as_int();
double object_size = node->declare_parameter("tag_lists.object.size").as_double();

Or you can declare more than one at a time (http://docs.ros2.org/dashing/api/rclc...), so long as they have the same type (not the case in your example), e.g. for this yaml:

robot1:
     laser_topic: /scan
     camera_topic: /left_camera
     tf_frame: robot1

You could declare them like this:

std::vector<std::string> params = node->declare_parameters("robot1", {
  {"laser_topic", "default_scan"},
  {"camera_topic", "default_image"},
  {"tf_frame", "default_world"},
});

But, if you don't know the names ahead of time, then you can have the node automatically declare all of the parameters in the yaml file (we consider them to be overrides, so we refer to those as "parameter overrides") with this option:

http://docs.ros2.org/dashing/api/rclc...

Ideally, we'd have a version of declare_parameters which didn't require a default value and that could take/return ParameterValue objects so that you could use it with a heterogeneous list of parameters (mixing int and float in your case). But we don't have that right now. But you could create a helper function in the meantime, our declare_parameters is quite simple:

https://github.com/ros2/rclcpp/blob/2...

EDIT1:

Also, if you want you can get the parameter overrides (combined from the yaml files and the programmatic parameter overrides from the NodeOptions) via the NodeParameters API. You can get it from the node with get_node_parameters_interface() and then get the overrides with get_parameter_overrides(), e.g.:

auto node_parameters_iface = node->get_node_parameters_interface();
std::map<std::string, rclcpp::ParameterValue> & parameter_overrides =
  node_parameters_iface->get_parameter_overrides();

And then you could iterate of that how ever you like.

Preview: (hide)

Comments

Yes, one problem is that base and object are not known in advance.

I tried to circumvent this with a structure like:

tag_lists:
  - frame: base
    id: 9
  - frame: object
    id: 14

But the parser expects that all values are of the same type: Failed to parse yaml params file '[configuration].yaml': Sequence should be of same type. Value type 'integer' do not belong at line_num [line with "id: 9"], at /tmp/binarydeb/ros-dashing-rcl-yaml-param-parser-0.7.5/src/parser.c:931

Christian Rauch gravatar image Christian Rauch  ( Jun 21 '19 )edit
1

answered Jul 30 '19

mario gravatar image

I think I have the same problem right now. I am building a quadruped robot and I want to have the code independend from the actual names of the motors (they should only be mapped via the yaml and urdf).

I made a pretty hacky solution. My yaml looks like this:

param_tester:
ros__parameters:
    my_names: ["one", "two", "three"]
    my_nodes:
        one:
            id : 0
            init : 1500
        two:
            id : 1
            init : 1234
        three:
            id : 2
            init : 4321

And my code would then looks like this:

 this->declare_parameter("my_names")
 std::vector<std::string> n = this->get_parameter("my_names").as_string_array();

for(std::string &name : n) {
    this->declare_parameter("my_nodes." + name + ".id");
    this->declare_parameter("my_nodes." + name + ".init");
    int64_t id = this->get_parameter("my_nodes." + name + ".id").as_int();
    int64_t init = this->get_parameter("my_nodes." + name + ".init").as_int();
    RCLCPP_INFO(this->get_logger(), "Name: '%s'", name.c_str());
    RCLCPP_INFO(this->get_logger(), "id: %d", id);
    RCLCPP_INFO(this->get_logger(), "init: %d", init);
}

Is there any simpler/easier way to do this? I don't really like the repeating name thing, but it does work. This actually should have been an answer to the comment from Christian Rauch, but the comment was too long.

Edit: fixed the code.

Preview: (hide)
0

answered Oct 26 '22

psammut gravatar image

updated Oct 26 '22

I have a similar problem and opted to import these from a settings json defined by a thrift file. I can iterate over the data structures and set stuff up accordingly. Sucks that it breaks the ros2 parameter idiom, but ros2 parameters don't support structs and containers of structs - which is the crux of this problem.

Preview: (hide)

Question Tools

4 followers

Stats

Asked: Jun 17 '19

Seen: 4,810 times

Last updated: Oct 25 '22