restruct project
This commit is contained in:
parent
91347be23e
commit
83100f7fa4
11
.gitignore
vendored
11
.gitignore
vendored
@ -1,7 +1,16 @@
|
|||||||
.venv/
|
.venv/
|
||||||
*.vscode/
|
*.vscode/
|
||||||
build/
|
build/
|
||||||
|
dist/
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
*__pycache__/
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
dataset/
|
dataset/
|
||||||
models/
|
models/
|
||||||
|
|
||||||
|
# 训练与回放产物不属于源码。已被 Git 跟踪的历史文件不会因本规则自动删除。
|
||||||
|
logs/
|
||||||
|
outputs/
|
||||||
|
*.pt
|
||||||
|
*.onnx
|
||||||
|
events.out.tfevents.*
|
||||||
|
|||||||
@ -1 +1,3 @@
|
|||||||
recursive-include source/engineai_lab/assets *
|
recursive-include source/engineai_lab/assets *
|
||||||
|
global-exclude *.pyc *.pyo
|
||||||
|
prune source/engineai_lab/assets/__pycache__
|
||||||
|
|||||||
55
README.md
55
README.md
@ -19,6 +19,9 @@ engineai-lab
|
|||||||
└── engineai_lab
|
└── engineai_lab
|
||||||
├── algorithms
|
├── algorithms
|
||||||
├── assets
|
├── assets
|
||||||
|
│ ├── gen2
|
||||||
|
│ │ ├── meshes
|
||||||
|
│ │ └── urdf
|
||||||
│ └── pm01
|
│ └── pm01
|
||||||
│ ├── meshes
|
│ ├── meshes
|
||||||
│ └── urdf
|
│ └── urdf
|
||||||
@ -26,6 +29,10 @@ engineai-lab
|
|||||||
├── tasks
|
├── tasks
|
||||||
│ └── velocity
|
│ └── velocity
|
||||||
│ ├── config
|
│ ├── config
|
||||||
|
│ │ ├── common
|
||||||
|
│ │ ├── gen2
|
||||||
|
│ │ │ ├── agents
|
||||||
|
│ │ │ └── stages
|
||||||
│ │ └── pm01
|
│ │ └── pm01
|
||||||
│ └── mdp
|
│ └── mdp
|
||||||
└── utils
|
└── utils
|
||||||
@ -58,7 +65,7 @@ Create and activate a new environment with Python 3.11:
|
|||||||
git clone https://github.com/isaac-sim/IsaacLab.git
|
git clone https://github.com/isaac-sim/IsaacLab.git
|
||||||
cd IsaacLab
|
cd IsaacLab
|
||||||
git checkout 4df6560e
|
git checkout 4df6560e
|
||||||
./isaaclab -i rsl_rl # Install rsl-rl dependency
|
./isaaclab.sh -i rsl_rl # Install rsl-rl dependency
|
||||||
```
|
```
|
||||||
|
|
||||||
We highly recommend using the main branch`(4df6560e)` of Isaac Lab, as it can support rsl-rl-lib >= 5.0 and Isaac Sim >= 5.0 .
|
We highly recommend using the main branch`(4df6560e)` of Isaac Lab, as it can support rsl-rl-lib >= 5.0 and Isaac Sim >= 5.0 .
|
||||||
@ -76,6 +83,11 @@ pip install -e .
|
|||||||
For a step-by-step Chinese guide to importing and training a new humanoid robot, see
|
For a step-by-step Chinese guide to importing and training a new humanoid robot, see
|
||||||
[EngineAI Lab 新人形机器人行走训练攻略](docs/humanoid_locomotion_onboarding_guide.md).
|
[EngineAI Lab 新人形机器人行走训练攻略](docs/humanoid_locomotion_onboarding_guide.md).
|
||||||
|
|
||||||
|
Gen2 的阶段划分、训练/续训/迁移命令和配置修改约定见
|
||||||
|
[Gen2 分阶段训练说明](source/engineai_lab/tasks/velocity/config/gen2/README.md);
|
||||||
|
URDF、mesh 与简化碰撞体约定见
|
||||||
|
[Gen2 资产说明](source/engineai_lab/assets/gen2/README.md)。
|
||||||
|
|
||||||
### Supported Robots
|
### Supported Robots
|
||||||
|
|
||||||
This repository currently supports the following environments from the EngineAI Robots family:
|
This repository currently supports the following environments from the EngineAI Robots family:
|
||||||
@ -84,6 +96,12 @@ This repository currently supports the following environments from the EngineAI
|
|||||||
|--------|--------|--------|
|
|--------|--------|--------|
|
||||||
PM01|`Flat-PM01-v0`|Basic flat-terrain locomotion
|
PM01|`Flat-PM01-v0`|Basic flat-terrain locomotion
|
||||||
PM01|`Flat-AMP-PM01-v0`|AMP-based motion imitation on flat terrain
|
PM01|`Flat-AMP-PM01-v0`|AMP-based motion imitation on flat terrain
|
||||||
|
Gen2|`Flat-Gen2-v0`|基础低速行走,包含站立指令采样
|
||||||
|
Gen2|`Flat-Gen2-Speed-v0`|平地速度与稳定性巩固
|
||||||
|
Gen2|`Flat-Gen2-Natural-v0`|自然对侧摆臂
|
||||||
|
Gen2|`Flat-Gen2-Fast-v0`|课程提升至 1.6 m/s
|
||||||
|
Gen2|`Flat-Gen2-Sprint-v0`|课程提升至 3.0 m/s
|
||||||
|
Gen2|`Flat-Gen2-NaturalRun-v0`|3.0 m/s 自然跑姿微调
|
||||||
|
|
||||||
*More robots and environments are coming soon!*
|
*More robots and environments are coming soon!*
|
||||||
|
|
||||||
@ -94,6 +112,39 @@ PM01|`Flat-AMP-PM01-v0`|AMP-based motion imitation on flat terrain
|
|||||||
python scripts/play.py --task=Flat-PM01-v0 --num_envs 128 --load_run <name>
|
python scripts/play.py --task=Flat-PM01-v0 --num_envs 128 --load_run <name>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Gen2 Staged Training
|
||||||
|
|
||||||
|
基础阶段从头训练:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/home/xtkuang/App/anaconda3/envs/engineai_lab/bin/python scripts/train.py \
|
||||||
|
--task Flat-Gen2-v0 \
|
||||||
|
--num_envs 4096 \
|
||||||
|
--max_iterations 1500 \
|
||||||
|
--run_name gen2_walk_v0 \
|
||||||
|
--device cuda:0 \
|
||||||
|
--rl_device cuda:0 \
|
||||||
|
--headless
|
||||||
|
```
|
||||||
|
|
||||||
|
跨阶段只迁移策略权重,例如 Fast -> Sprint:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/home/xtkuang/App/anaconda3/envs/engineai_lab/bin/python scripts/train.py \
|
||||||
|
--task Flat-Gen2-Sprint-v0 \
|
||||||
|
--num_envs 4096 \
|
||||||
|
--resume True \
|
||||||
|
--load_mode finetune \
|
||||||
|
--load_run '<fast_run_directory>' \
|
||||||
|
--checkpoint model_<iteration>.pt \
|
||||||
|
--run_name gen2_sprint_v1 \
|
||||||
|
--device cuda:0 \
|
||||||
|
--rl_device cuda:0 \
|
||||||
|
--headless
|
||||||
|
```
|
||||||
|
|
||||||
|
同一 Task 中断续训才使用 `--load_mode resume`。所有阶段和对应 Play Task 的完整表格见上面的 Gen2 分阶段训练说明。
|
||||||
|
|
||||||
### Evaluating a Policy
|
### Evaluating a Policy
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -142,7 +193,7 @@ If you have any questions about using this repository, we're here to help!
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
EngineAI-Lab is released under [BSD-3 License](LICENSE).
|
This checkout does not currently contain a `LICENSE` file, while its package metadata and the upstream README use different license labels. Add a license file and align the metadata before redistribution.
|
||||||
|
|
||||||
## Acknowledgement
|
## Acknowledgement
|
||||||
|
|
||||||
|
|||||||
@ -29,10 +29,11 @@
|
|||||||
|
|
||||||
| 模块 | 典型路径 | 职责 |
|
| 模块 | 典型路径 | 职责 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 机器人资产 | `source/gen2_lab/assets/` | URDF、mesh、简化碰撞体和审计结果 |
|
| 机器人资产 | `source/engineai_lab/assets/gen2/` | URDF、mesh 和简化碰撞体;审计结果写入 `outputs/` |
|
||||||
| 机器人配置 | `source/engineai_lab/robots/gen2.py` | q0、初始高度、关节顺序、执行器参数和资产加载 |
|
| 机器人配置 | `source/engineai_lab/robots/gen2.py` | q0、初始高度、关节顺序、执行器参数和资产加载 |
|
||||||
| 环境配置 | `source/engineai_lab/tasks/velocity/config/gen2/flat_env_cfg.py` | scene、action、observation、command、reward、reset、termination、curriculum |
|
| 环境共享配置 | `source/engineai_lab/tasks/velocity/config/gen2/common_env_cfg.py` | scene、action、observation、reset、termination 和 Play 公共设置 |
|
||||||
| PPO 配置 | `source/engineai_lab/tasks/velocity/config/gen2/agents/rsl_rl_ppo_cfg.py` | 网络、熵系数、迭代数和实验目录 |
|
| 分阶段环境 | `source/engineai_lab/tasks/velocity/config/gen2/stages/` | 每阶段独立的 reward、command、curriculum 和覆盖参数 |
|
||||||
|
| 分阶段 PPO | `source/engineai_lab/tasks/velocity/config/gen2/agents/` | 每阶段独立的迭代数、熵系数和学习率 |
|
||||||
| MDP 函数 | `source/engineai_lab/tasks/velocity/mdp/` | 自定义命令、观测、奖励和终止逻辑 |
|
| MDP 函数 | `source/engineai_lab/tasks/velocity/mdp/` | 自定义命令、观测、奖励和终止逻辑 |
|
||||||
| 任务注册 | `source/engineai_lab/tasks/velocity/config/gen2/__init__.py` | Gym task ID 到环境/PPO 配置的映射 |
|
| 任务注册 | `source/engineai_lab/tasks/velocity/config/gen2/__init__.py` | Gym task ID 到环境/PPO 配置的映射 |
|
||||||
| 训练入口 | `scripts/train.py` | 创建环境、加载 checkpoint、运行 RSL-RL |
|
| 训练入口 | `scripts/train.py` | 创建环境、加载 checkpoint、运行 RSL-RL |
|
||||||
@ -45,10 +46,17 @@
|
|||||||
source/engineai_lab/robots/<robot>.py
|
source/engineai_lab/robots/<robot>.py
|
||||||
source/engineai_lab/tasks/velocity/config/<robot>/
|
source/engineai_lab/tasks/velocity/config/<robot>/
|
||||||
├── __init__.py
|
├── __init__.py
|
||||||
├── flat_env_cfg.py
|
├── registry.py
|
||||||
|
├── common_env_cfg.py
|
||||||
|
├── stages/
|
||||||
|
│ ├── walk.py
|
||||||
|
│ ├── speed.py
|
||||||
|
│ └── natural.py
|
||||||
└── agents/
|
└── agents/
|
||||||
├── __init__.py
|
├── __init__.py
|
||||||
└── rsl_rl_ppo_cfg.py
|
├── walk_ppo_cfg.py
|
||||||
|
├── speed_ppo_cfg.py
|
||||||
|
└── natural_ppo_cfg.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## 3. 总体推进路线和阶段门
|
## 3. 总体推进路线和阶段门
|
||||||
@ -151,13 +159,13 @@ Gen2 实例中,PM01 参考腿长约 `0.82 m`,Gen2 有效腿长约 `0.786 m`
|
|||||||
可以把 Gen2 的静态检查脚本复制并参数化:
|
可以把 Gen2 的静态检查脚本复制并参数化:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/xtkuang/Projects/cmvr/RL/engineai_amp
|
cd /home/xtkuang/Projects/cmvr/RL/cmvr_ai_lab
|
||||||
|
|
||||||
/home/xtkuang/App/anaconda3/envs/engineai_lab/bin/python \
|
/home/xtkuang/App/anaconda3/envs/engineai_lab/bin/python \
|
||||||
scripts/gen2_check_rl_readiness.py
|
scripts/gen2_check_rl_readiness.py
|
||||||
```
|
```
|
||||||
|
|
||||||
该脚本检查质量、惯量、关节限位、碰撞重叠、脚底高度和 reset z。用于新机器人时,必须修改其中的 URDF、foot link、robot cfg 和高度常量路径。当前脚本仍按旧名称 `GEN2_BASE_HEIGHT_TARGET` 搜索高度;复制时应改为新机器人的 `ROBOT_PELVIS_HEIGHT_TARGET`/`ROBOT_TORSO_HEIGHT_TARGET`,任何 `UNKNOWN` 都不能当作通过。
|
该脚本检查质量、惯量、关节限位、碰撞重叠、脚底高度、reset z,以及 pelvis/torso 高度目标。用于新机器人时,必须修改其中的 URDF、foot link、robot cfg 和高度常量路径;任何 `unknown` 都会使检查失败,不能当作通过。
|
||||||
|
|
||||||
### 5.2 视觉 mesh 不适合直接作为训练碰撞体
|
### 5.2 视觉 mesh 不适合直接作为训练碰撞体
|
||||||
|
|
||||||
@ -530,10 +538,9 @@ gym.register(
|
|||||||
### 10.1 静态检查
|
### 10.1 静态检查
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
/home/xtkuang/App/anaconda3/envs/engineai_lab/bin/python -m py_compile \
|
/home/xtkuang/App/anaconda3/envs/engineai_lab/bin/python -m compileall \
|
||||||
source/engineai_lab/robots/<robot>.py \
|
source/engineai_lab/robots/<robot>.py \
|
||||||
source/engineai_lab/tasks/velocity/config/<robot>/flat_env_cfg.py \
|
source/engineai_lab/tasks/velocity/config/<robot>/
|
||||||
source/engineai_lab/tasks/velocity/config/<robot>/agents/rsl_rl_ppo_cfg.py
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 10.2 环境加载检查
|
### 10.2 环境加载检查
|
||||||
@ -761,7 +768,7 @@ Gen2 在这一步才把 `0.4 m/s` 固定命令下的实际速度从约 `0.23`
|
|||||||
下列 `<...>` 是需要替换的占位符,不能原样执行。
|
下列 `<...>` 是需要替换的占位符,不能原样执行。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/xtkuang/Projects/cmvr/RL/engineai_amp
|
cd /home/xtkuang/Projects/cmvr/RL/cmvr_ai_lab
|
||||||
|
|
||||||
/home/xtkuang/App/anaconda3/envs/engineai_lab/bin/python scripts/train.py \
|
/home/xtkuang/App/anaconda3/envs/engineai_lab/bin/python scripts/train.py \
|
||||||
--task Flat-<Robot>-v0 \
|
--task Flat-<Robot>-v0 \
|
||||||
@ -774,7 +781,9 @@ cd /home/xtkuang/Projects/cmvr/RL/engineai_amp
|
|||||||
--headless
|
--headless
|
||||||
```
|
```
|
||||||
|
|
||||||
### 14.2 从 checkpoint 续训
|
### 14.2 同一 Task 从 checkpoint 完整续训
|
||||||
|
|
||||||
|
同一任务中断后继续时使用 `resume`,它会恢复 optimizer、iteration 和 curriculum:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
/home/xtkuang/App/anaconda3/envs/engineai_lab/bin/python scripts/train.py \
|
/home/xtkuang/App/anaconda3/envs/engineai_lab/bin/python scripts/train.py \
|
||||||
@ -783,6 +792,7 @@ cd /home/xtkuang/Projects/cmvr/RL/engineai_amp
|
|||||||
--seed 42 \
|
--seed 42 \
|
||||||
--max_iterations <additional_iterations> \
|
--max_iterations <additional_iterations> \
|
||||||
--resume True \
|
--resume True \
|
||||||
|
--load_mode resume \
|
||||||
--load_run <previous_run_directory> \
|
--load_run <previous_run_directory> \
|
||||||
--checkpoint model_<iteration>.pt \
|
--checkpoint model_<iteration>.pt \
|
||||||
--run_name <new_run_name> \
|
--run_name <new_run_name> \
|
||||||
@ -793,7 +803,29 @@ cd /home/xtkuang/Projects/cmvr/RL/engineai_amp
|
|||||||
|
|
||||||
当前 CLI 的 `--resume` 使用布尔值参数,应写成 `--resume True`,不能只写一个裸 `--resume`。
|
当前 CLI 的 `--resume` 使用布尔值参数,应写成 `--resume True`,不能只写一个裸 `--resume`。
|
||||||
|
|
||||||
### 14.3 TensorBoard
|
### 14.3 跨阶段迁移 checkpoint
|
||||||
|
|
||||||
|
从一个 Task 进入下一阶段时必须使用 `finetune`。它只加载 actor/critic,使用新阶段的 optimizer 和 curriculum:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/home/xtkuang/App/anaconda3/envs/engineai_lab/bin/python scripts/train.py \
|
||||||
|
--task Flat-<Robot>-<NextStage>-v0 \
|
||||||
|
--num_envs 4096 \
|
||||||
|
--seed 42 \
|
||||||
|
--max_iterations <iterations> \
|
||||||
|
--resume True \
|
||||||
|
--load_mode finetune \
|
||||||
|
--load_run <previous_stage_run_directory> \
|
||||||
|
--checkpoint model_<iteration>.pt \
|
||||||
|
--run_name <next_stage_run_name> \
|
||||||
|
--device cuda:0 \
|
||||||
|
--rl_device cuda:0 \
|
||||||
|
--headless
|
||||||
|
```
|
||||||
|
|
||||||
|
checkpoint 会记录原 Task ID;跨 Task 使用完整 `resume` 会被训练入口拒绝,这是为了防止错误恢复旧 curriculum。
|
||||||
|
|
||||||
|
### 14.4 TensorBoard
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
/home/xtkuang/App/anaconda3/envs/engineai_lab/bin/tensorboard \
|
/home/xtkuang/App/anaconda3/envs/engineai_lab/bin/tensorboard \
|
||||||
@ -1136,8 +1168,10 @@ fixed-speed benchmark
|
|||||||
当前仓库中可以直接参考:
|
当前仓库中可以直接参考:
|
||||||
|
|
||||||
- [`robots/gen2.py`](../source/engineai_lab/robots/gen2.py)
|
- [`robots/gen2.py`](../source/engineai_lab/robots/gen2.py)
|
||||||
- [`config/gen2/flat_env_cfg.py`](../source/engineai_lab/tasks/velocity/config/gen2/flat_env_cfg.py)
|
- [`config/gen2/README.md`](../source/engineai_lab/tasks/velocity/config/gen2/README.md)
|
||||||
- [`config/gen2/agents/rsl_rl_ppo_cfg.py`](../source/engineai_lab/tasks/velocity/config/gen2/agents/rsl_rl_ppo_cfg.py)
|
- [`config/gen2/common_env_cfg.py`](../source/engineai_lab/tasks/velocity/config/gen2/common_env_cfg.py)
|
||||||
|
- [`config/gen2/stages/`](../source/engineai_lab/tasks/velocity/config/gen2/stages)
|
||||||
|
- [`config/gen2/agents/`](../source/engineai_lab/tasks/velocity/config/gen2/agents)
|
||||||
- [`mdp/commands.py`](../source/engineai_lab/tasks/velocity/mdp/commands.py)
|
- [`mdp/commands.py`](../source/engineai_lab/tasks/velocity/mdp/commands.py)
|
||||||
- [`mdp/rewards.py`](../source/engineai_lab/tasks/velocity/mdp/rewards.py)
|
- [`mdp/rewards.py`](../source/engineai_lab/tasks/velocity/mdp/rewards.py)
|
||||||
- [`scripts/train.py`](../scripts/train.py)
|
- [`scripts/train.py`](../scripts/train.py)
|
||||||
|
|||||||
@ -24,6 +24,15 @@ def add_rsl_rl_args(parser: argparse.ArgumentParser):
|
|||||||
arg_group.add_argument("--resume", type=bool, default=None, help="Whether to resume from a checkpoint.")
|
arg_group.add_argument("--resume", type=bool, default=None, help="Whether to resume from a checkpoint.")
|
||||||
arg_group.add_argument("--load_run", type=str, default=None, help="Name of the run folder to resume from.")
|
arg_group.add_argument("--load_run", type=str, default=None, help="Name of the run folder to resume from.")
|
||||||
arg_group.add_argument("--checkpoint", type=str, default=None, help="Checkpoint file to resume from.")
|
arg_group.add_argument("--checkpoint", type=str, default=None, help="Checkpoint file to resume from.")
|
||||||
|
arg_group.add_argument(
|
||||||
|
"--load_mode",
|
||||||
|
choices=("resume", "finetune"),
|
||||||
|
default="resume",
|
||||||
|
help=(
|
||||||
|
"resume restores optimizer, iteration, and curriculum state; "
|
||||||
|
"finetune loads actor/critic weights into a fresh optimizer and curriculum."
|
||||||
|
),
|
||||||
|
)
|
||||||
# -- logger arguments
|
# -- logger arguments
|
||||||
arg_group.add_argument(
|
arg_group.add_argument(
|
||||||
"--logger", type=str, default=None, choices={"wandb", "tensorboard", "neptune"}, help="Logger module to use."
|
"--logger", type=str, default=None, choices={"wandb", "tensorboard", "neptune"}, help="Logger module to use."
|
||||||
|
|||||||
@ -14,9 +14,26 @@ import numpy as np
|
|||||||
|
|
||||||
|
|
||||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
URDF_PATH = REPO_ROOT / "source" / "gen2_lab" / "assets" / "robot_simplified_collision.urdf"
|
URDF_PATH = (
|
||||||
|
REPO_ROOT
|
||||||
|
/ "source"
|
||||||
|
/ "engineai_lab"
|
||||||
|
/ "assets"
|
||||||
|
/ "gen2"
|
||||||
|
/ "urdf"
|
||||||
|
/ "robot_simplified_collision.urdf"
|
||||||
|
)
|
||||||
GEN2_ROBOT_CFG = REPO_ROOT / "source" / "engineai_lab" / "robots" / "gen2.py"
|
GEN2_ROBOT_CFG = REPO_ROOT / "source" / "engineai_lab" / "robots" / "gen2.py"
|
||||||
GEN2_ENV_CFG = REPO_ROOT / "source" / "engineai_lab" / "tasks" / "velocity" / "config" / "gen2" / "flat_env_cfg.py"
|
GEN2_ENV_CFG = (
|
||||||
|
REPO_ROOT
|
||||||
|
/ "source"
|
||||||
|
/ "engineai_lab"
|
||||||
|
/ "tasks"
|
||||||
|
/ "velocity"
|
||||||
|
/ "config"
|
||||||
|
/ "gen2"
|
||||||
|
/ "common_env_cfg.py"
|
||||||
|
)
|
||||||
FOOT_LINKS = ("left_leg_link_6", "right_leg_link_6")
|
FOOT_LINKS = ("left_leg_link_6", "right_leg_link_6")
|
||||||
|
|
||||||
|
|
||||||
@ -91,14 +108,16 @@ def _load_base_init_z() -> float | None:
|
|||||||
return float(match.group(1).split(",")[2].strip())
|
return float(match.group(1).split(",")[2].strip())
|
||||||
|
|
||||||
|
|
||||||
def _load_base_height_target() -> float | None:
|
def _load_height_targets() -> dict[str, float]:
|
||||||
if not GEN2_ENV_CFG.exists():
|
if not GEN2_ENV_CFG.exists():
|
||||||
return None
|
return {}
|
||||||
text = GEN2_ENV_CFG.read_text(encoding="utf-8")
|
text = GEN2_ENV_CFG.read_text(encoding="utf-8")
|
||||||
match = re.search(r"GEN2_BASE_HEIGHT_TARGET\s*=\s*([0-9.]+)", text)
|
targets = {}
|
||||||
if match is None:
|
for name in ("GEN2_PELVIS_HEIGHT_TARGET", "GEN2_TORSO_HEIGHT_TARGET"):
|
||||||
return None
|
match = re.search(rf"{name}\s*=\s*([0-9.]+)", text)
|
||||||
return float(match.group(1))
|
if match is not None:
|
||||||
|
targets[name] = float(match.group(1))
|
||||||
|
return targets
|
||||||
|
|
||||||
|
|
||||||
def _link_tree(root: ET.Element):
|
def _link_tree(root: ET.Element):
|
||||||
@ -282,7 +301,7 @@ def main() -> None:
|
|||||||
non_adjacent_overlaps.sort(reverse=True)
|
non_adjacent_overlaps.sort(reverse=True)
|
||||||
|
|
||||||
base_z = _load_base_init_z()
|
base_z = _load_base_init_z()
|
||||||
base_target = _load_base_height_target()
|
height_targets = _load_height_targets()
|
||||||
foot_min_z = {box.name: float(box.vertices[:, 2].min()) for box in boxes if box.name in FOOT_LINKS}
|
foot_min_z = {box.name: float(box.vertices[:, 2].min()) for box in boxes if box.name in FOOT_LINKS}
|
||||||
lowest_foot_z = min(foot_min_z.values())
|
lowest_foot_z = min(foot_min_z.values())
|
||||||
suggested_base_z = -lowest_foot_z + 0.01
|
suggested_base_z = -lowest_foot_z + 0.01
|
||||||
@ -307,10 +326,21 @@ def main() -> None:
|
|||||||
world_min = min_z + base_z if base_z is not None else float("nan")
|
world_min = min_z + base_z if base_z is not None else float("nan")
|
||||||
print(f" {name}: relative={min_z:.6f} world_at_init={world_min:.6f}")
|
print(f" {name}: relative={min_z:.6f} world_at_init={world_min:.6f}")
|
||||||
print(f"base_init_z={base_z:.6f}" if base_z is not None else "base_init_z=unknown")
|
print(f"base_init_z={base_z:.6f}" if base_z is not None else "base_init_z=unknown")
|
||||||
print(f"base_height_target={base_target:.6f}" if base_target is not None else "base_height_target=unknown")
|
for target_name in ("GEN2_PELVIS_HEIGHT_TARGET", "GEN2_TORSO_HEIGHT_TARGET"):
|
||||||
|
target = height_targets.get(target_name)
|
||||||
|
print(f"{target_name.lower()}={target:.6f}" if target is not None else f"{target_name.lower()}=unknown")
|
||||||
print(f"suggested_base_z_for_1cm_foot_clearance={suggested_base_z:.6f}")
|
print(f"suggested_base_z_for_1cm_foot_clearance={suggested_base_z:.6f}")
|
||||||
|
|
||||||
hard_fail = bool(missing_inertial or bad_mass or bad_inertia or missing_limits or bad_limits or non_adjacent_overlaps)
|
missing_height_targets = len(height_targets) != 2
|
||||||
|
hard_fail = bool(
|
||||||
|
missing_inertial
|
||||||
|
or bad_mass
|
||||||
|
or bad_inertia
|
||||||
|
or missing_limits
|
||||||
|
or bad_limits
|
||||||
|
or non_adjacent_overlaps
|
||||||
|
or missing_height_targets
|
||||||
|
)
|
||||||
ground_penetration = base_z is not None and lowest_foot_z + base_z < -0.005
|
ground_penetration = base_z is not None and lowest_foot_z + base_z < -0.005
|
||||||
if hard_fail:
|
if hard_fail:
|
||||||
print("RESULT=FAIL")
|
print("RESULT=FAIL")
|
||||||
|
|||||||
@ -3,10 +3,10 @@
|
|||||||
|
|
||||||
The script does not modify the original robot.urdf. It writes:
|
The script does not modify the original robot.urdf. It writes:
|
||||||
|
|
||||||
- source/gen2_lab/assets/collision_simplified/*.stl
|
- source/engineai_lab/assets/gen2/meshes/collision_simplified/*.stl
|
||||||
- source/gen2_lab/assets/robot_simplified_collision.urdf
|
- source/engineai_lab/assets/gen2/urdf/robot_simplified_collision.urdf
|
||||||
- source/gen2_lab/assets/robot_simplified_collision_mesh.urdf
|
- source/engineai_lab/assets/gen2/urdf/robot_simplified_collision_mesh.urdf
|
||||||
- source/gen2_lab/assets/collision_simplified_audit.csv
|
- outputs/gen2_asset_audit/collision_simplified_audit.csv
|
||||||
|
|
||||||
The recommended URDF is robot_simplified_collision.urdf. It uses URDF box
|
The recommended URDF is robot_simplified_collision.urdf. It uses URDF box
|
||||||
primitives for collision. The generated STL files and mesh URDF are useful for
|
primitives for collision. The generated STL files and mesh URDF are useful for
|
||||||
@ -24,12 +24,14 @@ from xml.dom import minidom
|
|||||||
|
|
||||||
|
|
||||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
ASSET_DIR = REPO_ROOT / "source" / "gen2_lab" / "assets"
|
ASSET_DIR = REPO_ROOT / "source" / "engineai_lab" / "assets" / "gen2"
|
||||||
INPUT_URDF = ASSET_DIR / "robot.urdf"
|
URDF_DIR = ASSET_DIR / "urdf"
|
||||||
OUTPUT_DIR = ASSET_DIR / "collision_simplified"
|
MESH_DIR = ASSET_DIR / "meshes"
|
||||||
OUTPUT_PRIMITIVE_URDF = ASSET_DIR / "robot_simplified_collision.urdf"
|
INPUT_URDF = URDF_DIR / "robot.urdf"
|
||||||
OUTPUT_MESH_URDF = ASSET_DIR / "robot_simplified_collision_mesh.urdf"
|
OUTPUT_DIR = MESH_DIR / "collision_simplified"
|
||||||
AUDIT_CSV = ASSET_DIR / "collision_simplified_audit.csv"
|
OUTPUT_PRIMITIVE_URDF = URDF_DIR / "robot_simplified_collision.urdf"
|
||||||
|
OUTPUT_MESH_URDF = URDF_DIR / "robot_simplified_collision_mesh.urdf"
|
||||||
|
AUDIT_CSV = REPO_ROOT / "outputs" / "gen2_asset_audit" / "collision_simplified_audit.csv"
|
||||||
|
|
||||||
|
|
||||||
def _parse_floats(text: str | None, default: tuple[float, float, float]) -> tuple[float, float, float]:
|
def _parse_floats(text: str | None, default: tuple[float, float, float]) -> tuple[float, float, float]:
|
||||||
@ -235,7 +237,9 @@ def main() -> None:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
mesh_file = mesh.attrib["filename"]
|
mesh_file = mesh.attrib["filename"]
|
||||||
extent, center, tri_count = _binary_stl_bbox(ASSET_DIR / mesh_file)
|
# URDF 中只允许相对路径;从 URDF 所在目录解析,避免依赖当前工作目录。
|
||||||
|
source_mesh_path = (INPUT_URDF.parent / mesh_file).resolve()
|
||||||
|
extent, center, tri_count = _binary_stl_bbox(source_mesh_path)
|
||||||
proxy_center, size, scale = _proxy_box(name, extent, center)
|
proxy_center, size, scale = _proxy_box(name, extent, center)
|
||||||
|
|
||||||
original_xyz = _parse_floats(origin.attrib.get("xyz"), (0.0, 0.0, 0.0))
|
original_xyz = _parse_floats(origin.attrib.get("xyz"), (0.0, 0.0, 0.0))
|
||||||
@ -244,7 +248,7 @@ def main() -> None:
|
|||||||
proxy_origin_xyz = tuple(original_xyz[i] + rotated_center[i] for i in range(3))
|
proxy_origin_xyz = tuple(original_xyz[i] + rotated_center[i] for i in range(3))
|
||||||
|
|
||||||
stl_name = f"{name}_box_collision.stl"
|
stl_name = f"{name}_box_collision.stl"
|
||||||
stl_rel_path = f"collision_simplified/{stl_name}"
|
stl_rel_path = f"../meshes/collision_simplified/{stl_name}"
|
||||||
_write_binary_stl(OUTPUT_DIR / stl_name, _box_triangles(proxy_center, size))
|
_write_binary_stl(OUTPUT_DIR / stl_name, _box_triangles(proxy_center, size))
|
||||||
|
|
||||||
_replace_collision_with_box(collision, proxy_origin_xyz, original_rpy, size)
|
_replace_collision_with_box(collision, proxy_origin_xyz, original_rpy, size)
|
||||||
@ -277,6 +281,7 @@ def main() -> None:
|
|||||||
_write_pretty_xml(primitive_tree, OUTPUT_PRIMITIVE_URDF)
|
_write_pretty_xml(primitive_tree, OUTPUT_PRIMITIVE_URDF)
|
||||||
_write_pretty_xml(mesh_tree, OUTPUT_MESH_URDF)
|
_write_pretty_xml(mesh_tree, OUTPUT_MESH_URDF)
|
||||||
|
|
||||||
|
AUDIT_CSV.parent.mkdir(parents=True, exist_ok=True)
|
||||||
with AUDIT_CSV.open("w", newline="", encoding="utf-8") as file:
|
with AUDIT_CSV.open("w", newline="", encoding="utf-8") as file:
|
||||||
writer = csv.DictWriter(file, fieldnames=list(audit_rows[0].keys()))
|
writer = csv.DictWriter(file, fieldnames=list(audit_rows[0].keys()))
|
||||||
writer.writeheader()
|
writer.writeheader()
|
||||||
|
|||||||
77
scripts/gen2_validate_registry.py
Normal file
77
scripts/gen2_validate_registry.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""启动 Isaac Sim 后验证全部 Gen2 Task 注册和 Train/Play 契约。"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import importlib
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
SOURCE_ROOT = REPO_ROOT / "source"
|
||||||
|
if str(SOURCE_ROOT) not in sys.path:
|
||||||
|
sys.path.insert(0, str(SOURCE_ROOT))
|
||||||
|
|
||||||
|
from isaaclab.app import AppLauncher
|
||||||
|
|
||||||
|
|
||||||
|
# Isaac Lab 配置模块依赖 pxr,必须先启动 App,再导入 engineai_lab.tasks。
|
||||||
|
app_launcher = AppLauncher(headless=True)
|
||||||
|
simulation_app = app_launcher.app
|
||||||
|
|
||||||
|
|
||||||
|
def _load_class(entry_point: str):
|
||||||
|
module_name, class_name = entry_point.rsplit(":", 1)
|
||||||
|
return getattr(importlib.import_module(module_name), class_name)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
import gymnasium as gym
|
||||||
|
|
||||||
|
import engineai_lab.tasks # noqa: F401
|
||||||
|
from engineai_lab.tasks.velocity.config.gen2.agents.rsl_rl_ppo_cfg import (
|
||||||
|
Gen2FastPPORunnerCfg as LegacyFastRunnerCfg,
|
||||||
|
)
|
||||||
|
from engineai_lab.tasks.velocity.config.gen2.agents.fast_ppo_cfg import Gen2FastPPORunnerCfg
|
||||||
|
from engineai_lab.tasks.velocity.config.gen2.flat_env_cfg import Gen2FastEnvCfg as LegacyFastEnvCfg
|
||||||
|
from engineai_lab.tasks.velocity.config.gen2.registry import GEN2_TASK_SPECS
|
||||||
|
from engineai_lab.tasks.velocity.config.gen2.stages.fast import Gen2FastEnvCfg
|
||||||
|
|
||||||
|
# 旧模块现在是兼容层;类对象必须与新阶段模块完全相同。
|
||||||
|
assert LegacyFastEnvCfg is Gen2FastEnvCfg
|
||||||
|
assert LegacyFastRunnerCfg is Gen2FastPPORunnerCfg
|
||||||
|
|
||||||
|
configs = {}
|
||||||
|
for task_id, _, _ in GEN2_TASK_SPECS:
|
||||||
|
spec = gym.spec(task_id)
|
||||||
|
env_cfg_cls = _load_class(spec.kwargs["env_cfg_entry_point"])
|
||||||
|
runner_cfg_cls = _load_class(spec.kwargs["rsl_rl_cfg_entry_point"])
|
||||||
|
env_cfg = env_cfg_cls()
|
||||||
|
runner_cfg = runner_cfg_cls()
|
||||||
|
configs[task_id] = env_cfg
|
||||||
|
|
||||||
|
assert runner_cfg.experiment_name == "velocity_flat_terrain_gen2"
|
||||||
|
assert len(env_cfg.actions.joint_pos.joint_names) == 28
|
||||||
|
assert env_cfg.observations.policy.joint_pos.history_length == 15
|
||||||
|
print(f"OK {task_id}: {env_cfg_cls.__module__}.{env_cfg_cls.__name__}", flush=True)
|
||||||
|
|
||||||
|
train_play_pairs = (
|
||||||
|
("Flat-Gen2-v0", "Flat-Gen2-Play-v0"),
|
||||||
|
("Flat-Gen2-Speed-v0", "Flat-Gen2-Speed-Play-v0"),
|
||||||
|
("Flat-Gen2-Natural-v0", "Flat-Gen2-Natural-Play-v0"),
|
||||||
|
("Flat-Gen2-Fast-v0", "Flat-Gen2-Fast-Play-v0"),
|
||||||
|
("Flat-Gen2-Sprint-v0", "Flat-Gen2-Sprint-Play-v0"),
|
||||||
|
("Flat-Gen2-NaturalRun-v0", "Flat-Gen2-NaturalRun-Play-v0"),
|
||||||
|
)
|
||||||
|
for train_id, play_id in train_play_pairs:
|
||||||
|
train_cfg = configs[train_id]
|
||||||
|
play_cfg = configs[play_id]
|
||||||
|
assert train_cfg.actions.joint_pos.joint_names == play_cfg.actions.joint_pos.joint_names
|
||||||
|
assert train_cfg.actions.joint_pos.scale == play_cfg.actions.joint_pos.scale
|
||||||
|
|
||||||
|
print(f"RESULT=PASS tasks={len(configs)} train_play_pairs={len(train_play_pairs)}", flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@ -19,8 +19,17 @@ from mpl_toolkits.mplot3d.art3d import Poly3DCollection
|
|||||||
|
|
||||||
|
|
||||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
DEFAULT_URDF = REPO_ROOT / "source" / "gen2_lab" / "assets" / "robot_simplified_collision.urdf"
|
DEFAULT_URDF = (
|
||||||
DEFAULT_OUTPUT = REPO_ROOT / "source" / "gen2_lab" / "assets" / "gen2_collision_visualization.png"
|
REPO_ROOT
|
||||||
|
/ "source"
|
||||||
|
/ "engineai_lab"
|
||||||
|
/ "assets"
|
||||||
|
/ "gen2"
|
||||||
|
/ "urdf"
|
||||||
|
/ "robot_simplified_collision.urdf"
|
||||||
|
)
|
||||||
|
# 生成图属于检查产物,不写回运行时资产包。
|
||||||
|
DEFAULT_OUTPUT = REPO_ROOT / "outputs" / "gen2_asset_audit" / "gen2_collision_visualization.png"
|
||||||
GEN2_CFG = REPO_ROOT / "source" / "engineai_lab" / "robots" / "gen2.py"
|
GEN2_CFG = REPO_ROOT / "source" / "engineai_lab" / "robots" / "gen2.py"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -11,10 +11,11 @@ from isaaclab.app import AppLauncher
|
|||||||
# local imports
|
# local imports
|
||||||
import cli_args # isort: skip
|
import cli_args # isort: skip
|
||||||
|
|
||||||
# ensure repository root is on Python path for Hydra registry imports
|
# Prefer this checkout over any other editable ``engineai_lab`` installation.
|
||||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
if str(REPO_ROOT) not in sys.path:
|
SOURCE_ROOT = REPO_ROOT / "source"
|
||||||
sys.path.append(str(REPO_ROOT))
|
if str(SOURCE_ROOT) not in sys.path:
|
||||||
|
sys.path.insert(0, str(SOURCE_ROOT))
|
||||||
|
|
||||||
# add argparse arguments
|
# add argparse arguments
|
||||||
parser = argparse.ArgumentParser(description="Play an RSL-RL policy checkpoint.")
|
parser = argparse.ArgumentParser(description="Play an RSL-RL policy checkpoint.")
|
||||||
@ -171,6 +172,13 @@ def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agen
|
|||||||
# set the log directory for the environment (works for all environment types)
|
# set the log directory for the environment (works for all environment types)
|
||||||
env_cfg.log_dir = log_dir
|
env_cfg.log_dir = log_dir
|
||||||
|
|
||||||
|
# Fixed/keyboard replay already applies its own step or ramp controller.
|
||||||
|
# Disable the training-side ramp to avoid applying acceleration limits twice.
|
||||||
|
if args_cli.command_source != "random" and hasattr(
|
||||||
|
env_cfg.commands.base_velocity, "command_ramp_rates"
|
||||||
|
):
|
||||||
|
env_cfg.commands.base_velocity.command_ramp_rates = None
|
||||||
|
|
||||||
# create isaac environment
|
# create isaac environment
|
||||||
env = gym.make(args_cli.task, cfg=env_cfg, render_mode=None)
|
env = gym.make(args_cli.task, cfg=env_cfg, render_mode=None)
|
||||||
|
|
||||||
|
|||||||
@ -11,10 +11,11 @@ from isaaclab.app import AppLauncher
|
|||||||
# local imports
|
# local imports
|
||||||
import cli_args # isort: skip
|
import cli_args # isort: skip
|
||||||
|
|
||||||
# ensure repository root is on the Python path for Hydra registry imports
|
# Prefer this checkout over any other editable ``engineai_lab`` installation.
|
||||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
if str(REPO_ROOT) not in sys.path:
|
SOURCE_ROOT = REPO_ROOT / "source"
|
||||||
sys.path.append(str(REPO_ROOT))
|
if str(SOURCE_ROOT) not in sys.path:
|
||||||
|
sys.path.insert(0, str(SOURCE_ROOT))
|
||||||
|
|
||||||
# add argparse arguments
|
# add argparse arguments
|
||||||
parser = argparse.ArgumentParser(description="Train an RL agent with RSL-RL.")
|
parser = argparse.ArgumentParser(description="Train an RL agent with RSL-RL.")
|
||||||
@ -70,6 +71,49 @@ torch.backends.cudnn.deterministic = False
|
|||||||
torch.backends.cudnn.benchmark = False
|
torch.backends.cudnn.benchmark = False
|
||||||
|
|
||||||
|
|
||||||
|
def _restore_training_progress(env, runner, agent_cfg, checkpoint_infos):
|
||||||
|
"""Restore environment-side curriculum state after a full RSL-RL resume."""
|
||||||
|
base_env = env.unwrapped
|
||||||
|
engineai_info = checkpoint_infos.get("engineai_lab", {}) if isinstance(checkpoint_infos, dict) else {}
|
||||||
|
saved_task = engineai_info.get("task")
|
||||||
|
if saved_task is not None and saved_task != args_cli.task:
|
||||||
|
raise ValueError(
|
||||||
|
f"Checkpoint belongs to task {saved_task!r}, not {args_cli.task!r}. "
|
||||||
|
"Use --load_mode finetune for cross-task weight transfer."
|
||||||
|
)
|
||||||
|
|
||||||
|
if "common_step_counter" in engineai_info:
|
||||||
|
common_step_counter = int(engineai_info["common_step_counter"])
|
||||||
|
state_source = "checkpoint metadata"
|
||||||
|
else:
|
||||||
|
common_step_counter = (runner.current_learning_iteration + 1) * agent_cfg.num_steps_per_env
|
||||||
|
state_source = "legacy checkpoint iteration estimate"
|
||||||
|
|
||||||
|
base_env.common_step_counter = common_step_counter
|
||||||
|
base_env.curriculum_manager.compute(env_ids=None)
|
||||||
|
base_env.command_manager.reset(env_ids=None)
|
||||||
|
print(f"[INFO] Restored common_step_counter={common_step_counter} from {state_source}.")
|
||||||
|
command_term = base_env.command_manager.get_term("base_velocity")
|
||||||
|
if hasattr(command_term.cfg.ranges, "lin_vel_x"):
|
||||||
|
print(f"[INFO] Restored forward command range: {command_term.cfg.ranges.lin_vel_x}")
|
||||||
|
|
||||||
|
|
||||||
|
def _attach_training_state_to_checkpoints(env, runner):
|
||||||
|
"""Make RSL-RL's periodic saves include the environment curriculum counter."""
|
||||||
|
base_env = env.unwrapped
|
||||||
|
original_save = runner.save
|
||||||
|
|
||||||
|
def save_with_training_state(path, infos=None):
|
||||||
|
checkpoint_infos = dict(infos) if isinstance(infos, dict) else {}
|
||||||
|
checkpoint_infos["engineai_lab"] = {
|
||||||
|
"task": args_cli.task,
|
||||||
|
"common_step_counter": int(base_env.common_step_counter),
|
||||||
|
}
|
||||||
|
original_save(path, infos=checkpoint_infos)
|
||||||
|
|
||||||
|
runner.save = save_with_training_state
|
||||||
|
|
||||||
|
|
||||||
@hydra_task_config(args_cli.task, "rsl_rl_cfg_entry_point")
|
@hydra_task_config(args_cli.task, "rsl_rl_cfg_entry_point")
|
||||||
def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agent_cfg: RslRlOnPolicyRunnerCfg):
|
def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agent_cfg: RslRlOnPolicyRunnerCfg):
|
||||||
"""Train with RSL-RL agent."""
|
"""Train with RSL-RL agent."""
|
||||||
@ -133,8 +177,25 @@ def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agen
|
|||||||
# get path to previous checkpoint
|
# get path to previous checkpoint
|
||||||
resume_path = get_checkpoint_path(log_root_path, agent_cfg.load_run, agent_cfg.load_checkpoint)
|
resume_path = get_checkpoint_path(log_root_path, agent_cfg.load_run, agent_cfg.load_checkpoint)
|
||||||
print(f"[INFO]: Loading model checkpoint from: {resume_path}")
|
print(f"[INFO]: Loading model checkpoint from: {resume_path}")
|
||||||
# load previously trained model
|
if args_cli.load_mode == "finetune":
|
||||||
runner.load(resume_path)
|
runner.load(
|
||||||
|
resume_path,
|
||||||
|
load_cfg={
|
||||||
|
"actor": True,
|
||||||
|
"critic": True,
|
||||||
|
"optimizer": False,
|
||||||
|
"iteration": False,
|
||||||
|
"rnd": False,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
print("[INFO] Loaded actor/critic weights with a fresh optimizer and curriculum.")
|
||||||
|
else:
|
||||||
|
checkpoint_infos = runner.load(resume_path)
|
||||||
|
_restore_training_progress(env, runner, agent_cfg, checkpoint_infos)
|
||||||
|
|
||||||
|
print(f"[INFO] Optimizer learning rate after load: {runner.alg.learning_rate:.6g}")
|
||||||
|
|
||||||
|
_attach_training_state_to_checkpoints(env, runner)
|
||||||
|
|
||||||
# dump the configuration into log-directory
|
# dump the configuration into log-directory
|
||||||
dump_yaml(os.path.join(log_dir, "params", "env.yaml"), env_cfg)
|
dump_yaml(os.path.join(log_dir, "params", "env.yaml"), env_cfg)
|
||||||
|
|||||||
13
setup.py
13
setup.py
@ -30,7 +30,18 @@ setup(
|
|||||||
keywords=EXTENSION_TOML_DATA["package"]["keywords"],
|
keywords=EXTENSION_TOML_DATA["package"]["keywords"],
|
||||||
install_requires=INSTALL_REQUIRES,
|
install_requires=INSTALL_REQUIRES,
|
||||||
license="MIT",
|
license="MIT",
|
||||||
include_package_data=True,
|
# 运行时资产显式列出,避免 setuptools 把 mesh/urdf 目录误判为 namespace package。
|
||||||
|
include_package_data=False,
|
||||||
|
package_data={
|
||||||
|
"engineai_lab.assets": [
|
||||||
|
"pm01/meshes/*",
|
||||||
|
"pm01/urdf/*",
|
||||||
|
"gen2/README.md",
|
||||||
|
"gen2/urdf/*",
|
||||||
|
"gen2/meshes/*",
|
||||||
|
"gen2/meshes/collision_simplified/*",
|
||||||
|
]
|
||||||
|
},
|
||||||
python_requires=">=3.10",
|
python_requires=">=3.10",
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Natural Language :: English",
|
"Natural Language :: English",
|
||||||
|
|||||||
@ -1,4 +1,28 @@
|
|||||||
import os
|
"""运行时机器人资产路径。
|
||||||
|
|
||||||
# Conveniences to other module directories via relative paths
|
所有会被 Python 包加载的资产都必须位于 ``engineai_lab/assets`` 内,
|
||||||
ASSET_DIR = os.path.abspath(os.path.dirname(__file__))
|
这样 editable 安装和 wheel 安装使用相同的路径规则。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ASSET_ROOT = Path(__file__).resolve().parent
|
||||||
|
# 保留 PM01 现有的字符串拼接接口。
|
||||||
|
ASSET_DIR = str(ASSET_ROOT)
|
||||||
|
|
||||||
|
GEN2_ASSET_DIR = ASSET_ROOT / "gen2"
|
||||||
|
GEN2_URDF_DIR = GEN2_ASSET_DIR / "urdf"
|
||||||
|
GEN2_ORIGINAL_URDF_PATH = GEN2_URDF_DIR / "robot.urdf"
|
||||||
|
GEN2_SIMPLIFIED_COLLISION_URDF_PATH = GEN2_URDF_DIR / "robot_simplified_collision.urdf"
|
||||||
|
GEN2_SIMPLIFIED_COLLISION_MESH_URDF_PATH = GEN2_URDF_DIR / "robot_simplified_collision_mesh.urdf"
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"ASSET_DIR",
|
||||||
|
"ASSET_ROOT",
|
||||||
|
"GEN2_ASSET_DIR",
|
||||||
|
"GEN2_URDF_DIR",
|
||||||
|
"GEN2_ORIGINAL_URDF_PATH",
|
||||||
|
"GEN2_SIMPLIFIED_COLLISION_URDF_PATH",
|
||||||
|
"GEN2_SIMPLIFIED_COLLISION_MESH_URDF_PATH",
|
||||||
|
]
|
||||||
|
|||||||
70
source/engineai_lab/assets/gen2/README.md
Normal file
70
source/engineai_lab/assets/gen2/README.md
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# Gen2 运行时资产
|
||||||
|
|
||||||
|
本目录是 `engineai_lab` Python 包内唯一的 Gen2 运行时资产位置。这样 editable 安装、wheel 安装和从其他工作目录启动时都会得到同一份 URDF 与 mesh。
|
||||||
|
|
||||||
|
```text
|
||||||
|
gen2/
|
||||||
|
├── README.md
|
||||||
|
├── urdf/
|
||||||
|
│ ├── robot.urdf
|
||||||
|
│ ├── robot_simplified_collision.urdf
|
||||||
|
│ └── robot_simplified_collision_mesh.urdf
|
||||||
|
└── meshes/
|
||||||
|
├── *_visual.stl
|
||||||
|
├── *_collision.stl
|
||||||
|
└── collision_simplified/
|
||||||
|
└── *_box_collision.stl
|
||||||
|
```
|
||||||
|
|
||||||
|
## 三份 URDF 的职责
|
||||||
|
|
||||||
|
| 文件 | 用途 | 是否用于训练 |
|
||||||
|
|---|---|---|
|
||||||
|
| `robot.urdf` | 原始高精度 visual/collision,作为生成源和人工核对基准 | 否 |
|
||||||
|
| `robot_simplified_collision.urdf` | visual mesh + URDF box collision,接触更稳定、计算更轻 | **是,唯一默认训练资产** |
|
||||||
|
| `robot_simplified_collision_mesh.urdf` | visual mesh + 简化 box STL,便于外部工具叠加检查 | 否 |
|
||||||
|
|
||||||
|
`source/engineai_lab/robots/gen2.py` 明确加载 `robot_simplified_collision.urdf`。文件缺失时会直接报错,不会静默回退到原始高精度碰撞体,避免不同安装环境得到不同训练物理。
|
||||||
|
|
||||||
|
URDF 内 mesh 使用相对路径:
|
||||||
|
|
||||||
|
```text
|
||||||
|
../meshes/<link>_visual.stl
|
||||||
|
../meshes/<link>_collision.stl
|
||||||
|
../meshes/collision_simplified/<link>_box_collision.stl
|
||||||
|
```
|
||||||
|
|
||||||
|
不要写机器相关的绝对路径或旧的 `source/gen2_lab` 路径。
|
||||||
|
|
||||||
|
## 重新生成简化碰撞体
|
||||||
|
|
||||||
|
生成脚本不会覆盖 `robot.urdf`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/xtkuang/Projects/cmvr/RL/cmvr_ai_lab
|
||||||
|
|
||||||
|
/home/xtkuang/App/anaconda3/envs/engineai_lab/bin/python \
|
||||||
|
scripts/gen2_generate_simplified_collisions.py
|
||||||
|
```
|
||||||
|
|
||||||
|
它会更新两份 simplified URDF 和 `meshes/collision_simplified/`,审计 CSV 写入 `outputs/gen2_asset_audit/`。
|
||||||
|
|
||||||
|
生成后必须执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/home/xtkuang/App/anaconda3/envs/engineai_lab/bin/python \
|
||||||
|
scripts/gen2_check_rl_readiness.py
|
||||||
|
|
||||||
|
/home/xtkuang/App/anaconda3/envs/engineai_lab/bin/python \
|
||||||
|
scripts/gen2_visualize_collisions.py
|
||||||
|
```
|
||||||
|
|
||||||
|
静态检查期望 `RESULT=PASS`,且 pelvis/torso 高度目标不能显示 `unknown`。可视化 PNG 写入 `outputs/gen2_asset_audit/`,不写回资产包。
|
||||||
|
|
||||||
|
## 修改约束
|
||||||
|
|
||||||
|
1. `robot.urdf` 是生成源,先在这里确认 link、joint、limit、mass、inertia 和 mesh。
|
||||||
|
2. 不要直接手改生成的 simplified URDF 后忘记同步生成脚本。
|
||||||
|
3. 训练前检查 29 个 link、28 个 joint、全部 mesh 引用和 q0 脚底高度。
|
||||||
|
4. 历史 `logs/**/params/env.yaml` 中的旧绝对路径只是训练快照,不要批量改写。
|
||||||
|
5. 供应商原始压缩包放在仓库 `third_party/gen2/`,不进入运行时 wheel。
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user