c++ parameters api feels overly complex

The api and documentation for publisher/subscriber and service/client is great. Pretty straight forward. Simple. Makes sense. Love it.

On the other hand the parameters is not so clean. In the beginner section of the documentation there are a few code smells:

  1. have to pass the string “my_parameter” into multiple functions (declare_parameter, get_parameter)
  2. this->get_parameter().get_parameter_value().get…how many times do i need to type “get” to get this thing
  3. we’re calling this whole get chain from a timer_callback…seems odd? why not a callback that’s called when it changes? or even better why not just have a value that updates when it should?

And again in the intermediate documentation:

  1. still passing strings around (declare_parameter, add_parameter_callback)
  2. have to hold on to these “cb_handle” objects (one per parameter??)
  3. the handler doesn’t even know the “type” of the parameter I just declared?

I’ve played around a very little bit with the parameter stuff on my own and one thing that I found a little disturbing is from the command line I can say “ros2 param set node variable value” which is great but there’s no guarantee that anyone was listening on the other end. If the implementer of the node says “eh I’ll declare these parameters but figure out how to use them later”…no way I’d know the difference as a user of that node.

Anyway. I’m not a great programmer by any means (so the following may simply be impossible) but as an end user it feels like the api should be more like:

int & my_int_param = this->declare_parameter("my_int_name", default_int_value, optional_int_callback);
// my_int_param is just a reference to an int that is updated behind the scenes when it should be
// if I care I could pass in a callback that gets called when the thing is about to be changed and modify/verify/reject the change
// that's it. now I use my_int_param throughout my node and I'm done

I did a little more digging and found this: https://github.com/ros2/rcl_interfaces/blob/rolling/rcl_interfaces/msg/ParameterValue.msg
which is also mildly terrifying. I (clearly) don’t know how this stuff works at all but the message format makes me think that every time I send a parameter it’s as if I’m sending a message containing a bunch of extra junk??

Anyway. I’m sure there are a lot of good reasons it is the way it is. I’d like to understand better if anyone knows. Does the api I proposed seem plausible? Is this the right place to bring this up? If not where should I?

2 Likes

To improve the developer experience of using parameters, I created this: GitHub - PickNikRobotics/generate_parameter_library: Declarative ROS 2 Parameters

Maybe check it out if you find using this API as frustrating as I did. We are now using it in ros2_controllers and moveit and some of our internal projects.

If you are asking what is going on with this awful message type… well the problem is that the ROS message type does not have proper algebraic data types. Maybe someday, when we get those in the IDL we can fix that message type that has one of each type in it. Maybe don’t call set_parameter service calls in your hot loop.

7 Likes

I’ve been trying to dig into this for a while and I think I’ve gotten the hang of it.
Parameters are way over-engineered, but that’s reasonable because they were under-cooked in ROS1
There’s a bit of API churn so callbacks have meant different things in past versions of ROS2

There’s three main stages to parameters: (yes, three, and no I’ve not seen an examplar package on github)

  • Declare - declare_parameter announces to other nodes (and CLI tools that you have a parameter that can be read/changed

  • Validate - Node->add_on_set_parameters_callback() is triggered when an external source asks if a new value is valid.
    DO NOT change the node’s actual stored parameter in this callback. (This is where every tutorial and example node updates parameters and stops, but you’re missing out on the real power of ROS2 parameters)
    You can reject parameters during the validate callback you set with add_on_set_parameters_callback(), and add a useful error message with the rejection

  • Update - ParameterEventHandler->add_parameter_callback(param_name, update_callback) is called when your node has had a chance to validate/reject the new value, and no other node has rejected the new value. All nodes in the network are ready to accept this new value, so you can safely change the stored value on the node now.

Type flexibility is still left as an exercise to the user. I find myself writing a lot of code to accept an integer value into a double parameter so that the CLI parameter updates aren’t so painful

If you’re properly configured for dynamic parameters, you don’t have to get_parameter after declaration because your validate and update callbacks will be triggered by the launch system

5 Likes