with our recent switch to ROS2, we want to make our setup ready for multi-robot operation. Unfortunately, there does not seem to be an established standard (or better yet, a REP) for this and I could only find little discussion on the topic.
Therefore, we would like the hear about everyone’s experiences and best-practices for a multi-robot setup in ROS2.
The main problem seems to be how to organize the tf tree and frame names. Currently, we are considering these three options:
Option A: tf/frame prefix
The robot_state_publisher has the option to prepend a frame_prefix (previously tf_prefix) which would make each frame name globally unique.
Pro:
Globally unique frame name
Contra:
Support was temporarily dropped during transition from melodic to noetic. Therefore, barely any node supports this anymore.
frame names do not match URDF link names (so nodes that use tf and URDF need to be aware of the prefix)
Option B: Multiple tf trees
Each robot has its own tf tree (/robot_name/tf). Nodes operate as usual in their own namespace. As far as I know, this is the option nav2 is using.
Additionally, tf transforms can be republished to a global tf topic with frame prefix.
Pro:
All nodes should work as usual in the robot-specific namespace
Contra:
Nodes that reason over multiple robots (global detection fusion, inter-robot collision avoidance …) need special handling
Sensor messages do not have a globally-unique frame id
Option C: Prefix URDF links
A robot-specific prefix can be inserted directly in the URDF, making the robot’s frame names globally unique.
Pro:
Should be supported by all nodes
Globally unique frame_id in sensor messages
TF frame names match URDF link names
Contra:
The robot name needs to be passed to pretty much every launch file as an argument
The MoveIt SRDF file needs to be a xacro to prepend the frame prefix to link names
What do you think? Does any one of you have experience with any of these options or do you even use an entirely different approach? I would be grateful for any experiences you could share.
Very valid points @Martin-Oehler. Personally, I’ve been using option A and C. A benefit of option A is that many configuration files can be re-used. If however, you combine multiple same robots into a single URDF (multiple xacro macros, e.g. humanoid robot with 2 arms and 2 legs), you will have to use option C. Although I prefer option A, I had to revert to option C. From this point of view (and to my best understanding) you should ask yourself whether you will have to combine robots into a single URDF.
There is a question of favoring local reasoning or global reasoning.
Then the question of how to represent a scene with several robots: are URDFs really suited for that?
You could also weigh in the real costs of the cons you’ve listed to help you choose. Like, is the handling of multi-robot algorithms really easier with Option C?
But my gut says:
favor local-first reasoning - in the real-world robots get disconnected, so multi-robot orchestration is not to be relied upon all the time
Thanks for your input. Just to avoid some confusion here: Option C does not mean to put all robots in the same URDF, but simply to insert the frame prefix in the URDF of each robot so you do not have to rely on the not-so-well supported frame prefix feature. The advantage would be that tf frame names match URDF link names.
What we used in the end on ROS 1 was a translation mechanism when the robots communicated over Nimbro network (a multimaster solution).
Each robot had its own standard /tf topic and a translation node was listening on it. We had a whitelist of frames that should be shared to other robots, so the translation node just waited for these frames, prefixed them with robot name and published them to a separate /tf_something topic that got transmitted over the Nimbro network to other robots.
This way, each robot by default only reasons about its own TFs and is not confused by the others. However, each node knows that if it additionally subscribes to /tf_something, it will get the (filtered and prefixed) TFs of all other robots.
I still think an approach like this can be the truly proper one even for ROS 2. Just instead of Nimbro network, you’d use the standard multirobot comms (be it whatever you use).
So each robot has its own /tf, and then all of them share a common /tf_something where they published selected and prefixed transforms that should be available to everyone.
go for Option B. Reasoning is simple: you don’t have to change any of your underlying code, files etc. but only run all code with namespace.
You don’t have to worry about urdf/sdf parameter parsing and sh*t like this. Write code once for one robot, put a namespace in front and run 100 times if needed. Also works fine for Gazebo, btw.
If you need interconnection of the tf-trees, you can write a tf-pub/sub that connects /myrobot_01/tf with /myrobot_02/tf with virtual frames and zero translation/rotation to the “original”. You can even have two “understandings” of the two robots what the tfs are, i.e. e.g. where robot 1 thinks robot 2 currently is and vice versa, without having to rely on a god-given truth.
The only “hurdle” is that you need to remap all my /tf and /tf_static-topics in you launch files to be relative:
You could use a known announcement topic or heuristics like every robot has a /name/description topic.
Even if you know the current robots, hard coding them would be bad design, so you should do something like that either way.
Thank you everyone for your valuable feedback. We decided to go for option B. It is the easiest to realize and most people seem to have used this option in the past. It is not perfect, but unfortunately, none of the options really are. It would be a good idea to have a REP for this going forward.