# How to set up a modular launch file to launch a variable number of robots and nodes?

Hi there!

Together with a colleague I'm trying to set up a system which will be able to launch a variable number of robots and robot types with their accompanying nodes.

We have several types of robots in Gazebo, e.g. the Clearpath Ridgeback or the Husarion ROSbot 2.0. Each robot with it's accompanying nodes will be launched inside it's own namespace: For Ridgeback this will be rdg##, for ROSbot this will be rbt##, where ## is replaced with the ID, such as 01 or 13.

An example robot ID's in a fleet could be: rgd01, rgd02, rdg05, rbt01, rbt11

But the list of robot ID's in the fleet can be kind of combinations really.

What we have working right now is a launch file structure which can launch a fixed number of Ridgeback robots (3) with the required nodes in their respective namespaces.

We've looked into using the parameter server and using rosparam inside launch files to look at a parameter containing a list of the desired robots ID's we which to launch, but hit a roadblock in trying to read from the parameter server inside a launch file (You can write server parameters, but not read server parameters?). Which made us doubt it this was the right approach.

Thus my question is; How should we approach setting up a system which is capable of launching a variable number or robots and robot types inside their own namespaces?

-Patrick

edit retag close merge delete

roslaunch does not support this directly. There is no loop construct. Perhaps recursion could be used, but I haven't tried whether it'll work.

You can write server parameters, but not read server parameters?

All parameters are stored on the parameter server before anything else is started (ie: nodes), so there are essentially two phases: store configuration on parameter server, then start nodes. There is no "read from parameter server" phase in between.

( 2020-06-02 05:15:49 -0600 )edit

Hmm ok, so the only variable input which can be given to a launch file is as an argument in the roslaunch command.

Say we get a param inside the launch file which contains the list [rdg01,rdg02,rbt05], then the next issue is, how do we iterate over this list, retrieve the namespace of each robot and set that as an arg for later usage in the namespace tag, especially since the number of items in the list varies. Any ideas?

You also mentioned recursion, which I assume would be including the launchfile itself with an include tag. This tag should imo be wrapped in a conditional which checks if the desired depth is reached to prevent infinite self launching. Which brings up the question of how to deal with counters in conditionals inside XML.. I'll look into this, but any knowledge shared is appreciated.

( 2020-06-02 05:31:12 -0600 )edit
1

I assume would be including the launchfile itself with an include tag.

Yes.

This tag should imo be wrapped in a conditional which checks if the desired depth is reached to prevent infinite self launching.

Of course.

Which brings up the question of how to deal with counters in conditionals inside XML

Not "XML", but in roslaunch XML. And roslaunch supports passing arguments to included .launch files, which in turn can use Python expressions in args. So it might be possible to pass a counter arg to an included .launch, have that .launch subtract 1, pass the arg on to another and instantiate whatever it wants with the value of the counter it was passed in its "layer" of recursion.

But again: I've not tested this, so there could be something which makes this "not work".

And we're of course really zooming in on implementation ...(more)

( 2020-06-02 06:12:27 -0600 )edit

I've managed to set up the basic recursion as a test. I must say it feels a bit finicky because I couldn't use < <= operators inside the eval. I had to resort to using:

<include file="$(find sfm_mpdm)/launch/recursion_test.launch" unless="$(eval arg('recursion_depth') == arg('robot_number'))">

Where the recursion_depth starts at 1 and is increment with each include. The robot_number is derived from the roslaunch argument (string):

roslaunch sfm_mpdm top_sfm_mpdm.launch robotlist_string:=_rdg01_rgd02_rdg03_rsb06

Using the following eval:

<arg name="robot_number" value="$(eval arg('robotlist_string').count('_'))" /> But it seems working with a test; As a test it sets up a range of variables, each in their own namespaces (rdg01, rdg02, etc..). Which are derived from the string robotlist_string. Shall I post the solution code as an answer (limited amount of characters here), allowing further improvements there using comments? ( 2020-06-02 08:24:38 -0600 )edit After some time: #q229489 is essentially a duplicate I believe. ( 2021-01-07 12:37:32 -0600 )edit ## 1 Answer Sort by » oldest newest most voted Thanks to gvdhoorn's comments, I've managed to set up some basic test which can launch a variable number of namespaces (and thus should also be capable of launching nodes in those namespaces) using recursion through including launch files. The follow code is the launch file which called 'top_sfm_mpdm_launch.launch' and is the launch file which is called in the terminal with roslaunch. <launch> <arg name="robotlist_string" default="_rdg01_rdg02_rdg03" doc="A string of robot namespace IDs prefixed with _'s. Default: '_rdg01_rdg02_rdg03'." /> <rosparam param="robotlist_string_num" subst_value="True">$(eval arg('robotlist_string').count("_"))</rosparam>

<include file="$(find sfm_mpdm)/launch/recursion_test.launch"> <!-- all vars that recursion_test.launch requires must be set --> <arg name="robot_number" value="$(eval arg('robotlist_string').count('_'))" />
<arg name="recursion_depth" value="1" />
<arg name="robotlist_string" value="$(arg robotlist_string)" /> </include> </launch> The included file 'recursion_test.launch' has the following code: <launch> <arg name="robot_number" /> <arg name="recursion_depth" /> <arg name="robotlist_string" /> <rosparam param="robot_number" subst_value="True">$(arg robot_number)</rosparam>
<rosparam param="recursion_depth" subst_value="True">$(arg recursion_depth)</rosparam> <arg name="offset" value="$(eval (arg('recursion_depth')-1) * 6)" />
<arg name="namespace" value="$(eval arg('robotlist_string')[ arg('offset')+1 : arg('offset')+6 ])" /> <group ns="$(arg namespace)">
<rosparam param="robot_namespace" subst_value="True">$(arg namespace)</rosparam> </group> <include file="$(find sfm_mpdm)/launch/recursion_test.launch" unless="$(eval arg('recursion_depth') == arg('robot_number'))"> <!-- all vars that recursion_test.launch requires must be set --> <arg name="robot_number" value="$(arg robot_number)" />
<arg name="recursion_depth" value="$(eval arg('recursion_depth')+1)" /> <arg name="robotlist_string" value="$(arg robotlist_string)" />
</include>

</launch>

The command used to launch the setup is: roslaunch sfm_mpdm top_sfm_mpdm.launch robotlist_string:=_rdg01_rgd02_rdg03_rsb06

Which produces the following parameters on the parameter server (parameter server was used for debugging and testing, you can of course launch nodes with this):

PARAMETERS
* /rdg01/robot_namespace: rdg01
* /rdg03/robot_namespace: rdg03
* /recursion_depth: 4
* /rgd02/robot_namespace: rgd02
* /robot_number: 4
* /robotlist_string_num: 4
* /rosdistro: melodic
* /rosversion: 1.14.5
* /rsb06/robot_namespace: rsb06

(1) Now as you might have noticed. I'm using a roslaunch argument (robotlist_string) with a string _id_id_id_id e.g. _rdg01_rsb01_rdg_05_rsb_02_rsb_12 which isn't ideal. I'd rather be able to use a list of id strings, but I couldn't get this to work with eval.

(2) Similarly, I couldn't get < or <= to work with eval. So the recursion depth checking is a bit too finicky for my liking and could quite easily end up in an infinite loop..

Any help on the above two mentioned issues (1 & 2) would be great! :)

more