Trolls & Feeders

Trolls & Feeders are a means to transfer and manage configuration between a continuously running subsystem and an application(s) that is free to start up and be torn down.

If you’re familiar with the way nodelet managers and nodelets work, then the principle here is relatively similar, except that it deals with configuration and not a runnable loop.

Feeders

Application side programs that don’t actually do anything except fire off the services to load/unload configuration at startup/shutdown. Feeders can fire off various kinds of configuration - ROSParam, Yaml (YAML > ROSParam which is useful for some situations) and latched topics (a continuous update mechanism). Right now, there is only support for the ROSParam style.

You shouldn’t need to write your own feeders - just create a launcher, point it at the feeder type you require and hook it up with a troll.

Trolls

Server side library that hooks up your program’s custom server callbacks (load/unload) to incoming requests from feeders.

The troll rescues you from the need to setup the underlying ROS mechanisms. You simply need to register your custom callback methods with it.

Workflow

The basic flow for a ros parameter server enabled feeder-troll system.

Feed the Troll

_images/feed_the_troll.png

Unload the Troll

_images/unload_the_troll.png

Examples - Parameter Feeder

Setting up a feeder merely requires writing a launcher and yaml file (no coding necessary). From the demo within this package:

<!-- Standalone feeder launcher, use with demo_params_troll.launch -->

<launch>
    <node pkg="feed_the_troll" type="param_feeder.py" name="$(anon feeder)">
        <rosparam ns="parameters" command="load" file="$(find feed_the_troll)/parameters/demo_params.yaml"/>
        <rosparam param="service_namespace">/troll</rosparam>
    </node>
</launch>
foo: bar
bar: foo
foobar: barfoo
complicated:
  dude: bob
  dudette: jane

Examples - Server

The server you will need to code yourself using the troll as a tool to quickly setup and integrate the infrastructure. The demo example within this package does not do anything except redirect the loaded/unloaded parameterisations to standard output, but anything is possible here.

import feed_the_troll
import rocon_console.console as console
import rospy

##############################################################################
# Classes
##############################################################################


class Server(object):
    def __init__(self):
        self.troll = feed_the_troll.trolls.ROSParameters(
            loading_handler=self.load,
            unloading_handler=self.unload
        )

    def load(self, unique_identifier, namespace):
        """
        :param uuid.UUID unique_identifier:
        :param str namespace: root namespace for configuration on the parameter server
        """
        print("Troll: loading [{0}][{1}]".format(unique_identifier, namespace))
        print("\n{0}".format(self.troll))
        parameters = rospy.get_param(namespace)
        print(console.green + "Loaded Parameters" + console.reset)
        for k, v in parameters.iteritems():
            print("  " + console.cyan + "{0}".format(k) + console.reset + ": " + console.yellow + "{0}".format(v) + console.reset)
        return (True, "Success")

    def unload(self, unique_identifier, namespace):
        """
        :param uuid.UUID unique_identifier:
        """
        print("Troll: unloading [{0}][{1}]".format(unique_identifier, namespace))
        parameters = rospy.get_param(namespace)
        print(console.green + "Unloaded Parameters" + console.reset)
        for k, v in parameters.iteritems():
            print("  " + console.cyan + "{0}".format(k) + console.reset + ": " + console.yellow + "{0}".format(v) + console.reset)
        return (True, "Success")

##############################################################################
# Main
##############################################################################

if __name__ == '__main__':

    rospy.init_node("troll")
    server = Server()
    rospy.spin()

And a launch file to kickstart it:

<!-- Standalone troll launcher, use with demo_params_feeder.launch -->
<launch>
    <node pkg="feed_the_troll" type="demo_param_server.py" name="troll"/>
</launch>