介绍service服务的编程实现
之前命令行操作中,我们有通过命令行在海龟仿真器当中去生成一只新的海龟;下面通过程序的形式可以去发布一个服务请求来实现客户端去让海龟仿真器产生一只新的海龟
Server端,小海龟仿真器
Client端,要实现的程序,及一个客户端请求的节点,其会发布一个产生海龟的请求发给Server端,Server端收到请求后会回馈一个response,看请求是否成功
中间用到service的名称是spawn,用到的消息结构是turtlesim::Spawn
整个节点仍由ROS Master做管理
第一步,创建功能包
cd ~/catkin_ws/src 进入到工作空间里src文件夹里
catkin_create_pkg learning_service roscpp rospy std_msgs geometry_msgs turtlesim 创建名为learning_service功能包
第二步,创建客户端代码( C++例)
初始化ROS节点
创建一个Client实例
发布服务请求数据
等待Server处理之后的应答结果
将课件代码/learning_service/src里的turtle_spawn.cpp代码文件放置于catkin_ws/src/learning_service/src里
turtle_spawn.cpp代码文件
/**
* 该例程将请求/spawn服务,服务数据类型turtlesim::Spawn
*/
#include <ros/ros.h>
#include <turtlesim/Spawn.h>
//包含这样一个数据类型的头文件
int main(int argc, char** argv)
{
// 初始化ROS节点,初始化节点名为turtle_spawn
ros::init(argc, argv, "turtle_spawn");
// 创建节点句柄
ros::NodeHandle node;
//接下来创建跟服务相关代码,第一个waitForService,查询当前系统里是否有spawn这样一个服务,没有则会一直等待spawn这样一个服务,故这是一个阻塞型的api或函数,确认该服务存在;可见只有服务在系统当中存在了,才能去请求该服务;第二个,紧接着创建客户端ServiceClient,是用来给spawn服务发送请求的,请求的数据类型是turtlesim::Spawn,服务的名字是spawn
// 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service
ros::service::waitForService("/spawn");
ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");
//紧接着封装请求数据,数据是turtlesim::Spawn这样类型的数据,定义一个srv,srv里要去添入请求类型的数据(在终端,我们需要请求数据包括产生海龟的xy坐标以及海龟的角度,角度没有时默认0,另外还有海龟的名字)
// 初始化turtlesim::Spawn的请求数据
turtlesim::Spawn srv;
srv.request.x = 2.0;
srv.request.y = 2.0;
srv.request.name = "turtle2";
//发送这样一个日志,告诉终端,已经准备好发送这样一个请求
// 请求服务调用
ROS_INFO("Call service to spwan turtle[x:%0.6f, y:%0.6f, name:%s]",
srv.request.x, srv.request.y, srv.request.name.c_str());
//调用call这样一个方法(之前在topic通讯是publish,subscrib,而在servic里请求的方法叫call),call也是阻塞型函数,会把request发出去并一直等待服务器给予反馈,直到海龟已经产生成功了,该句指令才会运行过去到最后一句显示调用结果,成功显示新产生海龟名字
add_turtle.call(srv);
// 显示服务调用结果
ROS_INFO("Spwan turtle successfully [name:%s]", srv.response.name.c_str());
return 0;
};
第三步,配置客户端代码编译规则
add_executable(turtle_spawn src/turtle_spawn.cpp)
target_link_libraries(turtle_spawn ${catkin_LIBRARIES})
将该两行代码粘贴至CMakeLists.txt文件,Install前,如下位置
第一句,添加一个把turtle_spawn.cpp的代码编译成turtle_spawn这样一个规则
第二句,给turtle_spawn添加对应的链接库
第四步.编译并运行客户端
cd ~/catkin_ws //回到工作空间根目录下
catkin_make //用该指令去做编译
编译结束后,可以在catkin_ws/devel/lib下面新产生的功能包learning_service,其里面就是刚才代码所编译生成的可执行文件
source devel/setup.bash //设置环境变量,可通过下面方法减去该步骤,跳过
roscore
rosrun turtlesim turtlesim_node
rosrun learning_service turtle_spawn //运行刚才实现的程序节点,运行learning_service里的一个节点,节点名为turtle_spawn
通过程序去发布请求产生一只新的海龟
第一句是请求service,请求内容:x,y坐标和name
第二句是产生成功后应答的数据
turtlesim一端也可以看到信息
发现有客户端请求这样的服务;
产生海龟,海龟1,海龟2 的位置
以上是使用c++程序来实现的产生一直海龟的service的客户端的实现方法,下面python程序
创建客户端代码(python)
(流程基本相同,只是函数及语法不同)
初始化ROS节点
创建一个Client实例
根据服务类型发布服务请求数据(比如这里的spawn类型,即包括海龟的x,y,角度,名字)
等待Server处理之后的应答结果
将课件代码/learning_service/scripts里的turtle_spawn.py代码文件放置于catkin_ws/src/learning_service/scripts里(专门创建一个文件夹scripts用于放python文件)
turtle_spawn.py代码文件
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 该例程将请求/spawn服务,服务数据类型turtlesim::Spawn
import sys
import rospy
from turtlesim.srv import Spawn
def turtle_spawn():
# ROS节点初始化
rospy.init_node('turtle_spawn')
# 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service
#第一句,等待确认这个服务是存在的;第二句,创建一个针对的是spawn的这个客户端,其数据类型是Spawn,需要最开始from turtlesim.srv import Spawn引入
rospy.wait_for_service('/spawn')
try:
add_turtle = rospy.ServiceProxy('/spawn', Spawn)
# 请求服务调用,输入请求数据
#接下来,请求调用,这里不是调用.call而是通过add_turtle,后面加请求数据x,y坐标,角度值,名字;这里也是阻塞型,等到服务器产生海龟并产生海龟之后就会得到response,然后return海龟的名字,然后打印
response = add_turtle(2.0, 2.0, 0.0, "turtle2")
return response.name
except rospy.ServiceException, e:
print "Service call failed: %s"%e
if __name__ == "__main__":
#服务调用并显示调用结果
print "Spwan turtle successfully [name:%s]" %(turtle_spawn())
#一进来就会调用print,后调用turtle_spawn()函数