Python 3.x 如何使用ros2服务触发异步IO非阻塞功能(无操作)

Python 3.x 如何使用ros2服务触发异步IO非阻塞功能(无操作),python-3.x,python-asyncio,ros2,Python 3.x,Python Asyncio,Ros2,我遇到的问题是: 要在后台启动异步函数,我需要一个asycio事件 循环 此事件循环通常存在于主线程中,启动时, 阻止该线程的执行(即启动后的代码行 在取消事件循环之前,不会运行事件循环) 但是,ROS2有自己的事件循环(executor),通常也在主线程中运行 并阻止执行。这意味着很难同时运行两个事件循环 我尝试的解决方案是在一个单独的线程中启动asyncio事件循环。这在节点构造函数中启动,在解构节点后停止 这看起来像这样: class IncrementPercentDoneSe

我遇到的问题是:

  • 要在后台启动异步函数,我需要一个asycio事件 循环

  • 此事件循环通常存在于主线程中,启动时, 阻止该线程的执行(即启动后的代码行 在取消事件循环之前,不会运行事件循环)

  • 但是,ROS2有自己的事件循环(executor),通常也在主线程中运行 并阻止执行。这意味着很难同时运行两个事件循环

我尝试的解决方案是在一个单独的线程中启动asyncio事件循环。这在节点构造函数中启动,在解构节点后停止

这看起来像这样:

class IncrementPercentDoneServiceNode(Node):
    

    def __create_task(self, f: Awaitable):
        self.__task = self.__loop.create_task(f)

    def __init__(self):

        super().__init__('increment_percent_done_service_node')
        
        self.__loop = asyncio.new_event_loop()
        self.__task: Optional[Task] = None
        self.__thread = threading.Thread(target=self.__loop.run_forever)
        self.__thread.start()
        self.done = False

        self.create_service(Trigger, 'start_incrementing',
            callback=lambda request, responce : (
                self.get_logger().info("Starting service"),
                self.__loop.call_soon_threadsafe(self.__create_task, self.__increment_percent_complete()),
                TriggerResponse(success=True, message='')
            )[-1]
        )

    def __del__(self):
        print("stopping loop")
        self.done = True
        if self.__task is not None:
            self.__task.cancel()
            
        self.__loop.stop()
        self.__thread.join()

    async def __increment_percent_complete(self):
        timeout_start = time.time()        
        duration = 5

        while time.time() < (timeout_start + duration):
            time_since_start = time.time() - timeout_start
            percent_complete = (time_since_start / duration) * 100.0
            self.get_logger().info("Percent complete: {}%".format(percent_complete))
            await asyncio.sleep(0.5)

        self.get_logger().info("leaving async function")
        self.done = True

if __name__ == '__main__':

    rclpy.init()
    test = IncrementPercentDoneServiceNode()
    e = MultiThreadedExecutor()
    e.add_node(test)
    e.spin()
class IncrementPercentDoneServiceNode(节点):
定义创建任务(self,f:等待):
self.\u任务=self.\u循环。创建任务(f)
定义初始化(自):
super()
self.\uuuu loop=asyncio.new\u event\u loop()
self.\u任务:可选[任务]=无
self.\uuuu thread=threading.thread(target=self.\uuuu loop.run\u)
self.\uuu thread.start()
self.done=False
self.create\u服务(触发“开始增量”,
回调=lambda请求,响应一次:(
self.get_logger().info(“启动服务”),
self.\uuuu循环。调用\u soon\u threadsafe(self.\uuuu创建\u任务,self.\uuu增量\u完成百分比()),
TriggerResponse(成功=真,消息=“”)
)[-1]
)
定义(自我):
打印(“停止循环”)
self.done=True
如果self.\u任务不是None:
self.\u任务.取消()
self.\u loop.stop()
self.\uu thread.join()
异步定义增量完成百分比(自):
timeout\u start=time.time()
持续时间=5
while time.time()<(超时\u开始+持续时间):
自启动以来的时间=time.time()-超时启动
完成百分比=(自开始/持续时间起的时间)*100.0
self.get_logger().info(“完成百分比:{}%.”格式(完成百分比))
等待异步睡眠(0.5)
self.get_logger().info(“退出异步函数”)
self.done=True
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
rclpy.init()
测试=递增百分比DoneServiceNode()
e=多线程执行器()
e、 添加_节点(测试)
e、 自旋()

这是明智的做法吗?有更好的办法吗?我如何用另一个服务取消
开始增量
服务?(我知道这是操作的目的,但我不能在本例中使用它们)。

ROS2有自己的事件循环(执行器)-是事件循环还是执行器?这两个概念在Python中非常不同。如果它运行一个asyncio事件循环,您可以将您的协程挂接到它的循环中,从而利用它。它表明它不使用asyncio偶数循环:有没有办法让一个异步函数在没有asycio事件循环的情况下工作?这很有趣,所以它们似乎支持协程,但不支持asyncio协程。在这种情况下,我想您的方法是正确的,尽管我仍然会使用一个异步IO事件循环(预先创建)并使用
asyncio.run\u coroutine\u threadsafe
向其提交任务。