ROS(kinetic)有用指令集

本文中的环境为Ubuntu16.04LTS + ROS(kinetic).

前言

由于从ROS的官网上的教程并没有实时更新,上面混杂了一些旧版本的东西,看起来有些麻烦,而且有的介绍过于繁琐,因此本篇博客旨在介绍笔者使用版本的精简指令集。关于指令的详细介绍,还需从官网上查看相关内容。

指令介绍

ROS安装指令

在执行下面的指令前,请注意先将源换位内地源,推荐清华源。

2021.08.09由于一些原因,官方的安装教程有所改变:
ROS 官方教程:http://wiki.ros.org/ROS/Tutorials
ROS kinetic 版本安装:http://wiki.ros.org/kinetic/Installation/Ubuntu
ROS rosdep 的问题
执行sudo rosdep init 出错,参考 解决方案1 解决方案2
上一步正确后,执行 rosdep update的问题,这个一般是由于Python版本不兼容造成的。注意,这时候系统不应存在已经加载的Python3环境(实际环境中有多个Python版本注意切换,双系统的虚拟环境,则应该关闭虚拟环境,还不行就要重启系统再尝试)
上述操作建议科学上网后再进行。

1
2
3
4
5
6
7
8
9
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
sudo apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net:80 --recv-key 0xB01FA116
sudo apt-get update #如果更新失败提示GPT error,输入sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com F42ED6FBAB17C654
sudo apt-get install ros-kinetic-desktop-full
sudo rosdep init
rosdep update
echo "source /opt/ros/kinetic/setup.bash" >> ~/.bashrc
source ~/.bashrc
sudo apt install python-rosinstall python-rosinstall-generator python-wstool build-essential

UR5的仿真环境安装与使用

1
2
3
4
sudo apt-get install ros-kinetic-universal-robot
roslaunch ur_gazebo ur5.launch
roslaunch ur5_moveit_config ur5_moveit_planning_execution.launch sim:=true
roslaunch ur5_moveit_config moveit_rviz.launch config:=true

ROS文件系统介绍

1
2
3
4
5
6
7
sudo apt-get install ros-kinetic-ros-tutorials		//安装ros-tutorials程序包
rospack find roscpp //rospack find [包名称] rospack (/rospack)允许你获取软件包的有关信息。在本教程中,我们只涉及到rospack中find参数选项,该选项可以返回软件包的路径信息。
roscd roscpp //roscd [本地包名称[/子目录]] 直接切换工作目录到某个软件包或者软件包集当中
pwd //输出当前工作目录
echo $ROS_PACKAGE_PATH //查看ROS_PACKAGE_PATH
roscd log //切换到ROS保存日记文件的目录下。如果你没有执行过任何ROS程序,系统会报错说该目录不存在。
rosls roscpp_tutorials //rosls [本地包名称[/子目录]] 直接按软件包的名称而不是绝对路径来罗列目录

使用catkin创建工作空间

1
2
3
4
5
6
source /opt/ros/kinetic/setup.bash					//sourced your environment
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/
catkin_make
source devel/setup.bash
echo $ROS_PACKAGE_PATH //确认环境变量包含了目录

创建ROS程序包

1
2
3
4
cd ~/catkin_ws/src
catkin_create_pkg beginner_tutorials std_msgs rospy roscpp //使用catkin_create_pkg命令来创建一个名为'beginner_tutorials'的新程序包,这个程序包依赖于std_msgs、roscpp和rospy
rospack depends1 beginner_tutorials //使用rospack命令来查看一级依赖包
rospack depends beginner_tutorials //使用rospack可以递归检测出所有的依赖包

本节还介绍了.xml文件的书写方法

编译ROS程序包

1
2
3
4
source /opt/ros/kinetic/setup.bash					//先source你的环境配置(setup)文件
cd catkin工作空间
catkin_make //编译
catkin_make --source my_src //编译存放在了my_src中,而非在默认工作空间中(~/catkin_ws/src)的源代码

理解ROS节点

1
2
3
4
5
6
7
8
9
sudo apt-get install ros-kinetic-ros-tutorials		//安装龟龟模拟器
roscore //运行所有ROS程序前首先要运行的命令
rosnode list //列出活跃的节点,本例为/rosout
rosnode info /rosout //返回特定节点的信息
rosrun turtlesim turtlesim_node //打开新终端,运行turtlesim。 rosrun [package_name] [node_name]
rosnode list //打开新终端,查看节点:/rosout /turtlesim
rosrun turtlesim turtlesim_node __name:=my_turtle //关闭原来的turtlesim,打开新终端运行。改变节点名称
rosnode list //查看新的节点名称:/rosout /my_turtle
rosnode ping my_turtle //使用另一个节点来ping该节点

理解ROS话题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
roscore 							//新开一个终端,打开ros
rosrun turtlesim turtlesim_node //新开一个终端。这是接受消息的节点
rosrun turtlesim turtle_teleop_key //新开一个终端,通过键盘来控制龟龟运动。这也是发布消息的节点
rosrun rqt_graph rqt_graph //新开一个终端,创建一个显示当前系统运行情况的动态图形
rostopic -h //新开一个终端,查看rostopic的子命令
rostopic echo /turtle1/cmd_vel //你可能看不到任何东西因为现在还没有数据发布到该话题上。接下来我们通过按下方向键使turtle_teleop_key节点发布数据。可以返回rqt_graph中刷新查看
rostopic list -v //查看有关所发布和订阅的话题及其类型的详细信息
rostopic type /turtle1/cmd_vel //查看发布在某个话题上的消息类型,会看到:geometry_msgs/Twist
rosmsg show geometry_msgs/Twist //查看消息的详细情况。根据该结果可以知道turtlesim节点所期望的消息类型(可看到包含linear和angular两个值),接下来我们就可以给turtle发布命令了
rostopic pub -1 /turtle1/cmd_vel geometry_msgs/Twist -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]'
//rostopic pub可以把数据发布到当前某个正在广播的话题上。用法:rostopic pub [topic] [msg_type] [args]
//以上命令会发送一条消息给turtlesim,告诉它以2.0大小的线速度和1.8大小的角速度开始移动。
//-1: (单个破折号)这个参数选项使rostopic发布一条消息后马上退出。
///turtle1/cmd_vel: 消息所发布到的话题名称
//geometry_msgs/Twist: 所发布消息类型
//--: 双破折号)这会告诉命令选项解析器接下来的参数部分都不是命令选项。这在参数里面包含有破折号-(比如负号)时是必须要添加的
//'[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]': 正如之前提到的,在一个geometry_msgs/Twist消息里面包含有两个浮点型元素:linear和angular。
//在本例中,'[2.0, 0.0, 0.0]'是linear的值,'[0.0, 0.0, 1.8]'是angular的值。这些参数其实是按照YAML语法格式编写的,这在YAML文档 (/ROS/YAMLCommandLine)中有更多的描述。
rostopic pub /turtle1/cmd_vel geometry_msgs/Twist -r 1 -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]'
//这条命令以1Hz的频率发布速度命令到速度话题上。使用rostopic pub -r命令来发布一个稳定的命令流
rostopic hz /turtle1/pose //rostopic hz命令可以用来查看数据发布的频率。 rostopic hz [topic]
rostopic type /turtle1/pose | rosmsg show
//结合rostopic type和rosmsg show命令来获取关于某个话题的更深层次的信息
//可以看到pose下有五个信息: x y theta linear_velocity angular_velocity
rosrun rqt_plot rqt_plot //使用rqt_plot命令来绘制正在发布到/turtle1/pose话题上的数据变化图形
//这会弹出一个新窗口,在窗口左上角的一个文本框里面你可以添加需要绘制的话题。在里面输入/turtle1/pose/x后之前处于禁用状态的加号按钮将会被使能变亮。按一下该按钮,并
//对/turtle1/pose/y重复相同的过程。现在你会在图形中看到turtle的x-y位置坐标图。
//按下减号按钮会显示一组菜单让你隐藏图形中指定的话题。现在隐藏掉你刚才添加的话题并添加/turtle1/pose/theta

理解ROS服务和参数

在小乌龟仍然启动的基础上:

1
2
3
4
5
6
7
8
9
rosservice list						//输出可用服务的信息
rosservice call //调用带参数的服务
rosservice type //输出服务类型
rosservice find //依据类型寻找服务find services by service type
rosservice uri //输出服务的ROSRPC uri
rosservice type clear //查看clear服务类型,clear是rosservice list中的一种服务类型。rosservice type [service]
rosservice call clear //代用clear服务,该服务清除了turtlesim_node的背景上的轨迹。rosservice call [service] [args]
rosservice type spawn| rossrv show //查看再生(spawn)服务的信息
rosservice call spawn 2 2 0.2 "" //调用再生服务,在给定的位置和角度生成一只新的乌龟。名字参数是可选的,这里我们不设具体的名字,让turtlesim自动创建一个。
1
2
3
4
5
6
7
8
9
10
11
12
13
rosparam set 						//设置参数
rosparam get //获取参数
rosparam load //从文件读取参数
rosparam dump //向文件中写入参数
rosparam delete //删除参数
rosparam list //列出节点在服务器上的参数名
rosparam set background_r 150 //修改背景颜色的红色通道。 rosparam set [param_name]
rosservice call clear //上述指令修改了参数的值,现在我们调用清除服务使得修改后的参数生效
rosparam get background_g //获取背景的绿色通道的值。 rosparam get [param_name]
rosparam get / //显示参数服务器上的所有内容
rosparam dump params.yaml //将所有的参数写入params.yaml文件。 rosparam dump [file_name]
rosparam load params.yaml copy //将yaml文件重载入新的命名空间,比如说copy空间。 rosparam load [file_name] [namespace]
rosparam get copy/background_b //获取copy空间的蓝色背景参数

使用 rqt_console 和 roslaunch

1
2
3
4
5
6
7
8
9
10
11
sudo apt-get install ros-kinetic-rqt ros-kinetic-rqt-common-plugins ros-kinetic-turtlesim
//预先安装需要的程序包,若安装,请忽略
rosrun rqt_console rqt_console //新终端中运行。 rqt_console属于ROS日志框架(logging framework)的一部分,用来显示节点的输出信息
rosrun rqt_logger_level rqt_logger_level //新终端中运行。 rqt_logger_level允许我们修改节点运行时输出信息的日志等级(logger levels)(包括 DEBUG、WARN、INFO和ERROR)。
rosrun turtlesim turtlesim_node //新终端中运行。启动turtlesim
rostopic pub /turtle1/cmd_vel geometry_msgs/Twist -r 1 -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, 0.0]'
//小龟龟动起来再查看rqt_console中的输出
roscd beginner_tutorials //打开之前创建的beginner_tutorials包,如果打不开,有两种办法解决:
//1.输入绝对路径:cd ~/catkin_ws/src/beginner_tutorials
//2.设置当前终端环境路径:export ROS_PACKAGE_PATH=~/catkin_ws/src:$ROS_PACKAGE_PATH。 成功后再输入:roscd beginner_tutorials
mkdir launch && cd launch //创建并打开launch文件夹

新建一个空文件,命名为turtlemimic.launch,并在其中写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<launch>

<group ns="turtlesim1">
<node pkg="turtlesim" name="sim" type="turtlesim_node"/>
</group>

<group ns="turtlesim2">
<node pkg="turtlesim" name="sim" type="turtlesim_node"/>
</group>

<node pkg="turtlesim" name="mimic" type="mimic">
<remap from="input" to="turtlesim1/turtle1"/>
<remap from="output" to="turtlesim2/turtle1"/>
</node>

</launch>

在3~9行我们创建了两个节点分组并以’命名空间(namespace)’标签来区分,其中一个名为turtulesim1,另一个名为turtlesim2,两个组里面都使用相同的turtlesim节点并命名为’sim’。这样可以让我们同时启动两个turtlesim模拟器而不会产生命名冲突。在11~14行我们启动模仿节点,并将所有话题的输入和输出分别重命名为turtlesim1和turtlesim2,这样就会使turtlesim2模仿turtlesim1。第1行和第16行分别表示luanch文件的开始标签和结束标签。

1
2
3
roslaunch beginner_tutorials turtlemimic.launch			//通过roslaunch命令启动launch文件,从而启动多个节点
rostopic pub /turtlesim1/turtle1/cmd_vel geometry_msgs/Twist -r 1 -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, -1.8]'
//在一个新终端中使用rostopic命令发送速度设定消息

使用rosed编辑ROS中的文件

如果习惯使用emacs,需要先将emacs设为默认编辑器,如果习惯使用vim,需要安装vim。这里以设置emacs为例。

1
2
3
emacs .bashrc				//新开一个终端。在.bashrc文件的最后一行写入:export EDITOR='emacs -nw'
echo $EDITOR //打开一个新终端,看是否定义了EDITOR。注意:.bashrc文件的改变,只会在新的终端才有效。已经打开的终端不受环境变量的影响。正常情况下回输出:emacs -nw
rosed roscpp Logger.msg //rosed可以直接通过package名来获取到待编辑的文件而无需指定该文件的存储路径了. rosed [package_name] [filename]

创建ROS消息和ROS服务

使用ROSmsg

创建一个rosmsg

1
2
3
cd ~/catkin_ws/src/beginner_tutorials			//打开之前创建的package
mkdir msg //创建msg文件夹
echo "int64 num" > msg/Num.msg //写入到Num.msg文件中

确保msg文件被转为C++,Python和其他语言的源代码。查看package.xml, 确保它包含以下两条语句。若没有,手动添加。

1
2
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

修改./beginner_tutorials/CMakeLists.txt:

1.寻找find_package,向其中添加message_generation。

1
2
3
4
5
6
find_package(catkin REQUIRED COMPONENTS 
roscpp
rospy
std_msgs
message_generation
)

2.寻找catkin_package,设置运行依赖。

1
2
3
4
5
6
7
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES beginner_tutorials
# CATKIN_DEPENDS roscpp rospy std_msgs
# DEPENDS system_lib
CATKIN_DEPENDS message_runtime
)

3.将add_message_files代码块去掉注释,并用自己的Num.msg代替Messag1.msg,如下:

1
2
3
4
add_message_files(
FILES
Num.msg
)

4.取消generate_messages()的注释,如下:

1
2
3
4
generate_messages(
DEPENDENCIES
std_msgs
)

使用rosmsg

1
2
rosmsg show beginner_tutorials/Num 				//通过rosmsg show命令,检查ROS是否能够识消息。正确情况下会输出:int64 num。
//如果不正常,需要设置path,输入指令:export ROS_PACKAGE_PATH=~/catkin_ws/src:$ROS_PACKAGE_PATH。再次输入show命令。

使用ROSsrv

创建一个srv

1
2
3
4
5
roscd beginner_tutorials
mkdir srv
roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv
//roscp [package_name] [file_to_copy_path] [copy_path]
//roscp是一个很实用的命令行工具,它实现了将文件从一个package复制到另外一个package的功能。

同样,需要确保srv文件被转换为C++和其他语言的源代码。

修改CMakeLists.txt:

1.和ROSmsg一样添加对message_generation的依赖。(message_generation 对msg和srv都起作用)

2.将add_message_files代码块去掉注释,并添加自己的srv文件,如下:

1
2
3
4
add_service_files(
FILES
AddTwoInts.srv
)

3.取消generate_messages()的注释,同ROSmsg,如下:

使用ROSsrv

1
rossrv show beginner_tutorials/AddTwoInts 		//通过rosmsg show命令,检查ROS是否能够识该服务。 rossrv show <service type>。跟rosmsg类似, 你也可以不指定具体的package名来查找服务文件

新开一个终端,打开工作空间并编译:

1
2
cd catkin_ws
catkin_make

所有在msg路径下的.msg文件都将转换为ROS所支持语言的源代码。生成的C++头文件将会放置在~/catkin_ws/devel/include/beginner_tutorials。

编写简单的消息发布器和订阅器 (C++)

创建一个发布器节点(“talker”),它将不断的在ROS网络中广播消息。创建一个接收器节点(“listener”),它来接收ROS网络中的广播消息。

./beginner_tutorials/src/talker.cpp

为了减少篇幅,将源码中的大段英文注释删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "ros/ros.h"					//ros/ros.h 是一个实用的头文件,它引用了 ROS 系统中大部分常用的头文件。
#include "std_msgs/String.h" //这引用了 std_msgs/String 消息, 它存放在 std_msgs package 里,是由 String.msg 文件自动生成的头文件。

#include <sstream>

/**
* This tutorial demonstrates simple sending of messages over the ROS system.
*/
int main(int argc, char **argv)
{

ros::init(argc, argv, "talker"); //初始化 ROS 。它允许 ROS 通过命令行进行名称重映射——然而这并不是现在讨论的重点。在这里,我们也可以指定节点的名称——运行过程中,节点的名称必须唯一。
//这里的名称必须是一个 base name ,也就是说,名称内不能包含 / 等符号。

ros::NodeHandle n; //为这个进程的节点创建一个句柄。第一个创建的 NodeHandle 会为节点进行初始化,最后一个销毁的 NodeHandle 则会释放该节点所占用的所有资源。


/**
*告诉 master 我们将要在 chatter(话题名) 上发布 std_msgs/String 消息类型的消息。这样 master 就会告诉所有订阅了 chatter *话题的节点,将要有数据发布。第二个参数是发布序列的大小。如果我们发布的消息的频率太高,缓冲区中的消息在大于 1000 个的时候就会开始丢弃先前发布的消息。

*NodeHandle::advertise() 返回一个 ros::Publisher 对象,它有两个作用: 1) 它有一个 publish() 成员函数可以让你在topic上发布消息; 2) 如果消息类型不对,它会拒绝发布。
*/
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

ros::Rate loop_rate(10); //ros::Rate 对象可以允许你指定自循环的频率。它会追踪记录自上一次调用 Rate::sleep() 后时间的流逝,并休眠直到一个频率周期的时间。这个例子中,我们让它以 10Hz 的频率运行。

int count = 0;
while (ros::ok()) //roscpp 会默认生成一个 SIGINT 句柄,它负责处理 Ctrl-C 键盘操作——使得 ros::ok() 返回 false。
{
std_msgs::String msg; //使用一个由 msg file 文件产生的『消息自适应』类在 ROS 网络中广播消息。现在我们使用标准的String消息,它只有一个数据成员 "data"。

std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();

ROS_INFO("%s", msg.data.c_str()); //ROS_INFO 和其他类似的函数可以用来代替 printf/cout 等函数。

chatter_pub.publish(msg); //向所有订阅 chatter 话题的节点发送消息。

ros::spinOnce(); //在这个例子中并不是一定要调用 ros::spinOnce(),因为我们不接受回调。然而,如果你的程序里包含其他回调函数,最好在这里加上
//ros::spinOnce()这一语句,否则你的回调函数就永远也不会被调用了。

loop_rate.sleep(); //调用 ros::Rate 对象来休眠一段时间以使得发布频率为 10Hz。
++count;
}


return 0;
}

./beginner_tutorials/src/listener.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include "ros/ros.h"
#include "std_msgs/String.h"

/**
* This tutorial demonstrates simple receipt of messages over the ROS system.
*/
void chatterCallback(const std_msgs::String::ConstPtr& msg) //这是一个回调函数,当接收到 chatter 话题的时候就会被调用。消息是以 boost shared_ptr 指针的形式传输,这就意味着你可以存储它而又不需要复制数据。
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}

int main(int argc, char **argv)
{

ros::init(argc, argv, "listener");

ros::NodeHandle n;

/**
* 告诉 master 我们要订阅 chatter 话题上的消息。当有消息发布到这个话题时,ROS 就会调用 chatterCallback() 函数。第二个参数是队列大小,以防我们处理消息的速度不够快,当缓存达到 1000
* 条消息后,再有新的消息到来就将开始丢弃先前接收的消息。
*
* NodeHandle::subscribe() 返回 ros::Subscriber 对象,你必须让它处于活动状态直到你不再想订阅该消息。当这个对象销毁时,它将自动退订 chatter 话题的消息。
*
* 有各种不同的 NodeHandle::subscribe() 函数,允许你指定类的成员函数,甚至是 Boost.Function 对象可以调用的任何数据类型。
*/
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);

/**
* ros::spin() 进入自循环,可以尽可能快的调用消息回调函数。如果没有消息到达,它不会占用很多 CPU,所以不用担心。一旦 ros::ok() 返回 false,ros::spin() 就会立刻跳出自循环。
* 这有可能是 ros::shutdown() 被调用,或者是用户按下了 Ctrl-C,使得 master 告诉节点要终止运行。也有可能是节点被人为关闭的。
*/
ros::spin();

return 0;
}

下边,我们来总结一下:

  • 初始化ROS系统
  • 订阅 chatter 话题
  • 进入自循环,等待消息的到达
  • 当消息到达,调用 chatterCallback() 函数

./beginner_tutorials/CMakeLists.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)

## Find catkin and any catkin packages
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)

## Declare ROS messages and services
add_message_files(FILES Num.msg)
add_service_files(FILES AddTwoInts.srv)

## Generate added messages and services
generate_messages(DEPENDENCIES std_msgs)

## Declare a catkin package
catkin_package()

## Build talker and listener
include_directories(include ${catkin_INCLUDE_DIRS})

add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker beginner_tutorials_generate_messages_cpp)

add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener beginner_tutorials_generate_messages_cpp)

测试

在工作空间目录下进行编译,并运行。

1
2
3
4
5
6
7
8
9
roscore									//新开一个终端
cd catkin_ws //新开一个终端
catkin_make
source ./devel/setup.bash //在运行你自己的程序前,已经source了catkin工作空间下的setup.sh文件
rosrun beginner_tutorials talker //启动发布器

cd catkin_ws //新开一个终端,并打开工作空间
source ./devel/setup.bash //在运行你自己的程序前,已经source了catkin工作空间下的setup.sh文件
rosrun beginner_tutorials listener //启动订阅器

编写简单的服务器和客户端(C++)

./beginner_tutorials/src/add_two_ints_server.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h" //beginner_tutorials/AddTwoInts.h是由编译系统自动根据我们先前创建的srv文件生成的对应该srv文件的头文件。

/**
* 这个函数提供两个int值求和的服务,int值从request里面获取,而返回数据装入response内,这些数据类型都定义在srv文件内部,函数返回一个boolean值。
* 两个int值进行相加,并存入response。然后一些关于request和response的信息被记录下来。最后,service完成计算后返回true值。
*/
bool add(beginner_tutorials::AddTwoInts::Request &req,
beginner_tutorials::AddTwoInts::Response &res)
{
res.sum = req.a + req.b;
ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
ROS_INFO("sending back response: [%ld]", (long int)res.sum);
return true;
}

int main(int argc, char **argv)
{
ros::init(argc, argv, "add_two_ints_server");
ros::NodeHandle n;

ros::ServiceServer service = n.advertiseService("add_two_ints", add); //建立service,并在ROS内发布出来。
ROS_INFO("Ready to add two ints.");
ros::spin();

return 0;
}

./beginner_tutorials/src/add_two_ints_client.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
#include <cstdlib>

int main(int argc, char **argv)
{
ros::init(argc, argv, "add_two_ints_client");
if (argc != 3)
{
ROS_INFO("usage: add_two_ints_client X Y");
return 1;
}

ros::NodeHandle n;
ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints"); //这段代码为add_two_ints service创建一个client。ros::ServiceClient 对象待会用来调用service。
beginner_tutorials::AddTwoInts srv; //这里,我们实例化一个由ROS编译系统自动生成的service类,并给其request成员赋值。
srv.request.a = atoll(argv[1]); // 一个service类包含两个成员request和response。同时也包括两个类定义Request和Response。
srv.request.b = atoll(argv[2]);

/**
* 这段代码是在调用service。由于service的调用是模态过程(调用的时候占用进程阻止其他代码的执行),一旦调用完成,将返回调用结果。
* 如果service调用成功,call()函数将返回true,srv.response里面的值将是合法的值。
* 如果调用失败,call()函数将返回false,srv.response里面的值将是非法的。
*/
if (client.call(srv))
{
ROS_INFO("Sum: %ld", (long int)srv.response.sum);
}
else
{
ROS_ERROR("Failed to call service add_two_ints");
return 1;
}

return 0;
}

./beginner_tutorials/CMakeLists.txt

在上一节的CMakeLists.txt后添加:

1
2
3
4
5
6
7
add_executable(add_two_ints_server src/add_two_ints_server.cpp)
target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
add_dependencies(add_two_ints_server beginner_tutorials_gencpp)

add_executable(add_two_ints_client src/add_two_ints_client.cpp)
target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
add_dependencies(add_two_ints_client beginner_tutorials_gencpp)

测试

在工作空间目录下进行编译,并运行。

1
2
3
4
5
6
7
8
9
roscore												//新开一个终端
cd catkin_ws //新开一个终端
catkin_make
source ./devel/setup.bash //在运行你自己的程序前,已经source了catkin工作空间下的setup.sh文件
rosrun beginner_tutorials add_two_ints_server //启动服务器

cd catkin_ws //新开一个终端,并打开工作空间
source ./devel/setup.bash //在运行你自己的程序前,已经source了catkin工作空间下的setup.sh文件
rosrun beginner_tutorials add_two_ints_client 1 3 //启动客户端
------ 本文结束感谢您的阅读------
Donate a cup of cola?