“所行之路已有人行!”
本节将学习如何如何编写客户端cpp以及配置相关文件。大概流程基本这样
准备工作
新建功能包client以及client/src/client_demo.cpp
程序源码及分析
代码
#include<ros/ros.h>
#include<turtlesim/Spawn.h> //声明请求和响应类型
int main(int argc, char **argv)
{
ros::init(argc, argv, "spawn_turtle") ;
ros::NodeHandle nh ;
//创建一个Client对象,实际上是该对喜完成服务调用
ros::ServiceClient spawnClient = nh.serviceClient<turtlesim::Spawn>("spawn") ;
//创建服务中的 请求(request)和响应对象(response)
turtlesim::Spawn::Request req ;
turtlesim::Spawn::Response resp ;
//为请求的每一个域赋值,响应的值来自服务器节点,是接受的
req.x = 2 ;
req.y = 3 ;
req.theta = M_PI/2 ;
req.name = "Leo" ;
//调用服务,成功返回true
bool success = spawnClient.call(req, resp) ;
if(success)
{
ROS_INFO_STREAM("Spawned a turtle named :" << resp.name) ;
}
else
{
ROS_INFO_STREAM("failed") ;
}
return 0 ;
}
基本分析在注释中都已经给出,这里详细的说流程几点:
- 包含消息头文件
<turtlesim/Spawn.h>
; - 创建客户端代码
ros::ServiceClient client = nh.serviceClient<消息类型>("服务名") ;
- 没有队列大小,因为服务调用直到响应抵达后才会返回,即客户端要等待服务调用完成;
- 一般来讲,一次服务的调用返回结束,client就会和server失去联系,但是仍有办法使之保持联系,在函数中后追true即可(缺省值为false)。
nh.serviceClient<消息类型>("服务名", true) ;
但是不提倡,会提升效率,但是更改工作量大。
- 创建服务的请求和响应对象,并为请求对象填充数据(为request的每一个域赋值),因为响应对象接受来自服务端的回复,所以一次服务结束后有内容;
还有一种做法是,不用单独的创建request和response对象,直接用一个srv即可。
- 调用服务
bool success = spawnClient.call(request, response) ;
服务调用成功会返回一个bool类型的真。
失败的原因可能是:
- 调用一个没有任何节点提供的服务;
比如上一个例子中同名小海龟节点的服务已经被一个客户端请求,便不再对其他节点提供服务。
配置文件
保证程序的功能包声明了对定义服务类型的功能包的依赖(本例中turtlesim/Spawn.h
)。主要是对turtle,其他必要步骤略,参考前文。
Cmakelist.txt
find_package(catkin REQUIRED COMPONENTS roscpp turtle)
package.xml
<build_depend>turtlesim</build_depend>
<build_export_depend>turtlesim</build_export_depend>
<exec_depend>turtlesim</exec_depend>
运行效果
首先我们打开roscore,运行一个小乌龟的图像。如图:
然后运行代码rosrun client spawn_turtle_node
,启动服务,如下:
且命令行收到回复:
$ rosrun client spawn_turtle_node
[ INFO] [1640428319.327275357]: Spawned a turtle named :Leo