Reconfiguration¶
About¶
Q) What to do when you need to share dynamic_reconfigure variables amongst a collection of nodes?
A good example is sharing navigation motion constraints amongst a collection of nodes that control the motion of the base (general navigation, parking, docking, ...) This standalone node volunteers for the responsibility of loading and managing dynamic reconfigure servers from a central location. All you need do is feed it with a yaml/rosparam configuration that will define the dynamic_reconfigure servers you wish to fire up along with any initial parameter overrides to use when instantiating them.
It also manages these for free. You can start with an initial collection of default dynamic reconfigure servers with their respective configurations. You can then also dynamically bring up, update or tear down servers as you wish at a later time so that the reconfiguration syncs with the starting up and tearing down of your higher level applications.
Reconfigure your Reconfiguration!
Server¶
You will need to prepare the following (no coding necessary!):
Reconfiguration Node
- (Optional) A yaml defining the default dyn-reconf servers to run on startup
- A launcher for starting the reconfiguration server
Feeders (Optional)
- A yaml defining new dyn-reconf servers to manage, or supercede default servers
- A launcher for starting the feeder with your yaml configuration
If you’re familiar with nodelet managers and nodelets, the feeders work similarly. When the feeder node launches it sends a request to the server to fire up or update the specified list of dynamic reconfigure servers. When it terminates, it will send one last service call off to the reconfiguration server to shutdown or reset the previously started reconfigure servers.
Some policies/conventions that apply:
Initialisation of Parameters : all parameters for new dyn-reconf servers are initialised by values in the overrides section in the reconfiguration node/feeder yamls, otherwise falling back to defaults from the .cfg files. Do not initialise these parameters directly as is the norm (e.g. via rosparam in roslaunch files).
Dynamic Loading on Top of Default Profiles : having a default dyn-reconf server started by the reconfiguration node and then later feeding the same dyn-reconf server with an updated set of overrides is a great means of toggling parameter profiles for a dyn-reconf server. It is important to note here that this implementation merges the incoming overrides with the currently saved parameters and that it restores these when the feeder shuts down.
Examples - Reconfiguration¶
An example set of files (also available as a demo within this package):
<launch>
<arg name="debug" default="true" doc="enable/disable verbose loading/unloading/updating information"/>
<node pkg="feed_the_troll" type="reconfiguration_server.py" name="reconfiguration" output="screen">
<rosparam command="load" file="$(find feed_the_troll)/parameters/demo_reconfiguration_server.yaml"/>
<param name="debug" value="$(arg debug)"/>
</node>
</launch>
servers:
# a dyn-recfg server definition, this will be started in the reconfiguration server's namespace
# note: the server name is simply a unique key enabling dyn-recfg server management,
# it does not have to related to the dyn-recfg server namespace!
bob:
module: feed_the_troll.cfg.DemoConfig
# namespace is optional....if not set, it will use the key ('bob') and create the dyn-recfg server in the
# reconfiguration node's private namespace (/reconfiguration/bob)
# overrides are optional...if not set, they will default to those in the .cfg files
# optionally direct the reconfigure server to be somewhere other than the reconfiguration server's private namespace
dudette:
module: feed_the_troll.cfg.DemoConfig
namespace: /foo/troll/dudette
# overrides are optional...and can be partial
overrides:
# int_param: 50
double_param: 0.6
str_param: Dudette
bool_param: False
size: 2
Feed it using this package’s parameter feeder:
<launch>
<node pkg="feed_the_troll" type="param_feeder.py" name="$(anon feeder)">
<rosparam command="load" file="$(find feed_the_troll)/parameters/demo_reconfiguration_feeder.yaml"/>
</node>
</launch>
# Point this feeder at the reconfiguration server - this is where the load/unload
# services can be found. It follws the usual ros namespacing conventions - i.e.
# it can be an absolute global name (e.g. /reconfiguration) or it can be
# representative of a sibling namespace as is the case here
server_namespace: reconfiguration
# Define new or override already started dyn-recfg servers here
parameters:
# Load up a brand new reconfigure server (this wasn't already started by the reconfiguration node)
dude:
# The dyn-recfg type (it is actually a python module) - don't forget the 'Config' suffix!
module: feed_the_troll.cfg.DemoConfig
# Load some initialisations to the param server that override those in the .cfg files
overrides:
int_param: 5
#double_param: 0.6
str_param: Dude
bool_param: False
size: 2
# Override a default server's settings
dudette:
module: feed_the_troll.cfg.DemoConfig
# optionally direct the reconfigure server to be somewhere other than the reconfiguration server's private namespace
namespace: /foo/troll/dudette
overrides:
int_param: 1
#double_param: 0.5
#str_param: Dudette
#bool_param: False
#size: 2
A snapshot of the rosparam server is useful to illustrate where the various parameters get sourced and eventually used for the reconfigure server. Note dude and dudette get placed in two places - dude is inside the reconfiguration server, while dudette has been explicitly instructed to start its dynamic reconfigure server elsewhere.
$ rosparam list
/feeder_snorriwork_18767_3776865491990798862/parameters/dude/module
/feeder_snorriwork_18767_3776865491990798862/parameters/dude/overrides/bool_param
/feeder_snorriwork_18767_3776865491990798862/parameters/dude/overrides/double_param
/feeder_snorriwork_18767_3776865491990798862/parameters/dude/overrides/int_param
/feeder_snorriwork_18767_3776865491990798862/parameters/dude/overrides/size
/feeder_snorriwork_18767_3776865491990798862/parameters/dude/overrides/str_param
/feeder_snorriwork_18767_3776865491990798862/parameters/dudette/module
/feeder_snorriwork_18767_3776865491990798862/parameters/dudette/namespace
/feeder_snorriwork_18767_3776865491990798862/parameters/dudette/overrides/int_param
/feeder_snorriwork_18767_3776865491990798862/server_namespace
/foo/troll/dudette/bool_param
/foo/troll/dudette/double_param
/foo/troll/dudette/int_param
/foo/troll/dudette/size
/foo/troll/dudette/str_param
/reconfiguration/bob/bool_param
/reconfiguration/bob/double_param
/reconfiguration/bob/int_param
/reconfiguration/bob/size
/reconfiguration/bob/str_param
/reconfiguration/debug
/reconfiguration/dude/bool_param
/reconfiguration/dude/double_param
/reconfiguration/dude/int_param
/reconfiguration/dude/size
/reconfiguration/dude/str_param
/reconfiguration/servers/bob/module
/reconfiguration/servers/dudette/module
/reconfiguration/servers/dudette/namespace
/reconfiguration/servers/dudette/overrides/bool_param
/reconfiguration/servers/dudette/overrides/double_param
/reconfiguration/servers/dudette/overrides/size
/reconfiguration/servers/dudette/overrides/str_param
/reconfiguration_client/name
Clients¶
Client programs that need to tune into the dynamic reconfigure servers simply
need to instantiate a dynamic reconfigure client, or more simply, a subscriber
listening to the dynamic reconfigure server’s private parameter_updates
topic.
Example - Python Client¶
from dynamic_reconfigure.client import Client
import functools
import rospy
import termcolor
def config_callback(config, namespace):
print("")
termcolor.cprint("Reconfiguration Client Callback", 'yellow', attrs=['bold'])
print("")
termcolor.cprint(" Reconfigure Client", "green")
print(" " + termcolor.colored("{0: <23}".format("Name"), 'cyan') + ": " + termcolor.colored("{0}".format("dudette"), 'yellow'))
print(" " + termcolor.colored("{0: <23}".format("Namespace"), 'cyan') + ": " + termcolor.colored("{0}".format(namespace), 'yellow'))
termcolor.cprint(" Parameters", "cyan")
for k, v in config.iteritems():
if k != "groups":
print(" " + termcolor.colored("{0: <21}".format(k), 'cyan') + ": " + termcolor.colored("{0}".format(v), 'yellow'))
print("")
Example - CPP Client¶
There is no official C++ client, but it’s easy to subscribe to the parameter_updates
topic and use the config_->__fromMessage__
method. Such a callback might look
similar to the following code:
typedef std::shared_ptr<feed_the_troll::DemoConfig> config_ptr DemoConfigPtr;
void configCB(dynamic_reconfigure::ConfigPtr config_update_msg)
{
DemoConfigPtr config = DemoConfigPtr(new feed_the_troll::DemoConfig());
config->__fromMessage__(*config_update_msg);
}