【ROS实战】04-自定义消息并实现ROS服务

如何自定义消息并实现ROS服务

本文将介绍如何在 ROS Noetic 中自定义消息,并依托自定义的消息开发一个简单的服务端与客户端进行交互。具体来说,我们将创建一个 ROS 服务,该服务接受两个整数并返回它们的和以及一个格式化的信息。我们还会展示如何通过命令行和 Python 代码调用该服务,步骤包括自定义服务消息、编写服务端和客户端、启动和调用服务等内容。

目标

  1. 自定义服务消息:我们将创建一个新的服务消息,定义输入输出的格式。
  2. 编写服务端代码:实现一个服务端,该服务接收两个整数并返回它们的和及一个固定格式的字符串。
  3. 编写客户端代码:实现一个客户端脚本,向服务端发送请求并接收响应。
  4. 通过命令行调用服务:展示如何通过命令行调用服务。

开始之前

在开始之前,你需要确保已安装 ROS Noetic,并且拥有一个工作空间(例如 ~/catkin_ws/)。如果你对 ROS 的背景或工作空间的概念不太了解,可以先阅读我专栏中的相关文章:《01-ROS安装详细指南》、《02-ROS架构介绍》和《03-从零实现小车运动控制的 ROS 功能包》。

1 创建 ROS 服务包

首先,我们需要在工作空间中创建一个新的包,并添加所需的依赖项。打开终端并进入工作空间目录:

1
cd ~/catkin_ws/src

然后使用 catkin_create_pkg 命令创建一个新的包:

1
catkin_create_pkg add_two_integers rospy std_msgs

这里我们创建了一个名为 add_two_integers 的包,并依赖了 rospystd_msgs 两个 ROS 包。

2 定义服务消息

接下来,我们需要定义自定义的服务消息文件。创建一个名为 srv 的文件夹,在其中定义服务消息。

  1. add_two_integers 包内创建 srv 文件夹:

    1
    mkdir ~/catkin_ws/src/add_two_integers/srv
  2. 在新建的srv文件夹下创建并编辑服务消息文件 AddTwoInts.srv,内容如下:

1
2
3
4
5
int64 a
int64 b
---
int64 sum
string info

这个服务消息文件包含了两个部分:

  • 请求部分:包含两个整数 ab,表示我们要相加的两个数字。
  • 响应部分:包含整数 sum,表示两个数字的和,以及字符串 info,准备同步返回一个格式化的信息(例如:”10 + 20 = 30”)。

3 修改 CMakeLists.txtpackage.xml

为了使 ROS 知道我们要生成服务消息,必须在 CMakeLists.txtpackage.xml 中进行相应的配置。

3.1 修改 CMakeLists.txt

打开 add_two_integers中的CMakeLists.txt 文件并找到find_package, add_service_filesgenerate_messages,确保它们没有被注释掉,并且和下面的内容一致:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
find_package(catkin REQUIRED COMPONENTS
rospy
std_msgs
message_generation
)

add_service_files(
FILES
AddTwoInts.srv
)
generate_messages(
DEPENDENCIES
std_msgs
)

这些配置告诉 ROS 编译器去生成服务相关的文件。

3.2 修改 package.xml

打开 add_two_integers中的 package.xml 文件,确保添加了对 message_generationmessage_runtime 的依赖:

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

4 编写服务端代码

现在我们来编写服务端代码。在 add_two_integers 包中创建一个名为 add_two_integers_server.py 的文件。

  1. 创建服务端脚本:

    1
    2
    mkdir -p ~/catkin_ws/src/add_two_integers/scripts
    touch ~/catkin_ws/src/add_two_integers/scripts/add_two_integers_server.py
  2. 编辑 add_two_integers_server.py,内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #!/usr/bin/env python3
    import rospy
    from add_two_integers.srv import AddTwoInts, AddTwoIntsResponse

    # 处理服务请求的函数
    def handle_add_two_ints(req):
    # 计算两个整数的和
    result = req.a + req.b
    # 格式化字符串信息
    info = "{} + {} = {}".format(req.a, req.b, result)
    rospy.loginfo(info)
    # 返回结果和信息
    return AddTwoIntsResponse(result, info)

    # 启动服务端节点
    def add_two_ints_server():
    rospy.init_node('add_two_ints_server') # 初始化 ROS 节点
    rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints) # 注册服务
    rospy.loginfo("Ready to add two integers.") # 服务启动的提示
    rospy.spin() # 保持节点运行

    if __name__ == "__main__":
    add_two_ints_server() # 启动服务

    代码解释

    • handle_add_two_ints:这个函数处理客户端请求,它计算两个整数的和,并返回结果和一个格式化的字符串。
    • add_two_ints_server:这个函数初始化 ROS 节点并注册服务,等待客户端调用。
  3. 为脚本添加执行权限:

    1
    chmod +x ~/catkin_ws/src/add_two_integers/scripts/add_two_integers_server.py

5 编写客户端代码

接下来我们编写一个客户端脚本,向服务端发送请求并获取响应。

  1. 创建客户端脚本:

    1
    touch ~/catkin_ws/src/add_two_integers/scripts/add_two_integers_client.py
  2. 编辑 add_two_integers_client.py,内容如下:

    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
    #!/usr/bin/env python3
    import rospy
    from add_two_integers.srv import AddTwoInts

    # 客户端调用服务的函数
    def add_two_ints_client(a, b):
    # 等待服务可用
    rospy.wait_for_service('add_two_ints')
    try:
    # 创建 ServiceProxy 对象
    add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts)
    # 调用服务并返回响应
    response = add_two_ints(a, b)
    return response.sum, response.info
    except rospy.ServiceException as e:
    print("Service call failed: %s" % e)

    if __name__ == "__main__":
    rospy.init_node('add_two_ints_client')
    # 获取用户输入
    a = int(input("Enter first integer: "))
    b = int(input("Enter second integer: "))
    # 调用服务并打印结果
    sum_result, info = add_two_ints_client(a, b)
    print(f"Result: {sum_result}, Info: {info}")

    代码解释

    • add_two_ints_client:该函数通过 ServiceProxy 创建客户端与服务端的通信。它将发送两个整数并接收响应。
    • 用户输入两个整数后,客户端将输出结果。
  3. 为客户端脚本添加执行权限:

    1
    chmod +x ~/catkin_ws/src/add_two_integers/scripts/add_two_integers_client.py

6 编译和运行

在编写完服务端和客户端代码后,我们需要编译工作空间。

  1. 回到工作空间目录并编译:

    1
    2
    cd ~/catkin_ws
    catkin_make
  2. 启动 ROS core:

    1
    roscore
  3. 启动服务端节点:

    1
    2
    source ~/catkin_ws/devel/setup.bash
    rosrun add_two_integers add_two_integers_server.py
  4. 启动客户端节点:

    打开一个新的终端并运行客户端脚本:

    1
    2
    source ~/catkin_ws/devel/setup.bash
    rosrun add_two_integers add_two_integers_client.py

    输入两个整数,客户端将显示结果和信息。

1
2
3
4
$ rosrun add_two_integers add_two_integers_client.py
Enter first integer: 1
Enter second integer: 1
Result: 2, Info: 1 + 1 = 2

7 通过命令行调用服务

首先,通过rosservice list可以查看当前正在运行的服务列表,这里可以看到我们的服务/add_two_ints

1
2
~/catkin_ws$ rosservice list
/add_two_ints

然后我们可以通过命令行调用这个服务。使用以下命令调用服务并传入两个整数。

1
rosservice call /add_two_ints 10 20

返回:

1
2
sum: 30
info: "10 + 20 = 30"

命令参数说明

  • rosservice call:这是调用 ROS 服务的命令。
  • /add_two_ints:这是服务的名称。
  • 10 20:这是请求的参数,表示我们要相加的两个整数。

总结

我们成功地在 ROS Noetic 中自定义了一个服务消息,并实现了一个简单的服务和客户端。本文展示了如何定义服务的输入和输出、编写服务端和客户端代码、启动和调用服务。同时,还展示了如何通过命令行调用服务。希望本教程能帮助你更好地理解 ROS 中的服务机制。