ROS2 Transition Strategy

In the near future we will begin transitioning packages to ROS2 and we are interested in getting feedback on possible strategies for managing ROS1 and ROS2 leveraging GitHub. We recently had a discussed at the ROS-Industrial Developers meeting and two approaches were identified below, but if other exist please let us know. Also we are interested in getting the communities feedback on the Pros and Cons of both approaches to help determine which is the best strategy moving forward.

Option 1 (Multiple Repositories)

  • Repository 1 (Core Libraries) - This would contain libraries that are ROS agnostic which would be leverage by the other repositories. (This may not always exist, depending on the nature of the repository.)
  • Repository 2 (ROS1) - This would contain ROS1 wrappers around the Core Libraries.
  • Repository 3 (ROS2) - This would contain ROS2 wrappers around the Core Libraries.

Option 2 (Single Repositories leveraging multiple branches)

  • Branch 1 (ROS1 and Core Libraries) - This would contain both core libraries and ROS1 wrappers.
  • Branch 2 (ROS2 and Core Libraries) - This would contain both core libraries and ROS2 wrappers.
  • Note: If improvements were made to core libraries they would be cherry-picked to the other branch.
3 Likes

I think thereā€™s a 3rd option:
Repo 1 - Core Libs (same as in option 1)
Repo 2 -

  • Branch 1 (eg: melodic_devel) - ROS wrappers
  • Branch 2 (eg: dashing_devel) - ROS2 wrappers

This option gives you the benefits of Option 1 without as many repos. It assumes the repo maintainers are the same for both ROS and ROS2 wrappers.

I am strongly in favour of Option 1. This is simply because independent functionality should always be separated into a dedicated library. There might be users of your library that want to use it with yet another communication middleware. IMHO, it is also easier to maintain a library interface than cherry-picking between branches and resolving merge conflicts.

I recommend avoiding using branches in a repository to manage completely separate pieces of code (ROS1 wrappers and ROS2 wrappers). Itā€™s not what branches were for and it makes it really easy to make mistakes. I think that option 1 is your best bet.

1 Like

While I like the separation of concerns with Option 1, why are there multiple reposities needed?
Isnā€™t it possible to have core, ROS1-wrappers and ROS2-wrappers in different packages within the same repository?

If set up well, the overhead of selecting which wrapper to compile should be less than having to deal with 3 repositories for 1 ROS node.

It does, but seeing as there is most likely a very small intersection between the code for ROS 1 and ROS 2, what would be the benefit apart from reducing the nr of repositories? Chances for cherry-picking are most likely slim (not zero though).

One reason would be to not tie versioning (ie: releases and history) to that of the ROS wrappers.

As nice as ROS is, there are other frameworks/middlewares/software systems out there, and separating things out like this makes integration with those easier (perhaps not just technically, but also psychologically: Iā€™ve seen companies refuse to use particular pieces of software just because it was hosted ā€œsomewhereā€ that they associated with bad practices and NIH).

I think it depends on the amount of shared code between the ROS1 and ROS2 versions. If you already have a ROS-agnostic core library, with separate ROS1 and ROS2 wrappers, thereā€™s probably very little shared code between the three, so itā€™s best to put them into separate repositories (option 1). Note that this also means that the core library needs a rather stable API, which is also good practice, but needs some discipline and automated testing. If the API is not yet stable, and there are a lot of ā€œtandemā€ changes (i.e., a change in the library requires a simultaneous change in the wrapper and vice versa), they should go into the same branch of the same repo (option 2).

If the core library is not separated out, but instead mingled with the ROS code (which is the case in the majority of ROS packages), there will be lot of shared code between the ROS1 and ROS2 versions, so they should go into separate branches of the same repo for ease of cherry-picking (option 2). That has its own set of challenges though - branches tend to diverge over time because people forget to cherry-pick.

In short: option 1 is cleaner, so go for it if practical. If itā€™s not practical for the reasons outlined above, choose option 2. Also, whichever option you choose, you will have the least headaches if the ROS1 and ROS2 wrappers are fairly thin, so you almost never need to touch them, and almost all your development happens in the core library. It depends on the package if this is possible or not.

I originally went down this rout and had one issue. When cloning down a branch that contains a packages for core, ros1 and ros2, I could not find a way to get it to build using ros1 (catkin_tools) and then ros2(colcon). My original thought was, that I would add AMENT_IGNORE to ROS1 packages and CATKIN_IGNORE to ros2 packages and it should build but that was not the case. It appears that both ros1 and ros2 ignore both.

2 Likes

That is exactly the issue we encountered when trying to share a non-ROS library with both ROS1 and ROS2.
If anyone has hints about this route, we would be interested.

Variant on this question ā€“ the question/answers speak to the repository strategy, but what about the release tracks and package naming between ROS1/ROS2?

I currently have this repository naming scheme:

  • driver (core C++ sensor driver library, standalone and not ROS specific)
  • driver-ros (ROS bindings to the driver library)
  • driver-ros2 (ROS2 bindings, complete rewrite, to the driver library)

My packages are currently part of the ROS index but I havenā€™t yet submitted to the ROS2 index. For ROS, the packages are named:

  • driver_core (points to the core driver_library repository)
  • driver (points to the driver-ros repository)

For consistency in my driver_ros2 bindings, I would like to also name the ROS2 package ā€˜driverā€™ (but of course point to the driver-ros2 repository). Is this acceptable, or is overloading the package name between ROS and ROS2 a bad idea?

When we originally started doing ROS 2 ports of popular packages, we were forking them all into separate repositories. However, this has the downside of both making it harder to share fixes, and of splitting the development community between the two forks. So for most things, we have gone back to a single repository for the ROS 1 and ROS 2 drivers, with different branches targeting the different releases. If your ROS 2 code is in any way related to the ROS 1 code, Iā€™d suggest that model. If your ROS 2 code is a from-scratch rewrite, and shares no history with the ROS 1 code, then Iā€™d suggest what you currently have.

The name can be the same between ROS 1 and ROS 2 packages, thatā€™s no problem. There are a quite a few packages where that is the case, e.g. robot_state_publisher, velodyne_driver, depthimage_to_laserscan (just to name a few).

1 Like

Thanks @clalancette. Indeed my ROS2 code is a from-scratch rewrite ā€“ we took advantage of lifecycle nodes for device management, so itā€™s quite different from the ROS1 code (standard nodelet).

There is shared code but itā€™s inside the ā€˜driverā€™ module, which is a standalone c++ library that has plenty of usage on its own, outside of ROS ā€“ hence the separate repository/package.

1 Like

This is actually what I had in mind when I wrote my comment here: ROS2 Transition Strategy - #6 by gavanderhoorn.

So for most things, we have gone back to a single repository for the ROS 1 and ROS 2 drivers, with different branches targeting the different releases.

@clalancette, Iā€™ve come to the same conclusion, that a single repo is just less work. However, Iā€™m curious about version tags when it comes to the bloom release process. How are others managing versions of both ROS1 and ROS2 packages in the same repo?
Do you prefix the tag with something like ros1_3.2.1 and ros2_2.2.1?

Curious to hear what others in the community have been doing.

No, that doesnā€™t work with bloom since it only understands X.Y.Z

What weā€™ve generally done is to bump the major version number for ROS 2. So if your package was at 0.9.10 on the ROS 1 branch, then change to 1.0.0 on the ROS 2 branch. For packages that were at 1.2.0 on the ROS 1 branch, change to 2.0.0 on the ROS 2 branch. And so on.

This gives you the ability to release into both ROS 1 and ROS 2; for ROS 1 you are limited to bumping the minor and patch release numbers, but hopefully that is good enough.

1 Like

Itā€™s also unfortunate that with semver, anything 1.x+ will be assumed to be stable.

Yes, itā€™s not perfect. On the other hand, if you really want to keep the major at 0, you can always use a different minor for ROS 1 and ROS 2, and just use the patch version.

Thanks for your thoughts! much appreciated. I agree that the versioning is kinda bending the semver rules a bit, but from a practical perspective I donā€™t see any major issues.

In some of our ROS packages we are already up to version 4 or 5 for ROS1, so my plan is to start all ROS2 package versioning at 20.x.x. This way we leave room for ROS1 to continue using major versions if needed, and we have the added benefit of knowing which versions are ROS1 and which are ROS2 simply from looking at the major version.

Why not 2000.x.x?

Would follow the precedent set by the ROS 2 REPs: rep index

2 Likes

Good point! I had forgot about the REP numbering. Iā€™ll match that.