ros笔记04--从零体验ros2行为通信方式
- 介绍
- 创建步骤
- 体验官方案例
- 基于python开发行为案例
- 创建action接口
- 创建action sever和client
- 注意事项
- 说明
介绍
行为是ros2中的一种通信方式,其多被用于一些长时间运行的任务,它包含了目标、反馈、结果三部分。
行为建立在主题和服务之上,其功能类似于服务,但它可以取消操作。行为通信方式还提供了稳定的反馈,而服务通信返回单个响应。
行为通行方式使用客户-服务模型,类似于发布者-订阅者模型。行为客户端节点向行为服务端节点发送目标,服务端节点确认目标并返回反馈流和执行结果。
如下图所示,图中可清晰的体现行为通信的数据流向。
创建步骤
体验官方案例
我们以官网小海龟为例子,打开小海龟节点和键盘终端遥控节点。
打开小海龟仿真节点
$ ros2 run turtlesim turtlesim_node
打开遥控节点
$ ros2 run turtlesim turtle_teleop_key
从终端输出的结果可以看到,若无打断的话目标可以正常完成,我们也可以通过F来中断一个行为目标。
初始状态 | 完成目标和取消 |
---|---|
我们也可以进一步通过 ros2 node info /node-name 来查看节点中包含的Action Servers 和 Clients。
$ ros2 node info /
...
Action Servers:
/turtle1/rotate_absolute: turtlesim/action/RotateAbsolute
$ ros2 node info /teleop_turtle
Action Clients:
/turtle1/rotate_absolute: turtlesim/action/RotateAbsolute
通过 ros2 action send_goal 让小海龟移动,通过 --feedback 来查看本次目标执行的反馈信息
小海龟旋转180度
$ ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: 3.14}"
Waiting for an action server to become available...
Sending goal:
theta: 3.14
Goal accepted with ID: 5e97b4ca2592488282727bbb36f2d90c
Result:
delta: -3.119999885559082
Goal finished with status: SUCCEEDED
输出旋转过程中的反馈信息
$ ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: 3.14}"
基于python开发行为案例
创建action接口
- 创建接口包
继续前几篇ros2博文,当前工作目录依旧为dev_ws $ cd dev_ws/src $ ros2 pkg create --license Apache-2.0 custom_action_interfaces
- 定义action文件
$ cd custom_action_interfaces $ mkdir action $ vim action/Fibonacci.action int32 order --- int32[] sequence --- int32[] partial_sequence
- 在CMakeLists中新增依赖
find_package(rosidl_default_generators REQUIRED) rosidl_generate_interfaces(${PROJECT_NAME} "action/Fibonacci.action" )
- 在package.xml下新增如下内容
<buildtool_depend>rosidl_default_generators</buildtool_depend> <member_of_group>rosidl_interface_packages</member_of_group>
- 构建并测试接口
输出接口信息:# Change to the root of the workspace $ cd dev_ws # Build $ colcon build --packages-select custom_action_interfaces 输出: Starting >>> custom_action_interfaces Finished <<< custom_action_interfaces [0.65s] Summary: 1 package finished [0.86s]
$ source install/local_setup.bash $ ros2 interface show custom_action_interfaces/action/Fibonacci
创建action sever和client
- 创建保 action_tutorials_py
$ cd dev_ws/src $ ros2 pkg create --build-type ament_python --license Apache-2.0 action_tutorials_py
- 创建 action server
vim action_tutorials_py/action_tutorials_py/fibonacci_action_server.pyimport time import rclpy from rclpy.action import ActionServer from rclpy.node import Node from custom_action_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...') feedback_msg = Fibonacci.Feedback() feedback_msg.partial_sequence = [0, 1] for i in range(1, goal_handle.request.order): feedback_msg.partial_sequence.append( feedback_msg.partial_sequence[i] + feedback_msg.partial_sequence[i-1]) self.get_logger().info('Feedback: {0}'.format(feedback_msg.partial_sequence)) goal_handle.publish_feedback(feedback_msg) time.sleep(1) goal_handle.succeed() result = Fibonacci.Result() result.sequence = feedback_msg.partial_sequence return result def main(args=None): rclpy.init(args=args) fibonacci_action_server = FibonacciActionServer() rclpy.spin(fibonacci_action_server) if __name__ == '__main__': main()
- 创建 action client
vim action_tutorials_py/action_tutorials_py/fibonacci_action_client.pyimport rclpy from rclpy.action import ActionClient from rclpy.node import Node from custom_action_interfaces.action import Fibonacci class FibonacciActionClient(Node): def __init__(self): super().__init__('fibonacci_action_client') self._action_client = ActionClient(self, Fibonacci, 'fibonacci') def send_goal(self, order): goal_msg = Fibonacci.Goal() goal_msg.order = order self._action_client.wait_for_server() self._send_goal_future = self._action_client.send_goal_async(goal_msg, feedback_callback=self.feedback_callback) self._send_goal_future.add_done_callback(self.goal_response_callback) def goal_response_callback(self, future): goal_handle = future.result() if not goal_handle.accepted: self.get_logger().info('Goal rejected :(') return self.get_logger().info('Goal accepted :)') self._get_result_future = goal_handle.get_result_async() self._get_result_future.add_done_callback(self.get_result_callback) def get_result_callback(self, future): result = future.result().result self.get_logger().info('Result: {0}'.format(result.sequence)) rclpy.shutdown() def feedback_callback(self, feedback_msg): feedback = feedback_msg.feedback self.get_logger().info('Received feedback: {0}'.format(feedback.partial_sequence)) def main(args=None): rclpy.init(args=args) action_client = FibonacciActionClient() action_client.send_goal(10) rclpy.spin(action_client) if __name__ == '__main__': main()
- 在 package.xml 中新增依赖项
<depend>action_tutorials_interfaces</depend> <exec_depend>rclpy</exec_depend>
- 在 setup.py中新增 entry point
entry_points={ 'console_scripts': [ 'fibonacci_action_server = action_tutorials_py.fibonacci_action_server:main', 'fibonacci_action_client = action_tutorials_py.fibonacci_action_client:main', ], },
- 构建 & 运行
输出结果如下:构建 $ cd dev_ws $ colcon build --packages-select action_tutorials_py 运行 $ source install/setup.bash $ ros2 run action_tutorials_py fibonacci_action_server $ ros2 run action_tutorials_py fibonacci_action_client $ ros2 action send_goal fibonacci custom_action_interfaces/action/Fibonacci "{order: 5}"
直接通过 ros2 action send_goal 来发送action请求
注意事项
- 官方文档直接使用 python3 fibonacci_action_server.py 和 python3 fibonacci_action_client.py 来执行验证,此处为了保持python项目的统一,直接设置了 entry point 并进行了编译构建。
说明
软件版本
ubuntu24.04 Desktop
ros2 jazzy
python 3.12.4(conda)
参考文档
ros2 jazzy 官方文档 Understanding actions
ros2 jazzy 官方文档 Writing an action server and client
ros2官网demos jazzy/action_tutorials/action_tutorials_py