# 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//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,在容器里跑 |