'My timing accuracy is related to the number of spin_once

My timing accuracy is related to the number of spin_once.

I try to call service 'compute_rectangle_area' at a constant rate. The strange thing happens.

If I set the rate to 1 Hz, It seems that I have to do spin_once four times to get correct timing!

If the times of spins is smaller than four, the programming will get stuck at the following screen. enter image description here If the time of repeats is bigger than four, the programming sleeps longer than expected. enter image description here

Here is my code.

#!/usr/bin/python3
from asyncio import Future
from functools import partial
 
import rclpy
from rclpy.node import Node
from robot_interfaces1.srv import ComputeRectangleArea
 
class ComputeRectangleAreaClientNode(Node):
    def __init__(self, node_name: str) -> None:
        super().__init__(node_name)
        self.client_ = self.create_client(ComputeRectangleArea,"compute_rectangle_area")
    
    def call_compute_rectangle_area_server(self,length:float,width:float)->bool:
        if(self.client_.wait_for_service(5)):
            msg_request = ComputeRectangleArea.Request()
            msg_request.length = length
            msg_request.width = width
            future = self.client_.call_async(msg_request)
            future.add_done_callback\
                (partial(self.callback_call_compute_rectangle_area_server,length=length,width=width))
            return True
        else:
            self.get_logger().error("Server is not ready.")
            return False
        pass
 
    def callback_call_compute_rectangle_area_server \
        (self,future:Future,length:float,width:float):
        if future.exception() == None:
            response_:ComputeRectangleArea.Response = future.result()
            self.get_logger().info(f"{length} * {width} = {response_.area}.")
 
        else:
            self.get_logger().error(f"The {self.get_name()} returns {future.exception()}.")
 
 
def main(args=None):
    rclpy.init(args=args)
    n = ComputeRectangleAreaClientNode("compute_rectangle_area_client")
    loop_rate = n.create_rate(1)
 
    counter = 0
    while rclpy.ok():
        if(n.call_compute_rectangle_area_server(67.4,45.6)):
            n.get_logger().info(f"Call {counter} times.")
        counter += 1
        rclpy.spin_once(n)
        rclpy.spin_once(n)
        rclpy.spin_once(n)
        rclpy.spin_once(n)
        rclpy.spin_once(n)
        rclpy.spin_once(n)
 
        loop_rate.sleep()
    rclpy.shutdown()
    
 
 
if __name__ == "__main__":
    main()


Solution 1:[1]

As per your comment, the problem with the logic is you're not using a timer where you should be; there isn't an issue with core logic apart from using spin_once() multiple times.

The reason you're seeing this issue is because spin_once has no relation to timing, it simply calls available callbacks. If you want data to be processed at a fixed rate, it is best to just let the callbacks and services run using rclpy.spin(node), then have a fixed rate timer calling a main loop.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 BTables