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

Can a node be a subscriber and client at the same time?

asked 2014-11-20 09:24:45 -0600

Clancy gravatar image

I figured out how to do this, but couldn't find any documentation anywhere so I figured I'd help out anyone that is trying to do the same thing.

edit retag flag offensive close merge delete

Comments

What do you mean with client? Are you mean that you want to call a server service? If so, yes you could do both, subscribe to regular topics and also call services from a server (being a client) in one node / nodelet.

Torsten gravatar image Torsten  ( 2014-11-20 10:11:38 -0600 )edit

I'll have to check out the nodelet solution. My answer will go through a couple more revisions, but I wanted to put up our immediate solution so that others can see a way of doing it. Thanks.

Clancy gravatar image Clancy  ( 2014-11-20 11:00:48 -0600 )edit

3 Answers

Sort by ยป oldest newest most voted
2

answered 2014-11-20 10:55:51 -0600

Clancy gravatar image

updated 2014-11-20 11:01:56 -0600

Yes. A node can be a subscriber and a client at the same time.

We needed a node that could subscribe to a topic and call a service request from another node at any given time. I couldn't find any solutions (probably because it is a relatively simple concept)... but I think there should be some documentation for other people stuck with the same problem.

The problem we were having: Most of the service request examples have the service request being called from inside the main function. When we tried to do this, we were only able to request a service once and then the node would be caught in a spin loop. To avoid this error, we decided to move the service request into the subscriber callback function. This way it would be called whenever the subscriber heard a message.

The basic idea: The client is instantiated in the main function, but it needs to be used in the callback function. I made a ros::ServiceClient pointer at a global level (w.r.t. the node) and then gave it the address of the client. In the callback function I dereference the client pointer and use it to request a service.

 void SUBSCRIBE_CALLBACK_FUNCTION (const PACKAGE_NAME::MESSAGE_NAME::ConstPtr& msg);
    ros::ServiceClient *clientPtr; //pointer for a client

    int main (int argc, char **argv)
    {

        ros::init(argc, argv, "MY_NODE");
        ros::NodeHandle n;
        ros::ServiceClient client = n.serviceClient<PACKAGE_NAME::SERVICE_NAME>("SERVICE_NAME"); //create the client

        clientPtr = &client; //give the address of the client to the clientPtr

        ros::Subscriber sub = n.subscribe<PACKAGE_NAME::MESSAGE_NAME>("TOPIC_NAME",1000,SUBSCRIBE_CALLBACK_FUNCTION); //subscribing

        ros::spin();

        return 0;

    }

    void SUBSCRIBE_CALLBACK_FUNCTION (const PACKAGE_NAME::MESSAGE_NAME::ConstPtr& msg)
    {
        PACKAGE_NAME::SERVICE_NAME srv;
        srv.request.someBool= false; //just some variables to give the service some structure.
        srv.request.someFloat = 4;
        srv.request.someFloat = 30;

        ros::ServiceClient client = (ros::ServiceClient)*clientPtr; //dereference the clientPtr

        if(client.call(srv)) //request service from the client
        {
            ROS_INFO("Success = %d", srv.response.success);
        }   
        else
        {
            ROS_ERROR("Failed to call service from motor_control_node");    
        }

    }
edit flag offensive delete link more

Comments

At least accept your own answer ;)

AReimann gravatar image AReimann  ( 2014-11-23 19:36:21 -0600 )edit

Ha! Unfortunately I don't have enough points yet.

Clancy gravatar image Clancy  ( 2014-11-23 23:16:58 -0600 )edit
1

answered 2014-11-20 09:54:38 -0600

Erwan R. gravatar image

I don't really get your goal : to answer the question, yes, a node can subscribe to a topic and be client for a service.

If you want to share a working solution, please post it.

edit flag offensive delete link more

Comments

Sorry for the ambiguity, hopefully the answer clears things up.

Clancy gravatar image Clancy  ( 2014-11-20 10:56:25 -0600 )edit
1

answered 2014-11-20 11:18:36 -0600

Torsten gravatar image

updated 2014-11-20 11:43:36 -0600

I think the answer to your primary question is clear, Yes they can have / be both!

There are several tutorials which describe the background, I dont think a all in one tutorial is needed.

Look at this tutorial for the ros main-loop behaviour: http://wiki.ros.org/ROS/Tutorials/Wri...

Look at this tutorial for the server-client stuff: http://wiki.ros.org/ROS/Tutorials/Wri...

If putting both together you will have a basic understanding to find the solution for your problem. Of course you can also use Nodelet's but they are more C++ object oriented style, so there you don't have a main-loop anymore, you can only use callbacks which includes timer callbacks desribed here:

http://wiki.ros.org/roscpp/Overview/T...

And now some notes regarding your code:

  1. Why dereference pointer, you can also do clientPtr->call(srv). You don't have to use a ptr at all.
  2. You don't have to call ros::spin(), you can also use ros::spinOnce(), so you will have your main loop to call the service.
  3. Make sure that you know when and how often you want to call the service. Everytime you got a incoming message (subscribed topic callback), every frame in your main loop (inside spinOnce main-loop), or maybe every x seconds (Timer callbacks). And this are only the synchronious ones.
edit flag offensive delete link more

Question Tools

1 follower

Stats

Asked: 2014-11-20 09:24:45 -0600

Seen: 3,636 times

Last updated: Nov 20 '14