Robotics StackExchange | Archived questions

Avoiding a circular dependency in a ROS node

how can I publish and subscribe simultaneously on 2 different topics. I have two nodes A and B. A is located in ROS and i am using roslibpy to communicate with B. A publishes first on customtopic1, B subscribes this, processes it and publishes on customtopic2 which is then subscribed by A. A processess this again and publishes back on custom_topic1. and then it works in the loop.

I am somehow getting stuck in an infinite loop.

I would want to do something like this :

ros = roslibpy.Ros(host='...', port=9090)
publisher = Topic(ros, '/custom_topic1', 'ros_tutorials/Msg2')
subscriber = Topic(ros, '/custom_topic2', 'ros_tutorials/Msg1')

def publish:
    publisher.publish(Message({'x': 123, 'y': [{'a': 1, 'b': 2, 'c': 3.14, 'e': 7.23, 'f': 2.353}]}))
    subscriber.subscribe(receive_message)
def receive_message(message):
    a = message['j']
    print('a')

Tosimplify it , A -> custom_topic1 -> B -> custom_topic2 -> A -> custom_topic1 (so, A will enter in a loop because it's subscribed to customtopic2 but publishes that again to customtopic1).

So this is entering a circular dependency. Any ideas how to avoid it ?

EDIT :

Here is the code for Node1:

import rospy
import sys
import time


from std_msgs.msg import String
from ros_tutorials.msg import VehicleControl
from ros_tutorials.msg import DetectedObjectList

Lane_Width = 2
Brake_Distance = 30

class DemoFunction():
    def __init__(self):
        print("entering constructor")
        #super().__init__('custom_talker')
        self.sub = rospy.Subscriber('objects',DetectedObjectList, self.object_callback)
    self.pub = rospy.Publisher('control',VehicleControl, queue_size = 10)


    def object_callback(self, msg):
        print ("entering callback")
    for obj in msg.objects:
            if self.obj_in_path(obj):
            print("Found object in path: \n{}".format(obj))
            self.send_brake_message()
        else:
            self.normal_driving()
            break

    def send_brake_message(self):
        print("entering brake function")
        self.pub.publish(VehicleControl(accelerator_pedal = 0.0, brake_pedal = 1.0, steering_wheel = 0.0, gear = 1.0))

    def normal_driving(self):
        print("entering normaldriving function")
        self.pub.publish(VehicleControl(accelerator_pedal = 0.0, brake_pedal = 0.0, steering_wheel = 0.0, gear = 1.0))

    def obj_in_path(self,obj):
        print("entering ovbect in path function")
    if (abs(obj.y) < Lane_Width and (obj.x < Brake_Distance)):
        return True
    return False


if __name__ == '__main__':

    print("entering main")
    rospy.init_node('aeb_node')
    try:
        DemoFunction()
        print("entering Try")
        rospy.spin()
    except rospy.ROSInterruptException:
        print("exception thrown")
        pass

This is the code for node 1 which is using ROS_bridge.

def start_sending():
        print('entering start_sending function')
        while True:
            #print('entering while true')
            if not ros.is_connected:
                break
            #print('not entering break and should advertise the publisher')
            publisher.advertise()
            publisher.publish(Message({'id': int(10), 'objects' : [{'id':int(1),'x':2,'y':1,'v_rad':3,'a_rad':4}]}))
            #print('Object found : x distance is : ')
            #time.sleep(0.1)


            def receive_message(message):
                print('entering receive_message')
                #assert message['data'] == 'hello world' , 'Unexpected message content'
                #LOGGER.info('Received message  : %d', message['brake_pedal'])
                a = message['brake_pedal']
                print('brake_pedal : ', a)

            def start_receiving():
                print('entering start receiving')
                listener.subscribe(receive_message)

            listener.subscribe(print)
        publisher.unadvertise()



    ros.on_ready(lambda: print('Is ROS connected ?', ros.is_connected))
    ros.on_ready(start_sending, run_in_thread=True)
    #ros.on_ready(start_receiving, run_in_thread=True)
    ros.get_topics(print)
    ros.get_nodes(print)


    ros.run_forever()

if __name__ == '__main__':

    print('main')

    import logging
    #logging.basicConfig(level=logging.DEBUG, format='[%(thread)03d] %(asctime)-15s [%(levelname)s] %(message)s')
    #LOGGER = logging.getLogger('test')

    run_topic_sub()

The problem would be that I need to run node1 first in order to publish, this info would be received by Node 2, processed and it will publish a control signal on another topic. This should be subscribed by Node1 but according to my code, it gets stuck in a deadlock i.e. in the callback function. I guess I need a return statement to exit the callback.

Asked by aks on 2018-10-23 09:51:49 UTC

Comments

Wouldn't service be sufficient for your cause? ROS service provides one-on-one communication where a "client" sends some data to the "service" and the service sends some data back to the client.

Asked by kump on 2018-10-23 10:02:44 UTC

could be one use case, but i think must also work with Messages. Or ? I implemented a similar sort of functionality with ROS2 but here i am getting a bit confused with ROS bridge.

Asked by aks on 2018-10-23 10:05:58 UTC

I suppose this depends on what you need to achieve. What you described does sound like a job for a ROS service. However if you need more nodes to get the information from topics custom_topic2 and custom_topic1 or if you want to allow loosing some messages, that is a job for ROS topic.

Asked by kump on 2018-10-24 02:12:39 UTC

@kump : I would like to use Topics since my node one would be sending an object list and thus as soon as it detects any objects, it should just keep on publishing irrespective of any request.

Asked by aks on 2018-10-29 03:15:33 UTC

ok. So why is the "circular dependancy" a problem, anyway?

Asked by kump on 2018-10-29 07:57:49 UTC

because i am somehow getting stuck in a deadlock. i.e. some implementation problem. If I publish in the callback function for the first node, it will never begin since it wont enter the callback (no new message). If I publish outside, it publishes once and then stays in callback, So i need a return.

Asked by aks on 2018-10-29 08:59:10 UTC

return statement in callback with which i can come out from callback and publish again.

Asked by aks on 2018-10-29 08:59:38 UTC

It's hard to understand if I can't see the code. Why should it stay in the callback if you publish outside of callback? That doesn't sound as a standard behavior.

Asked by kump on 2018-10-29 09:53:47 UTC

I have updated the code

Asked by aks on 2018-10-29 10:09:53 UTC

So if you run the Node 1, it sends one message into the VehicleControl topic and then nothing happens at all? Not even terminal output?

Asked by kump on 2018-10-31 07:03:41 UTC

Answers

This Node1 class has a call_back function that only prints "entering callback" in the terminal and that's all.

def object_callback(self, msg):
    print ("entering callback")
for obj in msg.objects:
        if self.obj_in_path(obj):
        print("Found object in path: \n{}".format(obj))
        self.send_brake_message()
    else:
        self.normal_driving()
        break

the for-loop is not part of the callback, since it has the same size of indentation as the callback function (it is at the same level). Therefor the for-loop will execute at the initiation of the class instance (when you first start the node) and never again.

There is no deadlock. The second node doesn't react, because it is not running. There is no piece of a code that would allow to run Node2.

I suggest you should try to build your nodes while following the official tutorials: http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber%28python%29

Asked by kump on 2018-10-31 07:11:51 UTC

Comments