880 lines
26 KiB
Markdown
880 lines
26 KiB
Markdown
# 10 · ROS2 核心概念速览(完整概念地图)
|
|
|
|
> **目标**:30 分钟内把 ROS2 所有核心概念装进脑子里,后续文档都能"秒懂"。
|
|
|
|
---
|
|
|
|
## 目录
|
|
|
|
- [一图总览](#一图总览)
|
|
- [1. 计算图(Computational Graph)](#1-计算图computational-graph)
|
|
- [2. Node(节点)](#2-node节点)
|
|
- [3. Topic(话题)](#3-topic话题)
|
|
- [4. Service(服务)](#4-service服务)
|
|
- [5. Action(动作)](#5-action动作)
|
|
- [6. Parameter(参数)](#6-parameter参数)
|
|
- [7. TF2(坐标变换)](#7-tf2坐标变换)
|
|
- [8. URDF(机器人模型)](#8-urdf机器人模型)
|
|
- [9. sensor_msgs(常用消息类型)](#9-sensor_msgs常用消息类型)
|
|
- [10. Launch(启动文件)](#10-launch启动文件)
|
|
- [11. DDS / QoS / 时间(底层)](#11-dds--qos--时间底层)
|
|
- [12. 6 大概念怎么一起工作](#12-6-大概念怎么一起工作)
|
|
- [13. 概念速查表](#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 看计算图
|
|
```bash
|
|
# 命令行看拓扑
|
|
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)
|
|
|
|
```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++)
|
|
|
|
```cpp
|
|
#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_node.py`](../src/py_pubsub/py_pubsub/publisher_node.py)(类名 `ChatterPublisher`)
|
|
- C++:[`src/cpp_pubsub/src/chatter_publisher.cpp`](../src/cpp_pubsub/src/chatter_publisher.cpp)(类名 `ChatterPublisher`)
|
|
|
|
---
|
|
|
|
## 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
|
|
|
|
```python
|
|
# 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
|
|
|
|
```cpp
|
|
// 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
|
|
```bash
|
|
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/py_pubsub/), [`src/cpp_pubsub/`](../src/cpp_pubsub/)
|
|
- 跨语言互通验证:[`docker/bringup_e2e.log`](../docker/bringup_e2e.log)
|
|
|
|
**深度**: [`doc/20-topics.md`](20-topics.md)
|
|
|
|
---
|
|
|
|
## 4. Service(服务)
|
|
|
|
### 4.1 是什么
|
|
**同步、一对一、双向**的请求/响应。
|
|
- 一次性调用 + 等结果
|
|
- 几毫秒到几秒
|
|
|
|
### 4.2 通信模型
|
|
|
|
```
|
|
Client ──call(req)──> Server
|
|
◀──response────
|
|
同步(阻塞),一次一答
|
|
```
|
|
|
|
### 4.3 .srv 文件定义
|
|
|
|
```srv
|
|
# example_interfaces/srv/AddTwoInts.srv
|
|
int64 a # Request 字段
|
|
int64 b
|
|
---
|
|
int64 sum # Response 字段
|
|
```
|
|
|
|
- `---` 上 = Request
|
|
- `---` 下 = Response
|
|
|
|
### 4.4 Server 端
|
|
|
|
```python
|
|
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 端(异步风格)
|
|
|
|
```python
|
|
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
|
|
```bash
|
|
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}"
|
|
```
|
|
|
|
**深度**: [`doc/30-services.md`](30-services.md)
|
|
|
|
---
|
|
|
|
## 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 文件定义
|
|
|
|
```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 端
|
|
|
|
```python
|
|
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 端
|
|
|
|
```python
|
|
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 卡死:
|
|
|
|
```python
|
|
from rclpy.executors import MultiThreadedExecutor
|
|
executor = MultiThreadedExecutor(num_threads=4)
|
|
executor.add_node(node)
|
|
executor.spin()
|
|
```
|
|
|
|
### 5.8 何时用 vs Service
|
|
| 持续 | 用 |
|
|
|---|---|
|
|
| < 几秒 | Service |
|
|
| 几秒 ~ 几小时 | Action |
|
|
|
|
**深度**: [`doc/40-actions.md`](40-actions.md)
|
|
|
|
---
|
|
|
|
## 6. Parameter(参数)
|
|
|
|
### 6.1 是什么
|
|
节点的**运行时配置项**。声明后可以运行时改,不重编译。
|
|
|
|
### 6.2 声明 / 读取 / 写
|
|
|
|
```python
|
|
class ChatterPublisher(Node):
|
|
def __init__(self, *, node_name='chatter_publisher'):
|
|
super().__init__(node_name)
|
|
# 声明参数 + 默认值(实际类名 ChatterPublisher / 参数 publish_rate_hz)
|
|
self.declare_parameter('publish_rate_hz', 2.0)
|
|
self.declare_parameter('topic_name', 'chatter')
|
|
|
|
# 读取
|
|
rate = self.get_parameter('publish_rate_hz').value
|
|
topic = self.get_parameter('topic_name').value
|
|
|
|
def change_param(self, new_rate):
|
|
# 运行时改
|
|
param = rclpy.parameter.Parameter(
|
|
'publish_rate_hz', rclpy.Parameter.Type.DOUBLE, new_rate
|
|
)
|
|
self.set_parameters([param])
|
|
```
|
|
|
|
### 6.3 CLI 改参数
|
|
|
|
```bash
|
|
ros2 param list # 节点的所有参数
|
|
ros2 param get /chatter_publisher_py publish_rate_hz
|
|
ros2 param set /chatter_publisher_py publish_rate_hz 5.0
|
|
ros2 param describe /chatter_publisher_py publish_rate_hz
|
|
ros2 param dump /chatter_publisher_py > params.yaml # 导出
|
|
ros2 param load /chatter_publisher_py params.yaml # 加载
|
|
```
|
|
|
|
### 6.4 launch 中覆盖
|
|
|
|
```python
|
|
Node(
|
|
package='py_pubsub',
|
|
executable='chatter_publisher',
|
|
name='chatter_publisher_py',
|
|
parameters=[{'publish_rate_hz': 5.0, 'topic_name': 'chatter'}], # 覆盖
|
|
)
|
|
```
|
|
|
|
CLI 启动:
|
|
```bash
|
|
ros2 run py_pubsub chatter_publisher --ros-args -p publish_rate_hz:=5.0 -p topic_name:=hello
|
|
```
|
|
|
|
### 6.5 YAML 文件
|
|
|
|
```yaml
|
|
# config/params.yaml
|
|
chatter_publisher_py:
|
|
ros__parameters:
|
|
publish_rate_hz: 5.0
|
|
topic_name: chatter
|
|
```
|
|
|
|
```python
|
|
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
|
|
|
|
```cpp
|
|
#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 几何变换(把点变换到其他系)
|
|
|
|
```cpp
|
|
#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
|
|
|
|
```python
|
|
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(不变的关系)
|
|
|
|
```bash
|
|
# 命令行:激光雷达固定在底盘
|
|
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
|
|
```
|
|
|
|
```python
|
|
# 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`
|
|
|
|
```bash
|
|
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 调试命令
|
|
```bash
|
|
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`](50-tf2.md)
|
|
|
|
---
|
|
|
|
## 8. URDF(机器人模型)
|
|
|
|
### 8.1 是什么
|
|
XML 格式的机器人描述:link / joint / 视觉 / 碰撞 / 物理参数。
|
|
|
|
### 8.2 最小 URDF
|
|
|
|
```xml
|
|
<?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 校验
|
|
```bash
|
|
sudo apt install liburdfdom-tools
|
|
check_urdf my_arm.urdf # 校验 URDF
|
|
urdf_to_graphiz my_arm.urdf # 生成 PDF/PNG 图
|
|
```
|
|
|
|
**深度**: [`doc/60-urdf.md`](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
|
|
|
|
```python
|
|
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 启动方式
|
|
```bash
|
|
ros2 launch <pkg> <launch_file.py>
|
|
ros2 launch <pkg> <launch_file.py> topic:=hello period_ms:=200
|
|
```
|
|
|
|
**深度**: [`doc/70-launch.md`](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` | 跨网段更稳 |
|
|
|
|
切换:
|
|
```bash
|
|
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`](20-topics.md) |
|
|
| Service | 同步 1对1 双向 | `create_service / create_client` | [`30-services`](30-services.md) |
|
|
| Action | 长任务 + 反馈 + 可取消 | `ActionServer / ActionClient` | [`40-actions`](40-actions.md) |
|
|
| Parameter | 运行时配置 | `declare_parameter / set_parameter` | 本篇 §6 |
|
|
| TF2 | 坐标系树 | `Buffer.lookup_transform` | [`50-tf2`](50-tf2.md) |
|
|
| URDF | 机器人 XML 描述 | `check_urdf` | [`60-urdf`](60-urdf.md) |
|
|
| Launch | 启动脚本 | `ros2 launch <pkg> <file>` | [`70-launch`](70-launch.md) |
|
|
| DDS | 通信中间件 | `RMW_IMPLEMENTATION=` | 本篇 §11 |
|
|
|
|
---
|
|
|
|
## 接下来读
|
|
|
|
按主题深读:
|
|
|
|
| 你想深挖什么 | 看 |
|
|
|---|---|
|
|
| Topic 实现细节、QoS、跨语言互通 | [`20-topics.md`](20-topics.md) |
|
|
| Service 实现、req/resp 时序 | [`30-services.md`](30-services.md) |
|
|
| Action 三件套、cancel、MultiThreadedExecutor | [`40-actions.md`](40-actions.md) |
|
|
| TF tree / lookup / static_transform | [`50-tf2.md`](50-tf2.md) |
|
|
| URDF / xacro / robot_state_publisher | [`60-urdf.md`](60-urdf.md) |
|
|
| launch 嵌套、参数覆盖、事件 | [`70-launch.md`](70-launch.md) |
|
|
| 三机部署(PC + RDK X5 + RK3506) | [`100-embedded-deployment.md`](100-embedded-deployment.md) |
|
|
| 进入具身智能 / VLA / 机器人 | [`99-embodied-ai.md`](99-embodied-ai.md) |
|
|
|
|
---
|
|
|
|
---
|
|
|
|
## 📖 阅读路径导航
|
|
|
|
> 💡 这是仓库 `doc/` 下所有文档的推荐阅读顺序。[返回 README 总导航](../README.md#-23-篇文档怎么读)
|
|
>
|
|
> ⏱ **本文预计阅读时间**: 90 分钟
|
|
> 📍 **当前位置**: 第 5 / 24 篇
|
|
|
|
- ⏮ **上一篇**: [本机 venv 工作流(可选)](02-virtualenv.md)
|
|
- ⏭ **下一篇**: [Topic pub/sub 深度](20-topics.md)
|