ROS (Robot Operating System) is currently the most popular platform for robot development. On ROS, the de facto standard for Java development is a package called rosjava.
I have created an experimental alternative to rosjava that I call Jyroscope. Jyroscope makes it easier to write ROS code using Java.
I’ve been using it for a number of small projects but the code is very much experimental:
You can download the full source from Github
In the remainder of this article, I will demonstrate how to use Jyroscope.
I assume that you have a ROS master running at http://localhost:11311/
.
These three lines can be used to initialize Jyroscope:
Jyroscope.addMsgSearchPath(Paths.get("msgs").toAbsolutePath().toString());
Jyroscope jyroscope = new Jyroscope();
jyroscope.addRemoteMaster("ros", "http://localhost:11311", "localhost", "/jy");
This code might typically appear in an application’s main method.
Let’s break it down line by line:
The first line tells ROS where to find message definitions.
The github repository contains a copy of ROS message files.
However, you could simply use the path to your ROS installation folder instead.
A message such as std_msgs/String
is resolved to <search path>/std_msgs/msg/String.msg
.
The second line creates the Jyroscope context.
The third line adds a ROS master to the context:
"ros"
is a prefix use to refer to this ROS master (Jyroscope supports multi-master development)"http://localhost:11311"
is the ROS master"localhost"
is the local address. If the ROS master is running remotely, then you should use the IP address of the client. For example, the client may be "10.0.0.3" if the master is "http://10.0.0.1:11311".Subscribing is very easy.
The following code will subscribe to the topic named "/string"
and receive std_msgs/String
messages:
import com.jyroscope.annotations.*;
public class DemoSubscriber {
@Subscribe("ros:/string")
public void receive(StringMessage message) {
System.out.println(message.data);
}
}
Note that there are no interfaces to implement!
There are no callbacks to register!
Jyroscope simply uses the @Subscribe
annotations to configure subscribers!
The subscribing method may have any name. It just needs to accept a single argument corresponding to the message type.
This code uses the "ros:"
prefix.
This prefix refers to the first argument declared when Jyroscope was initialized (i.e., the third line from initialization earlier):
jyroscope.addRemoteMaster("ros", "http://localhost:11311", "localhost", "/jy");
In the subscription example, a StringMessage
class is used.
That class is user-defined.
Here’s one way to implement it:
import com.jyroscope.annotations.*;
@Message("std_msgs/String")
public class StringMessage {
public String data;
}
Note the use of a single @Message
annotation.
The annotation declares the ROS message type to use for the Java-to-ROS mapping.
Jyroscope supports JavaBeans-style properties as well as public fields. This code will have the same effect:
import com.jyroscope.annotations.*;
@Message("std_msgs/String")
public class StringMessage {
private String data;
public String getData() {
return data;
}
public void setData(String value) {
this.data = value;
}
}
At runtime, Jyroscope compiles an internal class that translates between the ROS binary message message format and Java objects. The class is compiled and loaded so that translation is run efficiently. Reflection only happens once!
Publishing is also achieved using annotations:
import com.jyroscope.*;
import com.jyroscope.annotations.*;
public class DemoPublisher {
@Publish("ros:/string")
public Publisher<StringMessage> publisher;
}
When the class is created, Jyroscope will automatically create a ROS publisher.
Jyroscope will “inject” publishers into fields annotated with @Publish
.
The publisher can then be used directly:
public void sendMessage() {
StringMessage message = new StringMessage();
message.data = "Hello, World!";
publisher.handle(message);
}
Because Jyroscope performs dependency injection, nodes cannot be instantiated directly.
Instead, nodes should be created by Jyroscope using jyroscope.create
:
// Set up Jyroscope (as before)
Jyroscope.addMsgSearchPath(Paths.get("msgs").toAbsolutePath().toString());
Jyroscope jyroscope = new Jyroscope();
jyroscope.addRemoteMaster("ros", "http://localhost:11311", "localhost", "/jy");
// Create a new node
jyroscope.create(DemoPublisher.class);
Nodes can be instantiated directly, with new
.
However, in such cases, it should be followed by jyroscope.inject
:
jyroscope.inject(new DemoPublisher());
Jyroscope incorporates a number of other features to make ROS development easy:
@Init
allows for custom node initialization after dependency injection (the no-arg method is called after the @Publish
and @Subscribe
annotations are processed).@Name
can be applied to fields and JavaBean properties to override the default Java-to-ROS name conversion (e.g., @Name("frame_id") public String frameId;
).@Hide
can be used to hide fields or properties from Java-to-ROS translation.@Repeat
can be used on a no-arg method to set up application loops. For example, a method annotated with @Repeat(interval = 2000)
will be invoked every two seconds.In addition to supporting multi-master development, Jyroscope includes an internal master. The internal master does not support the ROS protocol. It is pure Java and involves no network overhead so it is very fast. To add an internal master, use the following command:
jyroscope.addLocalMaster("local");
The first argument is the prefix.
To subscribe or publish messages on the local master, the prefix is used before a topic name: e.g., @Subscribe("local:/string")
.
Jyroscope is not a production-ready platform for ROS development. Instead, it is a technology demo. I intend it to be a suggestion for an alternative approach to ROS development.
I am currently using Jyroscope for simple integration projects between Windows and Ubuntu systems. This is because Jyroscope provides a cross-platform development environment with almost no configuration or installation.
Much work remains to make this production ready.
However, I’m not sure if there is any value in doing so.
I do fix bugs when I encounter them.
The code still contains many “//TODO
” notes.
I’ve released this code prematurely to help shape the future of rosjava and perhaps also gauge interest in Jyroscope itself. If there is genuine interest in Jyroscope, then I’ll continue working on it. I’ll clean up the code up more agressively and see what can be done to make it more robust and easier for others to contribute.
Your comments and ideas would be very welcome!
Published 12 December 2015 by Benjamin Johnston.