1
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
# Quickstart 文档审计 & Bug 修复报告
|
||||
|
||||
> **审计时间**: 2026-08-04
|
||||
> **审计员**: 模型 MiniMax-M3(以"完全零基础小白"身份执行 README+doc/01-quickstart 的"30 分钟跑通 Hello World"全流程)
|
||||
> **审计方法**: 按文档命令逐字执行,把每一步实际操作结果记录下来。
|
||||
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
按 README.md 和 doc/01-quickstart.md **原样** 走一遍,**至少 5 个严重 bug** 会卡住小白:
|
||||
|
||||
| # | 位置 | 现象 | 严重度 | 修复 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | `README.md:92` 步骤 5 | `docker run -d ... ros2-humble-dev:latest` 缺保活命令,容器立刻 Exited (0) | 🔴 P0 | ✅ 改文档 + 加 `bash` 参数,或让脚本包办 |
|
||||
| 2 | `README.md:130` 步骤 7 | 教小白跑 `ros2 --version` — 这个选项不存在,会报 `unrecognized arguments: --version` | 🟡 P2 | ✅ 改文档 |
|
||||
| 3 | `README.md:194` 步骤 10 | `ros2 launch py_pubsub pubsub_launch.py` — launch 文件不在 py_pubsub,实际在 `bringup/launch/pubsub_launch.py` | 🔴 P0 | ✅ 改文档 |
|
||||
| 4 | `doc/01-quickstart.md:166-167` 步骤 4.2 | 教小白跑 `bash build.sh` — **仓库根根本没有 build.sh,文件不存在** | 🔴 P0 | ✅ 新增 `scripts/build.sh` 修复 |
|
||||
| 5 | `bringup/launch/pubsub_launch.py:38` | launch 把 `2.0`(double) 传给 C++ 节点 `publish_rate_hz`(int 参数),C++ 进程 crash `Wrong parameter type` | 🔴 P0 | ✅ 改 C++ 节点声明对齐 Python:double |
|
||||
| 6 | `README.md` vs `doc/01-quickstart.md` 整体 | **两份文档教小白两条完全不同的上手路径**(手动 docker run vs docker compose),小白不知道跟谁 | 🟡 P1 | 📝 文档风格统一(待办) |
|
||||
| 7 | `scripts/*.sh`(新建) | `set -euo pipefail` 跟 ROS 官方 `setup.bash` 不兼容(AMENT_TRACE_SETUP_FILES 未定义) | 🟡 P2 | ✅ 已修(改 `set -eo pipefail`) |
|
||||
|
||||
**剩余所有 bug 已修复或文档已更正,12 包 / 78 测试(64 pytest + 14 gtest)/ 跨语言互通全部跑通。**
|
||||
|
||||
---
|
||||
|
||||
## 详细记录
|
||||
|
||||
### Bug 1 — README 步骤 5:容器立刻退出(已修)
|
||||
|
||||
**原文档**:`README.md` line 92
|
||||
```powershell
|
||||
docker run -d --name ros2_dev -v "${PWD}:/root/ros2_ws" --network ros2_net ros2-humble-dev:latest
|
||||
```
|
||||
|
||||
**实际跑结果**:
|
||||
```
|
||||
CONTAINER ID IMAGE NAMES STATUS
|
||||
6fe266def5c0 ros2-humble-dev:latest ros2_dev Exited (0) 31 seconds ago
|
||||
```
|
||||
|
||||
**根因**:
|
||||
- 基础镜像 `osrf/ros:humble-desktop` 的 default ENTRYPOINT = `ros_entrypoint.sh`,会 source `/opt/ros/humble/setup.bash` 后 `exec bash`
|
||||
- Dockerfile 自定义 `CMD ["bash"]`,所以最终容器跑起来是 `bash`
|
||||
- `docker run -d` 没指定 tty/stdin,`bash` 立刻检测 stdin 关闭 → exit 0
|
||||
- 而 `docker-compose.yml` 用 `command: ["bash", "-lc", "tail -f /dev/null"]` 给容器保活
|
||||
|
||||
**修复**:
|
||||
1. README 把命令改成:`docker run -d -it --name ros2_dev -v "${PWD}:/root/ros2_ws" --network ros2_net ros2-humble-dev:latest bash` (挂后台 + 给 bash 命令保持运行)
|
||||
2. 或干脆统一走 docker compose,让用户跟 `make up` / `make shell` 一致
|
||||
3. **并且为用户补齐 `scripts/shell.sh`** 一行命令进入开发终端
|
||||
|
||||
### Bug 2 — README 步骤 7:`ros2 --version` 不存在(已修)
|
||||
|
||||
**原文档**:`README.md` line 130
|
||||
```bash
|
||||
ros2 --version # 应该显示 ROS 2 package version 1.0 (or similar)
|
||||
```
|
||||
|
||||
**实际跑结果**:
|
||||
```
|
||||
ros2: error: unrecognized arguments: --version
|
||||
```
|
||||
|
||||
**修复**: 改用 `dpkg -l ros-humble-rclcpp | tail -1` 或 `ros2 --help | head -5`,或者直接砍掉这步(在第 8 步编译成功就已足够证明 ROS2 装好)。
|
||||
|
||||
### Bug 3 — README 步骤 10:launch 包名错(已修)
|
||||
|
||||
**原文档**:`README.md` line 194
|
||||
```bash
|
||||
ros2 launch py_pubsub pubsub_launch.py
|
||||
```
|
||||
|
||||
**实际跑结果**:
|
||||
```
|
||||
Package 'py_pubsub' not found, ... unable to find launch action 'pubsub_launch.py'
|
||||
```
|
||||
(注: py_pubsub 下也有 `launch/pubsub_launch.py`,但 launch 文件**只在 build/install 后才被 find**,需要先 colcon build;但用户已经 build 过。这里实际可用 `ros2 launch bringup pubsub_launch.py`,因为 bringup 包才是 4 节点跨语言版本)
|
||||
|
||||
**修复**: 改文档 `ros2 launch bringup pubsub_launch.py`
|
||||
|
||||
### Bug 4 — doc/01-quickstart.md 步骤 4.2:`build.sh` 不存在(已修)
|
||||
|
||||
**原文档**:`doc/01-quickstart.md` line 166
|
||||
```bash
|
||||
bash build.sh
|
||||
```
|
||||
|
||||
**实际跑结果**:
|
||||
```
|
||||
bash: build.sh: No such file or directory
|
||||
```
|
||||
|
||||
**根因**: 文档假设存在一个项目根的 `build.sh` 聚合脚本,但仓库只产了 `Makefile`(走 docker compose)或 README 给的 `colcon build`(直接命令)。
|
||||
|
||||
**修复**: 新增 `scripts/build.sh` 修复文档承诺:
|
||||
- 接受 `WORKSPACE` 环境变量
|
||||
- 自动 source ROS2 + colcon build 12 个包
|
||||
- 后续 `scripts/test.sh` / `scripts/launch.sh` / `scripts/clean.sh` 同一规范
|
||||
|
||||
### Bug 5 — `bringup/launch/pubsub_launch.py` C++ 节点 crash(已修)
|
||||
|
||||
**原报错** (实际跑 launch 时):
|
||||
```
|
||||
[chatter_publisher_cpp-2] terminate called after throwing an instance of
|
||||
'rclcpp::exceptions::InvalidParameterTypeException'
|
||||
[chatter_publisher_cpp-2] what(): parameter 'publish_rate_hz' has invalid type:
|
||||
Wrong parameter type, parameter {publish_rate_hz} is of type {integer},
|
||||
setting it to {double} is not allowed.
|
||||
[ERROR] [chatter_publisher_cpp-2]: process has died [pid ..., exit code -6]
|
||||
```
|
||||
|
||||
**根因**:
|
||||
- `cpp_pubsub/src/chatter_publisher.cpp:22` 声明 `declare_parameter<int>("publish_rate_hz", 2, ...)`
|
||||
- `bringup/launch/pubsub_launch.py:38` 通过 `parameters=[{... 'publish_rate_hz': 2.0 ...}]` 传 double
|
||||
- C++ 节点启动时调用 `set_parameter` 检查类型不匹配 → 抛异常 → `terminate called`
|
||||
|
||||
**修复**: `publish_rate_hz` 改成 double(对齐 py_pubsub),语义 Hz 可以是 1.5、0.5 这种小数,int 不合理。
|
||||
|
||||
涉及文件:
|
||||
- `src/cpp_pubsub/src/chatter_publisher.cpp`: declare_parameter `<double>` + `.as_double()` + 周期计算加 `static_cast<int>`
|
||||
- `src/cpp_pubsub/test/test_pub_sub.cpp`: `as_int()` → `as_double()` + `EXPECT_DOUBLE_EQ(..., 2.0)`
|
||||
|
||||
### Bug 6 — 文档两条上手路径矛盾(待修,优先级中)
|
||||
|
||||
`README.md` (从零 30 分钟) 教小白:
|
||||
- 步骤 4:`docker build -t ros2-humble-dev:latest -f docker/Dockerfile .`
|
||||
- 步骤 5:`docker run -d --name ros2_dev ...`
|
||||
- 步骤 6:`docker exec -it ros2_dev bash`
|
||||
- 步骤 8: `cd /root/ros2_ws && colcon build --symlink-install`
|
||||
|
||||
`doc/01-quickstart.md` (5 分钟 Quickstart) 教的是另一套:
|
||||
- Step 2.2:`docker compose -f docker/docker-compose.yml build`
|
||||
- Step 3: `docker compose -f docker/docker-compose.yml up -d`
|
||||
- Step 4.2:`bash build.sh`(根本不存在)
|
||||
- 然后才讲`ros2 launch bringup pubsub_launch.py`
|
||||
|
||||
**修复方向**: 二选一:
|
||||
- A) README 完全改成 `make up` / `make shell` / `make colcon-build`(跟 Makefile 对齐)
|
||||
- B) doc/01-quickstart 完全删掉(README 是入口)
|
||||
|
||||
**已采取**: 先用脚本 (`scripts/*.sh`) 跟 Makefile 等价,无论走哪条路都能复用。然后**改 README 步骤 5 加 `-it` 和保活命令**,并删掉错误的 `ros2 --version`、`py_pubsub pubsub_launch.py`。
|
||||
|
||||
### Bug 7 — `set -u` 跟 ROS setup.bash 不兼容(已修)
|
||||
|
||||
**实际跑结果** (首次 `bash scripts/build.sh`):
|
||||
```
|
||||
/opt/ros/humble/setup.bash: line 8: AMENT_TRACE_SETUP_FILES: unbound variable
|
||||
```
|
||||
|
||||
**根因**: ROS 官方 `setup.bash` 默认不严格,有许多只在 trace 模式下读取的变量;`set -u` 在非 trace 下读这些变量会炸。
|
||||
|
||||
**修复**: 把 `set -euo pipefail` 改成 `set -eo pipefail`(保留 `-e` 严格错误检测,放弃 `-u` 未定义变量)。
|
||||
|
||||
---
|
||||
|
||||
## "Test-After-Fix" 验证
|
||||
|
||||
| 步骤 | 命令 | 结果 |
|
||||
|---|---|---|
|
||||
| 编译 | `docker exec ros2_dev bash scripts/build.sh` | ✅ `Summary: 12 packages finished [1min 45s]` |
|
||||
| 测试 | `docker exec ros2_dev bash scripts/test.sh` | ✅ `Summary: 78 tests (64 pytest + 14 gtest), 0 errors, 0 failures, 0 skipped` |
|
||||
| 启动 | `docker exec ros2_dev bash scripts/launch.sh pubsub_launch 15` | ✅ 4 节点全起,跨语言互通:C++ subscriber 收到 `Hello from PY` 和 `Hello from C++` |
|
||||
|
||||
---
|
||||
|
||||
## 后续 TODO(优先级降序)
|
||||
|
||||
1. ☐ `README.md` 整体改写,统一走 `docker compose`/`make` 路径(避免和 doc/01 二选一)
|
||||
2. ☐ `doc/01-quickstart.md` 整篇精简为 README 索引
|
||||
3. ☐ scripts/ 加 PowerShell 入口 (`make_up.ps1` / `make_shell.ps1`),让 Windows 小白不用装 make
|
||||
4. ☐ `AGENTS.md` 增加 "bug 修复记录写 `docs/bug_log/YYYY-MM-DD.md`" 规则
|
||||
@@ -39,6 +39,7 @@ htmlcov/
|
||||
*.xml
|
||||
.cache/
|
||||
.logs/
|
||||
stderr
|
||||
|
||||
# OS / 杂项
|
||||
Thumbs.db
|
||||
@@ -12,7 +12,7 @@
|
||||
4. **禁止问与思考循环**。给出明确方案,直接开干。
|
||||
5. **测试必须 100% 通过才能停手**。
|
||||
- `make colcon-build` + `make colcon-test` 全绿才能汇报"完成"
|
||||
- 当前实测:**12 包 / 78 用例(65 pytest + 13 gtest) 100% 通过**
|
||||
- 当前实测:**12 包 / 78 用例(64 pytest + 14 gtest) 100% 通过**
|
||||
6. **调试日志/临时输出统一放 `.logs/` 目录**。禁止在项目根目录散放 `*.log`、`*.xml` 等临时文件。
|
||||
- `.logs/` 已加入 `.gitignore`,不会进版本控制
|
||||
- 用法: `docker exec ... > .logs/build.log 2>&1`
|
||||
@@ -96,7 +96,7 @@ D:\xs\ros2\
|
||||
| 查看日志 | `make logs` |
|
||||
| 本机 venv 初始化 | `make venv-setup`(或 `powershell .\tools\setup_venv.ps1`) |
|
||||
|
||||
## 测试覆盖(12 包 / 74 用例 / 100% 目标)
|
||||
## 测试覆盖(12 包 / 78 用例 / 100% 目标)
|
||||
|
||||
| 包 | 类型 | 测试 |
|
||||
|---|---|---|
|
||||
|
||||
@@ -88,10 +88,22 @@ docker build -t ros2-humble-dev:latest -f docker/Dockerfile .
|
||||
|
||||
容器 = 用镜像启动的"Linux 虚拟机实例"。本仓库的容器名是 `ros2_dev`。
|
||||
|
||||
**方式 A — 推荐(走 docker compose,跟 Makefile 等价)**
|
||||
```powershell
|
||||
docker run -d --name ros2_dev -v "${PWD}:/root/ros2_ws" --network ros2_net ros2-humble-dev:latest
|
||||
docker compose -p ros2 -f docker/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
**方式 B — 手动 docker run**(等价;注意末尾的 `bash` 用来保活,**重要** ❗)
|
||||
|
||||
```powershell
|
||||
docker run -d -it --name ros2_dev `
|
||||
-v "${PWD}:/root/ros2_ws" `
|
||||
--network ros2_net `
|
||||
ros2-humble-dev:latest bash
|
||||
```
|
||||
|
||||
> ❗ **一定要带末尾的 `bash`**: 否则容器启动后立刻 `Exited (0)`,因为基础镜像默认 ENTRYPOINT 跑 bash,无 tty 时立刻退出。
|
||||
|
||||
**预期输出**:一串 hash(容器 ID),没有报错就行。
|
||||
|
||||
**验证容器跑起来了**:
|
||||
@@ -127,7 +139,9 @@ source /opt/ros/humble/setup.bash
|
||||
|
||||
**怎么验证**:
|
||||
```bash
|
||||
ros2 --version # 应该显示 ROS 2 package version 1.0 (or similar)
|
||||
ros2 --help | head -5 # 应该输出 ros2 CLI 用法
|
||||
# 或
|
||||
dpkg -l ros-humble-rclcpp | tail -1 # 应该看到已装的 ROS2 humble 版本行
|
||||
```
|
||||
|
||||
**常见错误**:直接输入 `ros2` 提示 `command not found` → 说明你忘了 source。
|
||||
@@ -143,6 +157,13 @@ echo "source /root/ros2_ws/install/setup.bash" >> ~/.bashrc # 这一行要等
|
||||
|
||||
`colcon` 是 ROS2 的官方编译工具(类似 `make` 但专为 ROS2 设计)。
|
||||
|
||||
**方式 A — 推荐**:用项目脚本(自带 source ROS2 + 12 包列表)
|
||||
```bash
|
||||
cd /root/ros2_ws
|
||||
bash scripts/build.sh
|
||||
```
|
||||
|
||||
**方式 B — 手动**:
|
||||
```bash
|
||||
cd /root/ros2_ws
|
||||
colcon build --symlink-install
|
||||
@@ -158,6 +179,12 @@ Summary: 12 packages finished [4 min 32 s]
|
||||
|
||||
### 9. 加载本项目环境 + 跑测试
|
||||
|
||||
**方式 A — 推荐**:用项目脚本
|
||||
```bash
|
||||
bash scripts/test.sh
|
||||
```
|
||||
|
||||
**方式 B — 手动**:
|
||||
```bash
|
||||
source install/setup.bash
|
||||
colcon test
|
||||
@@ -188,10 +215,17 @@ build/<package>/test_results/.../test_*.gtest.xml: PASS
|
||||
|
||||
**打开第一个终端**(容器内):
|
||||
|
||||
**方式 A — 推荐**:项目脚本(自带 source)
|
||||
```bash
|
||||
bash scripts/launch.sh pubsub_launch 30
|
||||
# 第 2 个参数 = 运行时长(秒),空 = 一直跑
|
||||
```
|
||||
|
||||
**方式 B — 手动**(跨语言 4 节点 Topic 互通)
|
||||
```bash
|
||||
source /opt/ros/humble/setup.bash
|
||||
source /root/ros2_ws/install/setup.bash
|
||||
ros2 launch py_pubsub pubsub_launch.py
|
||||
ros2 launch bringup pubsub_launch.py
|
||||
```
|
||||
|
||||
**预期输出**(每个终端都会一直打印,这是正常的):
|
||||
@@ -212,8 +246,8 @@ ros2 launch py_pubsub pubsub_launch.py
|
||||
你已经跑通了 ROS2 的 Hello World。现在你可以:
|
||||
|
||||
1. **继续学**:打开下方"学完之后下一步做什么"选下一个包
|
||||
2. **玩参数**:另开一个终端,输入 `ros2 param set py_publisher publish_rate_hz 5.0`,回到第一个终端你会看到消息频率从 1 Hz 变成 5 Hz
|
||||
3. **看节点关系图**:输入 `rqt_graph`(需要图形界面,详见 doc/85-docker.md)
|
||||
2. **玩参数**:另开一个终端,输入 `docker exec ros2_dev bash -lc "source /opt/ros/humble/setup.bash && source /root/ros2_ws/install/setup.bash && ros2 param set py_publisher publish_rate_hz 5.0"`,回到第一个终端你会看到消息频率从 1 Hz 变成 5 Hz
|
||||
3. **看节点关系图**:输入 `docker exec ros2_dev bash -lc "rqt_graph"`(需要图形界面,详见 doc/85-docker.md)
|
||||
|
||||
> **📖 想看更详细的图文版 + Windows 截图**?见 [`doc/01-quickstart.md`](doc/01-quickstart.md)(已读过的章节可跳读)。本节内容已覆盖 doc/01-quickstart 的核心流程。
|
||||
|
||||
@@ -506,6 +540,8 @@ docker rm -f ros2_dev # 删除(下次要从头 docker run)
|
||||
| [`LICENSE`](LICENSE) | MIT 协议 | 想二次发布时读 |
|
||||
| [`pyproject.toml`](pyproject.toml) | PEP 621 包元数据 | 想 IDE 配置时 |
|
||||
| [`AGENTS.md`](AGENTS.md) | 开发者铁律(给 AI Agent 看的) | **不要读**,这是给 AI 写代码时的规则 |
|
||||
| [`scripts/`](scripts/README.md) | 容器内常用命令脚本(build/test/launch/clean/shell/e2e) | 推荐用 |
|
||||
| [`.docs/bug_logs/`](.docs/bug_logs/2026-08-04_quickstart_audit.md) | 入门文档 bug 审计 + 修复历史 | 复盘 / 找历史坑时 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ py_lifecycle_composable 6 pytest ⭐(新增)
|
||||
cpp_qos_demo 4 gtest ⭐(新增)
|
||||
py_overlay_dds 6 pytest ⭐(新增)
|
||||
|
||||
总计: **78 用例**(pytest 65 + gtest 13),目标 100% 通过
|
||||
总计: **78 用例**(pytest 64 + gtest 14),目标 100% 通过
|
||||
|
||||
> 注:78 用例是当前仓库实测数(`colcon test` 结果),与 README/AGENTS 一致。
|
||||
> 上述表格列是"实测用例数"(非"测试文件数")。
|
||||
|
||||
+24
-19
@@ -163,7 +163,15 @@ cd /root/ros2_ws # 进入工作空间
|
||||
|
||||
### 4.2 编译所有包
|
||||
```bash
|
||||
bash build.sh
|
||||
bash scripts/build.sh
|
||||
```
|
||||
*(脚本封装了 `source /opt/ros/humble/setup.bash` + `colcon build --symlink-install` 12 个包;手动等价命令见下)*
|
||||
|
||||
**手动等价命令**:
|
||||
```bash
|
||||
source /opt/ros/humble/setup.bash
|
||||
cd /root/ros2_ws
|
||||
colcon build --symlink-install
|
||||
```
|
||||
|
||||
**预期输出(末尾)**:
|
||||
@@ -194,9 +202,7 @@ Summary: 7 packages finished [2min 30s]
|
||||
|
||||
### 4.3 跑测试(可选)
|
||||
```bash
|
||||
source /opt/ros/humble/setup.bash
|
||||
source /root/ros2_ws/install/setup.bash
|
||||
colcon test --packages-select py_pubsub cpp_pubsub py_srv py_action_demo cpp_robot_tf2 py_vision_demo bringup
|
||||
bash scripts/test.sh
|
||||
```
|
||||
|
||||
**预期**:
|
||||
@@ -211,9 +217,8 @@ Summary: 7 packages finished [25s]
|
||||
|
||||
### 5.1 启动 4 个节点(2 Python + 2 C++)
|
||||
```bash
|
||||
source /opt/ros/humble/setup.bash
|
||||
source /root/ros2_ws/install/setup.bash
|
||||
ros2 launch bringup pubsub_launch.py
|
||||
bash scripts/launch.sh pubsub_launch 30
|
||||
# 第 2 个参数是运行时长(秒);空着 = 一直跑
|
||||
```
|
||||
|
||||
**预期输出**:
|
||||
@@ -347,12 +352,12 @@ source .venv/bin/activate
|
||||
|
||||
- [ ] `docker ps` 看到 `ros2_dev` 容器 `Up`
|
||||
- [ ] `docker exec ros2_dev echo hello` 输出 `hello`
|
||||
- [ ] `bash build.sh` 7 packages 全 build 成功
|
||||
- [ ] `colcon test` 全过(7 packages, 0 failed)
|
||||
- [ ] `ros2 launch bringup pubsub_launch.py` 启动 4 节点
|
||||
- [ ] `ros2 topic list` 看到 `/chatter`
|
||||
- [ ] `ros2 topic hz /chatter` 显示 ~4Hz
|
||||
- [ ] `ros2 topic info chatter -v` 看到 Python + C++ pub/sub
|
||||
- [ ] `bash scripts/build.sh` 12 packages 全 build 成功
|
||||
- [ ] `bash scripts/test.sh` 全过(12 packages / 78 tests, 0 failed)
|
||||
- [ ] `bash scripts/launch.sh pubsub_launch 30` 启动 4 节点
|
||||
- [ ] `docker exec ros2_dev bash -c "ros2 topic list"` 看到 `/chatter`
|
||||
- [ ] `docker exec ros2_dev bash -c "ros2 topic hz /chatter --no-daemon"` 显示 ~4Hz
|
||||
- [ ] `docker exec ros2_dev bash -c "ros2 topic info /chatter -v --no-daemon"` 看到 Python + C++ pub/sub
|
||||
- [ ] venv 装好且激活,`python -c "import sys; print(sys.executable)"` 显示 `.venv` 路径
|
||||
|
||||
如果有任何一项 ✗,看 [Step 8 FAQ](#step-8-常见问题-faq)。
|
||||
@@ -379,12 +384,12 @@ source .venv/bin/activate
|
||||
```
|
||||
然后 `docker compose build` 重试。
|
||||
|
||||
### Q3: `bash build.sh` 报 `rcl_xxx not found`
|
||||
### Q3: `bash scripts/build.sh` 报 `rcl_xxx not found`
|
||||
**原因**: 没 source ROS2
|
||||
**解决**:
|
||||
```bash
|
||||
source /opt/ros/humble/setup.bash
|
||||
bash build.sh
|
||||
bash scripts/build.sh
|
||||
```
|
||||
|
||||
**或者** 把这句加进 `~/.bashrc`:
|
||||
@@ -427,8 +432,8 @@ sudo apt install ros-humble-<pkg>
|
||||
**解决**:
|
||||
```bash
|
||||
cd /root/ros2_ws
|
||||
rm -rf build install log
|
||||
colcon build --symlink-install
|
||||
bash scripts/clean.sh # 等价于 rm -rf build install log
|
||||
bash scripts/build.sh
|
||||
```
|
||||
|
||||
### Q10: 容器跑一段时间后磁盘满了
|
||||
@@ -436,8 +441,8 @@ colcon build --symlink-install
|
||||
**解决**:
|
||||
```bash
|
||||
# 进容器清理
|
||||
cd /root/ros2_ws && rm -rf build install log
|
||||
bash build.sh
|
||||
docker exec ros2_dev bash /root/ros2_ws/scripts/clean.sh
|
||||
docker exec ros2_dev bash /root/ros2_ws/scripts/build.sh
|
||||
|
||||
# 或清理 Docker
|
||||
docker system prune -a
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# scripts/ · ROS2 学习套件常用命令脚本
|
||||
|
||||
> **设计**:把所有"长 shell 命令 + docker exec 路径"封装成可执行 shell 脚本,
|
||||
> 避免小白每次都手敲容易出错的长命令。所有脚本都跑在**容器内** `ros2_dev`。
|
||||
>
|
||||
> **跟 Makefile / docker compose 不冲突**,就是更零碎场景的薄封装。
|
||||
|
||||
## 容器生命周期
|
||||
|
||||
| 脚本 | 作用 | 用法(PowerShell) |
|
||||
|---|---|---|
|
||||
| `build.sh` | 容器内编译 12 包(替代 `bash build.sh` 缺失的死链) | `docker exec ros2_dev bash /root/ros2_ws/scripts/build.sh` |
|
||||
| `test.sh` | 容器内跑 12 包测试 + 汇总 + 详细日志 | `docker exec ros2_dev bash /root/ros2_ws/scripts/test.sh` |
|
||||
| `launch.sh` | 容器内启动 `bringup/<name>.py` | `docker exec ros2_dev bash /root/ros2_ws/scripts/launch.sh pubsub_launch` |
|
||||
| `shell.sh` | 自动判断容器状态,进入开发终端 | `bash scripts/shell.sh` |
|
||||
|
||||
## 调用方式(2 选 1)
|
||||
|
||||
```bash
|
||||
# A. 直接通过 docker exec 调脚本(Widows / Linux 都行)
|
||||
docker exec ros2_dev bash /root/ros2_ws/scripts/build.sh
|
||||
docker exec ros2_dev bash /root/ros2_ws/scripts/test.sh
|
||||
docker exec ros2_dev bash /root/ros2_ws/scripts/launch.sh pubsub_launch
|
||||
```
|
||||
|
||||
```bash
|
||||
# B. 整段进容器再跑(Windows 推荐 + 调试时)
|
||||
docker exec -it ros2_dev bash
|
||||
source /opt/ros/humble/setup.bash
|
||||
source /root/ros2_ws/install/setup.bash
|
||||
bash scripts/build.sh
|
||||
```
|
||||
|
||||
## 维护脚本(项目根目录,不在容器内)
|
||||
|
||||
| 脚本 | 作用 |
|
||||
|---|---|
|
||||
| `make_build.ps1` | PowerShell 一键:`build 镜像 → up 容器` |
|
||||
| `make_up.ps1` | PowerShell 一键:`docker compose -p ros2 up -d` |
|
||||
| `make_shell.ps1` | PowerShell 一键:`make shell` 等价 |
|
||||
|
||||
*(计划中,后续补上)*
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# scripts/build.sh —— 在容器内编译所有 ROS2 包
|
||||
#
|
||||
# 修复 doc/01-quickstart.md 步骤 4.2 提到但仓库不存在的 `bash build.sh`。
|
||||
#
|
||||
# 使用:
|
||||
# docker exec ros2_dev bash /root/ros2_ws/scripts/build.sh
|
||||
# make colcon-build (走 docker-compose exec 包装,等价)
|
||||
# =============================================================================
|
||||
# 不用 -u:/opt/ros/humble/setup.bash 里有未定义变量,strict 会炸
|
||||
set -eo pipefail
|
||||
|
||||
WORKSPACE="${WORKSPACE:-/root/ros2_ws}"
|
||||
|
||||
source /opt/ros/humble/setup.bash
|
||||
cd "${WORKSPACE}"
|
||||
|
||||
colcon build --symlink-install \
|
||||
--packages-select \
|
||||
py_pubsub cpp_pubsub py_srv py_action_demo \
|
||||
cpp_robot_tf2 py_vision_demo py_params \
|
||||
cpp_custom_interface py_lifecycle_composable \
|
||||
cpp_qos_demo py_overlay_dds bringup
|
||||
|
||||
echo "[build.sh] ✅ Done. Verify with: ls install/"
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
# scripts/clean.sh —— 清空 build/install/log,修 symlink 冲突
|
||||
#
|
||||
# 触发场景:
|
||||
# - 之前 build 过,再 build 报 "existing path cannot be removed: Is a directory"
|
||||
# - 切换 RMW 实现
|
||||
# - 编译产物膨胀想回收磁盘
|
||||
#
|
||||
# 使用:
|
||||
# docker exec ros2_dev bash /root/ros2_ws/scripts/clean.sh
|
||||
set -eo pipefail
|
||||
|
||||
WORKSPACE="${WORKSPACE:-/root/ros2_ws}"
|
||||
cd "${WORKSPACE}"
|
||||
|
||||
echo "[clean.sh] rm -rf build install log"
|
||||
rm -rf build install log
|
||||
echo "[clean.sh] ✅ 清理完成。下一步:bash scripts/build.sh"
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
# scripts/clean_ros.sh —— 关停所有 chatter 相关后台进程
|
||||
# (在调试 launch.sh forever 模式后调用,防止残留进程)
|
||||
pkill -9 -f "ros2 launch" || true
|
||||
pkill -9 -f chatter_publisher || true
|
||||
pkill -9 -f chatter_subscriber || true
|
||||
sleep 1
|
||||
pgrep -af 'ros2|chatter' || echo "all clean"
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
# scripts/e2e_check.sh —— 端到端冒烟测试
|
||||
# 启动 bringup/pubsub_launch 8 秒,期间持续验证 chatter 真的有消息
|
||||
set -eo pipefail
|
||||
|
||||
WORKSPACE="${WORKSPACE:-/root/ros2_ws}"
|
||||
source /opt/ros/humble/setup.bash
|
||||
source "${WORKSPACE}/install/setup.bash"
|
||||
cd "${WORKSPACE}"
|
||||
|
||||
echo "[e2e] 启动 bringup/pubsub_launch.py (≈6s 冒烟)..."
|
||||
ros2 launch bringup pubsub_launch.py > /tmp/e2e_launch.log 2>&1 &
|
||||
LAUNCH_PID=$!
|
||||
sleep 3
|
||||
|
||||
echo
|
||||
echo "[e2e] === 验证 topic ==="
|
||||
echo "[e2e] --- topic list ---"
|
||||
ros2 topic list --no-daemon | head -10
|
||||
echo
|
||||
echo "[e2e] --- topic hz /chatter (3s) ---"
|
||||
timeout 3 ros2 topic hz /chatter --no-daemon 2>&1 | tail -5 || true
|
||||
echo
|
||||
echo "[e2e] --- topic info /chatter -v ---"
|
||||
ros2 topic info /chatter -v --no-daemon | head -20
|
||||
echo
|
||||
echo "[e2e] --- node list ---"
|
||||
ros2 node list --no-daemon
|
||||
|
||||
echo
|
||||
echo "[e2e] 关停 launch..."
|
||||
kill -INT "${LAUNCH_PID}" 2>/dev/null || true
|
||||
pkill -f "ros2 launch bringup" 2>/dev/null || true
|
||||
wait "${LAUNCH_PID}" 2>/dev/null || true
|
||||
echo
|
||||
echo "[e2e] ✅ DONE. 详细日志: cat /tmp/e2e_launch.log"
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
# scripts/launch.sh —— 跑 demo launch(可限时自动关停)
|
||||
#
|
||||
# 用法:
|
||||
# # 一直跑(前台,Ctrl+C 停)
|
||||
# docker exec -it ros2_dev bash /root/ros2_ws/scripts/launch.sh pubsub_launch
|
||||
#
|
||||
# # 跑 N 秒后自动停(脚本、CI、截图用)
|
||||
# docker exec ros2_dev bash /root/ros2_ws/scripts/launch.sh pubsub_launch 10
|
||||
#
|
||||
# 第一个参数:launch 文件名(不带 .py),默认 pubsub_launch
|
||||
# 第二个参数:运行时长(秒),默认 0 = 不限时
|
||||
set -eo pipefail
|
||||
|
||||
WORKSPACE="${WORKSPACE:-/root/ros2_ws}"
|
||||
NAME="${1:-pubsub_launch}"
|
||||
RUNTIME="${2:-0}"
|
||||
|
||||
source /opt/ros/humble/setup.bash
|
||||
source "${WORKSPACE}/install/setup.bash"
|
||||
|
||||
cd "${WORKSPACE}"
|
||||
|
||||
# RUNTIME 合法化:非整数 / 空 / "forever" → -1(一直跑)
|
||||
if ! [[ "${RUNTIME}" =~ ^-?[0-9]+$ ]]; then
|
||||
RUNTIME="-1"
|
||||
fi
|
||||
|
||||
echo "[launch.sh] starting: ros2 launch bringup ${NAME}.py (runtime=${RUNTIME}s; -1 = forever)"
|
||||
|
||||
if [ "${RUNTIME}" -ge 0 ]; then
|
||||
# 后台跑 + 给定时炸弹
|
||||
ros2 launch bringup "${NAME}.py" &
|
||||
LAUNCH_PID=$!
|
||||
if [ "${RUNTIME}" -gt 0 ]; then
|
||||
sleep "${RUNTIME}"
|
||||
echo "[launch.sh] reaching runtime limit, killing..."
|
||||
kill -INT "${LAUNCH_PID}" 2>/dev/null || true
|
||||
pkill -f "ros2 launch bringup ${NAME}" 2>/dev/null || true
|
||||
wait "${LAUNCH_PID}" 2>/dev/null || true
|
||||
else
|
||||
# 0 = 等用户 Ctrl+C
|
||||
wait "${LAUNCH_PID}" 2>/dev/null || true
|
||||
fi
|
||||
else
|
||||
# -1 = forever(后台跑,主进程立刻返回)
|
||||
nohup ros2 launch bringup "${NAME}.py" >/dev/null 2>&1 &
|
||||
disown
|
||||
echo "[launch.sh] ✅ launched in background (forever mode, pid=$!)"
|
||||
fi
|
||||
|
||||
echo "[launch.sh] ✅ exit"
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
# scripts/shell.sh —— 进入容器开发终端(交互式)
|
||||
#
|
||||
# 使用:
|
||||
# bash scripts/shell.sh (WSL / Git Bash / Linux / macOS)
|
||||
#
|
||||
# 容器长跑命令 = `tail -f /dev/null`,跟 docker-compose 等价。
|
||||
set -eo pipefail
|
||||
|
||||
CONTAINER="${CONTAINER:-ros2_dev}"
|
||||
|
||||
# 容器已跑 → 直接 exec
|
||||
if docker ps --format '{{.Names}}' | grep -qx "${CONTAINER}"; then
|
||||
exec docker exec -it "${CONTAINER}" bash -lc \
|
||||
'source /opt/ros/humble/setup.bash; source /root/ros2_ws/install/setup.bash; cd /root/ros2_ws; exec bash'
|
||||
fi
|
||||
|
||||
# 容器没跑 → 尝试 start,不行就 run(用宿主当前目录挂进去)
|
||||
if docker ps -a --format '{{.Names}}' | grep -qx "${CONTAINER}"; then
|
||||
docker start "${CONTAINER}" >/dev/null
|
||||
exec docker exec -it "${CONTAINER}" bash -lc \
|
||||
'source /opt/ros/humble/setup.bash; source /root/ros2_ws/install/setup.bash; cd /root/ros2_ws; exec bash'
|
||||
fi
|
||||
|
||||
echo "[shell.sh] 容器 ${CONTAINER} 不存在,自动用本目录启动..."
|
||||
docker run -d -it \
|
||||
--name "${CONTAINER}" \
|
||||
-v "${PWD}:/root/ros2_ws" \
|
||||
--network ros2_net \
|
||||
ros2-humble-dev:latest \
|
||||
bash >/dev/null
|
||||
|
||||
exec docker exec -it "${CONTAINER}" bash -lc \
|
||||
'source /opt/ros/humble/setup.bash; source /root/ros2_ws/install/setup.bash; cd /root/ros2_ws; exec bash'
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
# scripts/test.sh —— 跑 colcon test,所有 12 包
|
||||
#
|
||||
# 使用:
|
||||
# docker exec ros2_dev bash /root/ros2_ws/scripts/test.sh
|
||||
# make colcon-test
|
||||
set -eo pipefail
|
||||
|
||||
WORKSPACE="${WORKSPACE:-/root/ros2_ws}"
|
||||
source /opt/ros/humble/setup.bash
|
||||
cd "${WORKSPACE}"
|
||||
|
||||
colcon test \
|
||||
--packages-select \
|
||||
py_pubsub cpp_pubsub py_srv py_action_demo \
|
||||
cpp_robot_tf2 py_vision_demo py_params \
|
||||
cpp_custom_interface py_lifecycle_composable \
|
||||
cpp_qos_demo py_overlay_dds bringup
|
||||
|
||||
echo
|
||||
echo "[test.sh] === 测试结果汇总 ==="
|
||||
colcon test-result --all
|
||||
echo "[test.sh] (要看详细日志,跑: colcon test-result --all --verbose)"
|
||||
@@ -18,22 +18,22 @@ ChatterPublisher::ChatterPublisher(const rclcpp::NodeOptions & options)
|
||||
: rclcpp::Node("chatter_publisher", options), publish_count_(0)
|
||||
{
|
||||
// 1) 声明参数(类型模板版 + 描述符)
|
||||
this->declare_parameter<int>(
|
||||
"publish_rate_hz", 2,
|
||||
this->declare_parameter<double>(
|
||||
"publish_rate_hz", 2.0,
|
||||
rcl_interfaces::msg::ParameterDescriptor().set__description("发布频率 (Hz)"));
|
||||
this->declare_parameter<std::string>(
|
||||
"topic_name", "chatter",
|
||||
rcl_interfaces::msg::ParameterDescriptor().set__description("发布话题名"));
|
||||
|
||||
// 2) 读参数
|
||||
const int publish_rate_hz = this->get_parameter("publish_rate_hz").as_int();
|
||||
const double publish_rate_hz = this->get_parameter("publish_rate_hz").as_double();
|
||||
const std::string topic_name = this->get_parameter("topic_name").as_string();
|
||||
|
||||
// 3) 构造发布者 + 定时器
|
||||
publisher_ = this->create_publisher<std_msgs::msg::String>(topic_name, 10);
|
||||
|
||||
const auto period = (publish_rate_hz > 0) ?
|
||||
std::chrono::milliseconds(1000 / publish_rate_hz) :
|
||||
const auto period = (publish_rate_hz > 0.0) ?
|
||||
std::chrono::milliseconds(static_cast<int>(1000.0 / publish_rate_hz)) :
|
||||
std::chrono::milliseconds(1000);
|
||||
|
||||
timer_ = this->create_wall_timer(
|
||||
@@ -41,7 +41,7 @@ ChatterPublisher::ChatterPublisher(const rclcpp::NodeOptions & options)
|
||||
|
||||
RCLCPP_INFO(
|
||||
this->get_logger(),
|
||||
"ChatterPublisher started: rate=%d Hz, topic=\"%s\"",
|
||||
"ChatterPublisher started: rate=%.2f Hz, topic=\"%s\"",
|
||||
publish_rate_hz, topic_name.c_str());
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ TEST_F(PubsubTest, PublisherConstructsWithDefaults)
|
||||
{
|
||||
auto node = std::make_shared<cpp_pubsub::ChatterPublisher>();
|
||||
EXPECT_EQ(node->get_name(), std::string("chatter_publisher"));
|
||||
EXPECT_EQ(node->get_parameter("publish_rate_hz").as_int(), 2);
|
||||
EXPECT_DOUBLE_EQ(node->get_parameter("publish_rate_hz").as_double(), 2.0);
|
||||
EXPECT_EQ(node->get_parameter("topic_name").as_string(), std::string("chatter"));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user