ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
8

How do I publish exactly one message?

asked 2011-09-12 20:41:35 -0500

updated 2012-11-13 21:18:06 -0500

Hi everyone,

I have been working with ROS for sometime now, and quite happily. However, there is one sticky issue that I would like to resolve. I learnt the basics of publishing from the tutorial "Writing a simple publisher and subscriber". This is the gist of what is demonstrated:

ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

ros::Rate loop_rate(10);

int count = 0;

while (ros::ok())
{

  std_msgs::String msg;

  std::stringstream ss;
  ss << "hello world " << count;
  msg.data = ss.str();
  chatter_pub.publish(msg);
  ros::spinOnce();
  loop_rate.sleep();
  ++count;
}

I have been using the same format for publishing ever since. Obviously, this will keep publishing as long as the program terminates or roscore terminates. I would like to publish a single message, irrespective of message size. In this case, I would like publish one std_msgs::String message. How do I do that?

<edit> (Nov 14, 2012) Upon reading comments from Benoit Larochelle and Lorenz, I would like to make my question more specific. In my case, latched topics does not apply to the current application logic. I would like all current subscribers to get the message but not future subscribers. Also, the subscribers are external packages that listen to standard non-stamped messages. In addition, I cannot use actionlib since the subscribers do not use it as well. One example is to send a twist command (that does not have a timestamp) to a swarm of robots simultaneously. We ofcourse dont want future subscribers to react to an old command (latched) and we do not want to send the command at a specific rate, but only send it once. </edit>

I have tried simple removing the while loop and executing "publish" and "spinOnce()" just one time. This doesn't work, no messages are published.

Can anyone tell me how I can publish a single message?

Thanks Shanker Keshavdas DFKI, Saarbruecken, Germany

edit retag flag offensive close merge delete

Comments

I don't think topics were supposed to be used that way. I think there might be hacks possible, e.g. by using subscription callbacks and publish only to a selected set of subscribers. But I doubt that a really robust implementation is possible.

Lorenz gravatar image Lorenz  ( 2012-11-13 22:00:54 -0500 )edit

4 Answers

Sort by » oldest newest most voted
12

answered 2011-09-12 21:01:57 -0500

Lorenz gravatar image

In your case, I guess the problem is that ROS needs some time to register at the core and to establish all subscriber connections. When you just publish one single message, chances are good that it gets lost because the subscriber is not connected yet. A quick fix would be to add a sleep right after creation of the publisher.

If you really want to make sure that clients are connected, you can use the version of advertise which takes one parameter of type AdvertiseOptions see here. In AdvertiseOptions, you can set a connect callback that gets called whenever a subscriber connects.

Another possiblility is to use the method getNumSubscribers of the Publisher class returned by the advertise call. Just wait for at least one subscriber. You can do something like:

ros::Rate poll_rate(100);
while(chatter_pub.getNumSubscribers() == 0)
    poll_rate.sleep();
edit flag offensive delete link more

Comments

Latching the topic will also help if the subscriber is coming up and down, see the answer from @Martin Günther
tfoote gravatar image tfoote  ( 2011-09-17 16:13:17 -0500 )edit

Has this been fixed in Fuerte? I.e., am I guaranteed that my subscribers will get my message when I publish it (assuming no loss due to the network)?

Benoit Larochelle gravatar image Benoit Larochelle  ( 2012-11-12 03:26:46 -0500 )edit

There never was anything to fix. As soon as the connection is established, messages are guaranteed to arrive given there are no buffer overruns. And how should a publisher know that a subscriber will connect in the future. To resent the last message, you can latch though.

Lorenz gravatar image Lorenz  ( 2012-11-12 03:30:49 -0500 )edit

Ok, maybe I did not understand your answer correctly. You say "chances are good that it gets lost". Why is ROS relying on chance? Could it happen that a message gets published before the connection gets established? For example, if a publisher is created and starts publishing immediately?

Benoit Larochelle gravatar image Benoit Larochelle  ( 2012-11-12 03:45:13 -0500 )edit

ROS is not relying on chances. But how should a publisher know for which subscribers to wait before publishing? When a subscriber connects, it first needs to query the core for all publishers and then connect to each of them. This takes time and messages are only received after the connection is up.

Lorenz gravatar image Lorenz  ( 2012-11-12 03:48:38 -0500 )edit

Ok, this is clear, but I don't understand why we're discussing future subscribers. The problem described by Shanker is that the node shutdown right after publishing a single message, and any node that was already listening on that topic did not receive the message. Do we still have to sleep after?

Benoit Larochelle gravatar image Benoit Larochelle  ( 2012-11-12 03:54:46 -0500 )edit

It's still the same problem, the publisher doesn't know about the subscribers yet. When a publisher comes up, the subscribers get a notification to connect to it. This takes time. The publisher might have sent its message and terminated before any subscriber had a chance to connect.

Lorenz gravatar image Lorenz  ( 2012-11-12 03:57:22 -0500 )edit

Ok, so how can I ensure that my message will get to all current subscribers? Do I have to get the # of subscribers and count the calls to connect_cb? And then only when they are all connected do I call publish? Or maybe this is already done internally by ROS? Or is there an easier way?

Benoit Larochelle gravatar image Benoit Larochelle  ( 2012-11-12 04:03:36 -0500 )edit
5

answered 2011-09-12 21:39:15 -0500

In addition to what @Lorenz said, latching the connection might be useful. Also, I'm not sure what happens when your node exits right after sending the message, so waiting for some time (or for some subscribers) might be needed even when using latched connections.

edit flag offensive delete link more

Comments

Indeed, you will want to latch the value. ROS is like live streaming video -- if you miss the start of the game, you miss it (though you can get the last-published value if you latch).
kwc gravatar image kwc  ( 2011-09-13 04:52:57 -0500 )edit
4

answered 2011-10-10 22:41:45 -0500

updated 2011-10-10 22:43:32 -0500

Thanks for your answers.

I was successful in using latch and getNumSubscribers() to achieve a single message publisher, as explained above.

I would like to point out that using 'rostopic echo' command to verify a single message publisher has not been successful for me. When I run 'rostopic echo', I notice that an additional subscriber is listed under 'rostopic info'. Thus I assumed it would behave like a regular subscriber.

I observe that when I use a separate program to subscribe to the topic, I receive the message successfully. However, 'rostopic echo' does not display the single message that I am trying to publish.

Here is some demo code with the relevant portions:

// Code starts here

int main(int argc, char** argv)

{

ros::init(argc, argv, "VirtualObjectPublisher");

ros::NodeHandle VirtualObjectNode("~");

ros::Publisher VirtualObjectROSPublisher;

ros::Rate poll_rate(100);

bool latchOn = 1;

/* Some processing */

VirtualObjectROSPublisher = VirtualObjectNode.advertise<eu_nifti_env_msg_ros::ElementOfInterestMessage>("/eoi", 20, latchOn);

 while(VirtualObjectROSPublisher.getNumSubscribers()==0)
{
   ROS_ERROR("Waiting for subscibers");
   sleep(10);
}
ROS_ERROR("Got subscriber");
VirtualObjectROSPublisher.publish(publishEOImsg);
ros::spinOnce();

 while(ros::ok())
{
   sleep(3);    
}

return 0;

}

// I added the ROS_ERROR for debugging. The same with the sleep statements. I also added the final ros::ok(), loop since I didn't want the node to be destroyed prematurely, to better understand the problem.

My test procedure:

i. Terminal 1 : Start the above code

ii. After 3 seconds, Terminal 2 : 'rostopic echo /eoi'.

iii. Terminal 1 :

Terminal 2 does not echo the topic.

However, as I explained earlier, if I have a program with a Subscriber, it reads the message successfully.

Regards,

Shanker Keshavdas

edit flag offensive delete link more

Comments

5
Please edit your question if you want to add more info instead of answering it. This is a little unusual if you're used to forums, but it keeps the thread tidy.
Martin Günther gravatar image Martin Günther  ( 2011-10-11 00:10:32 -0500 )edit
2

answered 2012-11-13 02:06:24 -0500

Benoit Larochelle gravatar image

updated 2012-11-13 21:39:32 -0500

Based on the comments from Lorenz, the problem seems to be that the publisher is too fast; that is, it publishes before the subscriber has opened a direct connection to the publisher. Therefore, the goal is to make the publisher "wait" until all subscribers have established a connection. I don't think that the problem stems from closing the publisher too soon.

Here is an idea that I have not tested, but that should work with multiple subscribers. It is not 100% robust, but assuming that no subscribers comes or leaves exactly when the publisher is launched, it should not have problems.

EDIT: This method would not work because a functionality that I expected available from ROS does not seem to be there. See comments in the code.

int numSubscribersConnected = 0;

void connectCallback(const ros::SingleSubscriberPublisher& pub)
{
    numSubscribersConnected++;
}

int main(int argc, char** argv)
{
    ros::init(argc, argv, "MyPublisher");

    ros::NodeHandle nh("~");

    ros::Publisher pub = nh.advertise<foo::MyMessage>("/myTopic", 100, connectCallback);

    ros::Rate poll_rate(100);

    ros::Time maxTime(ros::Time::now()+1000); // Will wait at most 1000 ms

    // This is wrong. It returns only the number of subscribers that have already established their direct connections to this publisher
    //int numExistingSubscribers = pub.getNumSubscribers();

    // This method does not exist, as far as I know. In the comments below, I suggested having a similar functionality. This method would return the number of total subscribers to this topic over the ROS network
    int numExistingSubscribers = ros::getNumSubscribers("/myTopic");

    while(numExistingSubscribers!=numSubscribersConnected && ros::Time::now()<maxTime)
    {
        poll_rate.sleep();
    }

    // Now the publisher is ready

    foo::MyMessage msg;
    pub.publish(msg);

    return 0;
}
edit flag offensive delete link more

Comments

Personally, I always thought that this was taken care of by ROS. In my opinion, a publisher is not ready until the publisher-subscriber connections are ready. I would suggest that advertise() take a ros::Time parameter to specify the max timeout, and with zero it would keep the current behavior.

Benoit Larochelle gravatar image Benoit Larochelle  ( 2012-11-13 02:09:36 -0500 )edit

I don't think advertise should take a time parameter, this can easily be achieved by the user by adding sleep. ROS is designed to be independent of the number of publishers and subscribers. If you need something more reliable, consider using actionlib for instance.

Lorenz gravatar image Lorenz  ( 2012-11-13 02:27:47 -0500 )edit

My suggested solution is independent of the number of publishers and subscribers (correct me if I'm wrong). It handles all cases between 0 and N publishers and subscribers. Would a more reliable behavior hinder ROS in any way?

Benoit Larochelle gravatar image Benoit Larochelle  ( 2012-11-13 02:47:48 -0500 )edit

Well, you say 'until the publisher-subscriber-connections are ready'. A timeout does not imply anything like that. It's just not possible to know how many connections need to be ready to be sure everything is set up beforehand. The timeout can easily be implemented by the user as well if desired.

Lorenz gravatar image Lorenz  ( 2012-11-13 02:50:40 -0500 )edit

Then I must understand pub.getNumSubscribers() wrong. I thought that it told me how many connections need to be ready to be sure everything is set up beforehand. What does it tell me exactly then if not that?

Benoit Larochelle gravatar image Benoit Larochelle  ( 2012-11-13 02:57:50 -0500 )edit
1

getNumSubscribers() returns the number of subscribers with which a connection is established already. All will receive published messages. It should be basically the same as numSubscribersConnected in your example.

Lorenz gravatar image Lorenz  ( 2012-11-13 03:18:27 -0500 )edit
1

Ooohhh, now I see. The documentation is not clear on that though. In that case, my solution would not work at all. What I would then suggest for ROS is to have something like a centralized TopicManager (at the core), with a method getNumSubscribers(std::string&amp; topicName)

Benoit Larochelle gravatar image Benoit Larochelle  ( 2012-11-13 03:54:53 -0500 )edit

That just shifts the problem. The rpc call to the core is probably slower than the connection negotiation. Really, if you need a reliable connection, either implement the sleep yourself, use actionlib, use latched topics or publish data at a specific rate.

Lorenz gravatar image Lorenz  ( 2012-11-13 03:58:54 -0500 )edit

Question Tools

5 followers

Stats

Asked: 2011-09-12 20:41:35 -0500

Seen: 25,980 times

Last updated: Nov 13 '12