robagar / tello-asyncio

A library for controlling and interacting with the Tello EDU drone using modern asynchronous Python.
GNU Lesser General Public License v2.1
25 stars 5 forks source link

ROS integration #12

Closed iamrobusto closed 1 year ago

iamrobusto commented 2 years ago

How does one implement rospy subscriber callbacks with asyncio?

I've tried both create_taskand run_coroutine_threadsafe suggested here https://answers.ros.org/question/362598/asyncawait-in-subscriber-callback/ without success.

run_coroutine_threadsafe looks like the solution but I can't make it work.. Have you had experience with your library of inserting a coroutine into the main loop from another thread?

Example:

self.rl = asyncio.get_running_loop() 

takeOffSub = rospy.Subscriber('takeoff', Empty, self.takeoff_callback)

async def myTakeOff(self):
        await self.drone.takeoff()

def takeoff_callback(self,msg):
        print("taking off")
        future = asyncio.run_coroutine_threadsafe(self.myTakeOff(), self.rl)
        result = future.result()
        print(result)

ROS works on a publisher-subscriber topic model. In this case , when a message (Empty) is published (by another program running simultaneously) to the topic 'takeoff', takeoff-callback() is called. This cannot be a coroutine, so cannot be async; hence, the use of run_coroutine_threadsafe. This call never returns and myTakeOff() is not called. Any ideas what is wrong? Note: Execution does pass to the callback, "taking off" is printed. myTakeOff() runs successfully when called directly from the the main loop with await.

iamrobusto commented 2 years ago

I found one way : simply have the subscriber callbacks set a class var, say self.command, with the appropriate command. Then, in the main loop call the handy send() coroutine with self.command if it is not empty. Set self.command to blank after a command is sent.