442 lines
26 KiB
Markdown
442 lines
26 KiB
Markdown
# 把 SRU 导航策略移植到 Odin1 + ROS1 的完整复现指南
|
||
|
||
> 从论文 *Spatially-Enhanced Recurrent Memory for Long-Range Mapless Navigation via End-to-End Reinforcement Learning* 与配套开源仓库
|
||
> [`sru-project-website`](https://michaelfyang.github.io/sru-project-website/) 出发,把 SRU 端到端导航策略部署到 **Odin1 深度相机 + ROS1 Noetic + 任意能消费 `geometry_msgs/Twist`(`/cmd_vel`)的机器人**(默认硬件参考为 Unitree Go2)。本仓库 `sru_nav_go2_ros1` 即为此目标的产物。
|
||
>
|
||
> **English version**: see `PORTING_GUIDE_EN.md`.
|
||
|
||
---
|
||
|
||
## 0. 这份文档是什么 / 不是什么
|
||
|
||
**是**:一份「移植决策记录 + AI 复刻 prompt + 自动验收脚本」的三合一指南。读完即可:
|
||
|
||
1. 知道我们为什么这样改、改了哪些点;
|
||
2. 用一个 slash command 让 AI agent(Cascade / Claude Code 等)从开源仓库与论文出发,**自动重新生成本仓库**;
|
||
3. 用一个脚本一键校验生成结果是否合格。
|
||
|
||
**不是**:算法原理讲解(请直接读论文与原始代码仓)。
|
||
|
||
**适用读者**:
|
||
- 已具备 Ubuntu + ROS1 + Python/conda 基本使用能力;
|
||
- 持有 Unitree Go2(或任何能订阅 `/cmd_vel` 的腿足/轮式机器人)+ Odin1 深度相机;
|
||
- 想在自己机器人上跑 SRU,或想验证「AI agent 是否能从论文+开源代码复刻一个真机部署包」。
|
||
|
||
> 0 门槛承诺:除上述前置技能外,所有路径、IP、机器人型号、摄像头型号都设计为**通过环境变量或参数覆盖**,不用改源码。
|
||
|
||
---
|
||
|
||
## 1. 上游来源
|
||
|
||
| 资产 | 链接 / 路径 | 在本移植里的角色 |
|
||
|---|---|---|
|
||
| 论文 | *Spatially-Enhanced Recurrent Memory for Long-Range Mapless Navigation via End-to-End Reinforcement Learning* | 解释观测/动作空间、奖励、网络结构(VAE 深度编码器 + LSTM-SRU + actor head) |
|
||
| 项目主页 | <https://michaelfyang.github.io/sru-project-website/> | 索引仓库、视频、论文 |
|
||
| `sru-navigation-learning` | RL 训练代码(rsl_rl 风格)。给出 PPO/MDPO 算法、reward terms、observation manager | 用于核对 **reward 设计、动作 squash、观测层次**,并取得 ONNX 导出脚本 |
|
||
| `sru-navigation-sim` | IsaacLab 环境,含 B2W / AOW 训练配置 | 取得 `policy_scaling`、随机化范围、观测形状(depth `64×5×8`)等关键数字 |
|
||
| `sru-robot-deployment` | 原作者在 Unitree B2W + ZED-X 上的真机部署 | **本仓库直接对照重写的对象**:原版用 PyTorch + DLIO odom + 自研 SDK 桥;本仓库换成 ONNX + Odin1 odom + Go2 桥 |
|
||
| `sru-pytorch-spatial-learning` | SRU/LSTM-SRU 单元的 PyTorch 实现 | 解释 RNN 隐状态形状(`rnn_hidden_size=512, num_layers=1`),导出 ONNX 时的状态展开方式 |
|
||
| `sru-depth-pretraining` | VAE depth encoder 的预训练 | 取得 `vae_encoder.onnx` 的输入形状(`H×W` 深度图归一化后单通道)与潜变量维度 |
|
||
|
||
如果你不打算复刻、只想跑:把训练完得到的两个 onnx(`vae_encoder.onnx` + `nav_policy.onnx`)丢到 `sru_nav_go2_ros1/models/`,跳到 §6。
|
||
|
||
---
|
||
|
||
## 2. 移植目标矩阵
|
||
|
||
| 维度 | 原作者部署 (`sru-robot-deployment`) | 本仓库 (`sru_nav_go2_ros1`) | 抽象后接口 |
|
||
|---|---|---|---|
|
||
| 机器人 | Unitree **B2W**(轮足) | Unitree **Go2**(纯腿足) | 任何订阅 `/cmd_vel` (`geometry_msgs/Twist`, body frame) 的机器人 |
|
||
| 深度传感 | ZED-X | **Odin1**(`32FC1` depth, ~10 Hz) | `~depth_topic` (`sensor_msgs/Image`, 32FC1, 米) |
|
||
| 里程计 | DLIO(自研 LIO) | Odin1 `odometry_highfreq` | `~odom_topic` (`nav_msgs/Odometry`, world frame) |
|
||
| 推理后端 | PyTorch JIT | **ONNX Runtime** | `models/{vae_encoder,nav_policy}.onnx` |
|
||
| 计算平台 | x86 工控机 | **Jetson Orin NX (aarch64) + conda py3.8** | 任何 Linux + ROS1 Noetic + conda |
|
||
| 控制频率 | ~10 Hz | **5 Hz**(与训练一致;Odin depth ~10 Hz 自然降采样) | `~control_frequency` |
|
||
| 操作员手柄 | PS5 PerceptiveNavigationSE2 | 同 PS5 mapping,**新增 `require_joystick: false`** 头模式 | `--no-deadman` / yaml `require_joystick` |
|
||
| 安装与启动 | rosinstall 全家桶 | **`setup_conda_env.sh` + `launch_sru_nav.sh`** 两脚本 | env vars `ENV_NAME / ROS_DISTRO / CATKIN_WS` 全部可覆盖 |
|
||
|
||
---
|
||
|
||
## 3. 核心移植决策(逐项说明动机与对照位置)
|
||
|
||
### 3.1 推理:PyTorch → ONNX Runtime
|
||
|
||
- **动机**:Jetson Orin NX 上装 PyTorch + cv_bridge + onnxruntime 的依赖网很容易爆;onnxruntime 单文件加载、CPU/GPU EP 切换简单;ARM wheel 现成。
|
||
- **接口**:`@/sru_nav_go2_ros1/src/sru_nav_go2/model.py` 实现 `LearningModel`,分别加载 `vae_encoder.onnx`(深度→latent)与 `nav_policy.onnx`(latent + 状态向量 + 上一动作 + LSTM hidden → action mean + 新 hidden)。
|
||
- **隐状态约定**:训练侧 `rnn_hidden_size=512, num_layers=1`;导出 ONNX 时把 `(h, c)` 作为输入输出张量,本端在每帧之间显式持有;`reset_hidden_state` 对应训练里 `is_first` 标志。
|
||
- **action squash**:网络已包含 `tanh`,输出在 `(-1, 1)`。本端再乘 `policy_scale=[vx_max, vy_max, ω_max]`(默认 `[0.6, 0.3, 0.6]`)得到 SI 单位速度。
|
||
|
||
### 3.2 观测层:Odin1 替代 ZED-X + DLIO
|
||
|
||
- **深度图**:Odin1 发布 `sensor_msgs/Image` (`32FC1`,单位米)。`@/sru_nav_go2_ros1/src/sru_nav_go2/navigation_policy_node.py:209-225` 里做:
|
||
1. `cv_bridge.imgmsg_to_cv2(passthrough)`;
|
||
2. `nan_to_num(nan=0, posinf=2*max_depth, neginf=0)`;
|
||
3. 截断到 `[min_depth, max_depth] = [0.25, 10.0] m`,超界置 0。
|
||
- **里程计帧约定**:Odin1 odom 的 twist 是 **world frame**。我们在 `odom_callback` 里用机体四元数把它反旋到 body frame(与训练对齐)。如果你的 odom 已经是 body frame,把 `use_sim: true` 透传即可(`@/sru_nav_go2_ros1/src/sru_nav_go2/navigation_policy_node.py:189-197`)。
|
||
- **goal 帧约定**:`/goal_pose.header.frame_id` 必须等于 odom 的 `frame_id`(默认 `odom`),否则节点直接拒绝 — 这是为了防止操作员误把机体系坐标当成 odom 系发出去。
|
||
|
||
### 3.3 动作下发:直接 `/cmd_vel`
|
||
|
||
- 原作者在 B2W 上自带 SDK 桥;Go2 直接用 unitree_legged_sdk 写一个订阅 `/cmd_vel` 的小节点(与本仓库分离)即可。本包**不**绑定具体桥。
|
||
- `policy_scale` 默认 `[0.6, 0.3, 0.6]` 比训练侧 `[1.5, 1.0, 1.0]` 保守一半多,给真机留余量;训练时已经做过 `Uniform(0.6, 1.2)` 的 scale 随机化,所以网络对运行时缩放鲁棒。
|
||
|
||
### 3.4 安全:手柄 deadman 双模
|
||
|
||
| 模式 | 触发 | 行为 |
|
||
|---|---|---|
|
||
| 默认 (`require_joystick=true`) | 实机部署 | `cmd_vel_ratio=0`,必须 `/joy` 持续发布且 `axes[4]` 推到正才有速度;`>15 s` 无 joy → 强制归零 |
|
||
| 测试 (`require_joystick=false`) | `--no-deadman` 或 yaml 改 false | 起步即 `cmd_vel_ratio=1.0`,无 joy 也跑。**只在抬轮 / 仿真 / 空旷场地**用 |
|
||
|
||
代码位置:`@/sru_nav_go2_ros1/src/sru_nav_go2/navigation_policy_node.py:118-121, 272-287`。
|
||
|
||
### 3.5 安装层:conda + 系统 ROS 共生
|
||
|
||
Jetson Orin NX 出厂有 ROS Noetic(系统 python3.8 + cv_bridge.so);同时我们要 onnxruntime / opencv / numpy 等只在 conda env 里好装。两个世界要打通:
|
||
|
||
1. **PYTHONPATH 注入**:`/opt/ros/noetic/lib/python3/dist-packages` 加到 conda python 的搜索路径,使 `rospy / cv_bridge / sensor_msgs / tf2_ros` 可 import。
|
||
2. **shebang 锁定**:catkin_make 时必须 `-DPYTHON_EXECUTABLE=$(which python3)` **在 conda env 已激活** 状态下跑,否则 `devel/lib/.../sru_nav_node` 的 `#!` 会指向 `/usr/bin/python3`,import onnxruntime 失败。
|
||
3. **ABI 冲突修复**:conda env 自带的 `libffi.so.7` 和系统 `libp11-kit.so.0` 版本符号不兼容,导致 `cv_bridge` 调 OpenCV 时崩溃(`undefined symbol: ffi_type_pointer, version LIBFFI_BASE_7.0`)。`launch_sru_nav.sh` 自动 `LD_PRELOAD=/lib/aarch64-linux-gnu/libffi.so.7` 提前加载系统 libffi。
|
||
4. **rospy 隐性依赖**:`netifaces` / `defusedxml` 在系统 python 里靠 apt 提供,conda env 里要 pip 装。`setup_conda_env.sh` 已加进默认列表。
|
||
5. **时钟检查**:Jetson 无 RTC 电池常导致系统时间回退,pip TLS 拒接握手。脚本里前置 `date` 检测并提示 `ntpdate / timedatectl`。
|
||
|
||
完整自动化脚本:`@/sru_nav_go2_ros1/scripts/setup_conda_env.sh` + `@/sru_nav_go2_ros1/scripts/launch_sru_nav.sh`。
|
||
|
||
### 3.6 故意删减项
|
||
|
||
| 训练里有的东西 | 部署里去掉的原因 |
|
||
|---|---|
|
||
| Heightmap / heightscan critic | Go2 不带 LiDAR/heightmap,需要的话外部话题再加 |
|
||
| 距离/动量等 critic 通道 | critic 仅训练用,推理只跑 actor head |
|
||
| MDPO / distillation 双 actor-critic 互蒸馏 | 训练阶段优化器结构,与导出后的 actor 无关 |
|
||
| 训练时 reward terms 计算 | 部署不需要计算 reward |
|
||
| Action scale randomization (`Uniform(0.6, 1.2)`) | 部署是固定缩放;网络已经被训练成对该范围鲁棒 |
|
||
|
||
---
|
||
|
||
## 4. 文件清单与职能
|
||
|
||
```
|
||
sru_nav_go2_ros1/
|
||
├── CMakeLists.txt # catkin 包定义;catkin_python_setup() + install scripts
|
||
├── package.xml # 依赖:rospy, sensor_msgs, geometry_msgs, nav_msgs, cv_bridge, tf2_ros
|
||
├── setup.py # 把 src/sru_nav_go2 装成 python 包
|
||
├── README.md
|
||
├── config/
|
||
│ ├── sru_nav.yaml # 运行时全部 ROS 参数;唯一应该被用户改的文件
|
||
│ └── waypoints_example.yaml # 多目标巡航示例(odom 系列点)
|
||
├── launch/
|
||
│ └── sru_nav_go2.launch # joy_node + static_tf + sru_nav_node
|
||
├── models/
|
||
│ ├── vae_encoder.onnx # 训练侧 export_onnx 产物
|
||
│ └── nav_policy.onnx
|
||
├── scripts/
|
||
│ ├── setup_conda_env.sh # 创建 sru_nav env,装 onnxruntime/cv2/netifaces…
|
||
│ ├── launch_sru_nav.sh # 一键启动 wrapper(含 LD_PRELOAD / PYTHONPATH 修复)
|
||
│ ├── sru_nav_node # ROS 节点入口(catkin 安装时拷到 devel/lib/...)
|
||
│ └── waypoint_runner.py # 顺序发 /goal_pose
|
||
├── src/sru_nav_go2/
|
||
│ ├── constants.py # 所有训练对齐常量(控制频率、轴键映射、scale 等)
|
||
│ ├── model.py # ONNX Runtime 封装:VAE encoder + LSTM-SRU policy
|
||
│ ├── navigation_policy_node.py # 主节点:odom/depth/joy/goal 回调 + cmd_vel 输出
|
||
│ ├── utils.py # 四元数<->旋转、reproject 等数学小工具
|
||
│ ├── visualization.py # rviz marker(目标向量、moving_goal、waypoint)
|
||
│ └── waypoint_manager.py # 录制/回放 waypoint 列表
|
||
└── docs/
|
||
├── DEPLOY_GO2_NX.md # 部署到 NX 的实战记录
|
||
└── PORTING_GUIDE.md # ← 你正在读的文件
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 用 AI Agent 自动复刻本仓库
|
||
|
||
我们提供 **同一份移植 prompt 的三种调用形式**(内容等价,挑顺手的用):
|
||
|
||
| 入口 | 适用工具 | 启动方式 |
|
||
|---|---|---|
|
||
| `.windsurf/workflows/port-sru-to-ros.md` | Cascade(Windsurf IDE) | 在 Cascade 聊天框里输 `/port-sru-to-ros` |
|
||
| `.claude/commands/port-sru-to-ros.md` | Claude Code (CLI / IDE 插件) | `claude` 启动后 `/port-sru-to-ros` |
|
||
| `docs/PORTING_PROMPT.md` | 任意支持自定义 prompt 的 agent(Cursor、Continue、Aider…) | 把整篇内容粘进系统提示 / 任务描述 |
|
||
|
||
三个文件由同一份「**真理 prompt**」生成,差异只在启动头与文件路径约定。下面是完整的 prompt 内容(放在 `.windsurf/workflows/port-sru-to-ros.md` 里以 frontmatter 起头)。
|
||
|
||
> ⚠️ **可重复性边界**:现役 LLM 在生成 ~1500 LoC 的 ROS 移植代码时,单次成功率不是 100%。我们用 **分阶段 workflow** 把任务切成 6 步,每步都有可 grep 的验收点;任意一步偏题立刻回滚重试,比一次性长 prompt 的成功率高一个数量级。
|
||
|
||
### 5.1 分阶段 workflow 概览
|
||
|
||
| 阶段 | 输入 | 输出 | 验收 |
|
||
|---|---|---|---|
|
||
| 1. **勘查** | 上游 5 个仓库 + 论文 | 摘要 `notes/upstream_recon.md`:算法、观测/动作、I/O 形状、训练超参 | 文件存在且包含网络层数、obs dims、policy_scale |
|
||
| 2. **抽取推理路径** | `sru-navigation-learning` 的 export_onnx | `models/vae_encoder.onnx`、`models/nav_policy.onnx`、`docs/IO_SPEC.md` | onnxruntime 可加载,输入/输出名/形状记录 |
|
||
| 3. **生成 ROS 包骨架** | 上一步 IO_SPEC | `package.xml / CMakeLists.txt / setup.py / launch/ / config/sru_nav.yaml` | `catkin_make` 通过 |
|
||
| 4. **移植节点逻辑** | 原 `sru-robot-deployment` 节点 + IO_SPEC | `src/sru_nav_go2/{constants,model,utils,visualization,waypoint_manager,navigation_policy_node}.py` + `scripts/sru_nav_node` | 节点能 `roslaunch` 起来不 crash |
|
||
| 5. **写部署脚本** | 目标 platform 信息 | `scripts/setup_conda_env.sh` + `scripts/launch_sru_nav.sh` | 在干净 env 上跑通 §6 验收命令 |
|
||
| 6. **文档与验收** | 全部产物 | `README.md / docs/DEPLOY_*.md / scripts/verify_port.sh` | `bash scripts/verify_port.sh` 全绿 |
|
||
|
||
### 5.2 通用 prompt(与三个入口文件一致的核心内容)
|
||
|
||
```text
|
||
你是一名熟悉 ROS1 Noetic、conda、ONNX Runtime 与 PyTorch 的高级工程师。
|
||
|
||
【任务】
|
||
从以下上游资料出发,生成一个名为 `sru_nav_go2_ros1` 的 catkin 包,
|
||
把论文《Spatially-Enhanced Recurrent Memory for Long-Range Mapless
|
||
Navigation via End-to-End Reinforcement Learning》提出的端到端导航策略
|
||
部署到 Odin1 深度相机 + ROS1 Noetic + 任意能消费
|
||
`geometry_msgs/Twist (/cmd_vel)` 的机器人上。
|
||
|
||
【上游资料】
|
||
- 论文 PDF(按用户提供的路径读取,例如 ./2506.05997v2.pdf)
|
||
- 项目主页 https://michaelfyang.github.io/sru-project-website/
|
||
- 仓库(按用户工作区下子文件夹):
|
||
sru-navigation-learning — RL 训练 + ONNX 导出
|
||
sru-navigation-sim — IsaacLab 环境与训练配置
|
||
sru-robot-deployment — 原作者 B2W+ZED-X 真机部署(直接对标重写)
|
||
sru-pytorch-spatial-learning — SRU/LSTM-SRU 单元
|
||
sru-depth-pretraining — VAE depth encoder 预训练
|
||
- 已存在的 onnx 模型(如有):models/{vae_encoder,nav_policy}.onnx
|
||
|
||
【硬约束】
|
||
1. 目标传感器固定为 Odin1。深度话题默认 `/odin1/depth_img_competetion`
|
||
(sensor_msgs/Image, 32FC1, 单位米);里程计默认
|
||
`/odin1/odometry_highfreq` (nav_msgs/Odometry, world frame)。
|
||
2. 控制输出固定为 `/cmd_vel` (geometry_msgs/Twist, body frame),由调用方
|
||
桥接到具体机器人;本包不实现机器人 SDK 桥。
|
||
3. 推理后端只能用 onnxruntime(不引入 PyTorch 运行时依赖)。
|
||
4. 必须支持 conda env (默认 `sru_nav`, py3.8) 与系统 ROS 共生:
|
||
- PYTHONPATH 注入 /opt/ros/<DISTRO>/lib/python3/dist-packages
|
||
- 启动脚本 LD_PRELOAD 系统 libffi 修复 cv_bridge 的 ABI 冲突
|
||
- catkin_make 时锁定 PYTHON_EXECUTABLE 为 conda python
|
||
5. 所有路径、env、distro、机器人型号必须可被环境变量或 ROS 参数覆盖;
|
||
不得在源码里硬写 `/home/<user>` 一类 path。
|
||
6. 保留训练对齐的全部数值(control_frequency=5Hz, rnn_hidden=512,
|
||
policy_scale 默认 [0.6,0.3,0.6],joystick axis 映射等)。
|
||
7. 关键安全机制:手柄 deadman + 15 s 超时;提供 `require_joystick:
|
||
false` 旁路开关,但默认必须为 true。
|
||
|
||
【输出文件清单】(缺一不可)
|
||
package.xml, CMakeLists.txt, setup.py
|
||
launch/sru_nav_go2.launch
|
||
config/sru_nav.yaml, config/waypoints_example.yaml
|
||
scripts/setup_conda_env.sh, scripts/launch_sru_nav.sh,
|
||
scripts/sru_nav_node, scripts/waypoint_runner.py, scripts/verify_port.sh
|
||
src/sru_nav_go2/{__init__.py, constants.py, model.py, utils.py,
|
||
visualization.py, waypoint_manager.py,
|
||
navigation_policy_node.py}
|
||
docs/DEPLOY.md, docs/PORTING_GUIDE.md (中文), docs/PORTING_GUIDE_EN.md
|
||
models/README.md (说明从训练侧导出 onnx 的步骤)
|
||
|
||
【工作流程】(必须按顺序,每步完成后 self-check 再进下一步)
|
||
Step 1 — 勘查上游:读取仓库与论文,列出网络架构、观测维度、动作空间、
|
||
rsl_rl 算法配置、reward 列表,输出到 notes/upstream_recon.md。
|
||
Step 2 — IO 规范化:在训练仓里找到 export_onnx 入口,记录 vae_encoder
|
||
与 nav_policy 的 input/output 名与 shape,写到 docs/IO_SPEC.md。
|
||
Step 3 — 生成 catkin 骨架并 `catkin_make` 验证。
|
||
Step 4 — 实现节点:以 sru-robot-deployment 的 node 为模板,
|
||
替换传感器话题、剥离非 Odin 依赖、把 PyTorch 调用换成 onnxruntime。
|
||
Step 5 — 编写两份脚本(setup_conda_env.sh / launch_sru_nav.sh),
|
||
覆盖 §硬约束 4 的全部点。
|
||
Step 6 — 生成 verify_port.sh:检查包结构、shebang 指向、ONNX 推理、
|
||
rostopic 列表、yaml 必填字段。
|
||
|
||
【验收标准】(用户会跑 verify_port.sh 与下述命令)
|
||
1. `catkin_make` 在干净 catkin_ws 下 0 warning 0 error。
|
||
2. `bash scripts/setup_conda_env.sh --check` 全部通过。
|
||
3. `head -1 devel/lib/sru_nav_go2_ros1/sru_nav_node` 指向
|
||
conda env 的 python,不是 /usr/bin/python3。
|
||
4. `roslaunch sru_nav_go2_ros1 sru_nav_go2.launch require_joystick:=false`
|
||
起来后看到 `Navigation policy node is ready.` 且不刷错误。
|
||
5. 用 `rostopic pub` 发任意 odom + 一帧 32FC1 depth + 一个 goal_pose,
|
||
`/cmd_vel` 应输出非零 Twist。
|
||
6. `bash scripts/verify_port.sh` 全部 PASS。
|
||
|
||
【回答方式】
|
||
- 严禁直接生成 1000+ 行的单条回复;按 Step 拆分,每步先汇报计划再写文件。
|
||
- 每步结束后跑相应 self-check,把命令与输出贴回;自检失败立即修复。
|
||
- 如果上游仓库找不到某文件,明确报告并暂停,不得编造接口。
|
||
```
|
||
|
||
完整可直接执行的版本见 `@/.windsurf/workflows/port-sru-to-ros.md` 与 `@/.claude/commands/port-sru-to-ros.md`。
|
||
|
||
### 5.3 怎样验证「这份 prompt 真的能复刻本仓库」?
|
||
|
||
把上述 workflow 在一台干净机器上跑完,最后做:
|
||
|
||
```bash
|
||
diff -r --exclude=__pycache__ --exclude=.git \
|
||
./generated_sru_nav_go2_ros1/ \
|
||
./sru_nav_go2_ros1/
|
||
```
|
||
|
||
预期:**结构完全一致;逐行 diff 应集中在注释 / 字面量顺序上,不应在数值常量、话题名、函数签名上有差异**。如果出现关键差异(例如 `policy_scale` 改成 `[1, 1, 1]`、忘记 LD_PRELOAD),说明 agent 没遵 prompt,回到对应 Step 重启。
|
||
|
||
---
|
||
|
||
## 6. 个性化与 0 门槛使用
|
||
|
||
所有「可能因人而异」的点都开成参数。最常见的三类:
|
||
|
||
### 6.1 路径
|
||
|
||
| 参数 | 默认 | 覆盖方式 |
|
||
|---|---|---|
|
||
| catkin workspace | `$HOME/code/odin_sru_nav` | `CATKIN_WS=/path bash scripts/launch_sru_nav.sh` |
|
||
| conda env 名 | `sru_nav` | `ENV_NAME=my_env bash scripts/setup_conda_env.sh` |
|
||
| ROS distro | `noetic` | `ROS_DISTRO=melodic bash scripts/launch_sru_nav.sh`(仅 py3 兼容版本) |
|
||
| pip 镜像 | 清华源 | `PIP_INDEX_URL=https://pypi.org/simple bash scripts/setup_conda_env.sh` |
|
||
|
||
### 6.2 话题与帧
|
||
|
||
改 `config/sru_nav.yaml` 里这些字段,**不用改源码**:
|
||
|
||
```yaml
|
||
depth_topic: "/your/depth" # sensor_msgs/Image (32FC1, meters)
|
||
odom_topic: "/your/odometry" # nav_msgs/Odometry, world frame
|
||
joy_topic: "/joy"
|
||
goal_topic: "/goal_pose"
|
||
cmd_vel_topic: "/cmd_vel"
|
||
```
|
||
|
||
### 6.3 不同机器人
|
||
|
||
只需提供「订阅 `/cmd_vel` 的桥」。仓库**完全不感知机器人型号**。常见替换:
|
||
|
||
- Unitree Go2:用 `unitree_legged_sdk` 写 50 行的 cmd_vel→sport_mode 桥;
|
||
- Unitree B2 / B2W:用官方 ROS1 桥;
|
||
- Spot:`spot_ros` + `cmd_vel`;
|
||
- 任何 ROS 仿真:直接订阅 `/cmd_vel`。
|
||
|
||
唯一需要改的可能是 TF:默认 launch 里发 `base_link → odin1_base_link` 的静态 TF,参数 `odin1_x / y / z / roll / pitch / yaw`,按你的相机安装位置改。
|
||
|
||
### 6.4 检查环境(你提到要写进文档与 prompt 的部分)
|
||
|
||
**第一次部署时建议跑一遍**:
|
||
|
||
```bash
|
||
# 1) OS / ROS
|
||
lsb_release -a # Ubuntu 20.04 推荐 (Noetic 配套)
|
||
echo $ROS_DISTRO # 应为 noetic
|
||
which roscore # /opt/ros/noetic/bin/roscore
|
||
|
||
# 2) Conda
|
||
conda --version # ≥ 4.10
|
||
conda env list | grep -E "sru_nav|base"
|
||
|
||
# 3) 时钟(Jetson 无 RTC 电池常坏)
|
||
date # 确认是真实当前时间,否则 pip TLS 会拒接
|
||
|
||
# 4) 网络
|
||
ping -c 2 8.8.8.8 || echo "WARN: no internet, pip will fail"
|
||
|
||
# 5) 硬件
|
||
ls /dev/input/js* 2>/dev/null || echo "INFO: no joystick — use --no-deadman for headless tests"
|
||
rostopic list 2>/dev/null | grep odin1 || echo "WARN: Odin1 driver not running"
|
||
```
|
||
|
||
`scripts/verify_port.sh` 里把这一段固化为前置检查。
|
||
|
||
---
|
||
|
||
## 7. 验收脚本:`scripts/verify_port.sh`
|
||
|
||
设计成「**生成完代码后,开发者一键自检**」。完整列表见脚本本身(自带带 `[PASS]/[FAIL]/[WARN]` 标记),核心检查项:
|
||
|
||
| 检查项 | 命令 | 预期 |
|
||
|---|---|---|
|
||
| 包结构完整 | `find package.xml CMakeLists.txt setup.py launch config models scripts src/sru_nav_go2/{constants,model,navigation_policy_node}.py` | 全部存在 |
|
||
| 模型可加载 | `python -c "import onnxruntime as ort; ort.InferenceSession('models/vae_encoder.onnx')"` | 不抛异常,打印 input shape |
|
||
| catkin 通过 | `catkin_make -DPYTHON_EXECUTABLE=$(which python3)` | exit 0 |
|
||
| shebang 正确 | `head -1 devel/lib/sru_nav_go2_ros1/sru_nav_node \| grep -q miniconda3.envs.${ENV_NAME}` | match |
|
||
| 节点能起 | `timeout 8 roslaunch sru_nav_go2_ros1 sru_nav_go2.launch launch_joy:=false require_joystick:=false` | log 含 `Navigation policy node is ready` |
|
||
| 关键参数存在 | `grep -E "policy_scale\|require_joystick\|control_frequency" config/sru_nav.yaml` | 三项都命中 |
|
||
| LD_PRELOAD 修复 | `grep -q 'LD_PRELOAD.*libffi' scripts/launch_sru_nav.sh` | 命中 |
|
||
|
||
跑完终端打印 `ALL CHECKS PASSED` 才能放行真机。
|
||
|
||
---
|
||
|
||
## 8. 常见陷阱
|
||
|
||
按踩坑频率排序:
|
||
|
||
1. **catkin_make 时没在 conda env 里**:节点 shebang 烤进 `/usr/bin/python3`,运行时 `import onnxruntime` 失败。修:`conda activate sru_nav && catkin_make clean && catkin_make -DPYTHON_EXECUTABLE=$(which python3)`。
|
||
2. **`No module named netifaces / defusedxml`**:rospy 隐性依赖。`pip install netifaces defusedxml` 进 conda env,`setup_conda_env.sh` 已内置。
|
||
3. **`libp11-kit.so.0: undefined symbol: ffi_type_pointer`**:conda libffi 抢加载。`launch_sru_nav.sh` 里的 `LD_PRELOAD` 已修,新平台只需把版本号 7→8 同步。
|
||
4. **pip `certificate is not yet valid`**:Jetson 时钟回退。`sudo ntpdate -u ntp.aliyun.com && sudo hwclock --systohc`。
|
||
5. **`/cmd_vel` 全 0**:deadman 模式默认开。要么接手柄推 `axes[4]`,要么 `--no-deadman`(仅测试)。
|
||
6. **goal 被忽略**:`/goal_pose.frame_id` 必须等于 odom 的 `frame_id`。
|
||
|
||
---
|
||
|
||
## 9. 许可与致谢
|
||
|
||
- 算法与训练代码版权:原 SRU 作者团队,遵循其原 LICENSE。
|
||
- 本部署移植包 `sru_nav_go2_ros1` 在 MIT-style 许可下开源;包含上游派生代码的部分文件保留各自原始 header。
|
||
- Odin1 是第三方深度相机,本仓库不再分发其驱动。
|
||
|
||
如发现移植 bug 或想要一键支持更多机器人/相机,欢迎 issue + PR。
|
||
|
||
---
|
||
|
||
## 附录 A:与论文/训练代码核对的关键数字
|
||
|
||
| 名称 | 训练值 | 部署值 | 来源 |
|
||
|---|---|---|---|
|
||
| `control_frequency` | 10 Hz (env step) | 5 Hz | `constants.DEFAULT_CONTROL_FREQUENCY` |
|
||
| `rnn_hidden_size` | 512 | 512 | `b2w/agents/rsl_rl_cfg.py:36` |
|
||
| `rnn_num_layers` | 1 | 1 | 同上 |
|
||
| `policy_scaling` | `[1.5, 1.0, 1.0]` × `Uniform(0.8,1.2)/(0.6,1.0)/(0.8,1.2)` | `policy_scale=[0.6,0.3,0.6]`(保守) | `navigation_env_cfg.py:163` |
|
||
| `entropy_coef` | `0.00375` | n/a | `b2w/agents/rsl_rl_cfg.py:50` |
|
||
| `value_loss_coef` | `0.02` | n/a | 同上 |
|
||
| 深度图分辨率(VAE 输入) | 见 `image_input_dims=(64, 5, 8)` 编码后形状 | 任意输入分辨率,节点 resize 到训练规格 | `b2w/agents/rsl_rl_cfg.py:41` |
|
||
| `min_depth / max_depth` | 0.25 / 10.0 m | 0.25 / 10.0 m | `constants.py` |
|
||
| `JOYSTICK_TIMEOUT` | n/a | 15 s | `constants.py:16` |
|
||
|
||
附录里这张表是「训练-部署一致性」的最小验收清单。任何二次移植(换机器人 / 换相机)都应保留这些数字不变。
|
||
|
||
---
|
||
|
||
## 附录 B:从空机器开始的 0 门槛 quickstart
|
||
|
||
```bash
|
||
# === 系统级(一次性,需 sudo) =============================================
|
||
sudo apt-get update
|
||
sudo apt-get install -y curl git build-essential ros-noetic-desktop \
|
||
ros-noetic-joy ros-noetic-tf2-tools \
|
||
python3-catkin-tools
|
||
|
||
# === 装 miniconda(如已装可跳过) ==========================================
|
||
curl -O https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-$(uname -m).sh
|
||
bash Miniconda3-latest-Linux-$(uname -m).sh -b -p $HOME/miniconda3
|
||
echo 'source $HOME/miniconda3/etc/profile.d/conda.sh' >> ~/.bashrc
|
||
source ~/.bashrc
|
||
|
||
# === 拉本仓库(替换成你的 fork / 仓库地址) ================================
|
||
mkdir -p ~/code/odin_sru_nav/src
|
||
cd ~/code/odin_sru_nav/src
|
||
git clone https://github.com/<YOUR_FORK>/sru_nav_go2_ros1.git
|
||
|
||
# === 装 conda 依赖(含 onnxruntime / cv2 / netifaces ...) =================
|
||
cd sru_nav_go2_ros1
|
||
bash scripts/setup_conda_env.sh # 全自动;首次约 5–10 分钟
|
||
bash scripts/setup_conda_env.sh --check # 自检全 OK 才继续
|
||
|
||
# === 编译(必须在 conda env 里) ===========================================
|
||
conda activate sru_nav
|
||
cd ~/code/odin_sru_nav
|
||
catkin_make -DPYTHON_EXECUTABLE=$(which python3)
|
||
source devel/setup.bash
|
||
|
||
# === 一键启动(默认安全模式:必须接手柄) ==================================
|
||
cd src/sru_nav_go2_ros1
|
||
bash scripts/launch_sru_nav.sh
|
||
|
||
# 或:抬轮 / 仿真 / 闭环回归测试(无手柄) ==================================
|
||
bash scripts/launch_sru_nav.sh --no-deadman
|
||
|
||
# === 一键自检 ==============================================================
|
||
bash scripts/verify_port.sh
|
||
```
|
||
|
||
到这里只要 `verify_port.sh` 全绿,就能开始真机测试。
|