Ask Your Question
0

Using spinOnce inside Services

asked 2014-01-16 08:15:44 -0600

orion gravatar image

updated 2014-01-28 17:06:39 -0600

ngrennan gravatar image

I would like to know whether it is safe or bad practice to spin ROS inside a while loop that exists inside a service callback. I am unable to use actions for this particular goal, and have confirmed that spinOnce inside the loop behaves as I expected, but before I go further I wanted to see if I was missing anything. Basically, there will be another process that will break the while loop and return either true or false based on the purpose of the break.

Edit: This question originally pertained to C++ packages, but has since also started to focus on python. Basically, I may have a service that is called, but should not respond until another action has finished (communications). I do not want this pause to effect other functions of the package, so I would like to find a way to have the service wait on responding, but spin ROS and let other callbacks react. I see that AsyncSpinner may be a good solution, but not sure of one for python.

edit retag flag offensive close merge delete

2 Answers

Sort by ยป oldest newest most voted
1

answered 2014-01-16 22:52:01 -0600

Adolfo Rodriguez T gravatar image

IIUC from the question, you need to service ROS callbacks during the execution of a service callback. Have you considered an asynchronous spinner as an alternative?. This wiki entry on Callbacks and Spinning is quite complete and might prove useful to your usecase.

edit flag offensive delete link more

Comments

Example: I create a service. When the service callback begins, I initiate something. Until that something is finished, I do not send the response for the service. My thought was to have while loop on variable that the something would set to send the response. I will checkout the asynch spnner.

orion gravatar image orion  ( 2014-01-24 04:28:51 -0600 )edit
0

answered 2020-05-18 06:00:06 -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:40 -0600 )edit

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

1 follower

Stats

Asked: 2014-01-16 08:15:44 -0600

Seen: 706 times

Last updated: May 18