Becoming an "official" client library

Hi,

we had a discussion in the Rust WG meeting about what it means for a client library to become official and what it needs to comply with in order to achieve such status.

To start with, for us, to become an official client library it means that it’s being included in the default set of packages when someone installs ROS 2, it’s referenced in the documentation (e.g. examples are provided not only for C++ and Python, but also for this new library), etc.

Right now, only rclpy and rclcpp are referenced in the documentation, which are also maintained by the core ROS 2 team. Is that a requirement for any library? Can libraries be official, but not maintained by the core ROS 2 team? We see this in other projects (e.g. ros-planning has its own maintainer team, but it’s regarded as the official umbrella for motion and navigation planning).

However, client libraries can also be argued to be part of the ROS 2 core, so here’s where our doubts come from.

This comes from specific dicussion in the Rust WG as to how much rclrs (our Rust client library) needs to resemble rclcpp and rclpy for it to be considered as a candidate to be included in the stadard ROS 2 distribution. There are aspects of C++ and Python that either can’t be translated into Rust (e.g. no inheritance means no components in the way that rclcpp/rclpy do composition) or that some APIs can be better done differently (e.g. if we invert the relationship between nodes and executors we can take advantage of Rust’s ownership model).

Another example, we decided to use the ROS 2 message generation infrastructure for rclrs so that it’d be familiar, but perhaps that’s not even necessary for rclrs to become an official client library and we could leverage more from the Rust world instead of continuing with our mix of Python, C and Rust.

Anyway, with this discussion we’d like to formalize this criteria and perhaps lead to a REP that can be used as a reference for future client libraries.

6 Likes

I think the standard for an official client library – whatever that standard ends up being – will need to be higher than that of any particular package or set of packages. My intuition is that a client library being insufficiently maintained will have a much higher impact than a package that goes unmaintained. So I’m of the opinion that a client library should be considered part of the core.

That said, OSRF has a finite set of resources, and it may not be clear when/how to devote those resources to a client library. So I think it would be valuable for a REP to provide three tiers of client library official-ness:

  1. Official, meaning actively maintained by OSRF (rclpy and rclcpp)
  2. Endorsed, meaning it meets some minimum standard of active maintenance and someone from OSRF agrees it meets some client library standard, but they won’t guarantee that they’ll devote resources to it (ros2_rust)
  3. Unofficial.

I think the value of a slightly-more-formal endorsement process is that 1) it provides adopters more confidence in their ability to use ROS 2; 2) it requires substantially less resources from the OSRF; 3) it reduces the splitting of resources (for example, there are so many 3rd-party ROS 2 java clients 1 2 3 4 that are totally independent from each other, and a formal stamp of approval would hopefully reduce these parallel developments).

So I think

is a good idea.

I like the model of the C++ standard library and its implementation by different compilers. There is a formalized list of library features and compiler vendors implement them at their own speed. This page communicates what library feature is available for which compilers.

If ROS had a similar page for client libraries, having a column in such a table may stand as being an official library. Users can directly evaluate what they are up to in terms of language support.

P.S. The term client implies the existence of a server, probably meaning a reliance on rcl or rmw but I don’t think this is necessary for these libraries. I prefer the ROS standard library for C++/Python/Rust as it sounds more implementation-agnostic.

@esteve @cst0 thank you for leading Rust WG and all the effort for rclrs :+1:

although i am not sure about the procedure or criteria to be official, personally I am happy to support this rclrs rust client support for ROS 2 official.

Can libraries be official, but not maintained by the core ROS 2 team?

just a note, rclc could be in the similar situation?, which is already under ros2@github but in ros2.repos not maintained by ROS 2 core team. (CC: @JanStaschulat )

btw, do you happen to have the spread sheet that shows what API/features are supported or not? that would be really good information for this discussion?

Tomoya,

I’m actually not involved with rclrs at all, just interested in the conversation, so those kudo’s should all go to Esteve :smile:

@cst0 's response is far more eloquent than anything I would have come up with. I really like the idea of having a tier system for client libraries, much like we already do for operating system platforms and RMWs.

I think a new REP is most appropriate for this. We can discuss most of the details on the REP, but to seed the initial set of requirements for Endorsed/Official:

  1. It must be able to be released on the buildfarm (https://build.ros2.org), using bloom and rosdistro.
  2. It must support all of the target platforms for ROS 2 distributions (which generally includes the latest Ubuntu LTS, RHEL, and Windows).
  3. It must work with all of the official RMW libraries, which leads to…
  4. It is highly suggested that it uses the RCL/RMW layer from ROS 2 proper. I would say that this isn’t a hard requirement, but it is the most obvious way to satisfy 3.

To be “official”, I think it would also have to be tested nightly on https://ci.ros2.org, as well as be managed as part of ros2.repos . There are probably more requirements around becoming official, but we can work through those on the REP.

Oh, and in terms of how much the API needs to resemble rclcpp or rclpy; that one is really hard to say. For instance, we wouldn’t want to make an official or endorsed library that does away from pub/sub, services, actions, parameters, and so on. Similarly, we wouldn’t want to endorse one that renames all of these things to unfamiliar names. And we want to make sure that any client library that is “endorsed” supports all parts of the API.

But part of the reason for having the client libraries is so that we can do language-specific stuff in them, so I think some departure from the APIs in rclcpp and rclpy is totally fine.

For the message generation, I think we can endorse a client library that doesn’t use the default pipeline, but I also think that is going to be hard to keep up with. Besides supporting the current ROSIDL .msg, .srv, and .action files, the current pipeline also supports (a subset of) OMG IDL .idl files. But there are also proposals to support other IDL formats. So any client library that doesn’t use the default pipeline would have to implement all of this itself. Personally, it feels like it is a “highly suggested” to use the default pipeline, but that we can make exceptions there.

With the default message generation pipeline as it exists today, adding a new language client library to the build farm would entail that every message package on the build farm has to additionally generate bindings for the new language.

Does the build farm have the capacity for that, or would we need to update the default message generation pipeline to be on-demand before it can accommodate a new language?

My 2 cents, especially for unofficial client library writers…

For the Ada client library I had a somewhat frustrating experience trying to integrate with the existing pipeline, as it forces you to work entirely from sources, besides other CMake-related headaches.

To work around that, I currently generate on-demand only the message bindings explicitly requested by a package, when that package is compiled. This allows the Ada client library to reuse the base ROS 2 binary installation from repositories. The drawback is that this causes redundant generations, but they only affect the user of the unofficial language.

With the current approach I find a bit counter-intuitive that if, hypothetically, there were a few more official client libraries in the future, their bindings would be installed even for users not needing them. This imposes a distributed overhead on every user of ROS 2, which some may find controversial.

I did some steps to integrate the Ada client library into the build farm, but I couldn’t finish, and didn’t hit that issue yet. I will retry when time permits.

Is there a document somewhere that lists what API/features are desired and/or required of a client library? Otherwise, I might have to dig through RCL to try and document which features are currently missing.

To the best of my knowledge, we currently support:

  • Publishers and Subscribers
  • Clients and Services
  • Parameters (preliminary support)
  • Time source and clock API
  • Message generation
  • Loaned messages
  • Tunable QoS settings

Lifecycle nodes are in the works, as well as async support. I believe someone is also working on actions, though I might be mistaken. Timers aren’t quite in yet as well, though I’m not sure if they’re being worked on.

There is quite a bit of work left to do, but we were hoping for some guidance on what all we can take advantage of inside of Rust, and how far we can deviate (for lack of a better term) from the general “feel” of the other libraries as we work to get this up and running.

I think a good example of what I mean is to consider inheritance. ROS 2’s C++ client library, rclcpp, uses quite a lot of inheritance in its implementation. Rust, however, cannot do this - inheritance is explicitly not supported in the language. To get around this, we either have to write Rust code in a very unorthodox and error-prone way to try and mimic the structure of the other libraries, or jiggle how we do things slightly so that we can have users write ROS code without Rust’s compiler screaming at them, even at the cost of making users have to do some things a bit different from how the other libraries do so.

I would like to think that the contributors to the ROS 2 Rust client library have done an excellent job threading the needle so far, and have created something that works without an especially jarring shift for the end user. But now that we’re going to be tackling more advanced concepts and problems (such as async support), threading the needle becomes much more difficult. It may even become impossible. But, if we have a list of requirements that we must adhere to as a client library, while being allowed to write the underlying code in a way that is more natural to the language, our job becomes easier.

It’s not without cost, I will admit. If we stray too far, users expecting to write ROS 2 in a similar way to rclcpp or rclpy might be startled that things aren’t structured in a similar manner. However, I would argue that things already won’t be structured in a similar manner - rust is superficially similar to C++, but the process of solving problems with it is quite different. And, as client libraries for other languages potentially come into being, we will run into situations where trying to solve problems in the way that rclcpp did it may be even less feasable than it is for rust.

So, in short and in pseudo-programming terms, what I’m hoping for is basically a sort of “interface”. Something that we write the client library to follow so that it can communicate and work with ROS 2 in an efficient, safe manner, while making it so that users of the language provided by said client library don’t feel like they’re trying to fight either the language or ROS 2.

Hopefully what I said makes sense? @esteve, would you say this is a good summary?

I think that the idea of the client libraries is not to provide a direct 1:1 mapping of all concepts from one language to another, but rather to surface the “ROS features” in a way that makes the most sense for that language. This means that if it doesn’t make sense to implement something via inheritance in one language versus another, it should probably be done in a different way. The intent isn’t to force all languages to look like C/C++, but that is the implementation that we have currently.

To me, I think you would need (at a minimum) to have all of the ROS communication primitives (topic, services, actions, parameters) in place to be at least considered as a supported library. On top of that, things like lifecycle and command line parsing are probably critical so that alternative executables “act” like a ROS node, regardless of the language they are written in.

5 Likes

I think this is the number #1 feature. As long as the client library produces node executables that can talk to other ROS nodes (in any language) and support the parameter interface, it would qualify. The rest is about feature completeness and some deviations must be accepted. Good to see this is the general opinion in this thread.

This should not be ignored as well. We should not install a client library if we do not want to use it. This includes rclcpp as well. So it is better if client libraries for major languages aim for language purity and the use of native tooling first. This would be much more painless compared to building upon other languages.

For example, one case I think of now: The Rust client library would want to support zenoh middleware soon. Then probably the chain of dependencies would look like this: zenoh (Rust) → zenohc (C) → rmw_zenoh (C) → rcl(C) → rclrs (Rust).

But why not directly zenoh (Rust)rclrs (Rust)? Is it discussed? Just wondered.

1 Like

Due to how things are set up within ROS 2, we don’t really need to worry about this. We just need to be able to talk to rcl and we get access to whatever rmw layer lives below it for “free”. The Foreign Function Interface is a mild nuisance, but careful application of unsafe and a strict policy around said usage of unsafe has kept us out of trouble so far.

To be honest, we have had more difficulties with message generation than interfacing with RMW providers. So, as it is, no, we have not discussed bypassing rcl for direct zenoh support. Our focus has been trying to get to a rough parity with rclcpp and rclpy at present. And though we have made great improvements on that front, the most difficult parts lie ahead.

However, I am glad to see that the community seems to be supportive of giving languages a bit of room to go about solving issues in a way that best makes sense for the language.Though having some way of quantifying in writing what a client library needs would be very nice. @mjcarroll suggested support of topics, services, actions, parameters, lifecycle, and command-line parsing to be a good base-line for what a client library requires to BE a client library, and I would agree! Though I’m curious if the community would have other requirements added in as well? What is indispensable and what is merely nice to have?

2 Likes

I have one small addition to what has been said so far.

It is okay for me if the client library deviates from others on even some conceptual approaches. However, it is important for the things that remain similar to actually do the similar thing.

One bad example from ROS 1 is Time. In roscpp, it has fields sec, nsec. In rospy, it has secs, nsecs. This is exactly the wrong thing to do. Or, another example I only remember barely - changing the order of arguments. I think it was something about TF where source and target frames were swapped in c++ and python. Or, the classical problem - order of quaternion fields.

Incidentally, on the topic of expected features, I’ve found that not having a simple C interface to tf is a major PITA, as doing anything realistic without tf is a stretch, and binding to C++ is more involved than plain C. Thus, the Ada client library also provides a (currently very minimal) binding to tf, in a separate package.

1 Like

Congrats @esteve, @jhdcs and everyone else involved

Would it change if Zenoh did indeed prove itself and became a tier-1 RMW ?
(The pure rust to rust implementation may, provide the performance and ease of use, given it is actually achievable)
Or is it outside the scope and intention of this project ?

Would it change if Zenoh did indeed prove itself and became a tier-1 RMW ?

I think relying exclusively on Zenoh would disqualify rclrs from achieving “official” client library status. As Chris said earlier:

It must work with all of the official RMW libraries

Depending exclusively on Zenoh would undermine this requirement. A major design decision of ROS 2 was to be middleware agnostic, so the community can keep their software stable and compatible while the choice of underlying middleware may change over time or between different people’s deployments.

Theoretically if there was reason to believe that the RMW / RCL layers were adding undesirable overhead, it may be possible to use Rust “features” to switch the implementation between one that’s RCL-based and one that’s natively Zenoh-based, but I think that would be better to just do as a separate project… ROS-flavored Zenoh.

5 Likes