260323

昨日休息,今日继续。

我们上次通过引入官方的示例依赖来使用来 .srv文件和消息。

现在轮到我们自己来实现了。

我们可以通过下面的命令来创建这样的包:

ros2 pkg create --build-type ament_cmake --license Apache-2.0 tutorial_interfaces --description "示例.src 文件和示例消息"

值得注意的是,这些 .srv和 .msg文件只能放在 cmake 的包里面,而不能放在 python 包里面,因为需要调用底层的 C++ 进行编译,而 python 则难以完成这些工作。

然后在这个包下面创建 srv和 msg目录:

然后在 msg目录下,我们可以创建一个 Num.msg的文件,内容如下:

    int64 num

这个消息代表我们的消息会传输一个名为 num的 64 位整数。

然后继续,我们可以创建一个 Sphere.msg的文件,内容如下:

geometry_msgs/Point center
float64 radius

该自定义消息使用了geometry_msgs消息包的 Point center的消息。

接下来,我们可以在 srv目录中创建一个 AddThreeInts.srv的新文件,采用下面的响应结构:

int64 a
int64 b
int64 c
---
int64 sum

这个服务代表传入 a, b, c三个 64 位的整数参数,并返回一个名为 sum的 64 位整数。

然后将我们的 CMakeLists.txt添加下面的几行:

find_package(geometry_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
  "msg/Num.msg"
  "msg/Sphere.msg"
  "srv/AddThreeInts.srv"
  DEPENDENCIES geometry_msgs # Add packages that above messages depend on, in this case geometry_msgs for Sphere.msg
)

由于我们引入了新的依赖,同时接口需要通过rosidl_default_generators才能生成语言特定的代码,因此需要对该工具依赖进行声明,在我们的 package.xml里加入下面几行:

<depend>geometry_msgs</depend>
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>

接下来我们就可以运行下面的命令来进行构建该包了:

colcon build --packages-select tutorial_interfaces

值得注意的是,由于我们项目根环境是 python,因此需要额外安装 catkin_pkg、empy、ament_package, numpy, lark 这五个依赖才能成功进行 build,否则很可能会出现缺少依赖而报错的问题。

接下来就可以去看看我们的接口了,我们可以用 interface的列表功能来看到当前可用的全部接口。

但是,由于这个量很大,我们就需要使用 grep来看看我们的接口有没有成功注册:

(.venv) jese--ki@KiBall:~/Projects/learn/ros2/pub_sub$ ros2 interface list | grep -E "Num|Sphere|AddThreeInts"
    tutorial_interfaces/msg/Num
    tutorial_interfaces/msg/Sphere
    tutorial_interfaces/srv/AddThreeInts

接下来就可以查看具体的接口了:

(.venv) jese--ki@KiBall:~/Projects/learn/ros2/pub_sub$ ros2 interface show tutorial_interfaces/msg/Num
int64 num
(.venv) jese--ki@KiBall:~/Projects/learn/ros2/pub_sub$ ros2 interface show tutorial_interfaces/msg/Sphere 
geometry_msgs/Point center
        float64 x
        float64 y
        float64 z
float64 radius
(.venv) jese--ki@KiBall:~/Projects/learn/ros2/pub_sub$ ros2 interface show tutorial_interfaces/srv/AddThreeInts 
int64 a
int64 b
int64 c
---
int64 sum

可以看到全部都符合我们的预期。

接下来就可以去我们之前的话题发布/订阅的包里修改一下来尝试使用一下看看了:

修改发布者:

# Copyright 2016 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import rclpy
from rclpy.executors import ExternalShutdownException
from rclpy.node import Node

from tutorial_interfaces.msg import Num # 替换为我们包中的 Num


class MinimalPublisher(Node):

    def __init__(self):
        super().__init__('minimal_publisher')
        self.publisher_ = self.create_publisher(Num, 'topic', 10) # 消息类型替换为 Num
        timer_period = 0.5  # seconds
        self.timer = self.create_timer(timer_period, self.timer_callback)
        self.i = 0

    def timer_callback(self):
        msg = Num() # 替换为 Num 的实例化
        msg.num = self.i # 不需要字符串
        self.publisher_.publish(msg)
        self.get_logger().info('Publishing: "%s"' % msg.num) # 也替换为 Num
        self.i += 1


def main(args=None):
    try:
        with rclpy.init(args=args):
            minimal_publisher = MinimalPublisher()

            rclpy.spin(minimal_publisher)
    except (KeyboardInterrupt, ExternalShutdownException):
        pass


if __name__ == '__main__':
    main()

修改订阅者:

# Copyright 2016 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import rclpy
from rclpy.executors import ExternalShutdownException
from rclpy.node import Node

from tutorial_interfaces.msg import Num # 替换为我们包中的 Num


class MinimalSubscriber(Node):

    def __init__(self):
        super().__init__('minimal_subscriber')
        self.subscription = self.create_subscription(
            Num, # 替换为 Num
            'topic',
            self.listener_callback,
            10)
        self.subscription  # prevent unused variable warning

    def listener_callback(self, msg: Num):
        self.get_logger().info('I heard: "%s"' % msg.num) # 参数替换为 num


def main(args=None):
    try:
        with rclpy.init(args=args):
            minimal_subscriber = MinimalSubscriber()

            rclpy.spin(minimal_subscriber)
    except (KeyboardInterrupt, ExternalShutdownException):
        pass


if __name__ == '__main__':
    main()

在 package.xml中添加这个依赖:

<exec_depend>tutorial_interfaces</exec_depend>

然后打包,并运行看看效果:

(.venv) jese--ki@KiBall:~/Projects/learn/ros2/pub_sub$ ros2 run py_pubsub talker 
[INFO] [1774268029.734161193] [minimal_publisher]: Publishing: "0"
[INFO] [1774268030.226046858] [minimal_publisher]: Publishing: "1"
[INFO] [1774268030.725972689] [minimal_publisher]: Publishing: "2"
[INFO] [1774268031.225985784] [minimal_publisher]: Publishing: "3"
[INFO] [1774268031.725961439] [minimal_publisher]: Publishing: "4"
(.venv) jese--ki@KiBall:~/Projects/learn/ros2/pub_sub$ ros2 run py_pubsub listener 
[INFO] [1774268029.734427193] [minimal_subscriber]: I heard: "0"
[INFO] [1774268030.226315313] [minimal_subscriber]: I heard: "1"
[INFO] [1774268030.726154311] [minimal_subscriber]: I heard: "2"
[INFO] [1774268031.226168087] [minimal_subscriber]: I heard: "3"
[INFO] [1774268031.726134034] [minimal_subscriber]: I heard: "4"

可以看到功能完全符合我们的预期。

以上。