news 2026/4/22 2:16:08

ROS2核心概念之动作

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ROS2核心概念之动作

这些应用功能的实现中,另外一种ROS通信机制也会被常常用到——那就是动作。从这个名字上就可以很好理解这个概念的含义,这种通信机制的目的就是便于对机器人某一完整行为的流程进行管理。

回到顶部

一、通信模型

举个例子,比如我们想让机器人转个圈,这肯定不是一下就可以完成的,机器人得一点一点旋转,直到360度才能结束,假设机器人并不在我们眼前,发出指令后,我们根本不知道机器人到底有没有开始转圈,转到哪里了?

image-20220528005012082

OK,现在我们需要的是一个反馈,比如每隔1s,告诉我们当前转到多少度了,10度、20度、30度,一段时间之后,到了360度,再发送一个信息,表示动作执行完成。

这样一个需要执行一段时间的行为,使用动作的通信机制就更为合适,就像装了一个进度条,我们可以随时把控进度,如果运动过程当中,我们还可以随时发送一个取消运动的命令。

1.1 客户端/服务器模型

动作和服务类似,使用的也是客户端和服务器模型,客户端发送动作的目标,想让机器人干什么,服务器端执行动作过程, 控制机器人达到运动的目标,同时周期反馈动作执行过程中的状态。

image8

客户端发送一个运动的目标,想让机器人动起来,服务器端收到之后,就开始控制机器人运动,一边运动,一边反馈当前的状态;

如果是一个导航动作,这个反馈可能是当前所处的坐标;

如果是机械臂抓取,这个反馈可能又是机械臂的实时姿态;

当运动执行结束后,服务器再反馈一个动作结束的信息,整个通信过程就此结束。

1.2 一对多通信

和服务一样,动作通信中的客户端可以有多个,大家都可以发送运动命令,但是服务器端只能有一个,毕竟只有一个机器人,先执行完成一个动作,才能执行下一个动作。

1.3 同步通信

既然有反馈,那动作也是一种同步通信机制,之前我们也介绍过,动作过程中的数据通信接口,使用.action文件进行定义。

1.4 由服务和话题合成

我们再仔细看下上边的动图,是不是还会发现一个隐藏的秘密。动作的三个通信模块,竟然有两个是服务,一个是话题;

当客户端发送运动目标时,使用的是服务的请求调用,服务器端也会反馈一个应带,表示收到命令;

动作的反馈过程,其实就是一个话题的周期发布,服务器端是发布者,客户端是订阅者。

没错,动作是一种应用层的通信机制,其底层就是基于话题和服务来实现的。

回到顶部

二、动作案例

2.1 小海龟的动作

我们使用小海龟的案例加深对动作概念的理解。进入桌面系统,启动第一个终端,运行小海龟仿真器;

pi@NanoPC-T6:~/dev_ws$ ros2 run turtlesim turtlesim_node

该指令将启动一个蓝色背景的海龟仿真器;

启动第二个终端,运行如下指令:

pi@NanoPC-T6:~/dev_ws$ ros2 run turtlesim turtle_teleop_key

该指令将启动一个键盘控制节点,在该终端中点击键盘上的“上下左右”按键,就可以控制小海龟运动啦。

接下来使用action命令控制小海龟的动作,可以让海龟运动到某一指定的姿态:

pi@NanoPC-T6:~/Desktop$ ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: -1.57}"

Waiting for an action server to become available...

Sending goal:

theta: -1.57

Goal accepted with ID: 08d36694d66f4977b1bbf0f2bdd93d69

Result:

delta: 1.5360000133514404

Goal finished with status: SUCCEEDED

如果需要反馈结果,可以执行;

pi@NanoPC-T6:~/Desktop$ ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: 0}" --feedback

Waiting for an action server to become available...

Sending goal:

theta: 0.0

Goal accepted with ID: 2bbb1d8a0a7d474a9b5a41c2c2f0cd03

Feedback:

remaining: 1.5520000457763672

Feedback:

remaining: 1.5360000133514404

Feedback:

remaining: 1.5199999809265137

Feedback:

remaining: 1.503999948501587

......

2.2 机器人画圆

如何通过代码来实现动作的编程呢?动作虽然是基于话题和服务实现的,但在实际使用中,并不会直接使用话题和服务的编程方法,而是有一套针对动作特性封装好的编程接口,接下来,我们就来看看, 动作到底该如何实现。

image-20220528010032315

假设我们有一个机器人,我们希望通过动作的通信方法,让机器人转个圈,请编程实现动作通信中,客户端和服务器端的实现过程。

我们首先创建my_learning_action的Python版本的功能包;

pi@NanoPC-T6:~/dev_ws$ cd src

pi@NanoPC-T6:~/dev_ws/src$ ros2 pkg create --build-type ament_python my_learning_action

修改功能包my_learning_action依赖package.xml;

<depend>my_learning_interface</depend>

2.2.1 接口定义

我们使用的动作并不是ROS中的标准定义,我们通过MoveCircle.action进行自定义。

我们在my_learning_interface文件夹下创建子文件夹action,接着新建文件MoveCircle.action;

bool enable # 定义动作的目标,表示动作开始的指令

---

bool finish # 定义动作的结果,表示是否成功执行

---

int32 state # 定义动作的反馈,表示当前执行到的位置

动作由三个部分组成:

第一块是动作的目标:enable为true时,表示开始运动;

第二块是动作的执行结果:finish为true,表示动作执行完成;

第三块是动作的周期反馈:表示当前机器人旋转到的角度。

完成定义后,还需要在功能包的CMakeLists.txt中配置编译选项,让编译器在编译过程中,根据接口定义,自动生成不同语言的代码:

...

find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}

"action/MoveCircle.action"

)

...

编译程序:

pi@NanoPC-T6:~/dev_ws$ colcon build --paths src/my_learning_interface

2.2.2 通信模型

通信模型如下图所示,客户端发送给一个动作目标,服务器控制机器人开始运动,并周期反馈,结束后反馈结束信息。

image-20220528010217043

思路理清楚,接下来开始写代码。相比之前话题和服务的程序,动作通信的例程相对较长,我们一起来运行并分析一下。

2.2.3 服务端

打开my_learning_action功能包,在my_learning_action文件夹下创建action_move_server.py;

"""

ROS2动作示例-负责执行圆周运动动作的服务端

@author: zy

@since : 2025/12/14

"""

import time

import rclpy # ROS2 Python接口库

from rclpy.node import Node # ROS2 节点类

from rclpy.action import ActionServer # ROS2 动作服务器类

from my_learning_interface.action import MoveCircle # 自定义的圆周运动接口

class MoveCircleActionServer(Node):

def __init__(self, name):

super().__init__(name) # ROS2节点父类初始化

self._action_server = ActionServer( # 创建动作服务器(接口类型、动作名、回调函数)

self,

MoveCircle,

'move_circle',

self.execute_callback)

def execute_callback(self, goal_handle): # 执行收到动作目标之后的处理函数

self.get_logger().info('Moving circle...')

feedback_msg = MoveCircle.Feedback() # 创建一个动作反馈信息的消息

for i in range(0, 360, 30): # 从0到360度,执行圆周运动,并周期反馈信息

feedback_msg.state = i # 创建反馈信息,表示当前执行到的角度

self.get_logger().info('Publishing feedback: %d' % feedback_msg.state)

goal_handle.publish_feedback(feedback_msg) # 发布反馈信息

time.sleep(0.5)

goal_handle.succeed() # 动作执行成功

result = MoveCircle.Result() # 创建结果消息

result.finish = True

return result # 反馈最终动作执行的结果

def main(args=None): # ROS2节点主入口main函数

rclpy.init(args=args) # ROS2 Python接口初始化

node = MoveCircleActionServer("action_move_server") # 创建ROS2节点对象并进行初始化

rclpy.spin(node) # 循环等待ROS2退出

node.destroy_node() # 销毁节点对象

rclpy.shutdown() # 关闭ROS2 Python接口

完成代码的编写后需要设置功能包的编译选项,让系统知道Python程序的入口,打开功能包的setup.py文件,加入如下入口点的配置:

entry_points={

'console_scripts': [

'action_move_server = my_learning_action.action_move_server:main',

],

},

2.2.4 客户端

在my_learning_action文件夹下创建action_move_client.py;

"""

ROS2动作示例-请求执行圆周运动动作的客户端

@author: zy

@since : 2025/12/14

"""

import rclpy # ROS2 Python接口库

from rclpy.node import Node # ROS2 节点类

from rclpy.action import ActionClient # ROS2 动作客户端类

from my_learning_interface.action import MoveCircle # 自定义的圆周运动接口

class MoveCircleActionClient(Node):

def __init__(self, name):

super().__init__(name) # ROS2节点父类初始化

self._action_client = ActionClient( # 创建动作客户端(接口类型、动作名)

self, MoveCircle, 'move_circle')

def send_goal(self, enable): # 创建一个发送动作目标的函数

goal_msg = MoveCircle.Goal() # 创建一个动作目标的消息

goal_msg.enable = enable # 设置动作目标为使能,希望机器人开始运动

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: {%d}' % result.finish) # 日志输出执行结果

def feedback_callback(self, feedback_msg): # 创建处理周期反馈消息的回调函数

feedback = feedback_msg.feedback # 读取反馈的数据

self.get_logger().info('Received feedback: {%d}' % feedback.state)

def main(args=None): # ROS2节点主入口main函数

rclpy.init(args=args) # ROS2 Python接口初始化

node = MoveCircleActionClient("action_move_client") # 创建ROS2节点对象并进行初始化

node.send_goal(True) # 发送动作目标

rclpy.spin(node) # 循环等待ROS2退出

node.destroy_node() # 销毁节点对象

rclpy.shutdown() # 关闭ROS2 Python接口

完成代码的编写后需要设置功能包的编译选项,让系统知道Python程序的入口,打开功能包的setup.py文件,加入如下入口点的配置:

entry_points={

'console_scripts': [

'action_move_client = my_learning_action.action_move_client:main',

'action_move_server = my_learning_action.action_move_server:main',

],

},

2.2.5 编译运行

编译程序:

pi@NanoPC-T6:~/dev_ws$ colcon build --paths src/my_learning_action

启动第一个终端,启动动作示例的服务端;

pi@NanoPC-T6:~/dev_ws$ ros2 run my_learning_action action_move_server

[INFO] [1765726344.703410263] [action_move_server]: Moving circle...

[INFO] [1765726344.704012816] [action_move_server]: Publishing feedback: 0

[INFO] [1765726345.207813520] [action_move_server]: Publishing feedback: 30

[INFO] [1765726345.712013457] [action_move_server]: Publishing feedback: 60

[INFO] [1765726346.216313390] [action_move_server]: Publishing feedback: 90

[INFO] [1765726346.720621950] [action_move_server]: Publishing feedback: 120

[INFO] [1765726347.223664991] [action_move_server]: Publishing feedback: 150

[INFO] [1765726347.727581949] [action_move_server]: Publishing feedback: 180

[INFO] [1765726348.231748272] [action_move_server]: Publishing feedback: 210

[INFO] [1765726348.735497200] [action_move_server]: Publishing feedback: 240

[INFO] [1765726349.239607404] [action_move_server]: Publishing feedback: 270

[INFO] [1765726349.743132014] [action_move_server]: Publishing feedback: 300

[INFO] [1765726350.247313968] [action_move_server]: Publishing feedback: 330

启动第二个终端,启动动作示例的客户端;

pi@NanoPC-T6:~/dev_ws$ ros2 run my_learning_action action_move_client

[INFO] [1765726344.695723183] [action_move_client]: Goal accepted :)

[INFO] [1765726344.705272754] [action_move_client]: Received feedback: {0}

[INFO] [1765726345.212856772] [action_move_client]: Received feedback: {30}

[INFO] [1765726345.716884342] [action_move_client]: Received feedback: {60}

[INFO] [1765726346.221326019] [action_move_client]: Received feedback: {90}

[INFO] [1765726346.725441505] [action_move_client]: Received feedback: {120}

[INFO] [1765726347.227005493] [action_move_client]: Received feedback: {150}

[INFO] [1765726347.732291176] [action_move_client]: Received feedback: {180}

[INFO] [1765726348.236114807] [action_move_client]: Received feedback: {210}

[INFO] [1765726348.740333462] [action_move_client]: Received feedback: {240}

[INFO] [1765726349.244567077] [action_move_client]: Received feedback: {270}

[INFO] [1765726349.747922695] [action_move_client]: Received feedback: {300}

[INFO] [1765726350.251839829] [action_move_client]: Received feedback: {330}

[INFO] [1765726350.757741242] [action_move_client]: Result: {1}

终端中,我们可以看到客户端发送动作目标之后,服务器端开始模拟机器人运动,每30度发送一次反馈信息,最终完成运动,并反馈结束运动的信

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/20 13:53:01

解密Prompt系列66. 视觉Token爆炸→DeepSeek-OCR光学压缩

着 DeepSeek-OCR这篇论文&#xff0c;本章我们来回顾下多模态大模型&#xff08;VLM&#xff09;的核心技术演进。很多人认为&#xff1a;图像Token的信息密度和效率远不如文本。但 DeepSeek-OCR的核心价值&#xff0c;就是用实践证明了这是一个伪命题。它通过一套巧妙的串行视…

作者头像 李华
网站建设 2026/4/18 10:02:30

Typst列表符号终极指南:从异常诊断到完美渲染

Typst列表符号终极指南&#xff1a;从异常诊断到完美渲染 【免费下载链接】typst A new markup-based typesetting system that is powerful and easy to learn. 项目地址: https://gitcode.com/GitHub_Trending/ty/typst 在使用Typst进行文档排版时&#xff0c;列表符号…

作者头像 李华
网站建设 2026/4/16 21:24:43

NetBox 自动化导入资产 - IP地址

简介本文章主要讲解使用orb-agent 扫描网络收集IP信息&#xff0c;通过Diode 摄取到NetBox。这两个工具都是NetBox官方的自动化发现产品&#xff0c;下面是示意图。------------------| orb-agent ||------------------|| 网络扫描/资产发现 |------------------|| grpc 通过NM…

作者头像 李华
网站建设 2026/4/10 15:18:26

远程管理效能革命:Quasar网络传输架构的深度优化策略

远程管理效能革命&#xff1a;Quasar网络传输架构的深度优化策略 【免费下载链接】Quasar Remote Administration Tool for Windows 项目地址: https://gitcode.com/gh_mirrors/qua/Quasar 在日益复杂的网络环境中&#xff0c;远程管理工具的性能表现直接决定了运维效率…

作者头像 李华