# 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()); 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("chatter", 10); auto msg = std_msgs::msg::String(); msg.data = "hello"; pub->publish(msg); // Subscriber auto sub = node->create_subscription( "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 -v # 类型 + pub/sub 列表 ros2 topic echo # 实时打印 ros2 topic hz # 频率 Hz ros2 topic bw # 带宽 bytes/s ros2 topic pub "" --once # 发一条测试 ros2 bag record # 录包 ros2 bag play # 回放 ``` ### 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 # 类型 ros2 service call "" # 例: 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 ``` ### 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 ros2 launch 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 ` | [`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)