feat(level1): ROS2 完全体 12 包 / 80 测试 / 23 文档 / 工程化 / Docker 分组

This commit is contained in:
xs
2026-08-04 10:19:47 +08:00
parent 5ef38ab508
commit 549d6b337e
141 changed files with 8949 additions and 1594 deletions
+111 -163
View File
@@ -1,207 +1,155 @@
# ROS2 子项目 Agent 铁律
# 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` 全绿才能汇报"完成"。
- 任何环节失败 → 自动修 → 再跑,直到全过。
2. **禁止用 shell(PowerShell / cmd)编辑文件**。改/写/读文件一律用内置工具:Read / Edit / Write / Glob / Grep
- shell 只允许运行可执行命令,例如 `docker ...``colcon ...`
- **不允许**用 `Set-Content``Out-File``>``>>` 写文件
3. **禁止到处创建文件/目录**。所有产出放在本项目内或其默认位置。
4. **禁止问与思考循环**。给出明确方案,直接开干
5. **测试必须 100% 通过才能停手**
- `make colcon-build` + `make colcon-test` 全绿才能汇报"完成"
## 子项目结构
## 编程规范(必读)
**严格遵循 [`doc/CODING_STYLE.md`](doc/CODING_STYLE.md)**
核心要点:
- **Python**: type hints + Google docstring + 节点属性后缀 `_` + 私有方法前缀 `_`
- **C++**: 命名空间 + const-correct + override + 智能指针
- **测试**: conftest.py + session-scope fixture + pytest/gtest
- **包内必须有 README.md**(功能 + 关键概念 + 运行 + 测试 + 深度学习链接)
- **提交不修改 git config**: 用 `git -c user.name=x -c user.email=y commit` 临时设
## 项目结构(12 包 + 完全体)
```
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/
├── AGENTS.md # 本文件(铁律 + 工作流)
├── README.md # 项目入口 + 架构 + 启动
├── LICENSE # MIT
├── CHANGELOG.md # 变更日志
├── CONTRIBUTING.md # 贡献指南
├── pyproject.toml # PEP 621 workspace 元数据
├── requirements*.txt # venv 依赖
├── Makefile # 命令聚合(Linux/macOS)
├── .gitlab-ci.yml # GitLab CI 配置
├── 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)
│ ├── Dockerfile
│ ├── docker-compose.yml # name: ros2 + ros2_net 自定义网络
── *_e2e.log # 端到端验证日志
├── tools/
│ ├── setup_venv.sh # Linux/WSL/Docker 一键 venv
── setup_venv.ps1 # Windows 一键 venv(不污染系统 Python)
├── doc/ # 23 篇深度文档
│ ├── 00-overview.md / 00-levels.md
── 01-quickstart.md / 02-virtualenv.md
│ ├── 10-concepts.md / 20-topics.md / 30-services.md / 40-actions.md
│ ├── 50-tf2.md / 60-urdf.md
│ ├── 70-launch.md / 80-package-build.md / 85-docker.md
│ ├── 90-testing.md / 99-embodied-ai.md / 100-embedded-deployment.md
│ ├── CODING_STYLE.md # ⭐ 编程规范
│ ├── 15-params.md # ⭐ 参数系统深度
│ ├── 16-custom-interfaces.md ⭐
│ ├── 17-lifecycle.md # ⭐
│ ├── 18-composable.md # ⭐
│ ├── 19-qos.md # ⭐
│ ├── 20-bag.md # ⭐
│ └── 21-overlay-dds.md # ⭐
├── build.sh # 容器内 colcon build 一键脚
├── start.sh / start.ps1 # 一键启动 + 构建 + 进开发终端
├── tools/ # 本机开发工具
── 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)
── src/ # 12 个 ROS2 包
├── py_pubsub/ # Topic (Python)
├── cpp_pubsub/ # Topic (C++)
├── py_srv/ # Service (Python)
├── py_action_demo/ # Action 三件套 (Python)
├── cpp_robot_tf2/ # URDF + TF2 (C++)
├── py_vision_demo/ # Image + cv_bridge (Python)
├── py_params/ # ⭐ 参数系统 (Python)
├── cpp_custom_interface/ # ⭐ 自定义 msg/srv/action (C++)
├── py_lifecycle_composable/ # ⭐ Lifecycle + Composable (Python)
├── cpp_qos_demo/ # ⭐ QoS 9 种组合 (C++)
├── py_overlay_dds/ # ⭐ DDS 配置 + colcon overlay (Python)
└── bringup/ # 跨包 launch 聚合 (Python)
```
## 子项目工作流
## 工作流命令(make / 直接 docker compose)
| 步骤 | 命令 |
|---|---|
| 构建镜像(首次 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` |
| 构建镜像 | `make build` `docker compose -p ros2 -f docker/docker-compose.yml build` |
| 启动容器 | `make up``docker compose -p ros2 -f docker/docker-compose.yml up -d` |
| 容器内 build 12 包 | `make colcon-build` |
| 跑所有测试 | `make colcon-test` |
| 单包测试 | `make colcon-test-one PKG=py_pubsub` |
| 启动 full_demo(11 节点) | `make full-demo` |
| 启动单 demo | `make launch NAME=pubsub_launch` |
| 进入开发终端 | `make shell` |
| 查看日志 | `make logs` |
| 本机 venv 初始化 | `make venv-setup`(或 `powershell .\tools\setup_venv.ps1`) |
## 测试覆盖 (100% 通过)
## 测试覆盖(12 包 / 74 用例 / 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)
| 包 | 类型 | 测试 |
|---|---|---|
| py_pubsub | pytest | 11/11 |
| cpp_pubsub | gtest | 3/3 |
| py_srv | pytest | 6/6 |
| py_action_demo | pytest | 4/4 |
| cpp_robot_tf2 | gtest | 4/4 |
| py_vision_demo | pytest | 11/11 |
| py_params | pytest | 16/16 |
| cpp_custom_interface | gtest | 3/3 |
| py_lifecycle_composable | pytest | 6/6 |
| cpp_qos_demo | gtest | 4/4 |
| py_overlay_dds | pytest | 6/6 |
| bringup | launch | 6/6 |
| **总计** | | **80/80** |
## 子项目约定
### 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`。
### Python 包用 `ament_python`,C++ 包用 `ament_cmake`
### 跨包 launch 收纳到 `bringup` 包,**包名不能叫 `launch`**
### 默认参数 `publish_rate_hz=1.0`、`topic=chatter`、`queue_size=10`
### 节点命名 `<feature>`(`chatter_publisher`,`joint_state_publisher`)
### 跨语言互通:talker_py / talker_cpp 都用 `std_msgs/String`
## 嵌入式部署硬件清单(典型)
## 嵌入式部署硬件清单
| 设备 | 角色 | 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 × 2 (ARM 3核 + 512MB) | 实时控制 | ✅ 精简 ROS2 (`ros-humble-ros-base`) |
**关键提示**:
- 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`
**关键**: RK3506 是 Linux 应用处理器,**直接 apt 装 `ros-humble-ros-base`**,不用 micro-ROS。
三机同 LAN 同 `ROS_DOMAIN_ID`,通过 FastDDS multicast 自动发现。
## 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')` 是否覆盖到。
- Docker 默认用 **`rmw_fastrtps_cpp`**(不要在 compose 里设 Cyclone,镜像没装)
- 切换 RMW 时务必 `rm -rf build/ install/ log/` 再 build
- `ros2 launch``IncludeLaunchDescription` 复用其他包 launch 时,**被包含的 launch 必须被 colcon 实际安装** — 检查 `data_files``glob('launch/*.py')` 是否覆盖
- 本仓库用自定义网络 `ros2_net`(172.20.0.0/24,脱离 docker_default)
## 调试速查
| 症状 | 排查 |
|---|---|
| `rcl_xxx not found` | `source /opt/ros/humble/setup.bash` |
| `Command ['cat', path]` 空格丢了 | launch 里别用 cat,改用 `open().read()` |
| `Command ['cat', path]` 空格丢了 | launch 里 `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,在容器跑 |
| Windows venv import rclpy 飘红 | 正常,容器跑 |
## Git 提交
- **不修改全局 git config**,用 `git -c user.name=x -c user.email=y commit` 临时设
- 分支命名: `feat/<name>` / `fix/<name>` / `docs/<name>`
- commit message 格式: `<type>(<scope>): <subject>` + body + footer
- 不主动 commit / push,除非用户明确要求