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

How to make a service request inside a callback function

asked 2016-12-02 10:16:34 -0600

spungia gravatar image

Hey there,

I am new to ROS trying to get some code running on a robot. Therefore I went to some tutorials on the web and have a brief understanding of the ROS concept.

The task I am dealing with right now is as follows: given two nodes (AGENT and ROBOT) I want the robot to detect some objects which are defined by the AGENT-node in a single frame. So the AGENT sends a service-request (detect-object) to the ROBOT-node. So far so good the problem occurs now: The callback function executed inside the ROBOT-node needs to know which objects to detect and therefore sends a service request back to the AGENT-node asking for the object-list. (I know the detect-object.srv could forward the object-list but since it is not my framework I have to deal with the services as they are:/) The problem is (as far as I understand) that the first servic-request blocks the second... I found some comments on ros::AsyncSpinner but it is still not clear to me how to use this :/ So question 1): Is the process calling a service request inside a service request a good idea? question 2): If 1):yes is ros::AsyncSpinner the right choice? If 1): no what is a good idea then? question 3): if ros::AsyncSpinner is the right choice, could you please give a short explaination how to use it properly?

Thx a lot for your comments!

edit retag flag offensive close merge delete

Comments

When agent node sends a service request, you can pass the object list (can be obtained by calling that specific service and including it in the service request to the robot node). If you cannot modify either services, can make a third that includes both in the request to the robot node.

JoshMarino gravatar image JoshMarino  ( 2016-12-03 14:46:58 -0600 )edit

Passing the object list via this request seems the most convenient one! however, the services should stay fixed. Could you please go more into detail about the third service? Apart from this, would it be possible to send a service request inside a callback and how?

spungia gravatar image spungia  ( 2016-12-04 04:17:18 -0600 )edit

From the second service being called, is the object-list part of the service request message? Otherwise you would have to be modifying the service callback function which is not keeping the service fixed. Are you able to share any of either services so I can understand what is actually going on?

JoshMarino gravatar image JoshMarino  ( 2016-12-04 14:36:25 -0600 )edit

1 Answer

Sort by ยป oldest newest most voted
2

answered 2020-05-18 05:58:56 -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 I'm pretty sure 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:03:22 -0600 )edit

Question Tools

2 followers

Stats

Asked: 2016-12-02 10:16:34 -0600

Seen: 1,217 times

Last updated: May 18 '20