小鱼 ROS 2 新书上线!点击链接查看, 新书配套视频点击链接查看。
提问前必看的发帖注意事项—— 提问前必看!不符合要求的问题拒绝回答!!
社区使用指南—如何添加标签修改密码
ros2自定义客户端中spin_until_future_complete的问题
-
平台: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的状态,直到接收到请求再向下执行 -
@Lorry 这个可以看下动手学ROS2中关于服务客户端编写 的介绍。重点在SharedFuture对象上,通过它的成员函数wait_for()可以实现等待结果有效,但是你应该注意的是这会阻塞线程。
发送服务请求
3.1.2 async_send_request
接着我们来看看发送请求的API,地址
我们这里要用的是这个函数async_send_request()同时传入两个参数
- request,请求的消息,这里用于放a,b两个数。
- CallBack,回调函数,异步接收服务器的返回的函数。
SharedFuture
这个又臭又长的参数确实让人惊了下,函数的参数是客户端
AddTwoInts
类型的SharedFuture
对象,这个对象的定义如下可以看到其又是利用C++11的新特性
std::shared_future
创建的SharedResponse
类模板。类模板
std::shared_future
提供访问异步操作结果的机制,类似 std::future ,除了允许多个线程等候同一共享状态。我们具体看看std::shared_future的API
-
@小鱼 小鱼,我大概明白上面方法的意思。但是在实践上有些蒙(原谅我基础有点差)
动手学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吧?我其实是想实现客户端发送请求,然后同步获取响应,这能实现吗?
-
@Lorry 在 ros2自定义客户端中spin_until_future_complete的问题 中说:
client_->async_send_reques
注意这个函数有返回值,返回的就是future
-
@小鱼 这一点我忽略了
-
@小鱼 小鱼,使用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。(是我代码写错了? )
-
@小鱼 我对服务端的回调函数的定义也有些疑问,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了吗?
-
@Lorry 在 ros2自定义客户端中spin_until_future_complete的问题 中说:
不知道为什么每次等待100ms
std::future_status status =result.wait_for(100ms);
你的代码是这样写的呀,可以不传参试试
-
@Lorry 两者不同是因为编程语言限制,c++可以直接传指针,给指针赋值,而python直接将对象返回比较直接。
-
@小鱼 我找到解决办法了。我代码中是通过定时器完成定时的客户端请求,所以是在定时循环中发起的
async_send_request(request)
。当不指定定时器的回调组时,result.wait_for(100ms)
一定会等待100ms,并且状态一定是std::future_status::timeout
。我后来指定了定时器的回调组rclcpp::CallbackGroupType::MutuallyExclusive
,wait_for函数就能正常接收到服务端的返回信息。
其实我之前一直以为MutuallyExclusive回调组和不指定回调组的效果一样,看来还要再去看看回调组的原理(对了,wait_for函数必须要传参才行,不传参的是wait) -
@小鱼 贴一下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);
-
@Lorry ️好嘞