鱼香ROS社区
    • 版块
    • 最新
    • 未解决
    • 已解决
    • 群组
    • 注册
    • 登录
    紧急通知:禁止一切关于政治&VPN翻墙等话题,发现相关帖子会立马删除封号
    提问前必看的发帖注意事项: 社区问答规则(小鱼个人)更新 | 高质量帖子发布指南

    ros2自定义客户端中spin_until_future_complete的问题

    已定时 已固定 已锁定 已移动
    ROS 2相关问题
    ros2 service
    3
    13
    3.3k
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • LorryL
      Lorry
      最后由 编辑

      平台:ubuntu20.04 + foxy + c++

      问题:

      官方案例中,创建服务的客户端并没有使用面向对象的方式,主要是通过async_send_request( ) 函数发出请求,通过rclcpp::spin_until_future_complete( )阻塞线程,直到获取到response才会向下执行。
      我想将client封装为一个类,类中定义一个定时器以及一个客户端,定时器回调函数中会发送客户端请求,同时阻塞直到接受server的response(使用spin_until_future_complete方法)。这样有一个问题,rclcpp::spin_until_future_complete( )相当于执行了一次spin,而我定义的类,最终是要添加到executors中,executors再调用spin()方法,本质上就是spin中又调用了spin,在运行时会出现冲突。
      想向论坛的大佬请教一下,ros2中是否有可以替代spin_until_future_complete的其他方法,或者其他方式获取到server是否完成请求的状态?我能想到的就是设置一个循环不停检测server的状态,直到接收到请求再向下执行

      小鱼小 1 条回复 最后回复 回复 引用 0
      • 小鱼小
        小鱼 技术大佬 @Lorry
        最后由 编辑

        @Lorry 这个可以看下动手学ROS2中关于服务客户端编写 的介绍。重点在SharedFuture对象上,通过它的成员函数wait_for()可以实现等待结果有效,但是你应该注意的是这会阻塞线程。

        • https://fishros.com/d2lros2/#/humble/chapt3/get_started/5.服务之RCLCPP实现

        发送服务请求

        3.1.2 async_send_request

        接着我们来看看发送请求的API,地址

        替代文字

        我们这里要用的是这个函数async_send_request()同时传入两个参数

        • request,请求的消息,这里用于放a,b两个数。
        • CallBack,回调函数,异步接收服务器的返回的函数。

        SharedFuture

        这个又臭又长的参数确实让人惊了下,函数的参数是客户端AddTwoInts类型的SharedFuture对象,这个对象的定义如下

        image-20220606230244527

        可以看到其又是利用C++11的新特性std::shared_future创建的SharedResponse类模板。

        类模板 std::shared_future 提供访问异步操作结果的机制,类似 std::future ,除了允许多个线程等候同一共享状态。

        我们具体看看std::shared_future的API

        image-20220606231019702

        新书配套视频:https://www.bilibili.com/video/BV1GW42197Ck/

        LorryL 1 条回复 最后回复 回复 引用 0
        • LorryL
          Lorry @小鱼
          最后由 编辑

          @小鱼 小鱼,我大概明白上面方法的意思。但是在实践上有些蒙(原谅我基础有点差)
          动手学ROS2中代码:

          // 3.发送异步请求,然后等待返回,返回时调用回调函数
          client_->async_send_request(
          request, std::bind(&ServiceClient01::result_callback_, this,
          std::placeholders::_1));
          
          void result_callback_(
          rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedFuture result_future) 
          {
              auto response = result_future.get();
              RCLCPP_INFO(this->get_logger(), "计算结果:%ld", response->sum);
            }
          

          其中,SharedFuture是在回调中使用,回调中如果使用了wait_for(),也不能够实现同步获取response吧?我其实是想实现客户端发送请求,然后同步获取响应,这能实现吗?

          小鱼小 1 条回复 最后回复 回复 引用 0
          • 小鱼小
            小鱼 技术大佬 @Lorry
            最后由 小鱼 编辑

            @Lorry 在 ros2自定义客户端中spin_until_future_complete的问题 中说:

            client_->async_send_reques

            注意这个函数有返回值,返回的就是future

            新书配套视频:https://www.bilibili.com/video/BV1GW42197Ck/

            LorryL 3 条回复 最后回复 回复 引用 0
            • LorryL
              Lorry @小鱼
              最后由 编辑

              @小鱼 这一点我忽略了😿

              1 条回复 最后回复 回复 引用 0
              • LorryL
                Lorry @小鱼
                最后由 编辑

                @小鱼 小鱼,使用wait_for()确实能让线程等待,但是最终接收到的数据的状态一直都是timeout。
                其中,我客户端相关代码:

                auto result=Client_->async_send_request(request);
                    std::future_status status =result.wait_for(100ms);
                    if (status == std::future_status::timeout)//子线程还没执行完
                	{
                		RCLCPP_INFO(this->get_logger(),"Time out,Failed!");
                	}
                	else if (status == std::future_status::ready)//子线程已执行完
                	{
                		RCLCPP_INFO(this->get_logger(),"Ready.....");
                	}
                

                我监测了服务端用于处理请求的耗时,大概是5~20ms之间,客户端等待100ms应该能成功接收到返回的response。不知道为什么每次等待100ms,status 一直都是std::future_status::timeout。(是我代码写错了?😂 )

                小鱼小 1 条回复 最后回复 回复 引用 0
                • LorryL
                  Lorry @小鱼
                  最后由 编辑

                  @小鱼 我对服务端的回调函数的定义也有些疑问,rclpy的定义方式中,会将response作为返回值,在将response赋值后进行回调,如

                  def add_two_ints_callback(self, request, response):
                      response.sum = request.a + request.b
                      self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b))
                          return response
                  

                  而rclcpp中,并没有指定返回值,只是将response赋值,如:

                  void add(const std::shared_ptr<example_interfaces::srv::AddTwoInts::Request> request,
                            std::shared_ptr<example_interfaces::srv::AddTwoInts::Response>      response)
                  {
                    response->sum = request->a + request->b;
                    RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld",
                                  request->a, request->b);
                    RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum);
                  }
                  

                  那response是在赋值后直接发送给client了吗?

                  小鱼小 1 条回复 最后回复 回复 引用 0
                  • 小鱼小
                    小鱼 技术大佬 @Lorry
                    最后由 编辑

                    @Lorry 在 ros2自定义客户端中spin_until_future_complete的问题 中说:

                    不知道为什么每次等待100ms

                    std::future_status status =result.wait_for(100ms);

                    你的代码是这样写的呀,可以不传参试试

                    新书配套视频:https://www.bilibili.com/video/BV1GW42197Ck/

                    LorryL 2 条回复 最后回复 回复 引用 0
                    • 小鱼小
                      小鱼 技术大佬 @Lorry
                      最后由 编辑

                      @Lorry 两者不同是因为编程语言限制,c++可以直接传指针,给指针赋值,而python直接将对象返回比较直接。

                      新书配套视频:https://www.bilibili.com/video/BV1GW42197Ck/

                      1 条回复 最后回复 回复 引用 0
                      • LorryL
                        Lorry @小鱼
                        最后由 编辑

                        @小鱼 我找到解决办法了。我代码中是通过定时器完成定时的客户端请求,所以是在定时循环中发起的async_send_request(request)。当不指定定时器的回调组时,result.wait_for(100ms)一定会等待100ms,并且状态一定是std::future_status::timeout。我后来指定了定时器的回调组rclcpp::CallbackGroupType::MutuallyExclusive,wait_for函数就能正常接收到服务端的返回信息。
                        其实我之前一直以为MutuallyExclusive回调组和不指定回调组的效果一样,看来还要再去看看回调组的原理(对了,wait_for函数必须要传参才行,不传参的是wait)

                        小鱼小 1 条回复 最后回复 回复 引用 0
                        • LorryL
                          Lorry @小鱼
                          最后由 编辑

                          @小鱼 贴一下timer部分的代码,有同样问题的可以看一下

                          callback_group_timer=this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
                              Timer_ = this->create_wall_timer(
                                5000ms, std::bind(&S7ClinetNode::time_callback, this),callback_group_timer);
                          
                          1 1 条回复 最后回复 回复 引用 0
                          • 小鱼小
                            小鱼 技术大佬 @Lorry
                            最后由 编辑

                            @Lorry ☺️好嘞

                            新书配套视频:https://www.bilibili.com/video/BV1GW42197Ck/

                            1 条回复 最后回复 回复 引用 0
                            • 1
                              18801286624 @Lorry
                              最后由 编辑

                              @Lorry 在 ros2自定义客户端中spin_until_future_complete的问题 中说:

                              @小鱼 贴一下timer部分的代码,有同样问题的可以看一下

                              callback_group_timer=this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
                                  Timer_ = this->create_wall_timer(
                                    5000ms, std::bind(&S7ClinetNode::time_callback, this),callback_group_timer);
                              

                              现在遇到了同样的问题,也是result.wait_for一定会等待100ms,并且状态一定是timeout,区别是我是在订阅话题的回调函数中发起的客户端请求,请问有有办法可以解决吗?

                              1 条回复 最后回复 回复 引用 0
                              • 第一个帖子
                                最后一个帖子
                              皖ICP备16016415号-7
                              Powered by NodeBB | 鱼香ROS