fix: clamp std before distribution, lower init_noise to 0.5, NaN guard
This commit is contained in:
189
docs/dreamwaq_alignment.md
Normal file
189
docs/dreamwaq_alignment.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# DreamWaQ 复现与对齐记录
|
||||
|
||||
> 目标:让本地 `go1-dreamwaq-walk` 完全对齐并复现 [Manaro-Alpha/DreamWaQ](https://github.com/Manaro-Alpha/DreamWaQ.git)(基于 IsaacGym + rsl_rl 的实现),迁移到 MotrixLab(MotrixSim + SKRL-JAX)。
|
||||
|
||||
最后更新:2026-06-25
|
||||
|
||||
---
|
||||
|
||||
## 1. 架构总览
|
||||
|
||||
```
|
||||
CENet (VAE): history(5×45=225) → [128,64] → latent(16) + vel_est(3) = code(19)
|
||||
Decoder: code(19) → [64,128] → obs_recon(45)
|
||||
Actor: code(19) + obs(45) = 64 → [512,256,128] → action(12)
|
||||
Critic: privileged_obs(235) → [512,256,128] → value(1)
|
||||
|
||||
观测(45): ang_vel(3) + gravity(3) + commands(3) + dof_pos(12) + dof_vel(12) + actions(12)
|
||||
特权观测(235): obs(45) + base_vel(3) + heights(187, 17×11 网格)
|
||||
增广观测(254, 训练用): [code(19) | obs(45) | base_vel(3) | heights(187)]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 🔴 真正让机器人站不起来的 Bug(最关键)
|
||||
|
||||
排查发现并非训练不充分,而是两个环境 bug 让**约一半的训练数据是垃圾**:
|
||||
|
||||
### Bug A — 机器人生成在地形地下
|
||||
- **现象**:reset 时机器人固定生成在绝对高度 z=0.40,但 DreamWaQ 地形高度为 0–1.6m。在高地形格子上,机器人生成在**地表以下最多 1.2m**。
|
||||
- **影响**:31/64 的环境在生成瞬间就卡进地形 → 爆炸/立即终止/疯狂抖动 → dof_acc 高达 150 万 → 奖励被 `only_positive_rewards` 裁剪为 0。一半训练样本无效。
|
||||
- **修复**:新增 `_sample_terrain_height(xy)`,按生成点的局部地形高度设置 z = `terrain_z + 0.40`。修复后 0/64 在地下。
|
||||
- **位置**:[dreamwaq.py](../motrix_envs/src/motrix_envs/locomotion/go1/dreamwaq.py) `reset()` + `_sample_terrain_height()`
|
||||
|
||||
### Bug B — feet_air_time 奖励恒为 0
|
||||
- **现象**:腾空时间在 `update_feet_air_time` 中被 `*= ~contacts` 清零,**早于** reward 函数读取 `first_contact = (air_time > 0) * contacts`。落地瞬间 air_time 已是 0 → first_contact 永远为 0 → 步态奖励完全失效。
|
||||
- **影响**:机器人没有迈步激励,倾向于站着不动/拖行,学不会正常步态。
|
||||
- **修复**:按上游顺序重构 —— 先用 `contact_filt = contact OR last_contact` 算出 `first_contact` 和落地时的 air_time,**再**清零;reward 函数改为读取预计算值(无状态,对 DreamWaQ 的 `_get_reward` 二次调用安全)。
|
||||
- **位置**:[walk_np.py](../motrix_envs/src/motrix_envs/locomotion/go1/walk_np.py) `update_feet_air_time()` + `_reward_feet_air_time()`
|
||||
|
||||
### Bug C — 初始地形难度过高
|
||||
- **现象**:首次 reset 在 0-9 级随机生成(含最难地形)。上游用 `max_init_terrain_level=5`。
|
||||
- **修复**:首次生成限制在 0-5 级,由课程学习逐步提升。
|
||||
- **位置**:[dreamwaq.py](../motrix_envs/src/motrix_envs/locomotion/go1/dreamwaq.py) `reset()`
|
||||
|
||||
---
|
||||
|
||||
## 3. 对齐改动(与上游一致)
|
||||
|
||||
### 控制 / 物理(cfg.py + dreamwaq.py)
|
||||
| 参数 | 改动前 | 改动后(上游) |
|
||||
|---|---|---|
|
||||
| stiffness (Kp) | 80 | **28.0** |
|
||||
| damping (Kd) | 1.0 | **0.7** |
|
||||
| action_scale | 0.05 | **0.25** |
|
||||
| sim_dt / ctrl_dt | 0.01 / 0.01 | **0.005 / 0.02**(decimation=4,200Hz 物理 / 50Hz 控制) |
|
||||
|
||||
### 默认关节角度(cfg.py,非对称)
|
||||
```
|
||||
hips: ±0.1 (FL/RL: +0.1, FR/RR: -0.1)
|
||||
thighs: 前 0.8 / 后 1.0
|
||||
calves: -1.5 (全部)
|
||||
```
|
||||
|
||||
### 观测顺序(walk_np.py)
|
||||
改为上游顺序 `[ang_vel, gravity, commands, dof_pos, dof_vel, actions]`,并加入观测噪声(`add_noise=True, noise_level=1.0`)。
|
||||
|
||||
### 命令范围(dreamwaq.py)
|
||||
`lin_vel_x/y/yaw` 全部 `[-1, 1]`(之前 vel_x 上限是 2.0)。
|
||||
|
||||
### 奖励函数对齐(dreamwaq.py)
|
||||
- `smoothness`:基于 `joint_pos_target`(= action×scale + default)的二阶差分,而非原始 action
|
||||
- `base_height`:`square(base_z - mean(measured_heights) - 0.30)`,扣除局部地形高度
|
||||
- `power_distribution`:`var(torque×vel)` 跨全部 12 关节,而非按腿分组
|
||||
- 12 项奖励尺度与上游 Go1 base config 精确一致
|
||||
|
||||
### 域随机化(dreamwaq.py)
|
||||
motor_strength / Kp_factor / Kd_factor ∈ [0.9, 1.1],每 4 秒重采样(`rand_interval_s=4`),在 `_compute_torques` 中应用。
|
||||
|
||||
### 训练(dreamwaq_ppo.py + go1_dreamwaq.py)
|
||||
- **关键 bug 修复**:SKRL 字段名 `entropy_coef→entropy_loss_scale`、`clip_param→ratio_clip`、`gamma→discount_factor`。改前 entropy 被静默设为 0.0(字段名不存在)。
|
||||
- CENet 解码目标:重构**当前观测**(历史最后一帧),而非预测下一帧(匹配上游 `decode_target = obs_batch`)
|
||||
- 正交权重初始化 `orthogonal(sqrt(2))`,应用于 CENet/Actor/Critic 所有 Dense 层
|
||||
- PPO 超参:rollouts=24, lr=1e-3, entropy=0.01, gamma=0.99, lam=0.95, clip=0.2, epochs=5, mini_batches=4
|
||||
|
||||
### 地形(gen_dreamwaq_terrain.py)
|
||||
降低高度尺度(最大 2.73m → 1.66m),更适合 0.33m 高的机器人。生成后更新 XML 的 hfield `size`。
|
||||
|
||||
---
|
||||
|
||||
## 3.5 🔴🔴 部署缺少观测归一化("站不起来"的真正根因)
|
||||
|
||||
这是导致导出/play/sim2sim 中机器人**完全站不起来**的根本原因,独立于训练质量。
|
||||
|
||||
- **现象**:训练奖励正常(mean 35 / max 924),但导出 ONNX / `play_dreamwaq.py` / MuJoCo sim2sim 中机器人塌成一团,actor 输出动作幅度高达 12(关节目标偏移 ~3 rad,直接打到限位)。
|
||||
- **根因**:SKRL PPO 通过 `_add_runtime_config` 启用了 **`RunningStandardScaler` 状态预处理器**——训练时观测被 `clip((x - running_mean) / (sqrt(var) + 1e-8), -5, 5)` 归一化(running_mean 幅度达 ±3.2,std 0.14–3.2)。但部署管线直接喂**原始观测**给 actor,输入尺度完全错误 → 策略输出垃圾动作 → 倒地。
|
||||
- **验证**:加上归一化后,站立从 **0/16 → 16/16 upright**。
|
||||
- **修复**:
|
||||
- [export_dreamwaq_onnx.py](../scripts/export_dreamwaq_onnx.py):从 checkpoint 提取 `state_preprocessor` 的 mean/var(前 64 维 = code+obs),烘焙进 ONNX 模型(actor 前做归一化+clip)
|
||||
- [play_dreamwaq.py](../scripts/play_dreamwaq.py):`policy_forward` 中应用同样的归一化
|
||||
- sim2sim 用 ONNX,重新导出后自动修复
|
||||
- **注意**:`RunningStandardScaler` 是 SKRL 的附加项,上游 rsl_rl 没有(上游只用固定 obs_scales)。任何脱离 SKRL agent 的部署都**必须**复现这个归一化,否则策略失效。
|
||||
|
||||
---
|
||||
|
||||
## 3.6 🔴 训练不收敛:初始探索过猛(IsaacGym→MotrixSim reality gap)
|
||||
|
||||
修复部署归一化后发现策略**根本没学会站立**——在 MotrixSim 训练环境里 0/64 站立/行走,比"零动作"还差。
|
||||
|
||||
- **诊断**:零动作(纯 PD 保持默认姿态)奖励 **1.27/step**,27/64 站立;训练后的策略奖励 **0.035/step**,0/64 站立。**训练把策略训得比啥都不做还差**。
|
||||
- **根因**:`initial_log_std=0.0`(std=1)× `action_scale=0.25` = 每步 ±0.25 rad 随机抖动 @50Hz。实测:
|
||||
| 探索 std | 抖动 | 站立率 | 平均奖励 |
|
||||
|---|---|---|---|
|
||||
| 1.0(上游) | 0.25rad | 27/64 | **0.025** |
|
||||
| 0.37 | 0.09rad | 29/64 | 0.512 |
|
||||
| 0.2 | 0.05rad | 23/64 | 0.730 |
|
||||
|
||||
机器人在所有 std 下都能站(站立率相近),但 std=1 时剧烈抖动产生的 dof_acc/action_rate 惩罚把站立的正奖励**完全淹没**(净奖励 0.025 ≈ 噪声)→ PPO 没有指向站立的梯度 → 收敛到塌倒局部最优。
|
||||
- **为何上游能用**:DreamWaQ 的 std=1 是为 IsaacGym 调的;MotrixSim 接触/动力学不同(你能用的 go1 平地任务用的是 5× 更温和的 `action_scale=0.05`)。
|
||||
- **修复**:[go1_dreamwaq.py](../motrix_rl/src/motrix_rl/tasks/go1_dreamwaq.py) 设 `initial_log_std=-1.0`(std=0.37,抖动 0.09rad),让机器人早期保持直立、获得清晰的站立奖励信号后再 bootstrap 行走。这是 reality-gap 的必要偏离(上游用 0.0)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 🛠 原生 play 渲染崩溃修复
|
||||
|
||||
- **现象**:`play_dreamwaq.py` 启动 MotrixSim 渲染器时崩溃 `Couldn't get swap chain texture`,而其他 go1 任务的 play 正常。
|
||||
- **根因**:JAX 默认预分配 **75% 显存**(12GB 中 ~9GB),饿死了 Vulkan 渲染器。标准 play.py 因渲染器创建与 JAX 初始化间隔充足而不受影响。
|
||||
- **修复**:在 `import jax` 前设置 `os.environ["XLA_PYTHON_CLIENT_PREALLOCATE"] = "false"`,并优雅处理 `RenderClosedError`。
|
||||
- **注意**:训练占用 GPU 时无法同时跑原生渲染;请在训练结束后查看。
|
||||
|
||||
---
|
||||
|
||||
## 5. 修改的文件
|
||||
|
||||
| 文件 | 改动 |
|
||||
|---|---|
|
||||
| [motrix_envs/.../go1/cfg.py](../motrix_envs/src/motrix_envs/locomotion/go1/cfg.py) | PD 增益、关节角度、噪声尺度 |
|
||||
| [motrix_envs/.../go1/dreamwaq.py](../motrix_envs/src/motrix_envs/locomotion/go1/dreamwaq.py) | **地形生成修复**、ctrl 频率、命令范围、奖励、域随机化、观测噪声 |
|
||||
| [motrix_envs/.../go1/walk_np.py](../motrix_envs/src/motrix_envs/locomotion/go1/walk_np.py) | 观测顺序、**feet_air_time 修复** |
|
||||
| [motrix_rl/.../skrl/jax/train/dreamwaq_ppo.py](../motrix_rl/src/motrix_rl/skrl/jax/train/dreamwaq_ppo.py) | CENet 解码目标、VAE 缓冲、正交初始化 |
|
||||
| [motrix_rl/.../tasks/go1_dreamwaq.py](../motrix_rl/src/motrix_rl/tasks/go1_dreamwaq.py) | **SKRL 字段名修复**、超参 |
|
||||
| [scripts/gen_dreamwaq_terrain.py](../scripts/gen_dreamwaq_terrain.py) | 地形高度尺度 |
|
||||
| [scripts/play_dreamwaq.py](../scripts/play_dreamwaq.py) | **GPU 预分配修复** |
|
||||
| [scripts/dreamwaq_sim2sim_mujoco.py](../scripts/dreamwaq_sim2sim_mujoco.py) | PD/角度/decimation 与训练对齐 |
|
||||
| [scripts/export_dreamwaq_onnx.py](../scripts/export_dreamwaq_onnx.py) | CENet 编码器 + Actor → ONNX |
|
||||
|
||||
---
|
||||
|
||||
## 6. 使用方法
|
||||
|
||||
### 训练
|
||||
```bash
|
||||
uv run scripts/train_dreamwaq.py --num-envs 2048 --timesteps 100M --seed 42
|
||||
```
|
||||
> 用 2048 envs 避免 4096 在 ~70% 处的 OOM(exit 144)。检查点存于 `runs/go1-dreamwaq-walk/skrl/`。
|
||||
|
||||
### 原生查看(MotrixSim,GPU 空闲时)
|
||||
```bash
|
||||
uv run scripts/play_dreamwaq.py --num-envs 9
|
||||
```
|
||||
|
||||
### 导出 ONNX
|
||||
```bash
|
||||
uv run scripts/export_dreamwaq_onnx.py \
|
||||
--checkpoint runs/go1-dreamwaq-walk/skrl/<run>/checkpoints/best_agent.pickle \
|
||||
--vae runs/go1-dreamwaq-walk/skrl/vae_<N>.pkl \
|
||||
--output exports_go1_dreamwaq/policy.onnx
|
||||
```
|
||||
|
||||
### MuJoCo sim2sim
|
||||
```bash
|
||||
uv run scripts/dreamwaq_sim2sim_mujoco.py --terrain dreamwaq
|
||||
# 控件: W/S 前后 Q/E 左右 A/D 旋转 Space 停 R 重置 Esc 退出
|
||||
```
|
||||
|
||||
### TensorBoard
|
||||
```bash
|
||||
uv run tensorboard --logdir runs/go1-dreamwaq-walk
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 待办 / 注意事项
|
||||
|
||||
- **检查点兼容性**:改动前的检查点(含旧 PD/观测顺序/地形 bug)不兼容,应丢弃重训。
|
||||
- **未对齐项(有意保留)**:
|
||||
- 奖励未乘 `dt`(上游 `_prepare_reward_function` 会 `scale *= dt`)。这是全局均匀缩放,PPO 优势归一化会抵消,不影响行为;为避免干扰已调好的 value 学习而保留。
|
||||
- VAE 训练为 PPO 更新后独立一步,而非上游的联合优化(功能等价,更易维护)。
|
||||
- 地形几何为自定义生成器,非 IsaacGym `terrain_utils`(概念相似,具体形状不同)。
|
||||
- **OOM**:4096 envs 曾在 70% 处被杀(exit 144),改用 2048。
|
||||
168
docs/dreamwaq_rsl_handoff.md
Normal file
168
docs/dreamwaq_rsl_handoff.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# DreamWaQ on MotrixSim — rsl_rl 复现交接文档
|
||||
|
||||
> 目标:在 MotrixLab(MotrixSim + numpy 物理)上**忠实复现** [Manaro-Alpha/DreamWaQ](https://github.com/Manaro-Alpha/DreamWaQ)(原版基于 IsaacGym + 定制 rsl_rl-1.0.2),让 Go1 在地形上行走。
|
||||
>
|
||||
> 最后更新:2026-06-26。当前状态:rsl_rl 路线训练中,趋势健康(站立解决、行走学习中)。
|
||||
|
||||
---
|
||||
|
||||
## 0. 一句话总览
|
||||
|
||||
原版 DreamWaQ = **ActorCritic_DWAQ(CENet+Actor+Critic 一体网络) + PPO 联合训练 VAE + 金字塔地形课程**。我把上游定制的 `rsl_rl-1.0.2` 核心**原样移植**到 MotrixLab,写了个 numpy↔torch 环境适配器,并针对 MotrixSim 的两个 reality gap(hfield 大 z_scale 接触不稳、IsaacGym 超参偏激进)做了适配。
|
||||
|
||||
---
|
||||
|
||||
## 1. 两条路线(背景)
|
||||
|
||||
复现走过两条路线,**rsl_rl 是当前主线**:
|
||||
|
||||
| | SKRL-JAX 路线(旧,参考) | **rsl_rl 路线(当前主线)** |
|
||||
|---|---|---|
|
||||
| 代码 | `motrix_rl/skrl/jax/train/dreamwaq_ppo.py` | `motrix_rl/dwaq_rsl/`(新模块) |
|
||||
| 网络 | CENet 拼在 wrapper,Actor/Critic 在 SKRL | 上游 `ActorCritic_DWAQ` 一体网络 |
|
||||
| VAE 训练 | **单独一步**(CENet 不参与 PPO 梯度) | **PPO 联合优化**(和上游一致) |
|
||||
| 结果 | 站立解决,但**学不会走**(机器人趴/站着不动) | 趋势健康,行走学习中 |
|
||||
|
||||
**为什么切到 rsl_rl**:SKRL 路线机器人学不会走,根因是 **VAE 单独训练** —— CENet 的 code(latent+vel)早期是垃圾,actor 用不上。上游把 VAE 和 PPO 联合优化(一个 optimizer step),code 早期就有意义。切到 rsl_rl 后同样的 `std=1.0` 下 reward 从卡死的 0.025 变成快速上涨,这是关键转折。
|
||||
|
||||
> SKRL 路线的详细 bug 修复记录见 [dreamwaq_alignment.md](dreamwaq_alignment.md)(地下生成、feet_air_time、部署归一化等,这些 env 层 bug 两条路线都受益)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 新建模块:`motrix_rl/src/motrix_rl/dwaq_rsl/`
|
||||
|
||||
把上游 `rsl_rl-1.0.2` 的核心**原样移植**(纯 torch,去掉 isaacgym 依赖,改相对 import):
|
||||
|
||||
| 文件 | 来源 | 作用 |
|
||||
|---|---|---|
|
||||
| `actor_critic_dwaq.py` | 上游 `modules/actor_critic_DWAQ.py` **原样** | CENet(encoder 225→128→64 + latent/vel 头)+ decoder + Actor[512,256,128] + Critic[512,256,128]。`act(obs, obs_history)` 把 CENet code(19)和 obs(45)拼成 64 维喂 actor |
|
||||
| `ppo.py` | 上游 `algorithms/ppo.py` **原样** | PPO + **VAE 联合 loss**:`loss = surrogate + value + autoenc`,其中 `autoenc = MSE(vel, prev_priv[:,45:48]) + MSE(decode, obs) + beta*KL` |
|
||||
| `rollout_storage.py` | 上游 `storage/rollout_storage.py` **原样** | 经验存储,含 `prev_critic_obs`(VAE 的 vel_target 来源)、`observation_history` |
|
||||
| `utils.py` | 上游 `utils/utils.py` | `split_and_pad_trajectories` |
|
||||
| `runner.py` | 上游 `on_policy_runner.py` **适配** | 去掉 rsl_rl 包 import + isaacgym 噪声,改用本地模块。learn() 主循环、save/load、tensorboard 日志 |
|
||||
| `env_wrapper.py` | **新写** | `DwaqVecEnv`:MotrixLab `DreamWaQTask`(numpy)→ 上游 VecEnv 接口(torch)。见下 |
|
||||
|
||||
### `DwaqVecEnv`(env_wrapper.py)关键点
|
||||
|
||||
上游 runner 要求 `env.step()` 返回 **7 元组**:`(obs, privileged_obs, prev_privileged_obs, obs_hist, rewards, dones, extras)`。MotrixLab 的 `DreamWaQTask` 已提供 obs(45)、`info["privileged_obs"]`(235)、`info["obs_history"]`(N,5,45)。wrapper 做:
|
||||
- numpy↔torch 转换(MotrixSim 物理在 CPU,网络在 GPU,每步传输)
|
||||
- `obs_history` flatten:(N,5,45) → (N,225)
|
||||
- 维护 `prev_privileged_obs`(step 前的 privileged,VAE 用其 [45:48]=base_vel 作 vel_target)
|
||||
- `extras["time_outs"]` = truncated
|
||||
- **rewards/dones 是 (N,) 不是 (N,1)**(上游约定,storage 内部 view 成 (N,1)) — 这是个踩过的坑
|
||||
|
||||
### 维度速查
|
||||
```
|
||||
obs=45, privileged=235(=obs45+base_vel3+heights187), obs_hist=225(=5*45)
|
||||
actor_in=64(=code19+obs45), critic_in=235, cenet_in=225, cenet_out=19(=vel3+latent16)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 环境层改动:`motrix_envs/.../go1/dreamwaq.py`
|
||||
|
||||
(这些改动 rsl_rl 路线和 SKRL 路线共用同一个 `DreamWaQTask`)
|
||||
|
||||
1. **去掉命令课程**(关键):原来我加过一个命令课程(cmd_scale 0.3→1.0),但它让机器人**趴着刷小命令分**(小命令下趴着也能 tracking)。改回**上游 full range [-1,1]**(`commands.curriculum=False`),机器人被要求走,趴着不划算。见 `resample_commands`。
|
||||
2. **level 0 起步**:`reset()` 里 init terrain level 全设 0(平地),靠地形难度课程逐级升。原来 0-5 随机会让机器人 spawn 在难地形秒摔。
|
||||
3. **中心 spawn**:`_make_origins` 坐标中心化(hfield 中心在世界原点 (0,0)),spawn 在 cell 的 3m 平台中心 ±1m。
|
||||
4. **地形高度采样**:`_sample_terrain_height` / `_get_heights` 从 hfield 采样(`hf.height_matrix` + `hf.bound`),用于 spawn 高度 + privileged obs 的 heights。
|
||||
5. 奖励对齐上游(smoothness 用 joint_pos_target + step mask、power_distribution 用 `var(abs(power))`、base_height 扣地形高度等)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 地形:`scripts/gen_dreamwaq_terrain.py` + ⚠️ MotrixSim hfield 坑
|
||||
|
||||
### 生成器
|
||||
移植了上游 IsaacGym `terrain_utils` 的金字塔地形(纯 numpy):`pyramid_sloped_terrain` / `pyramid_stairs_terrain` / `discrete_obstacles_terrain` / `random_uniform_terrain`,10 行难度 × 20 列类型网格,每格 3m 中心平台,输出 16-bit PNG hfield + XML `<size>` 行。
|
||||
|
||||
### ⚠️⚠️ 最大的坑:MotrixSim hfield 在大 z_scale 下接触不稳
|
||||
|
||||
**实测结论**(零动作站立 20 步摔倒数):
|
||||
|
||||
| hfield z_scale | 机器人 above-terrain | 20步摔倒 |
|
||||
|---|---|---|
|
||||
| 3.78(上游全高金字塔) | 0.12(**趴**) | 1267 |
|
||||
| 1.66 | 0.14(趴) | 1049 |
|
||||
| **0.54(矮金字塔)** | **0.35(正常站立)** | **120** |
|
||||
| plane geom(对比) | 0.33 | **0** |
|
||||
|
||||
- MotrixSim 用 **hfield(高度场)**,陡台阶垂直面只能近似成陡坡,机器人脚(sphere geom)打滑;**z_scale 越大越不稳**。
|
||||
- 上游用 **trimesh(三角网格)**,垂直面精确。**调研确认 MotrixSim 不支持非凸 trimesh 碰撞**(mesh 是凸包),接触参数(condim/solref/solimp)调优也无效。
|
||||
- **解决**:把金字塔楼梯/坡的高度**缩小 ~4 倍**(`make_terrain` 里 `step_height=0.012+0.020*diff`、`slope=0.10*diff`),让 z_scale≈0.54。保留金字塔结构 + 难度课程,但高度适配 MotrixSim hfield。**这是必要的 reality-gap 妥协**(不完全忠实上游高度,但能训)。
|
||||
- `scene_dreamwaq_terrain.xml` 的 hfield `size="85 45 0.54 0.01"`:**第1/2 是半径**(全宽 170×90m),第3 是 z_scale,第4 是底部厚度。
|
||||
|
||||
**重新生成地形后必须同步更新 XML 的 z_scale**(gen 脚本会打印 `size="..."` 那行的值)。
|
||||
|
||||
---
|
||||
|
||||
## 5. 脚本(训练 / 导出 / 可视化)
|
||||
|
||||
| 脚本 | 作用 |
|
||||
|---|---|
|
||||
| `scripts/train_dreamwaq_rsl.py` | rsl_rl 训练。超参 dict 内嵌(对齐上游 Go1RoughCfgPPO)。`--num-envs 2048 --iterations 3000 --init-noise-std 1.0` |
|
||||
| `scripts/export_dreamwaq_rsl_onnx.py` | rsl 的 `.pt` checkpoint → ONNX。**rsl 无 state 归一化**(不像 SKRL),所以 ONNX 简单:`obs(1,45)+obs_history(1,5,45)→action(1,12)`,用 mean CENet code |
|
||||
| `scripts/gen_dreamwaq_terrain.py` | 生成矮金字塔地形 PNG |
|
||||
| `scripts/dreamwaq_sim2sim_mujoco.py` | MuJoCo sim2sim 可视化(用 ONNX)。PD/角度/decimation 已对齐(KP=28,KD=0.7,action_scale=0.25,decimation=4) |
|
||||
|
||||
### 命令
|
||||
```bash
|
||||
# 训练 (后台跑, print 会被 buffer, 用 tensorboard 看)
|
||||
PYTHONUNBUFFERED=1 uv run scripts/train_dreamwaq_rsl.py --num-envs 2048 --iterations 3000 --seed 1 --init-noise-std 1.0
|
||||
|
||||
# 监控 (print 被 buffer, 看 tensorboard)
|
||||
uv run tensorboard --logdir runs/go1-dreamwaq-walk/rsl_dwaq
|
||||
|
||||
# 导出最新 checkpoint
|
||||
JAX_PLATFORMS=cpu uv run scripts/export_dreamwaq_rsl_onnx.py # 自动找最新 model_*.pt
|
||||
|
||||
# MuJoCo 可视化 (本地有显示的机器)
|
||||
uv run scripts/dreamwaq_sim2sim_mujoco.py --terrain flat # 平地最干净
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 关键决策 + 为什么(reality gap 适配)
|
||||
|
||||
复现不是 1:1 照搬,MotrixSim vs IsaacGym 有真实差异,做了几处**有意的适配**(都在代码注释里标了原因):
|
||||
|
||||
1. **VAE 联合训练**(用 rsl_rl 而非 SKRL):核心。SKRL 路线 VAE 单独训练→学不会走。
|
||||
2. **去命令课程**:命令课程让机器人趴着刷小命令分;上游本来就 full range 无课程。
|
||||
3. **矮金字塔地形**(z_scale 0.54):MotrixSim hfield 大 z_scale 接触不稳的妥协。
|
||||
4. **level 0 平地起步**:机器人先在平地学走,靠地形课程升级。
|
||||
5. 其余超参/网络/奖励**严格对齐上游**(PD 28/0.7、action_scale 0.25、decimation 4、init_noise_std 1.0、rollouts 24、lr 1e-3、entropy 0.01 等)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 当前状态(2026-06-26)
|
||||
|
||||
训练中(`runs/go1-dreamwaq-walk/rsl_dwaq/06-26_00-10-27`),趋势健康:
|
||||
|
||||
| 指标 | it59 → it182 |
|
||||
|---|---|
|
||||
| mean_reward | 0.02 → 2.6 → **18.6** |
|
||||
| episode 长度 | 12 → 101 → **143** |
|
||||
| noise_std | 1.0 → 0.84 → **0.57**(在学) |
|
||||
| base_height | 稳定(**不趴**) |
|
||||
|
||||
机器人能站(above ~0.35)+ episode 越来越长 + std 持续降 = 在学走。**需等训练跑完(3000 iter,~2.5h)再导出验证实际行走**(站立率/前进速度跟踪/摔倒率,别只看奖励数字 — 之前吃过"奖励高但实际趴着"的亏)。
|
||||
|
||||
---
|
||||
|
||||
## 8. 已知问题 / 坑(接手必读)
|
||||
|
||||
1. **训练 print 被 buffer**:重定向到文件时 Python print 不实时 flush。**用 tensorboard 监控**,别等 print。(已加 `PYTHONUNBUFFERED=1` 缓解)
|
||||
2. **`ps` 的 TIME 是 CPU 累计时间**(跨核),不是 wall time。看 wall 用 `etime` 或 tfevents 时间戳。
|
||||
3. **VAE checkpoint 文件混在共享目录**(SKRL 路线遗留):`runs/.../skrl/vae_*.pkl`。rsl 路线 checkpoint 是 `rsl_dwaq/<run>/model_*.pt`,别搞混。
|
||||
4. **物理在 CPU**(MotrixSim numpy backend),网络在 GPU。每步 numpy↔torch 传输有开销,~16 it/min(2048 envs)。物理是瓶颈。
|
||||
5. **MotrixSim hfield 不支持陡台阶**(根本限制):如果要更忠实的高地形,得调研 MotrixSim 是否有 mesh 凸分解地形方案,或接受矮地形。
|
||||
6. **验证要看行为不看奖励**:`only_positive_rewards` + 一堆"别动"惩罚,机器人容易学"趴着/站着不动"刷分。验证时务必测**实际前进速度跟踪 + 摔倒率**,不能只看 mean_reward。
|
||||
|
||||
---
|
||||
|
||||
## 9. 下一步
|
||||
|
||||
1. 等当前训练跑完,导出 + MuJoCo 验证实际行走(前进/转向跟踪)。
|
||||
2. 若行走 OK:逐步提高地形难度(z_scale),找 MotrixSim hfield 能稳定接触的上限。
|
||||
3. 若行走不够好:检查地形课程是否真在升级(机器人有没有从 level 0 走到 level 1+)、调 init_noise_std。
|
||||
4. 长期:调研 MotrixSim mesh 地形(凸分解)以更忠实复现陡台阶。
|
||||
Reference in New Issue
Block a user