Suppose you want to train a reinforcement learning agent with ROS and need ROS to shutdown once the training is finished. Automatically. How can this be accomplished?


One way to do this is by starting your ROS application via a Python script (which we did here). Once the training is done, we send a kill signal from within the ROS node running the RL algorithm to the startup script. We will implement this with Python's socket module which allows us to create a socket in the startup script that will listen to incoming commands. This socket will be used by our ROS node to communicate when to shutdown ROS.


Setup from the last blog post.


Adapting the main function

Continuing where we left off last time, all we need to do is extend by a helper function start_socket(host, port): it will start a socket that binds to a specified host and port. For an incoming connection, it first prints out its address and listens to it until it receives a b'killall' byte. Additionally, any received data point will be printed out.

def start_socket(host, port):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind((host, port))
        conn, addr = s.accept()
        with conn:
            print('Connected by', addr)
            while True:
                data = conn.recv(1024)
                if data:
                if data == b'killall':
                    # Send the signal to all the process groups
                    print('Received killall signal.')

We now need to extend our main function by adding the following lines before the time.sleep(5) command:

def main(args) 
   # code from the last blog post

    # add our new helper function
    # create socket and listen on the port
    HOST = socket.gethostbyname("localhost")
    PORT = 65432
    start_socket(HOST, PORT)

    # code from the last blog post
    print('Killing ROS and talker node.')
    os.killpg(os.getpgid(, signal.SIGTERM)
    os.killpg(os.getpgid(, signal.SIGTERM)

As we can see, once the while loop breaks and the start_socket function gets escaped, all processes get killed after 5 seconds.

Adapting the talker node

In a last step, we need to adapt the talker node:

  • it connects to the socket at the same host and port as in main
  • we add an iteration and iteration_max variable, which will be used for a counter to send the b'killall' command once iteration_max is reached

New code parts are marked with # NEW:

def talker():
    pub = rospy.Publisher('chatter', String, queue_size=10)
    rospy.init_node('talker', anonymous=True)
    rate = rospy.Rate(10)  # 10hz
    # NEW: connect to socket and initialize counter vars
    HOST = socket.gethostbyname("localhost")
    PORT = 65432
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))
    iteration = 0
    iteration_max = 15
    with tf.Session() as sess:
        while not rospy.is_shutdown():
            hello_str = "hello world %s" %
                tf.random.normal(shape=(1,)))  # sample from standard normal
            # NEW: send kill signal once iteration_max is reached
            iteration += 1
            if iteration == iteration_max:
                with s:

We are now ready to test our new setup.

Running the script

Finally, we can execute our new Python script:

root@ae3bbca49d56:/notebooks/workspace $ python 
Starting ROS
PGID ROS:  852
Connected by ('', 50348)
Received killall signal.
Killing ROS and talker node.

That's it.

You can now communicate with your startup script from within any ROS node by just connecting to the socket at the specified host and port.

Getagged mit:
Docker ROS Python3
blog comments powered by Disqus