第2章:ROS2核心概念速通:节点、话题、服务、动作、参数,以及ROS2与ROS1的差异
各位同学,欢迎来到第二章。
说实话,很多刚接触ROS2的朋友,上来就被一堆新名词砸晕了。节点、话题、服务、动作、参数……这些到底是个啥?跟ROS1又有什么区别?
别急,今天我就用最直白的方式,把这些核心概念给你捋清楚。我保证,听完这一章,你脑子里会有一个非常清晰的框架。
2.1 节点(Node):系统的最小执行单元
节点,说白了就是一个独立的可执行程序。它负责完成某一项具体的任务,比如控制激光雷达、处理图像、规划路径等等。
我个人习惯把节点想象成一个「小工人」。每个工人只干自己那摊活,互不干扰。这样设计的好处是——系统解耦,一个节点挂了,不影响其他节点。
核心要点:
- 每个节点是一个独立的进程
- 节点之间通过话题、服务、动作等方式通信
- 节点可以动态地创建和销毁
我的经验:我在做Apollo集成时,遇到过一个问题:所有传感器数据处理都写在一个大节点里,结果一个传感器出bug,整个系统崩溃。后来我拆成多个节点,每个传感器一个节点,问题瞬间解决。记住:节点越专一,系统越稳定。
2.2 话题(Topic):异步通信的「广播」
话题是ROS2里最常用的通信方式。它是一种发布/订阅模型,说白了就是「广播」。
想象一下:一个节点(发布者)在广播里喊话,其他节点(订阅者)只要感兴趣,就能听到。发布者不知道谁在听,订阅者也不知道谁在喊。这种松耦合的设计,让系统非常灵活。
# 发布者示例
rclpy.init()
node = rclpy.create_node('talker')
pub = node.create_publisher(String, 'chatter', 10)
# 订阅者示例
node = rclpy.create_node('listener')
sub = node.create_subscription(String, 'chatter', callback, 10)
注意:ROS2的话题通信是无连接的。发布者只管发,订阅者只管收。如果订阅者启动晚了,可能会错过一些消息。这在实时性要求高的场景下要特别小心。
2.3 服务(Service):同步的「请求-应答」
话题是单向广播,服务则是双向的。客户端发一个请求,服务端处理完返回一个响应。这就像你打电话问客服:「我的订单到哪了?」客服查完告诉你结果。
服务适合那些需要即时反馈的操作,比如查询状态、触发某个动作。
# 服务端
def add_two_ints_callback(request, response):
response.sum = request.a + request.b
return response
srv = node.create_service(AddTwoInts, 'add_two_ints', add_two_ints_callback)
# 客户端
client = node.create_client(AddTwoInts, 'add_two_ints')
future = client.call_async(AddTwoInts.Request(a=3, b=4))
避坑指南:我曾经在Apollo的某个模块里,用服务来频繁查询传感器状态。结果发现,服务调用是阻塞的,一旦服务端处理慢,整个客户端就卡住了。后来我改用话题+状态机的方式,才解决了这个问题。记住:服务适合低频、短时的操作。
2.4 动作(Action):带反馈的「长任务」
动作是ROS2里最强大的通信机制之一。它专门用来处理那些耗时较长的任务,比如机器人导航到某个点、机械臂抓取物体。
动作的特点是:
- 有目标:客户端发送一个目标
- 有反馈:服务端在执行过程中不断反馈进度
- 可取消:客户端可以随时取消任务
你想想看,如果导航到某个点用服务,那客户端就得一直等着,啥也干不了。但用动作,客户端可以一边等反馈,一边干别的事。
# 动作服务端
class FibonacciActionServer(ServerGoalHandle):
def execute_callback(self, goal_handle):
feedback_msg = Fibonacci.Feedback()
feedback_msg.sequence = [0, 1]
for i in range(1, goal_handle.request.order):
feedback_msg.sequence.append(
feedback_msg.sequence[i] + feedback_msg.sequence[i-1])
goal_handle.publish_feedback(feedback_msg)
goal_handle.succeed()
return Fibonacci.Result(sequence=feedback_msg.sequence)
2.5 参数(Parameter):运行时配置的「开关」
参数,说白了就是节点的配置项。比如激光雷达的扫描频率、路径规划的最大速度、PID控制器的系数等等。
ROS2的参数系统比ROS1强大多了。它支持动态配置,你可以在系统运行时修改参数,而不用重启节点。
# 声明参数
node.declare_parameter('max_speed', 1.0)
# 获取参数
max_speed = node.get_parameter('max_speed').value
# 动态设置参数(命令行)
ros2 param set /my_node max_speed 2.0
我的习惯:我一般把所有可调参数都放到一个YAML文件里,启动时一次性加载。这样既方便调试,也方便部署。在Apollo里,很多模块的参数也是这么管理的。
2.6 ROS2与ROS1的核心差异
好,概念讲完了。接下来聊聊ROS2和ROS1到底有啥不一样。说实话,这俩虽然名字像,但底层设计差别很大。
| 对比项 | ROS1 | ROS2 |
|---|---|---|
| 通信中间件 | 自定义的TCPROS/UDPROS | DDS(Data Distribution Service) |
| 实时性 | 不支持实时 | 支持实时(通过DDS的QoS策略) |
| 安全性 | 无内置安全机制 | 支持DDS安全(加密、认证) |
| 节点发现 | 依赖Master节点 | 去中心化,自动发现 |
| 跨平台支持 | 主要支持Linux | 支持Linux、Windows、macOS |
| QoS策略 | 无 | 支持可靠性、持久性、延迟等策略 |
为什么会这样?说白了,ROS1当年设计时没考虑那么多,就是个学术工具。ROS2是重新设计的,目标就是工业级、产品级。
注意:ROS2的DDS虽然强大,但也带来了复杂性。比如,不同DDS实现(Fast DDS、Cyclone DDS、Connext DDS)之间可能存在兼容性问题。我在集成Apollo时,就遇到过因为DDS版本不一致导致的通信失败。所以,建议统一使用同一款DDS实现。
2.7 知识体系总览
最后,我用一张图来总结本章的核心内容。这张图展示了ROS2的通信架构和各个概念之间的关系。
这张图里,你可以看到:节点是基础,话题、服务、动作、参数都是节点之间的通信方式。而DDS则是这一切的底层支撑。记住这个框架,后面学Apollo集成时,你会觉得豁然开朗。
最后说一句:ROS2的这些概念,刚开始可能觉得抽象。但只要你动手写几个小例子,马上就能理解。我建议你从话题开始,写一个发布者和订阅者,感受一下「广播」的魅力。然后试试服务,再试试动作。一步步来,别急。