25 KiB
10 · ROS2 核心概念速览(完整概念地图)
目标:30 分钟内把 ROS2 所有核心概念装进脑子里,后续文档都能"秒懂"。
目录
- 一图总览
- 1. 计算图(Computational Graph)
- 2. Node(节点)
- 3. Topic(话题)
- 4. Service(服务)
- 5. Action(动作)
- 6. Parameter(参数)
- 7. TF2(坐标变换)
- 8. URDF(机器人模型)
- 9. sensor_msgs(常用消息类型)
- 10. Launch(启动文件)
- 11. DDS / QoS / 时间(底层)
- 12. 6 大概念怎么一起工作
- 13. 概念速查表
一图总览
┌──────────────────────────────────────────────────────────────────┐
│ DDS 总线 (默认 fastdds) │
│ │
│ Topic /chatter Topic /image_raw Topic /tf │
│ Topic /joint_states Service /add_two_ints │
│ Action /fibonacci Parameter server │
└───────┬─────────────────┬──────────────────┬─────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌───────────┐
│ talker │ │ camera │ │ tf listener│
│ listener│ │processor │ │joint_pub │
│ (Node) │ │ (Node) │ │ (Node) │
└─────────┘ └──────────┘ └───────────┘
6 大概念:Node / Topic / Service / Action / Parameter / TF。 下面 12 节拆解。
1. 计算图(Computational Graph)
1.1 是什么
ROS2 程序由节点(Node)组成,节点之间通过话题/服务/动作连接,形成一张"计算图"。 图不是静态配置,是运行时由 DDS 自动发现。
1.2 看计算图
# 命令行看拓扑
ros2 node list
ros2 topic list
ros2 service list
ros2 action list
# 可视化(GUI)
ros2 run rqt_graph rqt_graph
1.3 三个关键术语
| 术语 | 含义 |
|---|---|
| Node | 一个独立运行的程序 |
| Edge | Node 之间的连接(Topic / Service / Action) |
| Discovery | DDS 自动找节点,无需中心注册 |
2. Node(节点)
2.1 是什么
ROS2 程序的基本单元。一个 Node = 一个进程 = 一项业务功能。
2.2 怎么写(Python)
import rclpy
from rclpy.node import Node
class MyNode(Node):
def __init__(self):
super().__init__('my_node_name') # 节点名,在 ROS Domain 内必须唯一
# 在这里创建 publisher / subscription / timer / param
self.timer_ = self.create_timer(0.5, self.tick)
def tick(self):
self.get_logger().info('tick')
def main():
rclpy.init() # 全局初始化
node = MyNode()
rclpy.spin(node) # 进入事件循环,阻塞
rclpy.shutdown() # 退出
if __name__ == '__main__':
main()
2.3 怎么写(C++)
#include "rclcpp/rclcpp.hpp"
class MyNode : public rclcpp::Node {
public:
MyNode() : rclcpp::Node("my_node_name") {
timer_ = this->create_wall_timer(
500ms, std::bind(&MyNode::tick, this));
}
private:
void tick() {
RCLCPP_INFO(this->get_logger(), "tick");
}
rclcpp::TimerBase::SharedPtr timer_;
};
int main(int argc, char * argv[]) {
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MyNode>());
rclcpp::shutdown();
return 0;
}
2.4 关键 API
| API | 用途 |
|---|---|
super().__init__('name') |
节点构造时设名字 |
create_publisher(...) |
创建发布者 |
create_subscription(...) |
创建订阅者 |
create_service(...) |
创建服务端 |
create_action_server(...) |
创建动作服务端 |
create_timer(period, cb) |
创建定时器 |
declare_parameter(...) |
声明参数 |
get_logger().info(...) |
打日志 |
get_clock() |
取时钟 |
rclpy.spin(node) |
进入事件循环,处理所有回调 |
2.5 生命周期
rclpy.init() ─┐
├─ 实例化 Node ─ 创建 pub/sub/service ─ spin 等待回调
实例化 Node ─┘
│
▼
rclpy.spin(node) ── 阻塞,处理 timer / subscription / service 回调
│
▼ (Ctrl+C 或 shutdown)
rclpy.shutdown() ── 清理
2.6 本仓库对照
- Python:
src/py_pubsub/py_pubsub/publisher_member_function.py - C++:
src/cpp_pubsub/src/publisher_member_function.cpp
3. Topic(话题)
3.1 是什么
异步、多对多、单向的发布订阅通道。
- 异步:
publish()不阻塞 - 多对多:1 个 publisher,任意多个 subscriber
- 单向:消息只从 pub 到 sub,反方向不通
3.2 通信模型
Publisher ──publish()──> Topic (chatter) ──callback(msg)──> Subscriber
────────────────────────────────────────────────────
异步,非阻塞 多对多,单向 事件循环触发
3.3 vs 其他通信方式
| Topic | Service | Action | |
|---|---|---|---|
| 同步 | 异步 | 同步 | 异步 |
| 一对多 | ✅ | ❌ | ❌ |
| 反馈 | ❌ | ❌ | ✅ |
| 取消 | N/A | ❌ | ✅ |
3.4 Python API
# Publisher
pub = node.create_publisher(
std_msgs.msg.String, # 消息类型
'chatter', # topic 名
10 # QoS 深度
)
# 构造 + 发送
msg = std_msgs.msg.String()
msg.data = 'hello'
pub.publish(msg) # 异步,不等任何东西
# Subscriber
sub = node.create_subscription(
std_msgs.msg.String, # 类型
'chatter', # topic 名
callback, # 回调函数
10 # QoS 深度
)
def callback(msg):
node.get_logger().info(f'recv: {msg.data}')
3.5 C++ API
// Publisher
auto pub = node->create_publisher<std_msgs::msg::String>("chatter", 10);
auto msg = std_msgs::msg::String();
msg.data = "hello";
pub->publish(msg);
// Subscriber
auto sub = node->create_subscription<std_msgs::msg::String>(
"chatter", 10,
[this](const std_msgs::msg::String::SharedPtr msg) {
RCLCPP_INFO(this->get_logger(), "recv: %s", msg->data.c_str());
});
3.6 QoS(服务质量)速查
| 维度 | 取值 | 默认 | 影响 |
|---|---|---|---|
| Reliability | RELIABLE / BEST_EFFORT | RELIABLE | 必须 pub/sub 一致 |
| History | KEEP_LAST(N) / KEEP_ALL | KEEP_LAST(10) | 队列大小 |
| Durability | VOLATILE / TRANSIENT_LOCAL | VOLATILE | 晚订阅者是否收到旧数据 |
兼容规则:
- RELIABLE ↔ RELIABLE ✅
- BEST_EFFORT ↔ BEST_EFFORT ✅
- BEST_EFFORT → RELIABLE ✅(sub 容忍丢)
- RELIABLE → BEST_EFFORT ❌(sub 不发 ACK,pub 报错)
3.7 常用 CLI
ros2 topic list # 所有 topic
ros2 topic info <topic> -v # 类型 + pub/sub 列表
ros2 topic echo <topic> # 实时打印
ros2 topic hz <topic> # 频率 Hz
ros2 topic bw <topic> # 带宽 bytes/s
ros2 topic pub <topic> <type> "<msg>" --once # 发一条测试
ros2 bag record <topic> # 录包
ros2 bag play <bag> # 回放
3.8 本仓库对照
- 包:
src/py_pubsub/,src/cpp_pubsub/ - 跨语言互通验证:
docker/bringup_e2e.log
深度: doc/20-topics.md
4. Service(服务)
4.1 是什么
同步、一对一、双向的请求/响应。
- 一次性调用 + 等结果
- 几毫秒到几秒
4.2 通信模型
Client ──call(req)──> Server
◀──response────
同步(阻塞),一次一答
4.3 .srv 文件定义
# example_interfaces/srv/AddTwoInts.srv
int64 a # Request 字段
int64 b
---
int64 sum # Response 字段
---上 = Request---下 = Response
4.4 Server 端
from example_interfaces.srv import AddTwoInts
srv = node.create_service(
AddTwoInts, # 服务类型
'add_two_ints', # 服务名
callback # 回调签名:callback(req, resp) -> resp
)
def callback(request, response):
response.sum = request.a + request.b
return response # 必须 return response
4.5 Client 端(异步风格)
client = node.create_client(AddTwoInts, 'add_two_ints')
while not client.wait_for_service(timeout_sec=1.0):
node.get_logger().info('waiting...')
req = AddTwoInts.Request()
req.a = 12; req.b = 30
future = client.call_async(req)
# 必须 spin 让 future 完成
rclpy.spin_until_future_complete(node, future, timeout_sec=5.0)
result = future.result()
print(result.sum) # 42
4.6 何时用
| 场景 | 用 Service |
|---|---|
| 拍照(几 ms) | ✅ |
| 关节角度查询 | ✅ |
| 计算查询 | ✅ |
| 周期性相机帧 | ❌ → Topic |
| 长任务(几秒到几分钟) | ❌ → Action |
4.7 常用 CLI
ros2 service list
ros2 service type <name> # 类型
ros2 service call <name> <type> "<req>"
# 例:
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 12, b: 30}"
5. Action(动作)
5.1 是什么
Service 的"长任务"版本:
- client 发 Goal
- server 周期性推 Feedback
- 最终返回 Result
- client 可中途 cancel
适合:抓取、导航、SLAM。
5.2 通信模型
Client Server
│ Goal │
├────────────────────>│
│ ▼ 执行中
│ Feedback │
│<────────────────────┤
│ Feedback │
│<────────────────────┤
│ Result │
│<────────────────────┤
│ cancel (可选) │
├────────────────────>│
5.3 .action 文件定义
# example_interfaces/action/Fibonacci.action
int32 order
---
int32[] sequence # Result
---
int32[] sequence # Feedback
| 段 | 含义 |
|---|---|
| 第一段 | Goal(client 发) |
--- |
分隔 |
| 第二段 | Result(server 最终给一次) |
--- |
分隔 |
| 第三段 | Feedback(server 周期性推 0..N 次) |
5.4 Server 端
import rclpy.action
action_server = rclpy.action.ActionServer(
node,
Fibonacci, # ActionType
'fibonacci', # action 名
execute_callback # 签名:cb(goal_handle) -> result
)
def execute_callback(goal_handle):
order = goal_handle.request.order
feedback = Fibonacci.Feedback()
result = Fibonacci.Result()
sequence = [0, 1]
for i in range(1, order):
if goal_handle.is_cancel_requested:
goal_handle.canceled()
return Fibonacci.Result()
sequence.append(sequence[i] + sequence[i-1])
feedback.sequence = sequence
goal_handle.publish_feedback(feedback)
goal_handle.succeed()
result.sequence = sequence
return result
5.5 Client 端
client = ActionClient(node, Fibonacci, 'fibonacci')
client.wait_for_server()
goal = Fibonacci.Goal()
goal.order = 6
future = client.send_goal_async(
goal,
feedback_callback=lambda msg: print('fb:', msg.feedback.sequence)
)
future.add_done_callback(cb_goal_response)
5.6 状态机
PENDING → ACCEPTED → EXECUTING → SUCCEEDED
ABORTED
CANCELED
5.7 MultiThreadedExecutor(关键!)
Action server 的 execute + publish_feedback 都在主线程。用 MultiThreadedExecutor 避免 Feedback 卡死:
from rclpy.executors import MultiThreadedExecutor
executor = MultiThreadedExecutor(num_threads=4)
executor.add_node(node)
executor.spin()
5.8 何时用 vs Service
| 持续 | 用 |
|---|---|
| < 几秒 | Service |
| 几秒 ~ 几小时 | Action |
6. Parameter(参数)
6.1 是什么
节点的运行时配置项。声明后可以运行时改,不重编译。
6.2 声明 / 读取 / 写
class Talker(Node):
def __init__(self):
super().__init__('talker_py')
# 声明参数 + 默认值
self.declare_parameter('period_ms', 500)
self.declare_parameter('topic', 'chatter')
# 读取
period = self.get_parameter('period_ms').value
topic = self.get_parameter('topic').value
def change_param(self, new_period):
# 运行时改
param = rclpy.parameter.Parameter(
'period_ms', rclpy.Parameter.Type.INTEGER, new_period
)
self.set_parameters([param])
6.3 CLI 改参数
ros2 param list # 节点的所有参数
ros2 param get /talker_py period_ms
ros2 param set /talker_py period_ms 200
ros2 param describe /talker_py period_ms
ros2 param dump /talker_py > params.yaml # 导出
ros2 param load /talker_py params.yaml # 加载
6.4 launch 中覆盖
Node(
package='py_pubsub',
executable='talker',
parameters=[{'period_ms': 200, 'topic': 'chatter'}] # 覆盖
)
CLI 启动:
ros2 run py_pubsub talker --ros-args -p period_ms:=200 -p topic:=hello
6.5 YAML 文件
# config/params.yaml
talker_py:
ros__parameters:
period_ms: 200
topic: chatter
Node(package='py_pubsub', executable='talker',
parameters=[get_package_share_directory('my_pkg') + '/config/params.yaml'])
7. TF2(坐标变换)
7.1 是什么
管理机器人所有坐标系之间相对位姿的工具。维护一棵 TF tree。
7.2 典型 TF tree
world
└─ map (SLAM)
└─ odom (AMCL / 里程计)
└─ base_link (底盘)
├─ base_footprint
├─ laser_frame (激光雷达)
├─ camera_optical_frame (相机)
└─ arm_base (机械臂)
└─ shoulder (关节 1)
└─ upper_arm (关节 2)
└─ wrist (关节 3)
└─ gripper (夹爪)
7.3 C++ Listener API
#include "tf2_ros/buffer.h"
#include "tf2_ros/transform_listener.h"
tf2_ros::Buffer buffer(node->get_clock());
tf2_ros::TransformListener listener(buffer, node);
// 查"gripper 在 base_link 下"
geometry_msgs::msg::TransformStamped tf = buffer.lookupTransform(
"base_link", // target frame
"gripper", // source frame
tf2::TimePointZero); // latest
RCLCPP_INFO(node->get_logger(),
"gripper in base_link: x=%.3f y=%.3f z=%.3f",
tf.transform.translation.x, ...);
7.4 几何变换(把点变换到其他系)
#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
geometry_msgs::msg::PointStamped p_in, p_out;
p_in.header.frame_id = "world";
p_in.point.x = 1.0; p_in.point.y = 2.0; p_in.point.z = 3.0;
p_out = buffer.transform(p_in, "robot");
// p_out 在 robot 系下
7.5 Python Listener
from tf2_ros import Buffer, TransformListener
buffer = Buffer()
listener = TransformListener(buffer, node)
try:
tf = buffer.lookup_transform(
'base_link', 'gripper', rclpy.time.Time(),
timeout=rclpy.duration.Duration(seconds=1.0))
print(tf.transform.translation)
except Exception as e:
node.get_logger().warn(f'lookup failed: {e}')
7.6 静态 TF(不变的关系)
# 命令行:激光雷达固定在底盘
ros2 run tf2_ros static_transform_publisher \
--x 0.1 --y 0 --z 0.2 --roll 0 --pitch 0 --yaw 0 \
--frame-id base_link --child-frame-id laser_frame
# launch 里
Node(package='tf2_ros', executable='static_transform_publisher',
arguments=['--x', '0.1', '--y', '0', '--z', '0.2',
'--frame-id', 'base_link', '--child-frame-id', 'laser_frame'])
7.7 动态 TF(URDF + JointState)
robot_state_publisher (ROS2 系统包):
- 输入:URDF +
/joint_states - 输出:动态 TF 到
/tf
sudo apt install ros-humble-robot-state-publisher
ros2 run robot_state_publisher robot_state_publisher \
--ros-args -p robot_description:="$(xacro arm.urdf)"
7.8 调试命令
ros2 run tf2_tools view_frames # 生成 frames.pdf
ros2 run tf2_ros tf2_echo base_link gripper # 实时打印
ros2 topic hz /tf # /tf 频率
ros2 topic info /tf_static -v # 静态 TF 列表
深度: doc/50-tf2.md
8. URDF(机器人模型)
8.1 是什么
XML 格式的机器人描述:link / joint / 视觉 / 碰撞 / 物理参数。
8.2 最小 URDF
<?xml version="1.0"?>
<robot name="my_arm">
<link name="base_link">
<visual><geometry><box size="0.1 0.1 0.1"/></geometry></visual>
<collision><geometry><box size="0.1 0.1 0.1"/></geometry></collision>
<inertial><mass value="1.0"/><inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.01"/></inertial>
</link>
<joint name="joint1" type="revolute">
<parent link="base_link"/>
<child link="link1"/>
<origin xyz="0 0 0.05"/>
<axis xyz="0 0 1"/>
<limit lower="-3.14" upper="3.14" effort="1.0" velocity="1.0"/>
</joint>
<link name="link1">
<visual><geometry><box size="0.05 0.05 0.1"/></geometry></visual>
<collision><geometry><box size="0.05 0.05 0.1"/></geometry></collision>
<inertial><mass value="0.5"/><inertia ixx="0.005" ixy="0" ixz="0" iyy="0.005" iyz="0" izz="0.005"/></inertial>
</link>
</robot>
8.3 关节类型
| type | 含义 | DoF |
|---|---|---|
revolute |
转动(有限位) | 1 |
continuous |
转动(无限位) | 1 |
prismatic |
滑动 | 1 |
fixed |
固定 | 0 |
floating |
6 DoF | 6 |
8.4 校验
sudo apt install liburdfdom-tools
check_urdf my_arm.urdf # 校验 URDF
urdf_to_graphiz my_arm.urdf # 生成 PDF/PNG 图
深度: doc/60-urdf.md
9. sensor_msgs(常用消息类型)
| 消息 | 关键字段 | 用途 |
|---|---|---|
std_msgs/String |
string data |
通用 |
std_msgs/Header |
stamp, frame_id |
时间戳 + 坐标系 |
sensor_msgs/Image |
height/width/encoding/step/data |
相机图像 |
sensor_msgs/CameraInfo |
K/D/R/P |
相机内参/外参 |
sensor_msgs/PointCloud2 |
height/width/data/is_bigendian |
3D 点云 |
sensor_msgs/JointState |
name[]/position[]/velocity[]/effort[] |
关节状态 |
sensor_msgs/Imu |
orientation/angular_velocity/linear_acceleration |
IMU |
geometry_msgs/Pose |
Point position + Quaternion orientation |
位姿 |
geometry_msgs/PoseStamped |
Header + Pose |
带时间戳位姿 |
geometry_msgs/Twist |
Vector3 linear + Vector3 angular |
速度指令 |
geometry_msgs/Transform |
Vector3 translation + Quaternion rotation |
TF 变换 |
nav_msgs/Odometry |
pose + twist + covariances |
里程计 |
nav_msgs/Path |
PoseStamped[] |
路径 |
trajectory_msgs/JointTrajectory |
JointTrajectoryPoint[] |
关节轨迹(ros2_control 用) |
trajectory_msgs/JointTrajectoryPoint |
positions[]/velocities[]/accelerations[]/effort[] |
单点轨迹 |
vision_msgs/Detection2DArray |
detections[] |
目标检测结果 |
10. Launch(启动文件)
10.1 是什么
Python 脚本,描述"启动哪些节点 + 参数",取代 ROS1 XML。
10.2 最小 launch
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='my_pkg',
executable='my_node',
name='my_node',
output='screen',
parameters=[{'param1': 'value1'}],
),
])
10.3 启动方式
ros2 launch <pkg> <launch_file.py>
ros2 launch <pkg> <launch_file.py> topic:=hello period_ms:=200
深度: doc/70-launch.md
11. DDS / QoS / 时间(底层)
11.1 DDS 是什么
ROS2 默认用 DDS(Data Distribution Service)做底层通信。
本仓库默认 FastDDS(rmw_fastrtps_cpp)。
DDS 提供的核心能力:
- 自动节点发现(基于 UDP multicast)
- 多种 QoS(可靠 / 尽力而为)
- 实时性
11.2 RMW(ROS Middleware)
ROS2 用 RMW 抽象层,RMW 是 DDS 的 ROS 包装:
| RMW 实现 | 包 | 适用 |
|---|---|---|
rmw_fastrtps_cpp |
ros-humble-rmw-fastrtps-cpp(默认) |
通用 |
rmw_cyclonedds_cpp |
ros-humble-rmw-cyclonedds-cpp |
跨网段更稳 |
切换:
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
11.3 时间
| 概念 | 含义 | 用法 |
|---|---|---|
system time |
墙钟时间(默认) | 仿真时需配 use_sim_time: true |
sim time |
仿真时钟(/clock topic) |
Gazebo / 录包回放 |
Header.stamp |
消息时间戳 | TF 同步、消息时间关联 |
12. 6 大概念怎么一起工作
┌────────────────────────────────────────────────┐
│ application │
│ (决策 / 控制 / 感知 / 学习) │
└─────────┬──────────────────────────────────────┘
│ 用 TF / Topic / Service / Action / Parameter
┌─────────▼──────────────────────────────────────┐
│ ROS2 中间件 (rclcpp/rclpy) │
│ ┌────────────────────────────────────────┐ │
│ │ Topic Pub/Sub (异步多对多单向) │ │
│ │ Service Req/Resp (同步一对一双向) │ │
│ │ Action Goal/Feedback/Result (长任务) │ │
│ │ Param 配置 │ │
│ │ TF2 坐标变换树 │ │
│ └────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────┐ │
│ │ DDS (默认 fastdds) │ │
│ └────────────────────────────────────────┘ │
└────────────────────────────────────────────────┘
│ 走 UDP multicast / unicast
┌─────────▼──────────────────────────────────────┐
│ 网络 │
└─────────────────────────────────────────────────┘
13. 概念速查表
| 概念 | 一句话 | API | 深度文档 |
|---|---|---|---|
| Node | 一个进程 = 一项业务 | rclpy.spin(node) |
本篇 §2 |
| Topic | 异步多对多单向 | create_publisher / create_subscription |
20-topics |
| Service | 同步 1对1 双向 | create_service / create_client |
30-services |
| Action | 长任务 + 反馈 + 可取消 | ActionServer / ActionClient |
40-actions |
| Parameter | 运行时配置 | declare_parameter / set_parameter |
本篇 §6 |
| TF2 | 坐标系树 | Buffer.lookup_transform |
50-tf2 |
| URDF | 机器人 XML 描述 | check_urdf |
60-urdf |
| Launch | 启动脚本 | ros2 launch <pkg> <file> |
70-launch |
| DDS | 通信中间件 | RMW_IMPLEMENTATION= |
本篇 §11 |
接下来读
按主题深读:
| 你想深挖什么 | 看 |
|---|---|
| Topic 实现细节、QoS、跨语言互通 | 20-topics.md |
| Service 实现、req/resp 时序 | 30-services.md |
| Action 三件套、cancel、MultiThreadedExecutor | 40-actions.md |
| TF tree / lookup / static_transform | 50-tf2.md |
| URDF / xacro / robot_state_publisher | 60-urdf.md |
| launch 嵌套、参数覆盖、事件 | 70-launch.md |
| 三机部署(PC + RDK X5 + RK3506) | 100-embedded-deployment.md |
| 进入具身智能 / VLA / 机器人 | 99-embodied-ai.md |