[ROS2, rclpy] sharing global var between nodes

asked 2022-01-30 19:39:57 -0500

I want to share a variable between an action server and a timer callback that publishes a message. The purpose of this sharing, is to change the behavior of a finite state machine that is part of the timer callback. As far as I understood, executors are based on threading library (not sure though) , so to avoid "race condition" between the action server and timer callback, I am using ""threading.lock". A MWE of what I am doing is as follows. Each callback belongs to a separate node.

action server callback:

def __action_server_callback(self, goal_handle):
    self.get_logger().info('Executing goal...')
    global shared_var
    counter = 0
    start = time.time()
    while True:
        counter += 1
        lock.acquire()
        shared_var = counter
        lock.release()
        if (time.time() - start) > .3:
            break
        self.rate.sleep()
    print(shared_var)
    print(counter)
    for i in range(4):
        print(i)
        time.sleep(.25)
    result = Fibonacci.Result()
    goal_handle.succeed()
    return result

timer callback:

def __timer_callback(self):
    """This contains the main control loop."""
    global shared_var
    lock.acquire()
    counter = shared_var
    shared_var  = (shared_var + 1)%1000000
    lock.release()
    self.publish_all(topic=counter)
    counter_fb = self.get_subs_values()
    print_str = (f"{time.time()%1e4:+010.3f}|{counter:+010d}")
    print(print_str)

Is this a right approach to avoid "race condition"?

Other than this, is this a good practice for what I am intending in general (fast communication between two nodes in the same package)?

If not, can you please suggest a better practice?

Thanks

edit retag flag offensive close merge delete

Comments

I think we need more information to be able to give you a good answer. Are these two callbacks in the same class? Are these two nodes run as separate executable? Why do these nodes need to be separate? Could they be the same node?

ChuiV gravatar image ChuiV  ( 2022-02-04 15:24:02 -0500 )edit

Thanks for your answer. 1- These two callbacks are each in a separate node. Both node are ran by the same executor. 2- It does not make any difference if I put both callbacks in the same node. The only difference will be that the shared_var will be an instance variable of the Node class. The same question applies to that situation, as there might be a "race condition" accessing and modifying the shared_variable.

One solution to this problem is to put each callback to a separate node in a separate executable, so that they publish and subscribe to each other. However, my application is real-time in nature and this introduces at least 1 step delay between the processes.

farshid gravatar image farshid  ( 2022-02-04 18:44:54 -0500 )edit

If I were doing this, I think I'd make a python class TopLevelNode(rclpy.node.Node)and within that make another Node object on which I can use to call the service or timer, and I can manually spin the inner Node. Somebody else asked for details on doing that, here's an answer: https://answers.ros.org/question/3731...

In regards to the mutual access to your shared_var, I would recommend putting a mutex on it so that you can ensure that only one callback is using it at a time. Just be sure to release that mutex when you're not using it, otherwise you'll lock things up for good.

ChuiV gravatar image ChuiV  ( 2022-02-09 19:55:34 -0500 )edit

Hi, I had the same question but different problem. I have more than 3 subscriptions in a class but I notice that one callback blocks others . I have a variable that is being shared with all class functions. (e.g.) 1st callback doing something then 2nd callback updates this and so on...) can you recommend a different approach to this?

n000oob gravatar image n000oob  ( 2023-03-17 03:08:19 -0500 )edit
1

@n000oob I think it is better to open your separate topic. Concerning your problem, I think you are executing your node on a single threaded executor, you need to execute it on rclpy.executors.MultiThreadedExecutor and this way publishers and subscribers would not block each other. Take a look at the following link example using multithreaded executor

farshid gravatar image farshid  ( 2023-03-17 10:31:08 -0500 )edit