0
$\begingroup$

Rosanswers logo

I'm trying to set up a simple publisher using rosjava (pure java, I don't need Android). I've been following some tutorials and making lots of progress, but now I'm stuck. Here is the code I have so far:

import org.ros.concurrent.CancellableLoop;
import org.ros.namespace.GraphName;
import org.ros.node.AbstractNodeMain;
import org.ros.node.ConnectedNode;
import org.ros.node.NodeMain;
import org.ros.node.topic.Publisher;

import org.ros.node.NodeConfiguration;
import org.ros.node.Node;
import org.ros.internal.node.DefaultNode;

import org.ros.node.DefaultNodeFactory;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Executors;

public class Publisher_Test {
  
  private DefaultNode node;
  //private Publisher<std_msgs.String> publisher;

  private final ScheduledExecutorService scheduler =
           Executors.newScheduledThreadPool(1);

  public Publisher_Test() {
    NodeConfiguration node_cfg = NodeConfiguration.newPrivate();
    DefaultNodeFactory factory = new DefaultNodeFactory( scheduler );
    node = (DefaultNode)factory.newNode( node_cfg );

    System.out.println("In Constructor");
    final Publisher<std_msgs.String> publisher = node.newPublisher("chatter", std_msgs.String._TYPE);
    node.executeCancellableLoop( new CancellableLoop() {

            private int seq;
            
            @Override
        protected void setup() {
            seq=0;
            System.out.println("Setting up loop");
        }

            @Override
        protected void loop() throws InterruptedException {

            System.out.println("Looping");
            try {
              while (true) {
                    org.ros.message.std_msgs.String();
            std_msgs.String str = publisher.newMessage();
            str.setData( "Hello world! " + seq++ );
            publisher.publish(str);
            Thread.sleep(1000);
              }
            } catch (Exception e) {
              e.printStackTrace();
            }
        }
    });
  }
}

I've been basing this off of this question and this tutorial. Some of the syntax used is out of data, so I've been looking through the source code here to get things to compile.

I'm using ROS Hydro on an Ubuntu 13.04 machine. My goal is to get a publisher and subscriber implemented in pure java running in jython. I've compiled my class using javac, and can import it into jython fine, I get an error when I try to set up my node in the constructor for Publisher_Test:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    at org.ros.concurrent.ListenerGroup.addAll(ListenerGroup.java:85)
    at org.ros.concurrent.ListenerGroup.addAll(ListenerGroup.java:101)
    at org.ros.internal.node.DefaultNode.<init>(DefaultNode.java:132)
    at org.ros.node.DefaultNodeFactory.newNode(DefaultNodeFactory.java:41)
    at org.ros.node.DefaultNodeFactory.newNode(DefaultNodeFactory.java:46)
    at Publisher_Test.<init>(Publisher_Test.java:31)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at org.python.core.PyReflectedConstructor.constructProxy(PyReflectedConstructor.java:210)

java.lang.NullPointerException: java.lang.NullPointerException

From what I understand so far, it looks like DefaultNodeFactory is setting up the DefaultNode with the Collection to be null (which looking at the source code seems to be a valid thing to do), and then the addAll() method which is called in the DefaultNode constructor throws this exception when it tries to iterate on null.

I'm thinking I need to give a Listener to the DefaultNodeFactory, but I am unsure how I would go about doing this. What is a Listener and what is it supposed to do/represent? Is there a way to create a default one to use?

I am new to java, so any help would be very appreciated

Thanks!

EDIT 1

I've tried the to get the rosjava_tutorial_pubsub working, by following the package creation steps here. I'm able to build it using ./gradlew install (installApp doesn't exist), but I can't seem to figure out how to run it (there is no .build directory created, which the tutorial uses)

Ideally I would like to not have to create any packages or workspaces or use gradle at all, as I find it much easier to work that way and integrate the code into other projects. I know for rospy you can just do something simple like this:

# my_program.py
import rospy
from std_msgs.msg import Int32

def int_callback( data ):
  print( 'I saw the number %' % data.data )

def main():
  rospy.init_node( 'name', anonymous=True )
  sub = rospy.Subscriber( 'root/my_topic', Int32, int_callback )
  pub = rospy.Publisher( 'root/my_other_topic', Int32 )
  msg = Int32()
  i = 0
  while not rospy.is_shutdown():
    msg.data = i
    i += 1
    pub.publish( msg )

and this will be able to work no matter what directory you place it in, by just running python my_program.py and doesn't need to be part of a workspace or package. What I'm really looking for is some equivalent of this for java. I want to compile a class that handles publishing and subscribing, and then just import that class into another program and use it there.

Does this make sense? Let me know if I am crazy and doing something dumb. I think the java code above is almost there, except for the part about the Listeners.

EDIT 2

I've made it a little further now, everything compiles, but when I run it, I don't see anything happen. I have roscore running in another terminal, and when I do a rostopic list all I get is /rosout and /rosout_agg.

The changes I made to get it to compile are as follows:

  1. made the class extend AbstractNodeMain
  2. added a getDefaultNodeName() function to the class
  3. set the default node name of the NodeConfiguration to "publisher_test"
  4. added the class itself as a listener to the new node that is created

Here is the relevant bits of the new code. I see the "In Constructor" print show up, but not any of the other ones

// Imports go here
public class Publisher_Test extends AbstractNodeMain {


  private DefaultNode node;

  private final ScheduledExecutorService scheduler =
           Executors.newScheduledThreadPool(1);

  public Publisher_Test() {
    NodeConfiguration node_cfg = NodeConfiguration.newPrivate();
    node_cfg.setDefaultNodeName( "publisher_test" );
    DefaultNodeFactory factory = new DefaultNodeFactory( scheduler );
    //node = (DefaultNode)factory.newNode( node_cfg );
    List<NodeListener> listener = new ArrayList<NodeListener>();
    listener.add( this );
    node = (DefaultNode)factory.newNode( node_cfg, listener );

    final Publisher<std_msgs.String> publisher = node.newPublisher("chatter", std_msgs.String._TYPE);
    System.out.println("In Constructor");
    node.executeCancellableLoop( new CancellableLoop() {

            private int seq;
            
            @Override
        protected void setup() {
            seq=0;
            System.out.println("Setting up loop");
        }

            @Override
        protected void loop() throws InterruptedException {

            System.out.println("Looping");
            try {
              while (true) {
            std_msgs.String str = publisher.newMessage();
            str.setData( "Hello world! " + seq++ );
            publisher.publish(str);
            Thread.sleep(1000);
              }
            } catch (Exception e) {
              e.printStackTrace();
            }
        }
    });
  }

  @Override
  public GraphName getDefaultNodeName() {
    return GraphName.of("publisher_test");
  }
}

My guess is after the constructor is finished, this CancellableLoop thing just gets destroyed. I've tried inside an onStart() method, but that didn't work either. I've also tried putting the loop directly into the contructor (in which case it just stays stuck in the loop, and nothing seems to get published). The last thing I tried is to put the loop in a separate method, to let the constructor finish first, and then I call it. Still no luck :(

Any idea what might be going on?


Originally posted by Albatross on ROS Answers with karma: 157 on 2013-11-14

Post score: 1

$\endgroup$

1 Answer 1

0
$\begingroup$

Rosanswers logo

After many hours of trying various things, I've finally managed to get something that works! The key was writing my own version of RosRun for my application, and then creating an instance of the custom RosRun in the java application that I want to use ROS from.

The code looks something like this:

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

import org.ros.exception.RosRuntimeException;
import org.ros.internal.loader.CommandLineLoader;
import org.ros.node.DefaultNodeMainExecutor;
import org.ros.node.NodeConfiguration;
import org.ros.node.NodeMain;
import org.ros.node.NodeMainExecutor;

// This class will run a publisher and subscriber, and relay data between them.

public class Run_PubSub {

  private Talker pubNodeMain;
  private Listener subNodeMain;
        
  public Run_PubSub() {
    // Set up the executor for both of the nodes
    NodeMainExecutor nodeMainExecutor = DefaultNodeMainExecutor.newDefault();
    
    // Load the publisher
    String[] pubArgv = { "Talker" };
    CommandLineLoader pubLoader = new CommandLineLoader(Lists.newArrayList(pubArgv));
    String nodeClassName = pubLoader.getNodeClassName();
    System.out.println("Loading node class: " + pubLoader.getNodeClassName());
    NodeConfiguration pubNodeConfiguration = pubLoader.build();

    try {
      pubNodeMain = (Talker)pubLoader.loadClass(nodeClassName);
    } catch (ClassNotFoundException e) {
      throw new RosRuntimeException("Unable to locate node: " + nodeClassName, e);
    } catch (InstantiationException e) {
      throw new RosRuntimeException("Unable to instantiate node: " + nodeClassName, e);
    } catch (IllegalAccessException e) {
      throw new RosRuntimeException("Unable to instantiate node: " + nodeClassName, e);
    }

    Preconditions.checkState(pubNodeMain != null);
    nodeMainExecutor.execute(pubNodeMain, pubNodeConfiguration);

    // Load the subscriber
    String[] subArgv = { "Listener" };
    CommandLineLoader subLoader = new CommandLineLoader(Lists.newArrayList(subArgv));
    nodeClassName = subLoader.getNodeClassName();
    System.out.println("Loading node class: " + subLoader.getNodeClassName());
    NodeConfiguration subNodeConfiguration = subLoader.build();

    try {
      subNodeMain = (Listener)subLoader.loadClass(nodeClassName);
    } catch (ClassNotFoundException e) {
      throw new RosRuntimeException("Unable to locate node: " + nodeClassName, e);
    } catch (InstantiationException e) {
      throw new RosRuntimeException("Unable to instantiate node: " + nodeClassName, e);
    } catch (IllegalAccessException e) {
      throw new RosRuntimeException("Unable to instantiate node: " + nodeClassName, e);
    }

    Preconditions.checkState(subNodeMain != null);
    nodeMainExecutor.execute(subNodeMain, subNodeConfiguration);
  }

  public void do_stuff() {
    subNodeMain.do_stuff();
    pubNodeMain.do_stuff();
  }
}

This can be compiled with javac, and you don't need to use gradle or catkin (as long as you set up your classpath properly). I found this super easy to integrate into my other projects, and hopefully it is helpful to others too. I based it off of this code


Originally posted by Albatross with karma: 157 on 2013-11-26

This answer was ACCEPTED on the original site

Post score: 4


Original comments

Comment by uzair on 2013-11-26:
I actually have to publish from rospy and subscribe to that topic with rosjava. For the build tutorial u posted, I tried executing the command ./gradlew install but there was an error staing command not found. WHat do i do?

Comment by Albatross on 2013-11-26:
There should be a file called gradlew in that folder. Some versions of the code don't have it (the first one I tried didn't). I think if you use apt-get it won't be there. Try: git clone https://github.com/rosjava/rosjava_core

git checkout -b hydro origin/hydro hopefully that version will have it

Comment by Albatross on 2013-11-26:
I can't seem to get the formatting working right in the comments, but there are two commands there to install from source, that I got from this page: http://rosjava.github.io/rosjava_core/latest/installing.html

Comment by Mehdi. on 2014-03-23:
Thanks a lot for this code example it helps me a lot integrating some Ros functionalities into my project under eclipse. But I still have a small question: Where do you find the libraries needed for rosjava?

Comment by Albatross on 2014-03-25:
For me I didn't use eclipse, and I had to manually find and download various libraries to get it running. Most things were installed in the /opt/ros/hydro/share/maven/ directory, I kept a list of the other dependencies that I will post below.

Comment by Albatross on 2014-03-25:
guava version 13+ https://code.google.com/p/guava-libraries/wiki/Release15

DOMSerializer http://mvnrepository.com/artifact/org.apache.ws.commons.util/ws-commons-util/1.0.2

DNS java: sudo apt-get install libdnsjava-java

netty: http://mvnrepository.com/artifact/org.jboss.netty/netty/3.2.9.Final

Comment by Mehdi. on 2014-03-25:
Hey thanks for your help, I meanwhile was able to make it work finally! If some people have problems with 'node Talker not found' or something like that, like I did and want to avoid headaches, just delete the code inside try {} and replace the line : private Talker pubNodeMain; by private Talker pubNodeMain = new Talker(); same for listener

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.