ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

This turned out to be a bit of a fun puzzle to get ROS2 and Eventlet working together. In the end, the solution makes a lot of sense, but it just isn't super intuitive. Basically, the solution is first to import eventlet and run monkey_patch() before any other imports.


import eventlet
eventlet.monkey_patch()
from flask import Flask, request, jsonify, make_response
from flask_socketio import SocketIO, emit, Namespace
...

The issue is that when monkey-patching, it basically turns all the threads into blocking threads, which need to be explicitly slept so that other threads can process (i.e. the rest of the code). So the trick is to combine rclpy's spin_once() with the eventlet.greenthread.sleep()

class MyNode(Node):

    def __init__(self):
        super(MyNode,self).__init__('my_node')
        self.get_logger().info('Initializing My Node!') # Works fine
        self.app = Flask(__name__) # Flask Stuff
        self.my_ns = NS(self)     # Custom Flask Socket Namespace
        self.socketio = SocketIO(self.app, cors_allowed_origins="*") # Create Socket
        self.socketio.on_namespace(self.my_ns) # Pair the namespace
        self.process_timer = self.create_timer(.1,self.process)
        self.ros_thread = Thread(target=self.process,daemon=True)
        self.ros_thread.start()
        self.get_logger().info('Initialized!')

    def process(self):
        alive = True
        while alive:
            try:
                # Check that the ROS node is still alive
                self.assert_liveliness()
                # Spin the ROS node once...
                rclpy.spin_once(self,timeout_sec=0.0)
                # ...and then momentarily sleep so the other process (socket.io) can run.
                eventlet.greenthread.sleep()
            except:
                alive = False

def main(args=None):
    rclpy.init(args=args)

    node = MyNode()

    # SocketIO's run has eventlet sleeps incorporated when eventlet is imported
    node.socketio.run(node.app)

    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()