ROS机器人操做系统在机器人应用领域很流行,依托代码开源和模块间协做等特性,给机器人开发者带来了很大的方便。咱们的机器人“miiboo”中的大部分程序也采用ROS进行开发,因此本文就重点对ROS基础知识进行详细的讲解,给不熟悉ROS的朋友起到一个抛砖引玉的做用。本章节主要内容:html
1.ROS是什么node
2.ROS系统总体架构python
3.在ubuntu16.04中安装ROS kineticios
4.如何编写ROS的第一个程序hello_worldnginx
6.编写简单的service和clientdjango
7.理解tf的原理ubuntu
8.理解roslaunch在大型项目中的做用centos
9.熟练使用rvizbash
上一节介绍了两个ros节点经过发布与订阅消息的通讯方式,如今就介绍ros节点间通讯的另一种方式服务。咱们将学到:如何自定义服务类型、server端节点编写、client端节点编写等。我就以实现两个整数求和为例,client端节点向server端节点发送a、b的请求,server端节点返回响应sum=a+b给client端节点,通讯网络结构如图20。
(图20)服务请求与响应ROS通讯网络结构图
(1)功能包的建立
在catkin_ws/src/目录下新建功能包service_example,并在建立时显式的指明依赖roscpp和std_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.cpp和client_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里,request与response的具体数据类型都在前面建立的*.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_ints的service发起请求。
service_example::AddTwoInts srv;
定义了一个service_example::AddTwoInts服务类型的对象,该对象中的成员正是咱们在*.srv文件中定义的a、b、sum,咱们将待请求的数据填充到数据成员a、b,请求成功后返回结果会被自动填充到数据成员sum中。
if(client.call(srv)){...}
这一句即是经过client的方法call来向service发起请求,请求传入的参数srv在上面已经介绍过了。
(4)功能包的编译配置及编译
建立功能包service_example时,显式的指明依赖roscpp和std_msgs,依赖会被默认写到功能包的CMakeLists.txt和package.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用于声明可执行文件的依赖项,因为咱们自定义了*.srv,service_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。
(图22)server节点已经正常启动
最后,用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。
(图23)client节点已经正常启动
到这里,咱们编写的server和client就大功告成了,为了加深对整个程序工做流程的理解,我再把server与client的ROS通讯网络结构图拿出来,加深一下理解。
(图24)服务请求与响应ROS通讯网络结构图
------SLAM+语音机器人DIY系列【目录】快速导览------
第1章:Linux基础
第2章:ROS入门
第3章:感知与大脑
第4章:差分底盘设计
第5章:树莓派3开发环境搭建
第6章:SLAM建图与自主避障导航
2.google-cartographer机器人SLAM建图
第7章:语音交互与天然语言处理
第8章:高阶拓展
2.centos7下部署Django(nginx+uwsgi+django+python3)
----------------文章将持续更新,敬请关注-----------------
若是你们对博文的相关类容感兴趣,或有什么技术疑问,欢迎加入下面的《SLAM+语音机器人DIY》QQ技术交流群,一块儿讨论学习^_^
关于咱们:
视频教程: