As a Company deeply using ROS for its robotics products, we are facing the problem of the “if”, the “when” and, above all, the “how” to switch to ROS2. We are then trying to understand what is the best path forward. Did some of you already think about it? Or did you already design and prepare the migration? Or are you in the process of migrating? Are you able to do it in an incremental way, without stopping the normal development? Do you want to share here your thought?
Our current idea
Our system is composed of several (100+) ROS nodes, developed in C++ or Python, deeply interconnected through ROS topics, services and actions.
We studied different possible paths to a ROS2 system, taking into account that, for obvious reasons, we cannot stop the development and reserve all our development resources for the migration to ROS2: this is particularly true because it is something that has little to no visible (short-term) effect on the product side… we actually expect a reduction of performance during the migration, due to bugs or unforeseen effects of the changes in the code as well as in the middleware.
We understood that the migration between ROS1 and ROS2 is not just a matter of changing the API, since there are pieces of software that work in completely (or subtly) different ways: actions (implemented as three topics in ROS1, and as two services and a topic in ROS2), dynamic parameter reconfiguration (unneeded in ROS2), nodelets (unneeded in ROS2), concurrency model (meaning how the asynchronous subscribing / publishing threads are structured in ROS1/ROS2 and in C++/Python), etc.
We currently designed two possible paths for our migration, both of them share the following principles.
- we will migrate node by node, to allow to more easily debug the possible issues; this requires to use the
ros1_bridge
that our initial tests show to be a potential bottleneck, that we can possibly alleviate by carefully choosing the order of the nodes to migrate (always enforcing to have as little as possible topics to be shared between ROS1 and ROS2… but it’s not completely clear if, how and to what extent this is possible); actions are still not implemented in the bridge (although we know there is active development for them) - the final goal is to have a working system with the whole codebase using purely ROS2 (without additional interfaces, ROS bridges, etc.), but while the deprecation of the ROS1 middleware (and thus of the
ros1_bridge
) on our robots is urgent, the use of additional interface APIs or other temporary tricks can be acceptable for a while
The two paths differs as described below:
- rospy2/roscpp2 path. We found a couple of interface libraries that allow us to keep our ROS1 code while using a ROS2 middleware: GitHub - dheera/rospy2 and GitHub - dheera/roscpp2. Pros: in theory, this allows us to re-use our current code-base as it is or with limited changes. Cons: these two libraries (in particular roscpp2) are at a very early stage of development and then we would require to extend/finish their development. If we follow this path, we would then need to drive these two libraries to a usable state and then start migrating our nodes to ROS2 (i.e., compile them in a ROS2 workspace, potentially modifying the package structure, and run them using a ROS2 launcher), using the bridge to allow them to communicate with the rest of the system.
- ROS1as2 path. We can follow the opposite policy, developing a facade (called ROS1as2 from now on) that allow us to slowly changing the API of our code while still using a ROS1 middleware and workspace. Pros: this allows for a slower migration of the code (that could be also a Con), since some parts can be left using the ROS1 API directly. Cons: we have to develop this facade from scratch.
In the following I will describe in detail the two paths, please take into account that the path is to be considered node-by-node, i.e., every single node will follow this path, and the system will slowly migrate node by node to ROS2, leveraging the ros1_bridge
during the months that the system is still in a hybrid ROS1/ROS2 state.
Decisions / actions to be taken beforehand
rospy2/roscpp2 | ROS1as2 |
---|---|
Install a ROS2 middleware on robots, that allows to run ROS2 nodes. The system should also contain ros1_bridge . |
The deployment of a ROS2 middleware can be delayed |
Carefully choose an order of nodes / subsystems that, at any steps, reduces the required topics/services/actions to be shared between ROS1 and ROS2 nodes. | The decision of the order of the nodes to migrate is neither urgent nor important, but it should not be postponed too much. |
Implement the action interfaces, implement all the missing features in roscpp2 , allow to include the library without the need to copy it in every package, etc. |
Implement the ROS1as2 facade from scratch, including the API for the management of the topics, services, actions. |
Decide how to deal with features that are different between ROS1 and ROS2 (1. Parameter server and node access to the global parameters, 2. Nodes with dynamic parameter configuration, 3. Nodes with nodelets). Changes related to these features must be migrated anyway, even if using rospy2 / roscpp2
|
Decide how to deal with features that are different between ROS1 and ROS2 (1. Parameter server and node access to the global parameters, 2. Nodes with dynamic parameter configuration, 3. Nodes with nodelets). Changes related to these features must be migrated anyway, even if using ROS1as2 |
For each node - part 1
rospy2/roscpp2 | ROS1as2 |
---|---|
Modify the workspace / building system (i.e. CMakeLIsts.txt , package.xml , etc.) so that it can use the ROS2 facilities (e.g., colcon / ament ) |
The workspace and the building system remain related to ROS1 |
Modify the code of the node in such a way that it imports rospy2 libraries or #includes roscpp2 headers. Deep changes are required for features that are not compatible between the two frameworks (e.g. nodelets, dynamic parameters, etc.) | Modify the code in such a way that it pretends to use the ROS2 API, while having ROS1 underneath. Deep changes are required for features that are not compatible between the two frameworks (e.g. nodelets, dynamic parameters, etc.) but they can be postponed, since the node still uses ROS1 underneath. |
Start running the node in a ROS2 environment, connecting it to other nodes either by the ROS2 middleware (to nodes already migrated) or to ROS1 nodes through the ros1_bridge
|
Still run the node in a ROS1 environment, connecting it to other nodes by the ROS1 middleware |
In case of unexpected behavior, correct/debug the code (either by correcting the rospy2/roscpp2 interface, or by rewriting the code using the ROS2 API directly, if that is not possible; bugs in this case are more probable since the code is just pretending to be using the ROS2 API) |
Actions required to start the part 2 below, but only for the ROS1as2 path
rospy2/roscpp2 | ROS1as2 |
---|---|
Install a ROS2 middleware on robots, that allows to run ROS2 nodes. The system should also contain ros1_bridge . |
For each node - part 2
rospy2/roscpp2 ROS1as2
rospy2/roscpp2 | ROS1as2 |
---|---|
Modify the code to use ROS2 API. If the node works as expected in a ROS2 environment, this step can be postponed. |
Modify the workspace / building system (i.e. CMakeLists.txt , package.xml , etc.) so that it can use the ROS2 facilities (e.g. colcon / ament ). |
Correct / improve the code if it was not completely using the ROS2 API. Change the parts of the code that relates to features that are not compatible between the two frameworks (e.g. nodelets, dynamic parameters, etc.) |
|
Start running the node in a ROS2 environment, connecting it to other nodes either by the ROS2 middleware (to nodes already migrated) or to ROS1 nodes through the ros1_bridge
|
|
Check for bugs / unexpected behaviors due to the use of ROS2 underneath (since it is using directly the ROS2 API, this is less likely to happen wrt the other path) |