SLAM+语音机器人DIY系列:(二)ROS入门——6.编写简单的service和client

摘要                                          

ROS机器人操作系统在机器人应用领域很流行,依托代码开源和模块间协作等特性,给机器人开发者带来了很大的方便。我们的机器人“miiboo”中的大部分程序也采用ROS进行开发,所以本文就重点对ROS基础知识进行详细的讲解,给不熟悉ROS的朋友起到一个抛砖引玉的作用。本章节主要内容:

1.ROS是什么

2.ROS系统整体架构

3.在ubuntu16.04中安装ROS kinetic

4.如何编写ROS的第一个程序hello_world

5.编写简单的消息发布器和订阅器

6.编写简单的service和client

7.理解tf的原理

8.理解roslaunch在大型项目中的作用

9.熟练使用rviz

10.在实际机器人上运行ROS高级功能预览



6.编写简单的service和client  

上一节介绍了两个ros节点通过发布与订阅消息的通信方式,现在就介绍ros节点间通信的另外一种方式服务。我们将学到:如何自定义服务类型、server端节点编写、client端节点编写等。我就以实现两个整数求和为例,client端节点向server端节点发送ab的请求,server端节点返回响应sum=a+bclient端节点,通信网络结构如图20

20服务请求与响应ROS通信网络结构图

1)功能包的创建

catkin_ws/src/目录下新建功能包service_example,并在创建时显式的指明依赖roscppstd_msgs,依赖std_msgs将作为基本数据类型用于定义我们的服务类型。打开命令行终端,输入命令:

cd ~/catkin_ws/src/

#创建功能包service_example时,显式的指明依赖roscpp和std_msgs,
#依赖会被默认写到功能包的CMakeLists.txt和package.xml中
catkin_create_pkg service_example roscpp std_msgs

2)在功能包中创建自定义服务类型

通过前面的学习,我们知道服务通信过程中服务的数据类型需要用户自己定义,与消息不同,节点并不提供标准服务类型。服务类型的定义文件都是以*.srv为扩展名,并且被放在功能包的srv/文件夹下。

服务类型定义:

首先,在功能包service_example目录下新建srv目录,然后在service_example/srv/目录中创建AddTwoInts.srv文件,文件内容如下:

int64 a
int64 b
---
int64 sum

服务类型编译配置:

定义好我们的服务类型后,要想让该服务类型能在c++python等代码中被使用,必须要做相应的编译与运行配置。编译依赖message_generation,运行依赖message_runtime

打开功能包中的CMakeLists.txt文件,找到下面这段代码:

find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
)

message_generation添加进去,添加后的代码如下:

find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
  message_generation
)

继续,找到这段代码:

# add_service_files(
#   FILES
#   Service1.srv
#   Service2.srv
# )

去掉这段代码的#注释,将自己的服务类型定义文件AddTwoInts.srv填入,修改好后的代码如下:

add_service_files(
  FILES
  AddTwoInts.srv
)

继续,找到这段代码:

# generate_messages(
#   DEPENDENCIES
#   std_msgs
# )

去掉这段代码的#注释,generate_messages的作用是自动创建我们自定义的消息类型*.msg与服务类型*.srv相对应的*.h,由于我们定义的服务类型使用了std_msgs中的int64基本类型,所以必须向generate_messages指明该依赖,修改好后的代码如下:

generate_messages(
  DEPENDENCIES
  std_msgs
)

然后打开功能包中的package.xml文件,填入下面三句依赖:

<build_depend>message_generation</build_depend>
<build_export_depend>message_generation</build_export_depend>
<exec_depend>message_runtime</exec_depend>

检查ROS是否识别新建的服务类型:

我们通过<功能包名/服务类型名>找到该服务,打开命令行终端,输入命令:

source ~/catkin_ws/devel/setup.bash

rossrv show service_example/AddTwoInts

看到下面的输出,如图21,就说明新建服务类型能被ROS识别,新建服务类型成功了。

21新建服务类型能被ROS识别

3)功能包的源代码编写

功能包中需要编写两个独立可执行的节点,一个节点用来作为client端发起请求,另一个节点用来作为server端响应请求,所以需要在新建的功能包service_example/src/目录下新建两个文件server_node.cppclient_node.cpp,并将下面的代码分别填入。

首先,介绍server节点server_node.cpp,代码内容如下:

 

 1 #include "ros/ros.h"
 2 #include "service_example/AddTwoInts.h"
 3 
 4 bool add_execute(service_example::AddTwoInts::Request &req,
 5 service_example::AddTwoInts::Response &res)
 6 {
 7   res.sum = req.a + req.b;
 8   ROS_INFO("recieve request: a=%ld,b=%ld",(long int)req.a,(long int)req.b);
 9   ROS_INFO("send response: sum=%ld",(long int)res.sum);
10   return true;
11 } 
12 
13 int main(int argc,char **argv)
14 {
15   ros::init(argc,argv,"server_node");
16   ros::NodeHandle nh;
17 
18   ros::ServiceServer service = nh.advertiseService("add_two_ints",add_execute);
19   ROS_INFO("service is ready!!!");
20   ros::spin();
21 
22   return 0;
23 }

server节点代码进行解析。

#include “ros/ros.h”

#include “service_example/AddTwoInts.h”

包含头文件ros/ros.h,这是ROS提供的C++客户端库,是必须包含的头文件,就不多说了。service_example/AddTwoInts.h是由编译系统自动根据我们的功能包和在功能包下创建的*.srv文件生成的对应的头文件,包含这个头文件,程序中就可以使用我们自定义服务的数据类型了。

 

bool add_execute(...)

这个函数实现两个int64整数求和的服务,两个int64值从request获取,返回求和结果装入response里,requestresponse的具体数据类型都在前面创建的*.srv文件中被定义,这个函数返回值为bool型。

 

ros::init(argc,argv,”server_node”);

ros::NodeHandle nh;

初始化ros节点并指明节点的名称,声明一个ros节点的句柄,,就不多说了。

 

ros::ServiceServer service = nh.advertiseService(“add_two_ints”,add_execute);

这一句是创建服务,并将服务加入到ROS网络中,并且这个服务在ROS网络中以名称add_two_ints唯一标识,以便于其他节点通过服务名称进行请求。

 

ros::spin();

这一句话让程序进入自循环的挂起状态,从而让程序以最好的效率接收客户端的请求并调用回调函数,就不多说了。

 

接着,介绍client节点client_node.cpp,代码内容如下:

 

 1 #include "ros/ros.h"
 2 #include "service_example/AddTwoInts.h"
 3 
 4 #include <iostream>
 5 
 6 int main(int argc,char **argv)
 7 {
 8   ros::init(argc,argv,"client_node");
 9   ros::NodeHandle nh;
10 
11   ros::ServiceClient client =
12   nh.serviceClient<service_example::AddTwoInts>("add_two_ints");
13   service_example::AddTwoInts srv;
14   
15   while(ros::ok())
16   {
17     long int a_in,b_in;
18     std::cout<<"please input a and b:";
19     std::cin>>a_in>>b_in;
20 
21     srv.request.a = a_in;
22     srv.request.b = b_in;
23     if(client.call(srv))
24     {
25       ROS_INFO("sum=%ld",(long int)srv.response.sum);
26     }
27     else
28     {
29       ROS_INFO("failed to call service add_two_ints");
30     }
31   }
32   return 0;
33 }

client节点代码进行解析。

之前解释过的类似的代码就不做过多的解释了,这里重点解释一下前面没遇到过的代码。

ros::ServiceClient client =

 nh.serviceClient<service_example::AddTwoInts>("add_two_ints");

这一句创建client对象,用来向ROS网络中名称叫add_two_intsservice发起请求。

 

service_example::AddTwoInts srv;

定义了一个service_example::AddTwoInts服务类型的对象,该对象中的成员正是我们在*.srv文件中定义的absum,我们将待请求的数据填充到数据成员ab,请求成功后返回结果会被自动填充到数据成员sum中。

 

if(client.call(srv)){...}

这一句便是通过client的方法call来向service发起请求,请求传入的参数srv在上面已经介绍过了。

4)功能包的编译配置及编译

创建功能包service_example时,显式的指明依赖roscppstd_msgs,依赖会被默认写到功能包的CMakeLists.txtpackage.xml中,并且在功能包中创建*.srv服务类型时已经对服务的编译与运行做了相关配置,所以只需要在CMakeLists.txt文件的末尾行加入以下几句用于声明可执行文件就可以了:

add_executable(server_node src/server_node.cpp)
target_link_libraries(server_node ${catkin_LIBRARIES})
add_dependencies(server_node service_example_gencpp)

add_executable(client_node src/client_node.cpp)
target_link_libraries(client_node ${catkin_LIBRARIES})
add_dependencies(client_node service_example_gencpp)

这里面的add_executable用于声明可执行文件。target_link_libraries用于声明可执行文件创建时需要链接的库。add_dependencies用于声明可执行文件的依赖项,由于我们自定义了*.srvservice_example_gencpp的作用是让编译系统自动根据我们的功能包和在功能包下创建的*.srv文件生成的对应的头文件和库文件,service_example_gencpp这个名称是由功能包名称service_example加上_gencpp后缀而来的,后缀很好理解:生成c++文件就是_gencpp

生成python文件就是_genpy

接下来,就可以用下面的命令对功能包进行编译了:

cd ~/catkin_ws/
catkin_make -DCATKIN_WHITELIST_PACKAGES="service_example"

5)功能包的启动运行

首先,需要用roscore命令来启动ROS节点管理器,ROS节点管理器是所有节点运行的基础。

打开命令行终端,输入命令:

roscore

然后,用source devel/setup.bash激活catkin_ws工作空间,用rosrun <package_name> <node_name>启动功能包中的server节点。

再打开一个命令行终端,分别输入命令:

cd ~/catkin_ws/
source devel/setup.bash
rosrun service_example server_node 

看到有输出servive is ready!!!”,就说明server节点已经正常启动并开始等待client节点向自己发起请求了,如图22

(图22server节点已经正常启动

最后,用source devel/setup.bash激活catkin_ws工作空间,用rosrun <package_name> <node_name>启动功能包中的client节点。

再打开一个命令行终端,分别输入命令:

cd ~/catkin_ws/
source devel/setup.bash
rosrun service_example client_node 

看到有输出提示信息please input a and b:”后,键盘键入两个整数,以空格分割,输入完毕后回车。如果看到输出信息“sum=xxx”,就说明client节点向server端发起的请求得到了响应,打印出来的sum就是响应结果,这样就完成了一次服务请求的通信过程,如图23

(图23client节点已经正常启动

到这里,我们编写的serverclient就大功告成了,为了加深对整个程序工作流程的理解,我再把serverclientROS通信网络结构图拿出来,加深一下理解。

24服务请求与响应ROS通信网络结构图

后记                

------SLAM+语音机器人DIY系列【目录】快速导览------

第1章:Linux基础

1.Linux简介

2.安装Linux发行版ubuntu系统

3.Linux命令行基础操作

第2章:ROS入门

1.ROS是什么

2.ROS系统整体架构

3.在ubuntu16.04中安装ROS kinetic

4.如何编写ROS的第一个程序hello_world

5.编写简单的消息发布器和订阅器

6.编写简单的service和client

7.理解tf的原理

8.理解roslaunch在大型项目中的作用

9.熟练使用rviz

10.在实际机器人上运行ROS高级功能预览

第3章:感知与大脑

1.ydlidar-x4激光雷达

2.带自校准九轴数据融合IMU惯性传感器

3.轮式里程计与运动控制

4.音响麦克风与摄像头

5.机器人大脑嵌入式主板性能对比

6.做一个能走路和对话的机器人

第4章:差分底盘设计

1.stm32主控硬件设计

2.stm32主控软件设计

3.底盘通信协议

4.底盘ROS驱动开发

5.底盘PID控制参数整定

6.底盘里程计标

第5章:树莓派3开发环境搭建

1.安装系统ubuntu_mate_16.04

2.安装ros-kinetic

3.装机后一些实用软件安装和系统设置

4.PC端与robot端ROS网络通信

5.Android手机端与robot端ROS网络通信

6.树莓派USB与tty串口号绑定

7.开机自启动ROS节点

第6章:SLAM建图与自主避障导航

1.在机器人上使用传感器

2.google-cartographer机器人SLAM建图

3.ros-navigation机器人自主避障导航

4.多目标点导航及任务调度

5.机器人巡航与现场监控

第7章:语音交互与自然语言处理

1.语音交互相关技术

2.机器人语音交互实现

3.自然语言处理云计算引擎

第8章:高阶拓展

1.miiboo机器人安卓手机APP开发

2.centos7下部署Django(nginx+uwsgi+django+python3)

----------------文章将持续更新,敬请关注-----------------

 

如果大家对博文的相关类容感兴趣,或有什么技术疑问,欢迎加入下面的《SLAM+语音机器人DIY》QQ技术交流群,一起讨论学习^_^

关于我们:

视频教程:

 

https://www.bilibili.com/video/av61448040

posted @ 2019-02-17 16:54 小虎哥哥爱学习 阅读(...) 评论(...) 编辑 收藏