ROS入门介绍三 ROS消息和服务、发布者订阅者、服务和客户端

使用rosedROS中编辑文件

使用rosed

rosedrosbash套件的一部分。利用它可以直接通过软件包名编辑包中的文件,而无需键入完整路径。

用法:

$ rosed [package_name] [filename]

示例:

$ rosed roscpp Logger.msg

这个例子演示了如何编辑roscpp软件包中的Logger.msg文件。

如果该实例没有运行成功,可能是因为你没有安装vim编辑器。请参考编辑器部分进行设置。

如果你不知道怎么退出vim,请尝试按下键盘上的Esc,然后分别按下:q!

如果文件名在包中不是唯一的,则菜单将提示你选择要编辑哪个文件。

Tab补全

使用这个方法,在不知道准确文件名的情况下,你也可以轻松地查看和编辑包中的所有文件。

用法:

$ rosed [package_name] <tab><tab>

示例:

$ rosed roscpp <tab><tab>

  • Empty.srv                   package.xml
  • GetLoggers.srv              roscpp-msg-extras.cmake
  • Logger.msg                  roscpp-msg-paths.cmake
  • SetLoggerLevel.srv          roscpp.cmake
  • genmsg_cpp.py               roscppConfig-version.cmake
  • gensrv_cpp.py               roscppConfig.cmake
  • msg_gen.py                 

编辑器

rosed默认的编辑器是vim。其实Ubuntu默认还有一个初学者更友好的编辑器nano,你可以把下面这行加到~/.bashrc文件中来更改默认编辑器:

export EDITOR='nano -w'

而如果想将默认编辑器设置为emacs,可以进行同样的操作:

export EDITOR='emacs -nw'

注意: .bashrc文件的改变只对新打开的终端有效。之前已经打开的终端不受更改环境变量的影响。

打开一个新的终端,看看是否定义了EDITOR变量:

$ echo $EDITOR

  • nano -w

or

emacs -nw

现在你已经成功配置和使用了rosed

创建ROS消息和服务

msgsrv介绍

  • msg(消息):msg文件就是文本文件,用于描述ROS消息的字段。它们用于为不同编程语言编写的消息生成源代码。
  • srv(服务):一个srv文件描述一个服务。它由两部分组成:请求(request)和响应(response)。

msg文件存放在软件包的msg目录下,srv文件则存放在srv目录下。

msg文件就是简单的文本文件,每行都有一个字段类型和字段名称。可以使用的类型为:

  • int8, int16, int32, int64 (以及 uint*)
  • float32, float64
  • string
  • time, duration
  • 其他msg文件
  • variable-length array[] 和 fixed-length array[C]

ROS中还有一个特殊的数据类型:Header,它含有时间戳和ROS中广泛使用的坐标帧信息。在msg文件的第一行经常可以看到Header header

下面是一个使用了Header、字符串原语和其他两个消息的示例: 下面是一个msg文件的样例,它使用了Headerstring,和其他另外两个消息的类型:

  Header header

  string child_frame_id

  geometry_msgs/PoseWithCovariance pose

  geometry_msgs/TwistWithCovariance twist

srv文件和msg文件一样,只是它们包含两个部分:请求和响应。这两部分用一条---线隔开。下面是一个srv文件的示例:

int64 A

int64 B

---

int64 Sum

在上面的例子中,AB是请求Sum是响应。

使用msg

创建msg

下面,我们将在之前创建的软件包里定义一个新的消息。

$ roscd beginner_tutorials

$ mkdir msg

$ echo "int64 num" > msg/Num.msg

上面是最简单的示例,.msg文件只有一行。当然,你可以通过添加更多元素(每行一个)来创建一个更复杂的文件,如下所示:

string first_name

string last_name

uint8 age

uint32 score

不过还有关键的一步:我们要确保msg文件能被转换为C++Python和其他语言的源代码。

打开package.xml, 确保它包含以下两行且没有被注释。如果没有,添加进去:

  <build_depend>message_generation</build_depend>

  <exec_depend>message_runtime</exec_depend>

注意,在构建时,其实只需要message_generation,而在运行时,我们只需要message_runtime

在你喜欢的文本编辑器中打开CMakeLists.txt文件(rosed是一个不错的选择)。

CMakeLists.txt文件中,为已经存在里面的find_package调用添加message_generation依赖项,这样就能生成消息了。直接将message_generation添加到COMPONENTS列表中即可,如下所示:

# 不要直接复制这一大段,只需将message_generation加在括号闭合前即可

find_package(catkin REQUIRED COMPONENTS

   roscpp

   rospy

   std_msgs

   message_generation

)

你可能注意到了,有时即使没有使用全部依赖项调用find_package,项目也可以构建。这是因为catkin把你所有的项目整合在了一起,因此如果之前的项目调用了find_package,你的依赖关系也被配置成了一样的值。但是,忘记调用意味着你的项目在单独构建时很容易崩溃。

还要确保导出消息的运行时依赖关系:

catkin_package(

  ...

  CATKIN_DEPENDS message_runtime ...

  ...)

找到如下代码块:

# add_message_files(

#   FILES

#   Message1.msg

#   Message2.msg

# )

删除#符号来取消注释,然后将Message*.msg替换为你的.msg文件名,就像下边这样:

add_message_files(

  FILES

  Num.msg

)

手动添加.msg文件后,我们要确保CMake知道何时需要重新配置项目。

现在必须确保generate_messages()函数被调用:

ROS Hydro及更新版本中,你需要取消下面几行的注释:

# generate_messages(

#   DEPENDENCIES

#   std_msgs

# )

  • 像这样:
  • generate_messages(
  •   DEPENDENCIES
  •   std_msgs

)

ROS Groovy及早期版本中,只需要取消这一行的注释:

generate_messages()

现在,你可以从msg文件定义中生成源代码文件了。如果现在就想这样做,请直接跳到msg和srv的一般步骤

使用rosmsg

以上就是创建消息的所有步骤。让我们通过rosmsg show命令看看ROS能否识别它。

用法:

$ rosmsg show [message type]

示例:

$ rosmsg show beginner_tutorials/Num

你会看到:

  • int64 num

在上面的例子中,消息类型包含两部分:

  • beginner_tutorials -- 定义消息的软件包
  • Num -- 消息的名称Num

如果不记得msg在哪个包中,也可以省略包名称。尝试:

$ rosmsg show Num

你会看到:

  • [beginner_tutorials/Num]:
  • int64 num

使用srv

创建srv

让我们使用之前创建的包再来创建服务:

$ roscd beginner_tutorials

$ mkdir srv

我们将从另一个包复制现有的srv定义,而不是手动创建新的srvroscp是一个实用的命令行工具,用于将文件从一个包复制到另一个包。

用法:

$ roscp [package_name] [file_to_copy_path] [copy_path]

现在我们可以从rospy_tutorials包中复制一个服务:

$ roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv

还有关键的一步:我们要确保msg文件能被转换为C++Python和其他语言的源代码。

如果没做过上面的教程,请先打开package.xml,确保它包含以下两行且没有被注释。如果没有,添加进去:

  <build_depend>message_generation</build_depend>

  <exec_depend>message_runtime</exec_depend>

如前所述,在构建时,其实只需要message_generation,而在运行时,我们只需要message_runtime

如果没做过上面的教程,在CMakeLists.txt文件中,为已经存在里面的find_package调用添加message_generation依赖项,这样就能生成消息了。直接将message_generation添加到COMPONENTS列表中即可,如下所示:

# 不要直接复制这一大段,只需将message_generation加在括号闭合前即可

find_package(catkin REQUIRED COMPONENTS

   roscpp

   rospy

   std_msgs

   message_generation

)

(别被名字迷惑,message_generationmsgsrv都适用)

此外,你也需要像之前对消息那样在package.xml中修改服务字段,因此请看上面描述的所需附加依赖项。

# add_service_files(

#   FILES

#   Service1.srv

#   Service2.srv

# )

删除#符号来取消注释,然后将Service*.srv替换为你的.srv文件名,就像下边这样:

add_service_files(

  FILES

  AddTwoInts.srv

)

现在,你可以从srv文件定义中生成源代码文件了。如果现在就想这样做,请直接跳到msg和srv的一般步骤

使用rossrv

以上就是创建服务的所有步骤。让我们通过rossrv show命令看看ROS能否识别它。

用法:

$ rossrv show <service type>

示例:

$ rossrv show beginner_tutorials/AddTwoInts

你会看到:

  • int64 a
  • int64 b
  • ---
  • int64 sum

rosmsg类似, 你也可以在不指定包名的情况下找到这样的服务:

$ rossrv show AddTwoInts

[beginner_tutorials/AddTwoInts]:

int64 a

int64 b

---

int64 sum

[rospy_tutorials/AddTwoInts]:

int64 a

int64 b

---

int64 sum

这里显示了两个服务。第一个是刚刚在beginner_tutorials包中创建的,第二个是之前rospy_tutorials包中已经存在的。

msgsrv的一般步骤

如果没做过上面的教程,请先修改下CMakeLists.txt

# generate_messages(

#   DEPENDENCIES

# #  std_msgs  # Or other packages containing msgs

# )

取消注释,然后添加任意你的消息用到的包含.msg文件的软件包(本例中为std_msgs),如下所示:

generate_messages(

  DEPENDENCIES

  std_msgs

)

现在我们已经创建了一些新消息,所以需要重新make一下软件包:

# In your catkin workspace

$ roscd beginner_tutorials

$ cd ../..

$ catkin_make

$ cd -

msg目录中的任何.msg文件都将生成所有支持语言的代码。C++消息的头文件将生成在~/catkin_ws/devel/include/beginner_tutorials/Python脚本将创建在~/catkin_ws/devel/lib/python2.7/dist-packages/beginner_tutorials/msg。而Lisp文件则出现在~/catkin_ws/devel/share/common-lisp/ros/beginner_tutorials/msg/

类似地,srv目录中的任何.srv文件都将生成支持语言的代码。对于C++,头文件将生成在消息的头文件的同一目录中。对于PythonLisp,会在msg目录旁边的srv目录中。

消息格式的完整规范在消息描述语言中。

如果你正在构建使用新消息的C++节点,则还需要声明节点和消息之间的依赖关系,参见catkin消息和服务构建文档

获取帮助

我们已经接触到不少ROS工具了。有时候很难记住每个命令所需要的参数。好在大多数ROS工具都提供了自己的帮助。

尝试:

$ rosmsg -h

  • 你可以看到一系列的rosmsg子命令。
  • Commands:
  •   rosmsg show     Show message description
  •   rosmsg list     List all messages
  •   rosmsg md5      Display message md5sum
  •   rosmsg package  List messages in a package

  rosmsg packages List packages that contain messages

同样也可以获得子命令的帮助:

$ rosmsg show -h

  • 这会显示rosmsg show所需的参数:
  • Usage: rosmsg show [options] <message type>
  • Options:
  •   -h, --help  show this help message and exit
  •   -r, --raw   show raw message text, including comments

复习

总结一下到目前为止我们接触过的一些命令:

  • rospack = ros+pack(age) : provides information related to ROS packages
  • roscd = ros+cd : changes directory to a ROS package or stack
  • rosls = ros+ls : lists files in a ROS package
  • roscp = ros+cp : copies files from/to a ROS package
  • rosmsg = ros+msg : provides information related to ROS message definitions
  • rossrv = ros+srv : provides information related to ROS service definitions
  • catkin_make : makes (compiles) a ROS package
  • rosmake = ros+make : makes (compiles) a ROS package (if you're not using a catkin workspace)

现在已经学习了如何创建ROS消息和服务

编写简单的发布者和订阅者(Python

编写发布者节点

节点是连接到ROS网络的可执行文件。在这里,我们将创建talker(发布者)节点,该节点将不断广播消息。

将目录切换到之前的教程中创建beginner_tutorials包中:

$ roscd beginner_tutorials

代码

首先让我们创建一个scripts目录来存放我们的Python脚本:

$ mkdir scripts

$ cd scripts

然后下载示例脚本talker.py放到scripts目录中并给执行权限:

$ wget https://raw.github.com/ros/ros_tutorials/noetic-devel/rospy_tutorials/001_talker_listener/talker.py # 若遇到网络问题,请打开上面文件的链接并复制文本内容到talker.py文件中

$ chmod +x talker.py

先不要运行它。你可以通过下面的命令查看和编辑这个文件。

$ rosed beginner_tutorials talker.py

  • 切换行号显示
  •    1 #!/usr/bin/env python
  •    2 # license removed for brevity
  •    3 import rospy
  •    4 from std_msgs.msg import String
  •    5
  •    6 def talker():
  •    7     pub = rospy.Publisher('chatter', String, queue_size=10)
  •    8     rospy.init_node('talker', anonymous=True)
  •    9     rate = rospy.Rate(10) # 10hz
  •   10     while not rospy.is_shutdown():
  •   11         hello_str = "hello world %s" % rospy.get_time()
  •   12         rospy.loginfo(hello_str)
  •   13         pub.publish(hello_str)
  •   14         rate.sleep()
  •   15
  •   16 if __name__ == '__main__':
  •   17     try:
  •   18         talker()
  •   19     except rospy.ROSInterruptException:
  •   20         pass

然后将以下内容添加到CMakeLists.txt文件。这样可以确保正确安装Python脚本,并使用合适的Python解释器。

catkin_install_python(PROGRAMS scripts/talker.py

  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}

)

解释

现在,让我们把代码分解。

切换行号显示

   1 #!/usr/bin/env python

每个Python ROS节点的最开头都有这个声明。第一行确保脚本作为Python脚本执行。

切换行号显示

   3 import rospy

   4 from std_msgs.msg import String

如果要编写ROS节点,则需要导入rospystd_msgs.msg的导入则是为了使我们能够重用std_msgs/String消息类型(即一个简单的字符串容器)来发布。

切换行号显示

   7     pub = rospy.Publisher('chatter', String, queue_size=10)

   8     rospy.init_node('talker', anonymous=True)

这部分代码定义了talker与其他ROS部分的接口。pub = rospy.Publisher("chatter", String, queue_size=10)声明该节点正在使用String消息类型发布到chatter话题。这里的String实际上是std_msgs.msg.String类。queue_size参数是在ROS Hydro及更新版本中新增的,用于在订阅者接收消息的速度不够快的情况下,限制排队的消息数量。对于ROS Groovy及早期版本来说,只需省略即可。

下一行的rospy.init_node(NAME, ...)非常重要,因为它把该节点的名称告诉了rospy——只有rospy掌握了这一信息后,才会开始与ROS主节点进行通信。在本例中,你的节点将使用talker名称。注意:名称必须是基本名称,例如不能包含任何斜杠/

anonymous = True会让名称末尾添加随机数,来确保节点具有唯一的名称。请参考从启动到关机——初始化ROS节点

切换行号显示

   9     rate = rospy.Rate(10) # 10hz

此行创建一个Rate对象rate。借助其方法sleep(),它提供了一种方便的方法,来以你想要的速率循环。它的参数是10,即表示希望它每秒循环10次(只要我们的处理时间不超过十分之一秒)!

切换行号显示

  10     while not rospy.is_shutdown():

  11         hello_str = "hello world %s" % rospy.get_time()

  12         rospy.loginfo(hello_str)

  13         pub.publish(hello_str)

  14         rate.sleep()

这个循环是一个相当标准的rospy结构:检查rospy.is_shutdown()标志,然后执行代码逻辑。你必须查看is_shutdown()以检查程序是否应该退出(例如有Ctrl+C或其他)。在本例中,代码逻辑即对public .publish(hello_str)的调用,它将一个字符串发布到chatter话题。循环的部分还调用了rate.sleep(),它在循环中可以用刚刚好的睡眠时间维持期望的速率。

你可能也见过rospy.sleep(),它和time.sleep()类似,不同的是前者还能用于模拟时间(参见Clock)。

此循环还调用了rospy.loginfo(str),它有3个任务:打印消息到屏幕上;把消息写入节点的日志文件中;写入rosoutrosout是一个方便的调试工具:您可以使用rqt_console来拉取消息,而不必在控制台窗口找你节点的输出。

std_msgs.msg.String是一个非常简单的消息类型,那更复杂的类型怎么发布呢?一般的经验法则是构造函数参数的顺序与.msg文件中的顺序相同。也可以不传入任何参数,直接初始化字段,例如:

msg = String()

msg.data = str

或者可以只初始化某些字段,并将其余字段保留为默认值:

String(data=str)

你可能会想知道最后一点:

切换行号显示

  17     try:

  18         talker()

  19     except rospy.ROSInterruptException:

  20         pass

除了标准的Python __main__检查,它还会捕获一个rospy.ROSInterruptException异常,当按下Ctrl+C或节点因其他原因关闭时,这一异常就会被rospy.sleep()rospy.Rate.sleep()抛出。引发此异常的原因是你不会意外地在sleep()之后继续执行代码。

现在我们需要编写一个节点来接收消息。

编写订阅者节点

代码

下载示例脚本listener.py放到scripts目录中并给执行权限:

$ roscd beginner_tutorials/scripts/

$ wget https://raw.github.com/ros/ros_tutorials/noetic-devel/rospy_tutorials/001_talker_listener/listener.py # 若遇到网络问题,请打开上面文件的链接并复制文本内容到listener.py文件中

$ chmod +x listener.py

文件内容类似于:

切换行号显示

   1 #!/usr/bin/env python

   2 import rospy

   3 from std_msgs.msg import String

   4

   5 def callback(data):

   6     rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)

   7     

   8 def listener():

   9

  10     # In ROS, nodes are uniquely named. If two nodes with the same

  11     # name are launched, the previous one is kicked off. The

  12     # anonymous=True flag means that rospy will choose a unique

  13     # name for our 'listener' node so that multiple listeners can

  14     # run simultaneously.

  15     rospy.init_node('listener', anonymous=True)

  16

  17     rospy.Subscriber("chatter", String, callback)

  18

  19     # spin() simply keeps python from exiting until this node is stopped

  20     rospy.spin()

  21

  22 if __name__ == '__main__':

  23     listener()

然后,编辑你CMakeLists.txt中的catkin_install_python()调用,如下所示:

catkin_install_python(PROGRAMS scripts/talker.py scripts/listener.py

  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}

)

解释

listener.py的代码类似于talker.py,只不过我们为订阅消息引入了一种新的基于回调的机制。

切换行号显示

  15     rospy.init_node('listener', anonymous=True)

  16

  17     rospy.Subscriber("chatter", String, callback)

  18

  19     # spin() simply keeps python from exiting until this node is stopped

  20     rospy.spin()

这声明你的节点订阅了chatter话题,类型是std_msgs.msgs.String。当接收到新消息时,callback函数被调用,消息作为第一个参数。

我们还稍微更改了对rospy.init_node()的调用。我们添加了anonymous=True关键字参数。ROS要求每个节点都有一个唯一的名称,如果出现具有相同名称的节点,则会与前一个节点发生冲突,这样一来,出现故障的节点很容易地被踢出网络。anonymous=True标志会告诉rospy为节点生成唯一的名称,这样就很容易可以有多个listener.py一起运行。

最后再补充一下,rospy.spin()只是不让你的节点退出,直到节点被明确关闭。与roscpp不同,rospy.spin()不影响订阅者回调函数,因为它们有自己的线程。

构建节点

我们使用CMake作为构建系统。是的,即使是Python节点也必须使用它。这是为了确保能为创建的消息和服务自动生成Python代码。

回到catkin工作空间,然后运行catkin_make

$ cd ~/catkin_ws

$ catkin_make

现在你已经编写了一个简单的发布者和订阅者,让我们来测试发布者和订阅者

检验简单的发布者和订阅者

运行发布者

确保roscore已经开启:

$ roscore

catkin specific 如果使用catkin,在运行你的程序前,请确保你在调用catkin_make后已经source过工作空间的setup.*sh文件:

# catkin工作空间中

$ cd ~/catkin_ws

$ source ./devel/setup.bash

上一教程中,我们制作了一个叫做talker的发布者,让我们运行它:

$ rosrun beginner_tutorials talker      # (C++)

$ rosrun beginner_tutorials talker.py   # (Python)

你会看到:

  • [INFO] [WallTime: 1314931831.774057] hello world 1314931831.77
  • [INFO] [WallTime: 1314931832.775497] hello world 1314931832.77
  • [INFO] [WallTime: 1314931833.778937] hello world 1314931833.78
  • [INFO] [WallTime: 1314931834.782059] hello world 1314931834.78
  • [INFO] [WallTime: 1314931835.784853] hello world 1314931835.78
  • [INFO] [WallTime: 1314931836.788106] hello world 1314931836.79

发布者节点已启动并运行。现在我们需要一个订阅者以接收来自发布者的消息。

运行订阅者

上一教程中,我们也制作了一个叫做listener的订阅者,让我们运行它:

$ rosrun beginner_tutorials listener     # (C++)

$ rosrun beginner_tutorials listener.py  # (Python)

你会看到:

  • [INFO] [WallTime: 1314931969.258941] /listener_17657_1314931968795I heard hello world 1314931969.26
  • [INFO] [WallTime: 1314931970.262246] /listener_17657_1314931968795I heard hello world 1314931970.26
  • [INFO] [WallTime: 1314931971.266348] /listener_17657_1314931968795I heard hello world 1314931971.26
  • [INFO] [WallTime: 1314931972.270429] /listener_17657_1314931968795I heard hello world 1314931972.27
  • [INFO] [WallTime: 1314931973.274382] /listener_17657_1314931968795I heard hello world 1314931973.27
  • [INFO] [WallTime: 1314931974.277694] /listener_17657_1314931968795I heard hello world 1314931974.28
  • [INFO] [WallTime: 1314931975.283708] /listener_17657_1314931968795I heard hello world 1314931975.28

完成后,按Ctrl+C停止listenertalker

现在已经研究了简单的发布者和订阅者,让我们再编写简单的服务和客户端

编写简单的服务和客户端(Python

编写服务节点

这里,我们将创建简单的服务(Service)节点add_two_ints_server,该节点将接收两个整数,并返回它们的和。

将当前目录切换到之前的教程中创建的beginner_tutorials包中:

$ roscd beginner_tutorials

请确保你已经按照之前教程中的指示创建了本教程中需要的服务AddTwoInts.srv。(确保在页面顶部选对了所使用的构建工具)

代码

beginner_tutorials包中创建scripts/add_two_ints_server.py文件并粘贴以下内容进去:

切换行号显示

   1 #!/usr/bin/env python
   2 
   3 from __future__ import print_function
   4 
   5 from beginner_tutorials.srv import AddTwoInts,AddTwoIntsResponse
   6 import rospy
   7 
   8 def handle_add_two_ints(req):
   9     print("Returning [%s + %s = %s]"%(req.a, req.b, (req.a + req.b)))
  10     return AddTwoIntsResponse(req.a + req.b)
  11 
  12 def add_two_ints_server():
  13     rospy.init_node('add_two_ints_server')
  14     s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints)
  15     print("Ready to add two ints.")
  16     rospy.spin()
  17 
  18 if __name__ == "__main__":
  19     add_two_ints_server()

别忘了给节点执行权限:

  • chmod +x scripts/add_two_ints_server.py

然后将以下内容添加到CMakeLists.txt文件。这样可以确保正确安装Python脚本,并使用合适的Python解释器。

catkin_install_python(PROGRAMS scripts/add_two_ints_server.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

解释

现在,让我们把代码分解。

使用rospy编写服务的难度非常小。我们使用init_node()声明我们的节点,然后再声明我们的服务:

切换行号显示

  12     s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints)

这声明了一个名为add_two_ints的新服务,其服务类型为AddTwoInts。所有的请求(request)都传递给了handle_add_two_ints函数。handle_add_two_intsAddTwoIntsRequest的实例调用,返回AddTwoIntsResponse实例。

就像订阅者中的例子一样,rospy.spin()可以防止代码在服务关闭之前退出。

编写客户端节点

代码

beginner_tutorials包中创建scripts/add_two_ints_client.py文件并粘贴以下内容进去:

切换行号显示

   1 #!/usr/bin/env python
   2 
   3 from __future__ import print_function
   4 
   5 import sys
   6 import rospy
   7 from beginner_tutorials.srv import *
   8 
   9 def add_two_ints_client(x, y):
  10     rospy.wait_for_service('add_two_ints')
  11     try:
  12         add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts)
  13         resp1 = add_two_ints(x, y)
  14         return resp1.sum
  15     except rospy.ServiceException as e:
  16         print("Service call failed: %s"%e)
  17 
  18 def usage():
  19     return "%s [x y]"%sys.argv[0]
  20 
  21 if __name__ == "__main__":
  22     if len(sys.argv) == 3:
  23         x = int(sys.argv[1])
  24         y = int(sys.argv[2])
  25     else:
  26         print(usage())
  27         sys.exit(1)
  28     print("Requesting %s+%s"%(x, y))
  29     print("%s + %s = %s"%(x, y, add_two_ints_client(x, y)))

别忘了给节点执行权限:

$ chmod +x scripts/add_two_ints_client.py

然后,在你的CMakeLists.txt中编辑catkin_install_python()调用,就像这样:

catkin_install_python(PROGRAMS scripts/add_two_ints_server.py scripts/add_two_ints_client.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

解释

现在,让我们把代码分解。

客户端(用来调用服务)的代码也很简单。对于客户端来说不需要调用init_node()。我们首先调用:

切换行号显示

  10     rospy.wait_for_service('add_two_ints')

这是一种很方便的方法,可以让在add_two_ints服务可用之前一直阻塞。

切换行号显示

  12         add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts)

这里我们为服务的调用创建了一个句柄(handle)。

切换行号显示

  13         resp1 = add_two_ints(x, y)
  14         return resp1.sum

然后我们可以使用这个句柄,就像普通的函数一样调用它。

因为我们已经将服务的类型声明为AddTwoInts,它会为你生成AddTwoIntsRequest对象 (you're free to pass in your own instead)。如果调用失败,rospy.ServiceException将会抛出,所以你应该弄一个合适的try/except部分。

构建节点

我们使用CMake作为构建系统。是的,即使是Python节点也必须使用它。这是为了确保能为创建的消息和服务自动生成Python代码。

切换当前目录到你的catkin工作空间,然后运行catkin_make

# 在你的catkin工作空间中
$ cd ~/catkin_ws
$ catkin_make

现在你已经编写了一个简单的服务和客户端。

检验简单的服务和客户端

运行服务

让我们先开始运行服务:

$ rosrun beginner_tutorials add_two_ints_server     # (C++)
$ rosrun beginner_tutorials add_two_ints_server.py  # (Python)

你会看到:

Ready to add two ints.

运行客户端

现在,运行客户端并附带必要的参数:

$ rosrun beginner_tutorials add_two_ints_client 1 3     # (C++)
$ rosrun beginner_tutorials add_two_ints_client.py 1 3  # (Python)

你会看到:

Requesting 1+3
1 + 3 = 4

现在,你已经成功地运行了你的第一个服务和客户端。

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

申扬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值