Ask Your Question
0

Avoiding a circular dependency in a ROS node

asked 2018-10-23 09:51:49 -0500

aks gravatar image

updated 2018-10-29 10:09:45 -0500

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 custom_topic1, B subscribes this, processes it and publishes on custom_topic2 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 custom_topic2 but publishes that again to custom_topic1).

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 ...
(more)
edit retag flag offensive close merge delete

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.

kump gravatar imagekump ( 2018-10-23 10:02:44 -0500 )edit

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.

aks gravatar imageaks ( 2018-10-23 10:05:58 -0500 )edit

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.

kump gravatar imagekump ( 2018-10-24 02:12:39 -0500 )edit

@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.

aks gravatar imageaks ( 2018-10-29 03:15:33 -0500 )edit

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

kump gravatar imagekump ( 2018-10-29 07:57:49 -0500 )edit

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.

aks gravatar imageaks ( 2018-10-29 08:59:10 -0500 )edit

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

aks gravatar imageaks ( 2018-10-29 08:59:38 -0500 )edit

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.

kump gravatar imagekump ( 2018-10-29 09:53:47 -0500 )edit

1 Answer

Sort by ยป oldest newest most voted
1

answered 2018-10-31 07:11:51 -0500

kump gravatar image

updated 2018-10-31 07:14:58 -0500

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/Wri...

edit flag offensive delete link more

Your Answer

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

Add Answer

Question Tools

2 followers

Stats

Asked: 2018-10-23 09:51:49 -0500

Seen: 111 times

Last updated: Oct 31 '18