0
$\begingroup$

I'm currently working on a node that has a service that calls other services within that service. Something like this:

Node A:

  • Service A
  1. Calls service B of Node B
  2. waits for service B to complete (spin until future complete)
  3. Calls service C of Node C after
  4. waits for service C to complete
  5. returns response

service A is in its own mutually exclusive callback group, and service B and C are in a separate different callback group

Currently these nodes are launched together in a launch file. When I call service A for the first time, it runs to completion as expected, and I receive a response and everything (calling the service in a separate terminal). All my debugging messages indicate that the first service call runs to completion.

However, when I try to call the same service again, nothing happens, it appears as though something is blocking the node from receiving any new service requests. Same thing happens when I try calling other services of Node A after (none of them run). The node doesn't seem to even receive a request as when I put debugging logs at the very start of the callback, they don't get printed.

Does anyone know what I might be doing wrong here? Any help would be greatly appreciated, nothing I've tried has worked for me so far.

Code snippets below:

Initializing client (for service B) within Node A:

self.platform_actuator_cli = self.node.create_client(
        MoveToPos,
        "/spc_unit_tester/platform_encoded_actuator",
        callback_group=self.node.client_cb_group
    )
    while not self.platform_actuator_cli.wait_for_service(timeout_sec=1):
        self.logger.info("Waiting for platform actuator service...")
    self.platform_actuator_req = MoveToPos.Request()

Initilizing service A within Node A:

self.home_platform_srv = self.node.create_service(
        HomePlatform,
        "home_platform",
        self.home_platform_cb,
        callback_group=self.node.server_cb_group
    )

Service A callback (which runs to completion returning response the first time it's called):

def home_platform_cb(self, _request, response):
    self.logger.info("Homing platform.")
    self.platform_actuator_req.pos = 0
    self.future = self.platform_actuator_cli.call_async(self.platform_actuator_req)
    rclpy.spin_until_future_complete(self.node, self.future)
    response.success = True
    return response
$\endgroup$

1 Answer 1

1
$\begingroup$

I ran into a similar issue and wanted to share my solution since it took me a lot of searching and effort to figure out.

In short: when using multiple callback groups you should use a MultiThreadedExecutor to run the node. You also need to use the executor's spin methods instead of calling rclpy directly.

When you call rclpy.spin_until_future_complete it first adds the node to an Executor, spins the Executor, then removes the node from the Executor. The last part is the issue. Removing a node from an executor means no callbacks will be executed.

It still exists. Running ros2 list node will still show the node and any topics or services related to it. But it will not interact with ROS because no executor knows to call it.

I trust rclpy has a good reason to have this add/remove logic. But in this case, spinning inside of a node callback, leads to this devastating unintended consequence.

The question does not mention executors but I imagine they are using the MultiThreadedExecutor because of the callback groups. After a node is added to an executor it can access its executor by the executor attribute. This is the key to spinning in a callback.

Rather than calling any of the rclpy spin functions directly - or even with the executor keyword - one should call the executor's spin methods directly in the node.

Replace lines like this:

rclpy.spin_until_future_complete(self.node, self.future)

With lines like this:

self.executor.spin_until_future_complete(self.future)

This is essentially what rclpy's spin functions do under the hood. Calling them directly skips the add/remove steps.

See the spin behavior for MultiThreadedExecutor.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.