init: ROS2 learning suite

This commit is contained in:
xs
2026-08-03 18:09:35 +08:00
commit 5ef38ab508
95 changed files with 13322 additions and 0 deletions
+207
View File
@@ -0,0 +1,207 @@
# ROS2 子项目 Agent 铁律
## 铁律 (Hard Rules)
**违反任何一条,所有变更立刻回滚。**
1. **只允许在本目录 `D:\xs\ros2` 下创建/修改/删除文件。**
- 任何系统临时目录(`%TEMP%``/tmp` 等) 一律禁止落盘。
- 在 Windows 下临时目录会残留垃圾文件且不清理,绝对禁止。
- 创建临时数据用 PowerShell 内存对象或 stdout,不写盘。
2. **禁止用 shell(PowerShell / cmd)编辑文件。**
- 改 / 写 / 读文件一律用内置工具:Read / Edit / Write / Glob / Grep。
- shell(本会话的 `bash` 工具)只允许运行可执行命令,例如 `docker ...``colcon ...`,**不允许用 `Set-Content``Out-File``>``>>` 写文件**。
- 禁止 `cd` / `Set-Location` 切换工作目录;需要别的目录时,在 `bash` 工具里用绝对路径,或者在 GUI / 编辑器里用内置工具定位。
3. **禁止到处创建文件/目录。**
- 不擅自创建 `.cache/``.tmp/``.bak/` 等隐藏目录。
- 不擅自创建 `test/xxx` 临时目录、"out/`、`日志/。
- 所有产出(镜像 `ros2-humble-dev:latest`、容器 `ros2_dev`、构建目录 `install/``build/``log/`)放在本项目内或其默认位置,不放别处。
4. **禁止问与思考循环。**
- 用户拒绝连续"要不要 / 要不要这样"。给出明确方案,直接开干。
- 必须问时,一次问清,不要反问。
5. **测试必须 100% 通过才能停手。**
- `docker compose build` + `colcon build` + `colcon test` + 节点启动 + `ros2 topic echo` 全绿才能汇报"完成"。
- 任何环节失败 → 自动修 → 再跑,直到全过。
## 子项目结构
```
D:\xs\ros2\
├── AGENTS.md # 本文件
├── README.md # 项目入口 + 架构 + 启动命令
├── pyproject.toml # PEP 621 workspace metadata(IDE 入口)
├── requirements.txt # venv runtime 依赖
├── requirements-dev.txt # venv 开发工具(ruff/black/mypy/pytest)
├── .flake8 / pyrightconfig.json # lint / 类型检查配置
├── .gitignore # 包含 .venv/ build/ install/ log/
├── docker/
│ ├── Dockerfile # ROS2 Humble 镜像
│ ├── docker-compose.yml # 容器编排
│ ├── bringup_e2e.log # 4 节点跨语言端到端验证日志(31KB)
│ ├── srv_e2e.log # Service 端到端(12+30=42,664B)
│ ├── robot_e2e.log # URDF + TF 端到端(11KB)
│ ├── vision_e2e.log # sensor_msgs/Image 端到端(49KB)
│ └── full_demo_e2e.log # 11 节点全开端到端(46KB)
├── tools/
│ ├── setup_venv.sh # Linux/WSL/Docker 一键 venv
│ └── setup_venv.ps1 # Windows 一键 venv(不污染系统 Python)
├── build.sh # 容器内 colcon build 一键脚本
├── start.sh / start.ps1 # 一键启动 + 构建 + 进开发终端
├── src/
│ ├── py_pubsub/ # ament_python — Topic pub/sub (Python)
│ │ ├── package.xml
│ │ ├── setup.py / setup.cfg
│ │ ├── py_pubsub/publisher_member_function.py
│ │ ├── py_pubsub/subscriber_member_function.py
│ │ ├── launch/pubsub_launch.py
│ │ ├── resource/py_pubsub
│ │ └── test/test_pubsub_launch.py # 4 pytest 用例
│ ├── cpp_pubsub/ # ament_cmake — Topic pub/sub (C++) + gtest
│ │ ├── package.xml
│ │ ├── CMakeLists.txt
│ │ ├── src/publisher_member_function.cpp
│ │ ├── src/subscriber_member_function.cpp
│ │ ├── launch/pubsub_launch.py
│ │ └── test/test_pub_sub.cpp # 2 gtest 用例
│ ├── py_srv/ # ament_python — Service demo
│ │ ├── package.xml
│ │ ├── setup.py / setup.cfg
│ │ ├── py_srv/add_two_ints_server.py
│ │ ├── py_srv/add_two_ints_client.py
│ │ ├── launch/srv_launch.py
│ │ └── test/test_srv.py # 1 pytest 用例
│ ├── py_action_demo/ # ament_python — Action 三件套 demo
│ │ ├── package.xml
│ │ ├── setup.py / setup.cfg
│ │ ├── py_action_demo/fibonacci_server.py
│ │ ├── py_action_demo/fibonacci_client.py
│ │ ├── launch/action_launch.py
│ │ └── test/test_action.py # 1 pytest 用例
│ ├── cpp_robot_tf2/ # ament_cmake — URDF + TF2 + JointState (C++)
│ │ ├── package.xml
│ │ ├── CMakeLists.txt
│ │ ├── urdf/simple_arm.urdf # 3 关节机械臂
│ │ ├── src/joint_state_publisher.cpp
│ │ ├── src/tf2_listener.cpp
│ │ ├── launch/robot_tf2_launch.py
│ │ └── test/test_tf2_lookup.cpp # 2 gtest 用例
│ ├── py_vision_demo/ # ament_python — sensor_msgs/Image (Python)
│ │ ├── package.xml
│ │ ├── setup.py / setup.cfg
│ │ ├── py_vision_demo/fake_camera.py
│ │ ├── py_vision_demo/image_processor.py
│ │ ├── launch/vision_launch.py
│ │ └── test/test_vision.py # 2 pytest 用例
│ └── bringup/ # ament_python — 顶层 launch 聚合
│ ├── package.xml
│ ├── setup.py / setup.cfg
│ ├── launch/
│ │ ├── pubsub_launch.py # 4 节点 Topic(pubsub)
│ │ ├── service_launch.py # AddTwoInts server
│ │ ├── action_launch.py # Fibonacci server
│ │ ├── robot_launch.py # URDF TF2(嵌套 cpp_robot_tf2)
│ │ ├── vision_launch.py # 嵌套 py_vision_demo
│ │ └── full_demo_launch.py # 11 节点一起
│ └── resource/bringup
└── doc/ # 14 篇深度文档
├── 00-overview.md # 架构 + 设计取舍
├── 01-quickstart.md # 5 分钟上手
├── 02-virtualenv.md # venv 工作流(本机不污染)
├── 10-concepts.md # Node/Topic/Service/Action/Parameter/TF
├── 20-topics.md # Topic pub/sub 深度
├── 30-services.md # Service 深度
├── 40-actions.md # Action 三件套深度
├── 50-tf2.md # TF2 坐标变换
├── 60-urdf.md # URDF 机器人模型
├── 70-launch.md # launch 文件系统
├── 80-package-build.md # colcon / ament 包构建
├── 85-docker.md # Docker 容器化开发
├── 90-testing.md # 测试金字塔策略
├── 99-embodied-ai.md # VLA / 机器人 / 具身智能路径
└── 100-embedded-deployment.md # 三机嵌入式部署(PC + RDK X5 + RK3506)
```
## 子项目工作流
| 步骤 | 命令 |
|---|---|
| 构建镜像(首次 5-10min) | `docker compose -f docker/docker-compose.yml build` |
| 启动容器 | `docker compose -f docker/docker-compose.yml up -d` |
| 容器内构建所有 7 个包 | `docker exec ros2_dev bash -lc "source /opt/ros/humble/setup.bash && cd /root/ros2_ws && colcon build --symlink-install --packages-select py_pubsub cpp_pubsub py_srv py_action_demo cpp_robot_tf2 py_vision_demo bringup"` |
| 跑所有单元/集成测试 | `docker exec ros2_dev bash -lc "source /opt/ros/humble/setup.bash && cd /root/ros2_ws && colcon test --packages-select py_pubsub cpp_pubsub py_srv py_action_demo cpp_robot_tf2 py_vision_demo bringup"` |
| 启动 4 节点 Topic | `docker exec ros2_dev bash -lc "source /root/ros2_ws/install/setup.bash && ros2 launch bringup pubsub_launch.py"` |
| 启动 Service 端到端 | `docker exec ros2_dev bash -lc "source install/setup.bash && ros2 launch bringup service_launch.py"` + `ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts '{a: 12, b: 30}'` |
| 启动 Action 端到端 | `ros2 launch bringup action_launch.py` + `ros2 action send_goal /fibonacci example_interfaces/action/Fibonacci '{order: 6}' --feedback` |
| 启动 TF2 演示 | `ros2 launch bringup robot_launch.py` |
| 启动 Vision 演示 | `ros2 launch bringup vision_launch.py` |
| 启动 11 节点 Full demo | `ros2 launch bringup full_demo_launch.py` |
| 进入开发终端 | `docker exec -it ros2_dev bash -lc "source /root/ros2_ws/install/setup.bash && exec bash"` |
| 本机 venv 初始化(不污染系统 Python) | `powershell .\tools\setup_venv.ps1` |
| 本机 venv 激活 | `.\.venv\Scripts\Activate.ps1` |
## 测试覆盖 (100% 通过)
| 包 | 测试类型 | 用例数 | 结果 |
|---|---|---|---|
| py_pubsub | pytest + in-process spin | 4/4 | PASSED |
| cpp_pubsub | gtest | 2/2 | PASSED |
| py_srv | pytest + Service in-process | 1/1 | PASSED |
| py_action_demo | pytest + Action in-process | 1/1 | PASSED |
| cpp_robot_tf2 | gtest | 2/2 | PASSED |
| py_vision_demo | pytest + Image in-process | 2/2 | PASSED |
| bringup | launch 6 文件就绪 | OK | PASSED |
**总计: 10/10 单元/集成测试 + 5 个端到端 demo 全部通过 100%。**
### 端到端日志(固化在 `docker/`)
- `bringup_e2e.log`:4 节点 Topic 跨语言互通(listener_cpp 同时收到 PY + CPP 消息)
- `srv_e2e.log`:Service 12+30=42
- `robot_e2e.log`:3 关节机械臂 + TF 实时打印(gripper 在 base_link 下位置)
- `vision_e2e.log`:fake_camera → image_processor 图像流(cv_bridge 解码 + 平均亮度)
- `full_demo_e2e.log`:11 节点同时运行(Topic + Service + Action + Robot + Vision)
## 子项目约定
### Python 包用 `ament_python`(`py_pubsub` / `py_srv` / `py_action_demo` / `py_vision_demo` / `bringup`)。
### C++ 包用 `ament_cmake`(`cpp_pubsub` / `cpp_robot_tf2`)。
### 跨包 launch 收纳到 `bringup` 包,包名**不能**叫 `launch`(与 ROS2 自带包同名,ament 索引冲突)。
### 所有 ROS2 节点默认参数 `period_ms=500`、`topic=chatter`,可在 launch 文件里覆盖。
### 源码注释一律中文,顶部 docstring 先写"用途 / 关键概念 / 运行方式",关键 API 旁写 inline。
### 跨语言互通演示:talker_py / talker_cpp 都用 `std_msgs/String`,见 `ros2 topic info chatter -v`。
## 嵌入式部署硬件清单(典型)
| 设备 | 角色 | ROS2 适配 |
|---|---|---|
| PC (x86) | 主控 | ✅ 完整 ROS2 + MoveIt2 + Nav2 + RViz |
| RDK X5 (ARM + 5 TOPS NPU) | 边缘 AI | ✅ 完整 ROS2 (视觉 / 语音 / SLAM) |
| RK3506 × 2 (ARM 3核 + 512MB RAM + 8GB eMMC) | 实时控制 | ✅ **精简 ROS2** (`ros-humble-ros-base`,不要 desktop) |
**关键提示**:
- RK3506 是 Linux 应用处理器(不是 MCU),**直接 apt 装 `ros-humble-ros-base`**,不用 micro-ROS
- 三机同 LAN 同 `ROS_DOMAIN_ID`,通过 FastDDS multicast 自动发现(或 `ROS_STATIC_PEERS` 单播)
- RK3506 用精简包 + 关 daemon + 关 GUI,可省 ~300MB RAM
- 详细步骤 + 故障排查见 `doc/100-embedded-deployment.md`
## RMW / 网络注意事项
- Docker Desktop on Windows + host network 模式下,默认 `rmw_fastrtps_cpp` 工作良好。
- **不要在 docker-compose.yml 里设 `RMW_IMPLEMENTATION=rmw_cyclonedds_cpp`**:镜像没装 cyclone dds,
CMake 配置阶段会失败。切换 RMW 时务必 `rm -rf build/ install/ log/` 再 build。
- `ros2 launch``IncludeLaunchDescription` 时,被包含的子 launch 文件必须在被包含包的 `share/<pkg>/launch/` 下被 colcon 实际安装 — 检查 `data_files``glob('launch/*.py')` 是否覆盖到。
## 调试速查
| 症状 | 排查 |
|---|---|
| `rcl_xxx not found` | `source /opt/ros/humble/setup.bash` |
| `Command ['cat', path]` 空格丢了 | launch 里别用 cat,改用 `open().read()` |
| `rcl_shutdown already called` | 测试 fixture 别在 callback 里 shutdown |
| `frame not exist` | robot_state_publisher 还没算完,等 1-2s |
| `Cannot connect to Docker daemon` | 启动 Docker Desktop |
| Windows venv import rclpy 飘红 | 正常,rclpy 没 Windows wheels,在容器里跑 |