"Official" Java Client Library

We are planning to use some Java nodes in a ROS2 project. When we searched for Java and ROS2 we encountered several repositories that allow to start ROS2 nodes in Java (e.g. https://github.com/ihmcrobotics/ihmc-java-ros2-communication and https://github.com/ros2-java).

Is ROS2-Java the “official” (or preferred) Java client library for ROS2?

3 Likes

I’ve been working on https://github.com/ros2-java. The dashing branch should be compatible with ROS 2 Dashing and Eloquent. I wouldn’t say the branch is stable yet as we don’t have a release, but I’m working on it, along with items listed here (contributions welcome!)

I wasn’t aware of https://github.com/ihmcrobotics/ihmc-pub-sub-group. At a glance it sounds like it requires you to use this specific communication layer, and so probably not as general as https://github.com/ros2-java. It might make sense to try and consolidate efforts between the two projects, I’m not sure.

3 Likes

Hi Jacob,

I am trying to create an example project for ros2-java. I followed the steps in the git README, but the step ament build --symlink-install --isolated keeps failing.

I tried to run it with a fresh installation of ROS2 Dashing on Ubuntu 18.04. A copy of my console output can be found here: https://pastebin.com/HRufnJrV. What version of ROS2 do you need to set ros2-java up? Can you provide me information on how to get it working?

Update:
I see that dashing is not yet supported (https://github.com/ros2-java/ros2_java/issues/92).
What version of ros do you suggest?

Update 2
I found the ros2-java-branch for ros2 dashing here. The building goes fine, until it starts building rcljava_examples. I get the following error:
> Task :compileJava FAILED /home/niels/opentcs/integration/ros2-java/ros2_java_ws/build_isolated/rcljava_examples/src/main/java/org/ros2/rcljava/examples/service/AddTwoIntsService.java:26: error: package example_interfaces.srv does not exist final example_interfaces.srv.AddTwoInts_Request request
See full log here.

It looks like there are files (example_interfaces) missing. Due to this error, it’s also not possible to run the examples. Can you provide me instructions on how to fix this problem?

@jacob I asked your remark “It might make sense to try and consolidate efforts between the two projects, I’m not sure.” in the ihcm-java-ros2-communication repo and their response is that the goals of the projects are different. Could you have a look at their points and share your vision there?

Reading the comments by Jesper it seems that his ideal:

a pure Java implementation of ROS2 below a pure Java implementation of the communication, avoiding any native code

is something which in ROS 1 led to almost all client libraries except the two main ones to never reach any form of feature parity or consistency in behaviour. They typically only implemented the bare minimum to be able to communicate with other ROS nodes, lacking many of the convenience features.

Even now there are things which are different between roscpp and rospy (although the situation has improved over the years), and those have the largest possible community around them.

The idea behind making all client libraries wrappers around rclc is exactly to avoid this.

I’m not sure this is being achieved right now, but feature parity and behavioural consistency between libraries would then depend on the wrappers getting updated to support new functionality in rclc, instead of maintainers having to implement new infrastructure completely from scratch. It’s probably too early to tell whether this is a lower-cost task, but that’s at least the idea.

Related page: About ROS 2 client libraries: Common functionality: the RCL.

2 Likes

Let’s continue the discussion here.

While my ideal would be a pure native implementation, this is a lot of work. I agree that wrapper rclc would be the neatest solution from our side.

However, ideally that means we can compile rclc as a stand-alone library that we can statically link and include in our project. I think messages should still be compiled to native java code and a day-to-day developer should not have to re-compile the messages for all platforms the code runs on to distribute.

We actually started on using RTPS before ROS2 came out and all the IHMC ROS2 packages do is provide a small wrapper around the RTPS libraries that set the correct topic names and data types as well as provide the ability to compile .msg to .idl (which can then be easily converted to .msg). When I worked on this there was little to no documentation on the new ROS2 libraries, so my assumptions might be a little outdated.

I’m a maintainer of IHMC Robotics’ ROS 2 support and just wanted to add a few things.

Our biggest requirement is allocation free communication in order for our realtime robot controller to accept commands and publish configuration status. We run a normal JDK on top of a patched Linux kernel allowing us to create realtime threads in Java. If our Java software allocates memory over time, it will trigger a garbage collect, causing missed realtime deadlines.

Our approach to supporting ROS 2 was as a bonus feature. We are basically using SWIG on FastRTPS with some modifications to ensure zero Java allocations. We periodically upgrade FastRTPS to a version matching a ROS 2 release, but it has been rarely necessary. The only incompatibility between releases so far has been between Ardent and Bouncy when they changed how the topics were partitioned. That change didn’t even require upgrading FastRTPS. I’m only saying this as an argument that it might not require that much maintenance for any custom solution.

Also, we aren’t using very many ROS 2 features. We hardly even use any of the standard messages, but we would like to change that. We are using basic publish and subscribe with some FastRTPS performance settings. Our intraprocess mode is currently 100% Java. We don’t support ROS services or parameters, for example.

Our message type support is currently a forked and modified version of the official python msg -> IDL generator (.em template), which we then convert to a Java type using ANTLR. We add several convenience features to the Java types like deep copy, Javadoc sourced from the .msg file, and some others. This is actually the nicest part of our library. We do lack .msg dependency management because we aren’t in a ROS 2 workspace, so we have a hacked together system for that.

I am still somewhat unfamiliar with how ros2-java works. If someone could explain the ideas and tech behind it, that would be enormously helpful.

Java is a very difficult step for ROS to support because its build system is completely different. We have a similar setup to a ROS workspace for Java achieved with a complicated Gradle plugin. We too have the ability to clone several repositories to a folder which automatically get assembled into a single build. When dependencies aren’t present via source code, they are resolved via Maven.

I am a big fan of ROS in general. They have solved a lot of the same problems as us. I would be extremely excited to see the integration of these two projects.

1 Like

@niels In order to build the dashing branch, you need to ensure you are also building against ROS 2 Dashing or greater. I think after sourcing a Dashing or Eloquent installation you should be able to build packages in this repos file. Before I merge the work I’m doing for dashing into the main line, I’ll update the instructions in the README.

@calvertdw Thanks for the added info!

As @gavanderhoorn pointed out, the idea is to build rcljava on top of common ROS 2 functionality (the same as rclcpp and rclpy). This way features like parameters and namespaces are consistent across programming languages. The communication layer (rmw) is also part of this common functionality. Here is an overview of the ROS 2 architecture.

Specifically, rcljava works by building on top of the core ROS 2 libraries, rcl (ROS 2 C libraries) and rosidl (ROS 2 code generation libraries). rcljava links against rcl and uses the JNI to interact with it. rosidl_generator_java uses tools provided by rosidl (written in CMake and Python) to generate JNI libraries and Java code for ROS 2 interfaces (messages, services, and actions). Under the hood, the generated code references C messages (provided by rosidl_generator_c) so that we can pass these objects to the communication layers. This is similar to what is done in rclpy.

Ultimately, I believe the goal is to have a ROS 2 Java client library that can be integrated easily into an existing ROS 2 system, where you might also have ROS code written in other languages.

Luckily, there is colcon support for Gradle: GitHub - colcon/colcon-gradle: An extension for colcon-core to support Gradle projects
We also have a Gradle plugin for assisting with building and installing artifacts to a desired location: GitHub - ros2-java/ament_gradle_plugin: A Gradle plugin for building Java and Android-based ROS2 projects
Currently, I think it requires this additional colcon package to work properly: GitHub - colcon/colcon-ros-gradle

@esteve is the original author of rcljava, and I believe the main focus was to support ROS 2 on Android devices (Ardent and Bouncy). I’ve recently picked up development with an interest in desktop applications, updating it to work with Dashing and Eloquent (and beyond); although I want to maintain Android functionality.

At a high-level, I think the two projects differ in that you are focused on realtime capabilities. I’m not sure at the moment how we could best combine efforts; I’d have to take some time to get a better understanding of your project.

@jacob Thank you for the clear explanation! That really helps me understand.

I think IHMC is fully on board with embracing rcljava with really only one major modification. Since our team is not currently developing in ROS workspaces, it would be an added layer of maintenance for our developers so we’d like to avoid requiring them to do so for now. Another issue is the sensitivity to the host OS. ROS 2 is not supported on any other Linux distro than Ubuntu 18.04. Currently we have a variety of machines running our ROS 2 Java library including Ubuntu 14.04, 16.04, 19.04, Arch Linux, and maybe a few more. Windows and Mac seem like they work fine either way.

For the above reasons, I think we are most interested in using rcljava as a Maven artifact with included static libraries. If you guys could help us learn how to assemble such rcljava .so, .dylib, and .dll libraries then we could manage the rest. As an example, here is how we do this now. If you view the contents of that JAR, you see that everything needed to run the native code is included for all OSs. This has worked very well for us and many other Java OSS projects.

@jespersmith seems to think it would be easy to hit our zero allocation requirements by modifying the JNI layer. So our proposed solution would be to lightly modify the JNI after it’s been generated, assemble static libraries, and publish to Maven. We would probably continue to use our message generator for now.

@niels I lied. Looks like that we are indeed missing example_interfaces from the repos file. I’ve opened a PR to fix this: Add example_interfaces to desktop repos file by jacobperron · Pull Request #101 · ros2-java/ros2_java · GitHub

I would love to switch to rcljava, but I think a major concern from us is how to distribute the resulting application(s). ROS2 support is a small part of out application, however it is hard to use ROS2 without making your application compile and start using the ROS2 build/launch systems.

I think to have Java developers adopt ROS2, it should play well with the Java build systems (Gradle/Maven) and it should be distributed as a single dependency line in your gradle file. (api: 'org.ros:rcljava:1.0.0) and start without a ROS2 workspace.

As for serializing/deserializing I think that is best done in the target language, or sending raw data should at least be exposed from rclc. The serialization format CDR is pretty trivial to implement and fast-rtps already includes Java code to generate the messages. On the other hand, native calls have an enormous overhead so having to drop down to native code for every field will be a major performance hit. Furthermore, string serialization needs some smarts to be allocation free and will not play well with JNI. Our current serialization libraries also allow serialization to YAML/JSON/XML which allows us to use the ROS idl format for all serialization needs.

To give some background I’ve tried to make a rcl for C# used with Unity a while ago. We work on Unity integration to give non-robot-developers/students an easy way to make our robot do certain tasks. However I ran in the following issues

  • Installing and using ROS2 under Windows is hard for non-developers (or at least was till Bouncy) and a lot of work for developers
  • You have to start your application/IDE from the ROS2 workspace (either a bat/sh file or terminal usage)
  • Does not play well with the packaging and application creation of Unity (or any other system).
  • Creating a new message requires rebooting in every OS and recompiling the native library and then publishing it.

After spending significant time on this, I created a trivial Websocket/JSON bridge (using our Java implementation) to talk to ROS2 which is much less work to support than even just installing/building ROS2.

I think that unfortunately, the current ROS2 ecosystem makes the easiest way to expose ROS2 in an existing application written in a different language than C/C++ (or a C/C++ application with a legacy build system/main) is to use a service like https://github.com/RobotWebTools/ros2-web-bridge.

@jacob Thank you for your response and your clear explanation, I managed to get it working now.

@jespersmith I’m involved with the development of java software that needs to communicate with ROS2 robots. I agree with you that it feels cumbersome to compile ros2-java with build systems that are not natively supported in most IDEs. However, there are some things I did to easy development.

I checked out and built the ros2-java workspace. In my own Java application, I load the built files of ros2-java using Gradle. I use the following script for that in build.gradle:

// ================== Set HERE your Ros2-Java workspace path. ==================
String ros2JavaPath = '/opt/ros2-java/ros2_java_ws/'
// =============================================================================

apply from: "${rootDir}/gradle/guice-project.gradle"

String dir_rcljava = ros2JavaPath + 'install_isolated/rcljava/share/rcljava/java/'
String dir_rcljava_common = ros2JavaPath + 'install_isolated/rcljava_common/share/rcljava_common/java/'
String dir_std_msgs = ros2JavaPath + 'install_isolated/std_msgs/share/std_msgs/java/'
String dir_geometry_msgs = ros2JavaPath + 'install_isolated/geometry_msgs/share/geometry_msgs/java/'
String dir_builtin_interfaces = ros2JavaPath + 'install_isolated/builtin_interfaces/share/builtin_interfaces/java/'

repositories {
    flatDir {
        dirs dir_rcljava, dir_rcljava_common
    }
}

task checkRos2JavaPath {
    if (!new File(ros2JavaPath + 'install_isolated/local_setup.sh').exists())
        throw new Exception("Ros2-Java not found OR not build. Specify the path in build.gradle.")
}

dependencies {
    // Include ROS2-Java dependencies
    checkRos2JavaPath
    compile fileTree(include: ['*.jar'], dir: dir_rcljava )
    compile fileTree(include: ['*.jar'], dir: dir_rcljava_common)
    compile fileTree(include: ['*.jar'], dir: dir_std_msgs)
    compile fileTree(include: ['*.jar'], dir: dir_geometry_msgs)
    compile fileTree(include: ['*.jar'], dir: dir_builtin_interfaces)
}

This is a simple script that automatically includes JAR files that are built by ros2-java. Using this way, you can run ros2-java examples in your own application without the need of building or running your program using ament built tools. The only condition is that you need to source ros-2java local setup in your environment (e.g. . ~/ros2_java_ws/install_isolated/local_setup.sh) before you execute your program.

I have no idea if this is the ‘right’ way of using ros2-java, so there might be better solutions out there.

@niels, Thank you for sharing the script.

While it makes it work for developers comfortable compiling C++ code on their computers, it will not solve the problem of distributing it to users. I’m especially thinking of Windows users (your script requires quite a bit of if statements to even make work on Windows probably). It also requires to compile all messages as C code before they can be used in Java.

We could have the build script include all of ROS’s .so files that rcljava depends on. However, it’ll be a CMakeList.txt from hell that probably breaks a lot. Also, we probably have to compile the whole of ROS2 on Linux to not depend on system libraries (tinyxml comes to mind) so not to depend on a specific Linux distribution.

I think the requirement from my side is a way to package rclc without the users having to pull in a ROS2 workspace and have the ability to serialize/de-serialize in pure Java.

@jespersmith I share your opinion. The script I posted is a typical example of fighting the symptoms instead of the cause. I only use it as work-around for now.

Indeed it would be way better to have a package that does not depend on a pre-existing ROS2 workspace that needs to be sourced.

Coming back to the main point of the discussion, I think that

  • rcl-java Supports most ROS2 functionality.
  • IHMC Java ROS 2 Communication supports only basic pub/sub for ROS2, however is much easier to include in your project. It also allows compiling .idl/.msg files straight from gradle without a ROS2 workspace.

A large portion of my workload is supporting/compiling libraries for easy internal use and external distribution. Adding functionality to IHMC Java ROS 2 Communication is less work than compiling native libraries that work on multiple computers.

For us (Halodi Robotics, I used to work with @calvertdw at IHMC) I think the solutions could be one of the following

  • The communication of ROS2 (services etc) is well documented and we implement it in Java on top of IHMC Pub Sub. This might result in a incomplete feature set, but is easy to distribute and maintain.
  • We can built a minimal rclc to be included in the rcl-java jar and distribute this. We use the existing Java serializer from IHMC to compile messages to Java, allowing all developers to easily compile their IDL files.
  • We built a windows installer for ROS2 that installs all requirements without any user intervention and depend on that. This would require a very smart library loader though that magically knows all library dependencies.

Thanks @jacob for the ping, and sorry for chiming in late, global pandemic and all…

I reached out to @calvertdw a couple of years (?) ago to see if both project could converge or share more code. I started rcljava in March in 2016, right after I had left Open Robotics and a year later or so IHMC released their ROS 2 layer. My memory is a bit fuzzy, so the timeline may not be entirely correct, @calvertdw correct me if I’m wrong.

Anyway, although both project may have similar goals, from what I learned from IHMC, the approaches are entirely different and it’s fine the projects are kept separate. From what I remember, IHMC has an entire Java stack for their robots and use DDS directly as the communication protocol, with the ROS 2 layer is a compatibility add-on. On the other hand, rcljava aims at adding Java as one of the supported languages for ROS 2, with the primary goal of it being used for ROS applications.

A pure native ROS 2 implementation is unlikely to happen, as @gavanderhoorn pointed out, ROS 1’s client libraries behavior was inconsistent (rosjava and roscpp for example share absolutely no code at all and their APIs are not 1:1)

The way rcljava works is that it generates both pure Java and C code for the messages, the latter is a glue between rcl and the Java messages. If at some point, ROS 2 ships with rcljava, the only C messages that will need to be compiled will be the ones that the user creates, the rest of the messages (e.g. std_msgs, sensor_msgs, etc.) will already be available as part of the ROS 2 distribution.

I recall pushing a branch that allowed users to plug their own memory allocation mechanism from Java, but I can’t seem to find it. Anyway, the idea is as follows:

  • The user may provide a Java function to allocate messages and any structures. For example, such function may borrow bytes from a statically allocated array initialized at the start of the application.

  • Any time malloc is called in the JNI code or a new object (e.g. a message) is created as is now, rcljava would use the user-provided function. This would involve changing the API slightly so that instead of calling new on the message class, the user would use a factory-like method.

However, that wouldn’t be enough for making rcljava suitable for realtime, since the executor blocks and it’d break any realtime guarantees. Plus it’d also depend on the underlying DDS implementation you may be using, FastRTPS is far from being realtime. Perhaps Cyclone DDS could work though.

Now that ROS 2 Foxy will include the WaitSet API (https://github.com/ros2/rclcpp/pull/1047) in rclcpp, the other client libraries can replicate it and provide an alternative to the executor-based approach for subscriptions. I’m planning to add such API to rcljava, which would pave the way for avoiding blocking calls.

This is precisely the problem that Apex.AI has been working on solving, making ROS 2 realtime, my colleague Christopher Ho gave an excellent talk about it at ROSCon 2018 (Video: ROSCon 2018 Madrid: ROS 2 on Autonomous Driving Vehicles on Vimeo Slides: https://roscon.ros.org/2018/presentations/ROSCon2018_ROS2onAutonomousDrivingVehicles.pdf).

Disclaimer: I work for Apex.AI until the end of the month. Also disclaimer: I’ll be available for hire/contract work starting May :slight_smile:

I gave a talk about how client libraries are implemented for ROS 2 at ROSCon 2018 (Video: ROSCon 2018 Madrid: ROS 2 for Android, iOS and Universal Windows Platform: a demonstration of ROS 2... on Vimeo Slides: https://roscon.ros.org/2018/presentations/ROSCon2018_ROS2%20for%20Android,%20iOS%20and%20Universal%20Windows%20Platform.pdf) The talk presents ros2-java, ros2-dotnet and ros2-objc as use cases and also delves into how to make ROS 2 portable for non-Unix/non-Windows platforms (e.g. Android, iOS and UWP)

You’d still a way for Java messages to interact with rcl (rclc is the C client library for ROS 2, whereas rcl is the library that all client libraries build on top). An alternative to having the C glue would be to use JNA in rcljava and get rid of the JNI generated code, however I don’t know how that would affect with realtime, as it uses System.loadLibrary anyway and probably allocates objects on the heap.

The only differences between desktop Java and Android in rcljava are in the JNI code, but it’s encapsulated in a way that does not affect the user code, and the build system, but again it’s also encapsulated as a Gradle plugin so there’s no difference to the user. As such, rcljava have always supported both desktop and Android, and will continue to do so, since there’s no much work involved in supporting one or the other.

2 Likes

Thank you for your comprehensive answer @esteve. Am I right that rcljava and ros2-java refer to the same project?

In our research group, @niels is working on a graduation assignment where we makes the connection between a fleet manager (OpenTCS) and ROS 2. The goal is to manage a group of mobile ground robots from OpenTCS. OpenTCS is written in Java and the robot specific code (the driver) is also written in Java. Currenly, Niels managed to have two-way communication between OpenTCS and the Navigation 2 stack in ROS 2, so that is going well.

We do have a question where would like to have your opinion. In Navigation 2, actions are used. However, these are currently not implemented in ros2-java. Moreover, it turned out that part of the assignment from Niels can be skipped, so he might have some time to pick up an additional task within his assignment. Implementing actions in ros2-java might be an interesting topic. We would like to know if you think this would be a realistic subtask within a computer science bachelor assignment.

Yes, sorry, both names are interchangeable.

That’d be an excellent contribution, thanks! You may not need to implement the entire actions stack, it seems that client-side would be enough for this particular usecase, though it’d be great if server-side is implemented as well.

I think it should be doable, but it depends on how familiar @niels is with the internals of ROS 2 and ros2-java. What’s the timeframe for this?

In any case, be sure to reach out to me at esteve.fernandez@gmail.com if there’s anything I can help with.

1 Like

@esteve it’s great to read that the implementation of rcljava actions may be a part of my bachelor thesis. I think it would be a valuable addition for rcljava. Also for me personally, it would be a good learning experience to contribute to an open-source software product such as ROS 2 as I did not do something similar before.

Back on topic: Next week I will contact you with more details and a plan, as I’m willing to participate. I’m not very familiar with ROS2 development, but I have ~7 weeks available I can spend on the development. I assume that would be enough. Right now I’m still busy with the development of an OpenTCS vehicle driver for ROS2. Functionally speaking, the driver is quite mature. Today I made a demonstration video which shows the two-way communication between ROS2 and the OpenTCS fleet manager (https://youtu.be/x_Bjo7l0uc4).

Once that’s finished I can fucus on the development of actions in rcljava.

2 Likes