第二章:ROS2核心概念与工具
各位同学,欢迎来到第二章。说实话,ROS2的概念体系乍一看有点多,但别慌。我当年刚接触时也觉得眼花缭乱,后来发现,只要抓住几个核心的“通信骨架”,整个系统就清晰了。这一章,我们就来把这些骨架一一拆解。
2.1 节点:系统的“细胞”
节点,说白了就是一个独立的可执行进程。每个节点只负责一件事,比如读取激光雷达数据、处理IMU信息、或者控制电机。我个人的习惯是,一个传感器对应一个节点,这样调试起来特别方便。
核心要点:节点之间通过话题、服务、动作进行通信,节点本身不存储全局状态。
创建节点很简单,用Python的话几行代码就行:
import rclpy
from rclpy.node import Node
class MySensorNode(Node):
def __init__(self):
super().__init__('sensor_node')
self.get_logger().info('传感器节点已启动!')
def main(args=None):
rclpy.init(args=args)
node = MySensorNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
我的小技巧:节点命名一定要有规律。比如 lidar_front_node、imu_back_node,这样用 ros2 node list 查看时一目了然。
2.2 话题:异步的“广播站”
话题是ROS2里最常用的通信方式。发布者只管发,订阅者只管收,双方不需要知道对方的存在。我在做多传感器融合时,经常把原始传感器数据发布成话题,然后融合节点去订阅。
举个例子,发布激光雷达数据:
class LidarPublisher(Node):
def __init__(self):
super().__init__('lidar_pub')
self.pub = self.create_publisher(String, 'lidar_data', 10)
self.timer = self.create_timer(0.1, self.publish_data)
def publish_data(self):
msg = String()
msg.data = '激光雷达数据帧'
self.pub.publish(msg)
避坑指南:我曾经遇到过话题名写错导致数据收不到的情况。建议用 ros2 topic list 确认话题是否存在,再用 ros2 topic echo /话题名 查看数据是否正常。
2.3 服务:同步的“一问一答”
服务适合需要立即得到结果的场景,比如请求当前传感器状态、触发一次校准。和话题不同,服务是同步的——客户端发请求,服务器处理完才返回响应。
你想想看,如果每次获取传感器数据都用服务,那系统效率会很低。所以服务一般用于配置、查询这类低频操作。
| 通信方式 | 特点 | 适用场景 |
|---|---|---|
| 话题 (Topic) | 异步、一对多、持续流 | 传感器数据、控制指令 |
| 服务 (Service) | 同步、一对一、请求-响应 | 参数查询、触发动作 |
| 动作 (Action) | 异步、可取消、有反馈 | 导航、机械臂运动 |
2.4 动作:带反馈的“长任务”
动作是ROS2里比较高级的通信方式。它适合执行时间较长的任务,比如让机器人走到某个位置。动作有三个阶段:目标发送、持续反馈、最终结果。
我记得有一次做多传感器标定,需要机器人转一圈采集数据。用动作就很合适——发送“旋转一周”的目标,过程中不断接收当前角度反馈,完成后拿到采集结果。
2.5 参数:节点的“配置旋钮”
参数就是节点的配置项。比如激光雷达的采样频率、IMU的滤波系数。我个人习惯把经常需要调整的值都做成参数,这样不用改代码就能调。
self.declare_parameter('sample_rate', 10)
rate = self.get_parameter('sample_rate').get_parameter_value().integer_value
实用技巧:用 ros2 param list 查看所有参数,用 ros2 param set /节点名 参数名 值 动态修改参数,非常方便。
2.6 Launch文件:一键启动的艺术
当你有十几个节点要启动时,一个个敲命令显然不现实。Launch文件就是用来解决这个问题的。它用Python或XML描述要启动哪些节点、带什么参数。
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(package='sensor_driver', executable='lidar_node'),
Node(package='sensor_driver', executable='imu_node'),
Node(package='fusion', executable='fusion_node'),
])
2.7 rqt工具:可视化调试利器
rqt是ROS2的图形化工具集。我最常用的是 rqt_graph,它能直观显示节点之间的通信关系。做多传感器融合时,看一眼rqt_graph就知道数据流对不对。
其他好用的还有:
- rqt_plot:实时绘制数据曲线,看传感器值变化
- rqt_console:查看日志输出,过滤不同级别的消息
- rqt_tf_tree:查看tf2坐标树
2.8 tf2:坐标变换的“交通枢纽”
多传感器融合绕不开坐标变换。激光雷达有自己的坐标系,IMU有另一个坐标系,它们都安装在机器人上。tf2就是用来管理这些坐标系之间关系的。
举个例子,激光雷达检测到一个障碍物在 lidar_frame 下坐标是 (1, 0, 0),但你需要知道它在机器人基座 base_link 下的位置。用tf2可以自动完成这个变换。
from tf2_ros import TransformListener, Buffer
self.tf_buffer = Buffer()
self.tf_listener = TransformListener(self.tf_buffer, self)
# 获取变换
transform = self.tf_buffer.lookup_transform(
'base_link', 'lidar_frame', rclpy.time.Time())
我曾经踩过的坑:tf2的坐标系名称大小写敏感,而且必须保证发布频率足够。如果坐标变换超时,所有依赖它的节点都会报错。建议用 tf2_monitor 工具检查发布频率。
嗯,这一章的内容就到这里。这些概念是ROS2的基石,后面的多传感器融合实战都会用到它们。建议你动手敲一遍代码,用 ros2 topic echo 看看数据长什么样,用 rqt_graph 看看节点怎么连的。实践出真知,光看是学不会的。