260325
目前为止,我们已经完成了 ROS2 官方的初学者教程的全部内容,耗时约一周左右。
接下来就可以进入到中等难度的教程了,虽然说是中等,但是大概率也难不到哪里去。
首先是rosdep这个 ROS2 官方的依赖管理器。
这个东西虽然很有用, 但是对我们来说用处不大, 因为我们在进行 python 开发(尤其在进行机器学习相关开发的时候)的时候通常需要精确管理版本, 而 package.xml 通常难以这样管理 python 依赖,因此我们主要还是得使用 pyproject.toml 或 requirements.txt 来进行管理, 而非使用 package.xml 来进行管理。
简单的来说:
- package.xml 负责 ROS 包层面的依赖声明
- pyproject.toml / requirements.txt 负责 Python 环境,尤其是精确版本控制
以上。
接下来我们就可以去写一下自定义动作(Action)了。
通常来说,一个 .action文件的定义如下:
# 请求
---
# 结果
---
# 反馈
即由三部分组成,分别是请求、结果和反馈,中间使用 --- 进行分隔。
我们可以在我们之前的 tutorial_interfaces这里创建一个计算斐波那契数列的src/tutorial_interfaces/action/Fibonacci.action动作文件:
int32 order
---
int32[] sequence
---
int32[] sequence
这里我们的请求是数列的前N项,返回的结果是整个数列,反馈是目前已经计算出的数列。
然后在 CMakeLists.txt中的 rosidl_generate_interfaces 这里添加"action/Fibonacci.action"。
接下来进行构建即可使用接口查看工具来找到我们这个接口的定义:
(.venv) jese--ki@KiBall:~/Projects/learn/ros2/pub_sub$ ros2 interface list | grep "Fibonacci"
example_interfaces/action/Fibonacci
tutorial_interfaces/action/Fibonacci
然后我们就可以看看整个接口的定义了:
(.venv) jese--ki@KiBall:~/Projects/learn/ros2/pub_sub$ ros2 interface show tutorial_interfaces/action/Fibonacci
int32 order
---
int32[] sequence
---
int32[] sequence
以上。
除了创建动作接口以外,我们肯定还要调用这个接口的,我们可以在 py_srvcli这个包里继续创建动作的提供者和客户端。
首先是 src/py_srvcli/py_srvcli/service_member_action.py:
import rclpy
from rclpy.action import ActionServer
from rclpy.executors import ExternalShutdownException
from rclpy.node import Node
from tutorial_interfaces.action import Fibonacci
class FibonacciActionServer(Node):
def __init__(self):
super().__init__('fibonacci_action_server')
self._action_server = ActionServer(
self,
Fibonacci,
'fibonacci',
self.execute_callback)
def execute_callback(self, goal_handle):
self.get_logger().info('Executing goal...')
result = Fibonacci.Result()
return result
def main(args=None):
try:
with rclpy.init(args=args):
fibonacci_action_server = FibonacciActionServer()
rclpy.spin(fibonacci_action_server)
except (KeyboardInterrupt, ExternalShutdownException):
pass
if __name__ == '__main__':
main()
这里我们创建了一个节点以及一个动作提供者,可以看到我们的动作 Action和话题Topic、服务Service等都不同,另外两者都是通过节点内部的创建方法就可以直接进行创建,而动作则需要专门使用一个类进行实例化且需要刻意传入一个节点(我们这里是 self,即该节点自身)。
因为在 ROS2 的底层中,话题和服务都是一等公民,而动作则是一个极其复杂的缝合怪。
它本质上是一个状态管理器,同时是一个打包的话题+服务。它自身在底层并没有专门的 DDS 通信机制,都是新创建三种(总共五个)底层通道:
- 目标服务(Goal Service):客户端发送任务,服务端回复“接受”或“拒绝”。
- 结果服务(Result Service):客户端请求最终结果,服务端在任务结束时返回。
- 反馈/取消/状态话题(Feedback Topic):服务端在执行任务的漫长过程中,不断向外高频广播进度(比如“已经走了 50%”),以及取消请求和状态机的话题。
因此我们这里需要手动创建一个实例,而非直接在节点内部就可以创建。
这里我们创建这个实例的时候,传入了下面四个参数:
- self节点作为提供者进行执行。
- 动作类型Fibonacci。
- 动作名称fibonacci。
- 该动作的执行回调self.execute_callback,该动作被调用的时候将作为实际执行动作的函数来进行回调。
值得注意的是,ROS2 官方文档这里写的有问题,应当是 A ROS 2 node to add the action server to: self.,而非 A ROS 2 node to add the action client to: self.:
