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

Is it possible to have a Subscriber inside the Service Server

asked 2019-12-08 13:02:23 -0600

Yehor gravatar image

updated 2022-01-22 16:10:12 -0600

Evgeny gravatar image

Hello,

I want to create a ROS service which will perform the docking based on the IR sensors. But for that I need a consistent feedback from IR receivers to navigate, so another words I have to have subscriber inside the service server which will get the information from IR topics while performing docking. Do I can implement subscriber inside the Service server??

Thank you

edit retag flag offensive close merge delete

2 Answers

Sort by ยป oldest newest most voted
2

answered 2019-12-08 13:54:03 -0600

gvdhoorn gravatar image

updated 2019-12-08 15:17:36 -0600

I want to create a ROS service which will perform the docking based on the IR sensors. [..] Do I can implement subscriber inside the Service server??

Yes you can, but you don't want to do this (for multiple reasons).

The main reason not to do this would be: services are not meant for long running operations, as both client and server will be blocked until the service has completed. Even worse: clients have no way of cancelling a service invocation, so they are completely "at the mercy" of the service server during that time.

Instead, implement an action server and update the client periodically on the progress.

Actions are comparable to C++ and Python futures and/or asynchronous services. Clients can choose to wait for completion, or register a callback and get notified about both progress and on completion, but are otherwise completely decoupled from the action server.

See also wiki/ROS/Patterns/Communication and #q11834.

And as an example, you may want to take a look at the kobuki_auto_docking package, and the accompanying tutorial: wiki/kobuki/Tutorials/Automatic Docking. They use the same approach.

Actually: you may even be able to reuse the code: it also uses IR sensors and commands the robot using geometry_msgs/Twist messages. If your robot has a similar interface -- and your docking station is setup similarly -- you may be able to just reuse kobuki_auto_docking, which could save you a lot of time.


1) In case I'm going to use an action server, I am going to implement a subscriber inside the server, because that what I need.

Yes, you'd add your subscriber to the action server.

And is it possible to use it with Python?

actionlib also supports Python.

2) I saw kobuki auto docking, but I didn't find an action server, only the client. So a don't know how do they implement the server. If you know where it is, could you please provide me the link.

See kobuki_auto_docking/src/auto_docking_ros.cpp.

edit flag offensive delete link more

Comments

Thank you! But can I clarify something?

1) In case I'm going to use an action server, I am going to implement a subscriber inside the server, because that what I need. Is it right? And is it possible to use it with Python?

2) I saw kobuki auto docking, but I didn't find an action server, only the client. So a don't know how do they implement the server. If you know where it is, could you please provide me the link. Because I will also need to set up a dock station, I am talking about the encoding of the signals which I have to send from IR.

Thank you

Yehor gravatar image Yehor  ( 2019-12-08 15:08:57 -0600 )edit
2

answered 2020-05-18 05:59:44 -0600

Andreas Z gravatar image

The problem is, that you can't use the spin_node_until_future_complete() command inside the outer callback. The solution is to use an MultiThreadedExecutor and different callback groups. Then you can call the inner service by giving it a callback function (instead of waiting for the return code with spin_node_until_future_complete).

If you want to wait for the result from the inner service call before the outer service callback finishes you need to block the thread with a while(return_from_inner_service == false) loop. This is important if your outer callback is from a service, because then you want to receive the inner service result and use it to compute the result of the outer service. But this is bad design, because you block the spin() call from the outer service. Luckily we can still handle other callbacks with the MultiThreadedExecutor as long as we assign them to a different callback group. If the outer callback is from a topic you don't want to block the callback, just handle the inner service call inside the assigned callback function. It will execute asynchronous.

Here is some code example in C++ (assuming all necessary headers included and ROS services created)

Create the MultiThreadedExecuter and an instance of the Node in main:

int main(int argc, char *argv[])
{
    rclcpp::init(argc, argv);
    rclcpp::executors::MultiThreadedExecutor executor;
    auto node = std::make_shared<Communication>();
    executor.add_node(node);
    executor.spin();
    rclcpp::shutdown();
    return 0;
}

Create seperate Callback group and assign it to the inner service client (inside the Node/class constructor):

callback_group_input_ = this->create_callback_group(rclcpp::callback_group::CallbackGroupType::MutuallyExclusive);
get_input_client_ = this->create_client<petra_core::srv::GetInput>("GetInput", rmw_qos_profile_services_default, callback_group_input_);

Inside the outer callback from a topic or service, send the inner_request and define a callback for it:

int choice = -1;
    auto inner_client_callback = [&,this](rclcpp::Client<petra_core::srv::GetInput>::SharedFuture inner_future)
        { 
            auto result = inner_future.get();
            choice = stoi(result->input);
            RCLCPP_INFO(this->get_logger(), "[inner service] callback executed");
        };
    auto inner_future_result = get_input_client_->async_send_request(inner_request, inner_client_callback);

If it is necessary to wait inside the outer callback for the result block the thread:

// Wait for the result.
    while (choice < 0 && rclcpp::ok())
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }

I hope it helps you, or whoever has the same problem. I was stuck with this for weeks...

edit flag offensive delete link more

Comments

+1 for the effort of posting this, but please note that your answer is ROS 2 specific, while the OP was/is using ROS 1.

That makes your answer essentially irrelevant here in this case.

gvdhoorn gravatar image gvdhoorn  ( 2020-05-18 07:02:55 -0600 )edit

Hey, Thanks for answer. Although it might not relevant to given question, but its going to help many in future like its helping me right now.

aarsh_t gravatar image aarsh_t  ( 2021-10-28 16:16:56 -0600 )edit

Thanks for the explanation about the callbacks. Really helpful

vanguard478 gravatar image vanguard478  ( 2022-03-10 15:08:04 -0600 )edit

Question Tools

1 follower

Stats

Asked: 2019-12-08 13:02:23 -0600

Seen: 2,042 times

Last updated: May 18 '20